emacs-diffs
[Top][All Lists]
Advanced

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

master 751f170 2/2: Add new functions to replace strings/regexp in a reg


From: Lars Ingebrigtsen
Subject: master 751f170 2/2: Add new functions to replace strings/regexp in a region
Date: Mon, 16 Aug 2021 07:20:49 -0400 (EDT)

branch: master
commit 751f1707f009c714dbfe047ef43443a5c0c3df89
Author: Lars Ingebrigtsen <larsi@gnus.org>
Commit: Lars Ingebrigtsen <larsi@gnus.org>

    Add new functions to replace strings/regexp in a region
    
    * doc/lispref/searching.texi (Search and Replace): Document them.
    * lisp/subr.el (replace-string-in-region)
    (replace-regexp-in-region): New functions.
    
    * lisp/emacs-lisp/shortdoc.el (regexp, buffer): Mention them.
---
 doc/lispref/searching.texi  | 26 ++++++++++++++-----
 etc/NEWS                    |  6 +++++
 lisp/emacs-lisp/shortdoc.el |  6 +++++
 lisp/subr.el                | 61 +++++++++++++++++++++++++++++++++++++++++++++
 test/lisp/subr-tests.el     | 46 ++++++++++++++++++++++++++++++++++
 5 files changed, 139 insertions(+), 6 deletions(-)

diff --git a/doc/lispref/searching.texi b/doc/lispref/searching.texi
index 1d3e2d9..fe47e7c 100644
--- a/doc/lispref/searching.texi
+++ b/doc/lispref/searching.texi
@@ -2540,9 +2540,9 @@ associated with it still exists.
 @cindex replacement after search
 @cindex searching and replacing
 
-  If you want to find all matches for a regexp in part of the buffer,
-and replace them, the best way is to write an explicit loop using
-@code{re-search-forward} and @code{replace-match}, like this:
+  If you want to find all matches for a regexp in part of the buffer
+and replace them, the most flexible way is to write an explicit loop
+using @code{re-search-forward} and @code{replace-match}, like this:
 
 @example
 (while (re-search-forward "foo[ \t]+bar" nil t)
@@ -2553,9 +2553,23 @@ and replace them, the best way is to write an explicit 
loop using
 @xref{Replacing Match,, Replacing the Text that Matched}, for a
 description of @code{replace-match}.
 
-  However, replacing matches in a string is more complex, especially
-if you want to do it efficiently.  So Emacs provides two functions to do
-this.
+@findex replace-regexp-in-region
+  If it's more convenient, you can also use the
+@code{replace-regexp-in-region}, which does something similar to the
+loop above, but is optionally delimited to a specific region (and
+doesn't change point).  Furthermore, it does the searches
+case-sensitively, and performs the replacements without changing case
+in the replacement.
+
+@example
+(replace-regexp-in-region "foo[ \t]+bar" "foobar")
+@end example
+
+@findex replace-string-in-region
+  There's also @code{replace-string-in-region}, which works along the
+same lines, but searches for literal strings instead.
+
+  Emacs also has special functions for replacing matches in a string.
 
 @defun replace-regexp-in-string regexp rep string &optional fixedcase literal 
subexp start
 This function copies @var{string} and searches it for matches for
diff --git a/etc/NEWS b/etc/NEWS
index c2b53e4..54168e8 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -2443,6 +2443,12 @@ images are marked.
 
 ** Miscellaneous
 
++++
+*** New function 'replace-regexp-in-region'.
+
++++
+*** New function 'replace-string-in-region'.
+
 ---
 *** New function 'mail-header-parse-addresses-lax'.
 This takes a comma-separated string and returns a list of mail/name
diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc.el
index 1b0fbfd..7d4a69f 100644
--- a/lisp/emacs-lisp/shortdoc.el
+++ b/lisp/emacs-lisp/shortdoc.el
@@ -700,6 +700,8 @@ There can be any number of :example/:result elements."
   (match-substitute-replacement
    :no-eval (match-substitute-replacement "new")
    :eg-result "new")
+  (replace-regexp-in-region
+   :no-value (replace-regexp-in-region "[0-9]+" "Num \\&"))
   "Utilities"
   (regexp-quote
    :eval (regexp-quote "foo.*bar"))
@@ -894,6 +896,10 @@ There can be any number of :example/:result elements."
    :no-value (erase-buffer))
   (insert
    :no-value (insert "This string will be inserted in the buffer\n"))
+  (subst-char-in-region
+   :no-eval "(subst-char-in-region (point-min) (point-max) ?+ ?-)")
+  (replace-string-in-region
+   :no-value (replace-string-in-region "foo" "bar"))
   "Locking"
   (lock-buffer
    :no-value (lock-buffer "/tmp/foo"))
diff --git a/lisp/subr.el b/lisp/subr.el
index 1cae3ee..0a31ef2 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -3859,6 +3859,67 @@ Point in BUFFER will be placed after the inserted text."
     (with-current-buffer buffer
       (insert-buffer-substring current start end))))
 
