Commit 3ee4cd64 authored by Juri Linkov's avatar Juri Linkov
Browse files

query-replace backward

* lisp/replace.el (query-replace-read-args): Split a non-negative arg
and a negative arg into separate elements.
(query-replace, query-replace-regexp, replace-string)
(replace-regexp): Add arg `backward'.  Doc fix.
(replace-match-maybe-edit): When new arg `backward' is non-nil,
move point to the beginning of the match.
(replace-search, replace-highlight): Use new arg `backward'
to set the value of `isearch-forward'.
(perform-replace): Add arg `backward' and use it to perform
replacement backward. 

* lisp/isearch.el (isearch-query-replace): Use a negative prefix arg
to call `perform-replace' with a non-nil arg `backward'.

Fixes: debbugs:14979
parent 0cda6b7b
......@@ -507,6 +507,10 @@ when it's nil).
*** `query-replace' skips invisible text when `search-invisible' is nil,
and opens overlays with hidden text when `search-invisible' is `open'.
*** A negative prefix arg of replacement commands replaces backward.
`M-- M-%' replaces a string backward, `M-- C-M-%' replaces a regexp
backward, `M-s w words M-- M-%' replaces a sequence of words backward.
+++
*** By default, prefix arguments do not now terminate Isearch mode.
Set `isearch-allow-prefix' to nil to restore old behavior.
......
2013-12-19 Juri Linkov <juri@jurta.org>
* replace.el (query-replace-read-args): Split a non-negative arg
and a negative arg into separate elements.
(query-replace, query-replace-regexp, replace-string)
(replace-regexp): Add arg `backward'. Doc fix.
(replace-match-maybe-edit): When new arg `backward' is non-nil,
move point to the beginning of the match.
(replace-search, replace-highlight): Use new arg `backward'
to set the value of `isearch-forward'.
(perform-replace): Add arg `backward' and use it to perform
replacement backward. (Bug#14979)
* isearch.el (isearch-query-replace): Use a negative prefix arg
to call `perform-replace' with a non-nil arg `backward'.
2013-12-18 Juri Linkov <juri@jurta.org>
* vc/log-edit.el (log-edit-hook): Add `log-edit-insert-message-template'
......
......@@ -1667,10 +1667,11 @@ the beginning or the end of the string need not match a symbol boundary."
(re-search-backward regexp bound noerror count)))
(defun isearch-query-replace (&optional delimited regexp-flag)
(defun isearch-query-replace (&optional arg regexp-flag)
"Start `query-replace' with string to replace from last search string.
The arg DELIMITED (prefix arg if interactive), if non-nil, means replace
only matches surrounded by word boundaries. Note that using the prefix arg
The ARG (prefix arg if interactive), if non-nil, means replace
only matches surrounded by word boundaries. A negative prefix
arg means replace backward. Note that using the prefix arg
is possible only when `isearch-allow-scroll' is non-nil or
`isearch-allow-prefix' is non-nil, and it doesn't always provide the
correct matches for `query-replace', so the preferred way to run word
......@@ -1688,6 +1689,8 @@ replacements from Isearch is `M-s w ... M-%'."
isearch-lax-whitespace)
(replace-regexp-lax-whitespace
isearch-regexp-lax-whitespace)
(delimited (and arg (not (eq arg '-))))
(backward (and arg (eq arg '-)))
;; Set `isearch-recursive-edit' to nil to prevent calling
;; `exit-recursive-edit' in `isearch-done' that terminates
;; the execution of this command when it is non-nil.
......@@ -1696,9 +1699,13 @@ replacements from Isearch is `M-s w ... M-%'."
(isearch-done nil t)
(isearch-clean-overlays)
(if (and isearch-other-end
(< isearch-other-end (point))
(if backward
(> isearch-other-end (point))
(< isearch-other-end (point)))
(not (and transient-mark-mode mark-active
(< (mark) (point)))))
(if backward
(> (mark) (point))
(< (mark) (point))))))
(goto-char isearch-other-end))
(set query-replace-from-history-variable
(cons isearch-string
......@@ -1718,19 +1725,21 @@ replacements from Isearch is `M-s w ... M-%'."
" word"))
"")
(if isearch-regexp " regexp" "")
(if backward " backward" "")
(if (and transient-mark-mode mark-active) " in region" ""))
isearch-regexp)
t isearch-regexp (or delimited isearch-word) nil nil
(if (and transient-mark-mode mark-active) (region-beginning))
(if (and transient-mark-mode mark-active) (region-end))))
(if (and transient-mark-mode mark-active) (region-end))
backward))
(and isearch-recursive-edit (exit-recursive-edit)))
(defun isearch-query-replace-regexp (&optional delimited)
(defun isearch-query-replace-regexp (&optional arg)
"Start `query-replace-regexp' with string to replace from last search string.
See `isearch-query-replace' for more information."
(interactive
(list current-prefix-arg))
(isearch-query-replace delimited t))
(isearch-query-replace arg t))
(defun isearch-occur (regexp &optional nlines)
"Run `occur' using the last search string as the regexp.
......
......@@ -226,9 +226,11 @@ the original string if not."
(let* ((from (query-replace-read-from prompt regexp-flag))
(to (if (consp from) (prog1 (cdr from) (setq from (car from)))
(query-replace-read-to from prompt regexp-flag))))
(list from to current-prefix-arg)))
(list from to
(and current-prefix-arg (not (eq current-prefix-arg '-)))
(and current-prefix-arg (eq current-prefix-arg '-)))))
(defun query-replace (from-string to-string &optional delimited start end)
(defun query-replace (from-string to-string &optional delimited start end backward)
"Replace some occurrences of FROM-STRING with TO-STRING.
As each match is found, the user must type a character saying
what to do with it. For directions, type \\[help-command] at that time.
......@@ -259,7 +261,9 @@ to be replaced will match a sequence of whitespace chars defined by the
regexp in `search-whitespace-regexp'.
Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
only matches surrounded by word boundaries.
only matches surrounded by word boundaries. A negative prefix arg means
replace backward.
Fourth and fifth arg START and END specify the region to operate on.
To customize possible responses, change the \"bindings\" in `query-replace-map'."
......@@ -267,7 +271,9 @@ To customize possible responses, change the \"bindings\" in `query-replace-map'.
(let ((common
(query-replace-read-args
(concat "Query replace"
(if current-prefix-arg " word" "")
(if current-prefix-arg
(if (eq current-prefix-arg '-) " backward" " word")
"")
(if (and transient-mark-mode mark-active) " in region" ""))
nil)))
(list (nth 0 common) (nth 1 common) (nth 2 common)
......@@ -277,12 +283,13 @@ To customize possible responses, change the \"bindings\" in `query-replace-map'.
(if (and transient-mark-mode mark-active)
(region-beginning))
(if (and transient-mark-mode mark-active)
(region-end)))))
(perform-replace from-string to-string t nil delimited nil nil start end))
(region-end))
(nth 3 common))))
(perform-replace from-string to-string t nil delimited nil nil start end backward))
(define-key esc-map "%" 'query-replace)
(defun query-replace-regexp (regexp to-string &optional delimited start end)
(defun query-replace-regexp (regexp to-string &optional delimited start end backward)
"Replace some things after point matching REGEXP with TO-STRING.
As each match is found, the user must type a character saying
what to do with it. For directions, type \\[help-command] at that time.
......@@ -313,7 +320,9 @@ to be replaced will match a sequence of whitespace chars defined by the
regexp in `search-whitespace-regexp'.
Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
only matches surrounded by word boundaries.
only matches surrounded by word boundaries. A negative prefix arg means
replace backward.
Fourth and fifth arg START and END specify the region to operate on.
In TO-STRING, `\\&' stands for whatever matched the whole of REGEXP,
......@@ -341,7 +350,9 @@ Use \\[repeat-complex-command] after this command for details."
(let ((common
(query-replace-read-args
(concat "Query replace"
(if current-prefix-arg " word" "")
(if current-prefix-arg
(if (eq current-prefix-arg '-) " backward" " word")
"")
" regexp"
(if (and transient-mark-mode mark-active) " in region" ""))
t)))
......@@ -352,8 +363,9 @@ Use \\[repeat-complex-command] after this command for details."
(if (and transient-mark-mode mark-active)
(region-beginning))
(if (and transient-mark-mode mark-active)
(region-end)))))
(perform-replace regexp to-string t t delimited nil nil start end))
(region-end))
(nth 3 common))))
(perform-replace regexp to-string t t delimited nil nil start end backward))
(define-key esc-map [?\C-%] 'query-replace-regexp)
......@@ -475,7 +487,7 @@ Fourth and fifth arg START and END specify the region to operate on."
to-strings ""))))
(perform-replace regexp replacements t t nil n nil start end)))
(defun replace-string (from-string to-string &optional delimited start end)
(defun replace-string (from-string to-string &optional delimited start end backward)
"Replace occurrences of FROM-STRING with TO-STRING.
Preserve case in each match if `case-replace' and `case-fold-search'
are non-nil and FROM-STRING has no uppercase letters.
......@@ -491,7 +503,8 @@ to be replaced will match a sequence of whitespace chars defined by the
regexp in `search-whitespace-regexp'.
Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
only matches surrounded by word boundaries.
only matches surrounded by word boundaries. A negative prefix arg means
replace backward.
Operates on the region between START and END (if both are nil, from point
to the end of the buffer). Interactively, if Transient Mark mode is
......@@ -513,7 +526,9 @@ and TO-STRING is also null.)"
(let ((common
(query-replace-read-args
(concat "Replace"
(if current-prefix-arg " word" "")
(if current-prefix-arg
(if (eq current-prefix-arg '-) " backward" " word")
"")
" string"
(if (and transient-mark-mode mark-active) " in region" ""))
nil)))
......@@ -521,12 +536,13 @@ and TO-STRING is also null.)"
(if (and transient-mark-mode mark-active)
(region-beginning))
(if (and transient-mark-mode mark-active)
(region-end)))))
(perform-replace from-string to-string nil nil delimited nil nil start end))
(region-end))
(nth 3 common))))
(perform-replace from-string to-string nil nil delimited nil nil start end backward))
(put 'replace-string 'interactive-only
"use `search-forward' and `replace-match' instead.")
(defun replace-regexp (regexp to-string &optional delimited start end)
(defun replace-regexp (regexp to-string &optional delimited start end backward)
"Replace things after point matching REGEXP with TO-STRING.
Preserve case in each match if `case-replace' and `case-fold-search'
are non-nil and REGEXP has no uppercase letters.
......@@ -543,7 +559,9 @@ In Transient Mark mode, if the mark is active, operate on the contents
of the region. Otherwise, operate from point to the end of the buffer.
Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
only matches surrounded by word boundaries.
only matches surrounded by word boundaries. A negative prefix arg means
replace backward.
Fourth and fifth arg START and END specify the region to operate on.
In TO-STRING, `\\&' stands for whatever matched the whole of REGEXP,
......@@ -582,7 +600,9 @@ which will run faster and will not set the mark or print anything."
(let ((common
(query-replace-read-args
(concat "Replace"
(if current-prefix-arg " word" "")
(if current-prefix-arg
(if (eq current-prefix-arg '-) " backward" " word")
"")
" regexp"
(if (and transient-mark-mode mark-active) " in region" ""))
t)))
......@@ -590,8 +610,9 @@ which will run faster and will not set the mark or print anything."
(if (and transient-mark-mode mark-active)
(region-beginning))
(if (and transient-mark-mode mark-active)
(region-end)))))
(perform-replace regexp to-string nil t delimited nil nil start end))
(region-end))
(nth 3 common))))
(perform-replace regexp to-string nil t delimited nil nil start end backward))
(put 'replace-regexp 'interactive-only
"use `re-search-forward' and `replace-match' instead.")
......@@ -1849,7 +1870,7 @@ but coerced to the correct value of INTEGERS."
new)))
(match-data integers reuse t)))
(defun replace-match-maybe-edit (newtext fixedcase literal noedit match-data)
(defun replace-match-maybe-edit (newtext fixedcase literal noedit match-data backward)
"Make a replacement with `replace-match', editing `\\?'.
NEWTEXT, FIXEDCASE, LITERAL are just passed on. If NOEDIT is true, no
check for `\\?' is made to save time. MATCH-DATA is used for the
......@@ -1873,6 +1894,9 @@ passed in. If LITERAL is set, no checking is done, anyway."
noedit nil)))
(set-match-data match-data)
(replace-match newtext fixedcase literal)
;; `replace-match' leaves point at the end of the replacement text,
;; so move point to the beginning when replacing backward.
(when backward (goto-char (nth 0 match-data)))
noedit)
(defvar replace-search-function nil
......@@ -1888,7 +1912,7 @@ It is called with three arguments, as if it were
`re-search-forward'.")
(defun replace-search (search-string limit regexp-flag delimited-flag
case-fold-search)
case-fold-search backward)
"Search for the next occurrence of SEARCH-STRING to replace."
;; Let-bind global isearch-* variables to values used
;; to search the next replacement. These let-bindings
......@@ -1907,7 +1931,7 @@ It is called with three arguments, as if it were
(isearch-case-fold-search case-fold-search)
(isearch-adjusted nil)
(isearch-nonincremental t) ; don't use lax word mode
(isearch-forward t)
(isearch-forward (not backward))
(search-function
(or (if regexp-flag
replace-re-search-function
......@@ -1919,7 +1943,7 @@ It is called with three arguments, as if it were
(defun replace-highlight (match-beg match-end range-beg range-end
search-string regexp-flag delimited-flag
case-fold-search)
case-fold-search backward)
(if query-replace-highlight
(if replace-overlay
(move-overlay replace-overlay match-beg match-end (current-buffer))
......@@ -1935,7 +1959,7 @@ It is called with three arguments, as if it were
(isearch-regexp-lax-whitespace
replace-regexp-lax-whitespace)
(isearch-case-fold-search case-fold-search)
(isearch-forward t)
(isearch-forward (not backward))
(isearch-other-end match-beg)
(isearch-error nil))
(isearch-lazy-highlight-new-loop range-beg range-end))))
......@@ -1951,7 +1975,7 @@ It is called with three arguments, as if it were
(defun perform-replace (from-string replacements
query-flag regexp-flag delimited-flag
&optional repeat-count map start end)
&optional repeat-count map start end backward)
"Subroutine of `query-replace'. Its complexity handles interactive queries.
Don't use this in your own program unless you want to query and set the mark
just as `query-replace' does. Instead, write a simple loop like this:
......@@ -2005,10 +2029,15 @@ make, or the user didn't cancel the call."
minibuffer-prompt-properties))))
;; If region is active, in Transient Mark mode, operate on region.
(when start
(setq limit (copy-marker (max start end)))
(goto-char (min start end))
(deactivate-mark))
(if backward
(when end
(setq limit (copy-marker (min start end)))
(goto-char (max start end))
(deactivate-mark))
(when start
(setq limit (copy-marker (max start end)))
(goto-char (min start end))
(deactivate-mark)))
;; If last typed key in previous call of multi-buffer perform-replace
;; was `automatic-all', don't ask more questions in next files
......@@ -2038,13 +2067,17 @@ make, or the user didn't cancel the call."
(unwind-protect
;; Loop finding occurrences that perhaps should be replaced.
(while (and keep-going
(not (or (eobp) (and limit (>= (point) limit))))
(if backward
(not (or (bobp) (and limit (<= (point) limit))))
(not (or (eobp) (and limit (>= (point) limit)))))
;; Use the next match if it is already known;
;; otherwise, search for a match after moving forward
;; one char if progress is required.
(setq real-match-data
(cond ((consp match-again)
(goto-char (nth 1 match-again))
(goto-char (if backward
(nth 0 match-again)
(nth 1 match-again)))
(replace-match-data
t real-match-data match-again))
;; MATCH-AGAIN non-nil means accept an
......@@ -2053,22 +2086,26 @@ make, or the user didn't cancel the call."
(and
(replace-search search-string limit
regexp-flag delimited-flag
case-fold-search)
case-fold-search backward)
;; For speed, use only integers and
;; reuse the list used last time.
(replace-match-data t real-match-data)))
((and (< (1+ (point)) (point-max))
((and (if backward
(> (1- (point)) (point-min))
(< (1+ (point)) (point-max)))
(or (null limit)
(< (1+ (point)) limit)))
(if backward
(> (1- (point)) limit)
(< (1+ (point)) limit))))
;; If not accepting adjacent matches,
;; move one char to the right before
;; searching again. Undo the motion
;; if the search fails.
(let ((opoint (point)))
(forward-char 1)
(forward-char (if backward -1 1))
(if (replace-search search-string limit
regexp-flag delimited-flag
case-fold-search)
case-fold-search backward)
(replace-match-data
t real-match-data)
(goto-char opoint)
......@@ -2089,7 +2126,9 @@ make, or the user didn't cancel the call."
(setq match-again
(and nonempty-match
(or (not regexp-flag)
(and (looking-at search-string)
(and (if backward
(looking-back search-string)
(looking-at search-string))
(let ((match (match-data)))
(and (/= (nth 0 match) (nth 1 match))
match))))))
......@@ -2126,11 +2165,11 @@ make, or the user didn't cancel the call."
(replace-highlight
(nth 0 real-match-data) (nth 1 real-match-data)
start end search-string
regexp-flag delimited-flag case-fold-search))
regexp-flag delimited-flag case-fold-search backward))
(setq noedit
(replace-match-maybe-edit
next-replacement nocasify literal
noedit real-match-data)
noedit real-match-data backward)
replace-count (1+ replace-count)))
(undo-boundary)
(let (done replaced key def)
......@@ -2145,7 +2184,7 @@ make, or the user didn't cancel the call."
(replace-highlight
(match-beginning 0) (match-end 0)
start end search-string
regexp-flag delimited-flag case-fold-search)
regexp-flag delimited-flag case-fold-search backward)
;; Bind message-log-max so we don't fill up the message log
;; with a bunch of identical messages.
(let ((message-log-max nil)
......@@ -2175,6 +2214,7 @@ make, or the user didn't cancel the call."
(get delimited-flag 'isearch-message-prefix))
"word ") "")
(if regexp-flag "regexp " "")
(if backward "backward " "")
from-string " with "
next-replacement ".\n\n"
(substitute-command-keys
......@@ -2203,7 +2243,7 @@ make, or the user didn't cancel the call."
(setq noedit
(replace-match-maybe-edit
next-replacement nocasify literal
noedit real-match-data)
noedit real-match-data backward)
replace-count (1+ replace-count)))
(setq done t replaced t))
((eq def 'act-and-exit)
......@@ -2211,7 +2251,7 @@ make, or the user didn't cancel the call."
(setq noedit
(replace-match-maybe-edit
next-replacement nocasify literal
noedit real-match-data)
noedit real-match-data backward)
replace-count (1+ replace-count)))
(setq keep-going nil)
(setq done t replaced t))
......@@ -2220,7 +2260,7 @@ make, or the user didn't cancel the call."
(setq noedit
(replace-match-maybe-edit
next-replacement nocasify literal
noedit real-match-data)
noedit real-match-data backward)
replace-count (1+ replace-count)
real-match-data (replace-match-data
t real-match-data)
......@@ -2230,7 +2270,7 @@ make, or the user didn't cancel the call."
(setq noedit
(replace-match-maybe-edit
next-replacement nocasify literal
noedit real-match-data)
noedit real-match-data backward)
replace-count (1+ replace-count)))
(setq done t query-flag nil replaced t)
(if (eq def 'automatic-all) (setq multi-buffer t)))
......@@ -2274,7 +2314,7 @@ make, or the user didn't cancel the call."
(setq noedit
(replace-match-maybe-edit
next-replacement nocasify literal noedit
real-match-data)
real-match-data backward)
replaced t))
(setq done t))
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment