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.
This commit is contained in:
Lars Ingebrigtsen 2021-08-16 13:20:35 +02:00
parent 42be416578
commit 751f1707f0
5 changed files with 139 additions and 6 deletions

View file

@ -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

View file

@ -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

View file

@ -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"))

View file

@ -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.

View file

@ -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