emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 3dd8763: Add commands 'kill-matching-lines' and 'copy-matching-li


From: Lars Ingebrigtsen
Subject: master 3dd8763: Add commands 'kill-matching-lines' and 'copy-matching-lines'
Date: Tue, 20 Jul 2021 08:11:43 -0400 (EDT)

branch: master
commit 3dd87631fca9384fce9f9a72df02ae55b1d3c946
Author: Earl Hyatt <okamsn@protonmail.com>
Commit: Lars Ingebrigtsen <larsi@gnus.org>

    Add commands 'kill-matching-lines' and 'copy-matching-lines'
    
    * doc/emacs/search.texi: Document these additions.
    * lisp/replace.el:
    Add the commands 'kill-matching-lines' and 'copy-matching-lines'.
    
    'kill-matching-lines' is like 'flush-lines', but adds the lines to the
    kill ring as a single string, keeping line endings.
    'copy-matching-lines' is like 'kill-matching-lines', but only copies
    those lines instead of killing them.
---
 doc/emacs/search.texi |  11 +++++
 etc/NEWS              |   5 ++
 lisp/replace.el       | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 140 insertions(+)

diff --git a/doc/emacs/search.texi b/doc/emacs/search.texi
index e6b066e..a1760ad 100644
--- a/doc/emacs/search.texi
+++ b/doc/emacs/search.texi
@@ -1971,6 +1971,17 @@ it never deletes lines that are only partially contained 
in the region
 (a newline that ends a line counts as part of that line).
 
 If a match is split across lines, this command keeps all those lines.
+
+@findex kill-matching-lines
+@item M-x kill-matching-lines
+Like @code{flush-lines}, but also add the matching lines to the kill
+ring.  The command adds the matching lines to the kill ring as a
+single string, including the newlines that separated the lines.
+
+@findex copy-matching-lines
+@item M-x copy-matching-lines
+Like @code{kill-matching-lines}, but the matching lines are not
+removed from the buffer.
 @end table
 
 @node Search Customizations
diff --git a/etc/NEWS b/etc/NEWS
index 922b2ab..953c952 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -484,6 +484,11 @@ highlighting on heading lines using standard outline 
faces.  This
 works well only when there are no conflicts with faces used by the
 major mode.
 
+** New commands 'copy-matching-lines' and 'kill-matching-lines'.
+These commands are similar to the command 'flush-lines',
+but add the matching lines to the kill ring as a single string,
+including the newlines that separate the lines.
+
 
 * Changes in Specialized Modes and Packages in Emacs 28.1
 
diff --git a/lisp/replace.el b/lisp/replace.el
index ed81097..7e30f1f 100644
--- a/lisp/replace.el
+++ b/lisp/replace.el
@@ -1054,6 +1054,130 @@ also print the number."
                               count))
     count))
 
+(defun kill-matching-lines (regexp &optional rstart rend interactive)
+  "Kill lines containing matches for REGEXP.
+
+When called from Lisp (and usually when called interactively as
+well, see below), applies to the part of the buffer after point.
+The line point is in is killed if and only if it contains a match
+for REGEXP starting after point.
+
+If REGEXP contains upper case characters (excluding those
+preceded by `\\') and `search-upper-case' is non-nil, the
+matching is case-sensitive.
+
+Second and third args RSTART and REND specify the region to
+operate on.  Lines partially contained in this region are killed
+if and only if they contain a match entirely contained in the
+region.
+
+Interactively, in Transient Mark mode when the mark is active,
+operate on the contents of the region.  Otherwise, operate from
+point to the end of (the accessible portion of) the buffer.
+
+If a match is split across lines, all the lines it lies in are
+killed.  They are killed _before_ looking for the next match.
+Hence, a match starting on the same line at which another match
+ended is ignored.
+
+Return the number of killed matching lines.  When called
+interactively, also print the number."
+  (interactive
+   (progn
+     (barf-if-buffer-read-only)
+     (keep-lines-read-args "Kill lines containing match for regexp")))
+  (if rstart
+      (progn
+       (goto-char (min rstart rend))
+       (setq rend (copy-marker (max rstart rend))))
+    (if (and interactive (use-region-p))
+       (setq rstart (region-beginning)
+             rend (copy-marker (region-end)))
+      (setq rstart (point)
+           rend (point-max-marker)))
+    (goto-char rstart))
+  (let ((count 0)
+        (case-fold-search
+        (if (and case-fold-search search-upper-case)
+            (isearch-no-upper-case-p regexp t)
+          case-fold-search)))
+    (save-excursion
+      (while (and (< (point) rend)
+                 (re-search-forward regexp rend t))
+        (unless (zerop count)
+          (setq last-command 'kill-region))
+       (kill-region (save-excursion (goto-char (match-beginning 0))
+                                     (forward-line 0)
+                                     (point))
+                     (progn (forward-line 1) (point)))
+        (setq count (1+ count))))
+    (set-marker rend nil)
+    (when interactive (message (ngettext "Killed %d matching line"
+                                        "Killed %d matching lines"
+                                        count)
+                              count))
+    count))
+
+(defun copy-matching-lines (regexp &optional rstart rend interactive)
+  "Copy lines containing matches for REGEXP to the kill ring.
+
+When called from Lisp (and usually when called interactively as
+well, see below), applies to the part of the buffer after point.
+The line point is in is copied if and only if it contains a match
+for REGEXP starting after point.
+
+If REGEXP contains upper case characters (excluding those
+preceded by `\\') and `search-upper-case' is non-nil, the
+matching is case-sensitive.
+
+Second and third args RSTART and REND specify the region to
+operate on.  Lines partially contained in this region are copied
+if and only if they contain a match entirely contained in the
+region.
+
+Interactively, in Transient Mark mode when the mark is active,
+operate on the contents of the region.  Otherwise, operate from
+point to the end of (the accessible portion of) the buffer.
+
+If a match is split across lines, all the lines it lies in are
+copied.
+
+Return the number of copied matching lines.  When called
+interactively, also print the number."
+  (interactive
+   (keep-lines-read-args "Copy lines containing match for regexp"))
+  (if rstart
+      (progn
+       (goto-char (min rstart rend))
+       (setq rend (copy-marker (max rstart rend))))
+    (if (and interactive (use-region-p))
+       (setq rstart (region-beginning)
+             rend (copy-marker (region-end)))
+      (setq rstart (point)
+           rend (point-max-marker)))
+    (goto-char rstart))
+  (let ((count 0)
+        (case-fold-search
+        (if (and case-fold-search search-upper-case)
+            (isearch-no-upper-case-p regexp t)
+          case-fold-search)))
+    (save-excursion
+      (while (and (< (point) rend)
+                 (re-search-forward regexp rend t))
+       (unless (zerop count)
+          (setq last-command 'kill-region))
+       (copy-region-as-kill (save-excursion (goto-char (match-beginning 0))
+                                             (forward-line 0)
+                                             (point))
+                             (progn (forward-line 1) (point)))
+        (setq count (1+ count))))
+    (set-marker rend nil)
+    (when interactive (message (ngettext "Copied %d matching line"
+                                        "Copied %d matching lines"
+                                        count)
+                              count))
+    count))
+
 (defun how-many (regexp &optional rstart rend interactive)
   "Print and return number of matches for REGEXP following point.
 When called from Lisp and INTERACTIVE is omitted or nil, just return



reply via email to

[Prev in Thread] Current Thread [Next in Thread]