+(defun replace-string-in-region (string replacement &optional start end)
+  "Replace STRING with REPLACEMENT in the region from START to END.
+The number of replaced occurrences are returned, or nil if STRING
+doesn't exist in the region.
+
+If START is nil, use the current point.  If END is nil, use `point-max'.
+
+Comparisons and replacements are done with fixed case."
+  (if start
+      (when (< start (point-min))
+        (error "Start before start of buffer"))
+    (setq start (point)))
+  (if end
+      (when (> end (point-max))
+        (error "End after end of buffer"))
+    (setq end (point-max)))
+  (save-excursion
+    (let ((matches 0)
+          (case-fold-search nil))
+      (goto-char start)
+      (while (search-forward string end t)
+        (delete-region (match-beginning 0) (match-end 0))
+        (insert replacement)
+        (setq matches (1+ matches)))
+      (and (not (zerop matches))
+           matches))))
+
+(defun replace-regexp-in-region (regexp replacement &optional start end)
+  "Replace REGEXP with REPLACEMENT in the region from START to END.
+The number of replaced occurrences are returned, or nil if REGEXP
+doesn't exist in the region.
+
+If START is nil, use the current point.  If END is nil, use `point-max'.
+
+Comparisons and replacements are done with fixed case.
+
+REPLACEMENT can use the following special elements:
+
+  `\\&' in NEWTEXT means substitute original matched text.
+  `\\N' means substitute what matched the Nth `\\(...\\)'.
+       If Nth parens didn't match, substitute nothing.
+  `\\\\' means insert one `\\'.
+  `\\?' is treated literally."
+  (if start
+      (when (< start (point-min))
+        (error "Start before start of buffer"))
+    (setq start (point)))
+  (if end
+      (when (> end (point-max))
+        (error "End after end of buffer"))
+    (setq end (point-max)))
+  (save-excursion
+    (let ((matches 0)
+          (case-fold-search nil))
+      (goto-char start)
+      (while (re-search-forward regexp end t)
+        (replace-match replacement t)
+        (setq matches (1+ matches)))
+      (and (not (zerop matches))
+           matches))))
+
 (defun yank-handle-font-lock-face-property (face start end)
   "If `font-lock-defaults' is nil, apply FACE as a `face' property.
 START and END denote the start and end of the text to act on.
diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el
index b57982a..21b8a27 100644
--- a/test/lisp/subr-tests.el
+++ b/test/lisp/subr-tests.el
@@ -694,5 +694,51 @@ See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=19350.";
     (should-not (buffer-local-boundp 'test-not-boundp buf))
     (should (buffer-local-boundp 'test-global-boundp buf))))
 
+(ert-deftest test-replace-string-in-region ()
+  (with-temp-buffer
+    (insert "foo bar zot foobar")
+    (should (= (replace-string-in-region "foo" "new" (point-min) (point-max))
+               2))
+    (should (equal (buffer-string) "new bar zot newbar")))
+
+  (with-temp-buffer
+    (insert "foo bar zot foobar")
+    (should (= (replace-string-in-region "foo" "new" (point-min) 14)
+               1))
+    (should (equal (buffer-string) "new bar zot foobar")))
+
+  (with-temp-buffer
+    (insert "foo bar zot foobar")
+    (should-error (replace-string-in-region "foo" "new" (point-min) 30)))
+
+  (with-temp-buffer
+    (insert "Foo bar zot foobar")
+    (should (= (replace-string-in-region "Foo" "new" (point-min))
+               1))
+    (should (equal (buffer-string) "new bar zot foobar"))))
+
+(ert-deftest test-replace-regexp-in-region ()
+  (with-temp-buffer
+    (insert "foo bar zot foobar")
+    (should (= (replace-regexp-in-region "fo+" "new" (point-min) (point-max))
+               2))
+    (should (equal (buffer-string) "new bar zot newbar")))
+
+  (with-temp-buffer
+    (insert "foo bar zot foobar")
+    (should (= (replace-regexp-in-region "fo+" "new" (point-min) 14)
+               1))
+    (should (equal (buffer-string) "new bar zot foobar")))
+
+  (with-temp-buffer
+    (insert "foo bar zot foobar")
+    (should-error (replace-regexp-in-region "fo+" "new" (point-min) 30)))
+
+  (with-temp-buffer
+    (insert "Foo bar zot foobar")
+    (should (= (replace-regexp-in-region "Fo+" "new" (point-min))
+               1))
+    (should (equal (buffer-string) "new bar zot foobar"))))
+
 (provide 'subr-tests)
 ;;; subr-tests.el ends here



reply via email to

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