GNU bug report logs - #14979
24.3; Feature Request: query-replace-backward

Previous Next

Package: emacs;

Reported by: Ben A. <ben.a <at> gmx.us>

Date: Mon, 29 Jul 2013 06:32:01 UTC

Severity: wishlist

Found in version 24.3

Done: Juri Linkov <juri <at> jurta.org>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 14979 in the body.
You can then email your comments to 14979 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-gnu-emacs <at> gnu.org:
bug#14979; Package emacs. (Mon, 29 Jul 2013 06:32:01 GMT) Full text and rfc822 format available.

Acknowledgement sent to Ben A. <ben.a <at> gmx.us>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org. (Mon, 29 Jul 2013 06:32:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Ben A. <ben.a <at> gmx.us>
To: bug-gnu-emacs <at> gnu.org
Subject: 24.3; Feature Request: query-replace-backward
Date: Sun, 28 Jul 2013 23:10:25 -0400
Hi,

A feature request: I often make use of query-replace and query-replace-regexp.  It would be useful to have a query-replace-backward and a query-replace-regexp-backward.  The backward variant would be executed when the forward variant is given a negative argument.

Much Appreciation,
Ben




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#14979; Package emacs. (Mon, 29 Jul 2013 07:12:02 GMT) Full text and rfc822 format available.

Message #8 received at 14979 <at> debbugs.gnu.org (full text, mbox):

From: Jambunathan K <kjambunathan <at> gmail.com>
To: Ben A. <ben.a <at> gmx.us>
Cc: 14979 <at> debbugs.gnu.org
Subject: Re: bug#14979: 24.3; Feature Request: query-replace-backward
Date: Mon, 29 Jul 2013 12:42:49 +0530
Ben A. <ben.a <at> gmx.us> writes:

> Hi,
>
> A feature request: I often make use of query-replace and
> query-replace-regexp.  It would be useful to have a
> query-replace-backward and a query-replace-regexp-backward.  The
> backward variant would be executed when the forward variant is given a
> negative argument.

One way of achieving this with the existing scheme of things is:

  1. M-<
  2. C-x n n
  3. C-M-%
  4. M->
  6. C-x n w

Narrow from beginning of buffer (or a defun or wherever) to point, do
replacement and widen.

(I think) forward searches are more efficient than backward searches
through the buffer.

May I know what is your specific usage scenario?  Are you editing a text
file or a program source?  Why is that you want only backward
replacements and STRICTLY AVOID forward replacements.

>
> Much Appreciation,
> Ben




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#14979; Package emacs. (Mon, 29 Jul 2013 07:15:02 GMT) Full text and rfc822 format available.

Message #11 received at 14979 <at> debbugs.gnu.org (full text, mbox):

From: Jambunathan K <kjambunathan <at> gmail.com>
To: Ben A. <ben.a <at> gmx.us>
Cc: 14979 <at> debbugs.gnu.org
Subject: Re: bug#14979: 24.3; Feature Request: query-replace-backward
Date: Mon, 29 Jul 2013 12:46:03 +0530
Jambunathan K <kjambunathan <at> gmail.com> writes:

>   1. M-<
>   2. C-x n n
>   3. C-M-%
>   4. M->
>   6. C-x n w

Forgot step 0,

0. C-SPC




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#14979; Package emacs. (Mon, 29 Jul 2013 08:31:02 GMT) Full text and rfc822 format available.

Message #14 received at 14979 <at> debbugs.gnu.org (full text, mbox):

From: Juri Linkov <juri <at> jurta.org>
To: Jambunathan K <kjambunathan <at> gmail.com>
Cc: "Ben A." <ben.a <at> gmx.us>, 14979 <at> debbugs.gnu.org
Subject: Re: bug#14979: 24.3; Feature Request: query-replace-backward
Date: Mon, 29 Jul 2013 11:22:47 +0300
[Message part 1 (text/plain, inline)]
>>   1. M-<
>>   2. C-x n n
>>   3. C-M-%
>>   4. M->
>>   6. C-x n w
>
> Forgot step 0,
>
> 0. C-SPC

What you described is a feature known in other editors as
"Current selection only".  In Emacs you don't need to use
`C-x n n' and `C-x n w' because `query-replace' limits the
search to the selected region without the need to narrow it.
However, other editors also provide another feature to replace
backwards like you can see in e.g. OpenOffice etc.

[query-replace.png (image/png, inline)]
[Message part 3 (text/plain, inline)]
So this is not a baseless wish to have the same in Emacs.

With the recent introduction of `replace-search' in replace.el
it's straightforward to implement this in Emacs where
`(isearch-forward t)' in `replace-search' could be replaced
with `(isearch-forward replace-forward)' where `replace-forward'
is a new option that can be changed in `isearch-query-replace'
according to the current value of `isearch-forward'.
The benefits of this feature is that it will replace exactly
the same matches that are lazy-highlighted and visited
by the backward isearch.

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#14979; Package emacs. (Mon, 29 Jul 2013 08:46:09 GMT) Full text and rfc822 format available.

Message #17 received at 14979 <at> debbugs.gnu.org (full text, mbox):

From: Jambunathan K <kjambunathan <at> gmail.com>
To: Juri Linkov <juri <at> jurta.org>
Cc: "Ben A." <ben.a <at> gmx.us>, 14979 <at> debbugs.gnu.org
Subject: Re: bug#14979: 24.3; Feature Request: query-replace-backward
Date: Mon, 29 Jul 2013 14:17:03 +0530
Juri Linkov <juri <at> jurta.org> writes:

> because `query-replace' limits the search to the selected region
> without the need to narrow it.

My bad.  I use it all the time.  

It is almost a second nature by now that it requires some effort to
remember it.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#14979; Package emacs. (Mon, 29 Jul 2013 21:06:02 GMT) Full text and rfc822 format available.

Message #20 received at 14979 <at> debbugs.gnu.org (full text, mbox):

From: Juri Linkov <juri <at> jurta.org>
To: Jambunathan K <kjambunathan <at> gmail.com>
Cc: "Ben A." <ben.a <at> gmx.us>, 14979 <at> debbugs.gnu.org
Subject: Re: bug#14979: 24.3; Feature Request: query-replace-backward
Date: Tue, 30 Jul 2013 00:02:55 +0300
> it's straightforward to implement this in Emacs where
> `(isearch-forward t)' in `replace-search' could be replaced
> with `(isearch-forward replace-forward)' where `replace-forward'
> is a new option that can be changed in `isearch-query-replace'
> according to the current value of `isearch-forward'.

Actually using the value of `isearch-forward' to make
decision about the direction of replacements is not good
because often `C-r' is used to find the uppermost match
from which to run forward replacements.

Maybe it would help if after the last replacement
`query-replace' asked whether to wrap and continue
replacements from the beginning of the buffer.
Then no matches will be missed when `query-replace'
happens to be started in the middle of the buffer.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#14979; Package emacs. (Tue, 30 Jul 2013 20:10:02 GMT) Full text and rfc822 format available.

Message #23 received at 14979 <at> debbugs.gnu.org (full text, mbox):

From: ben.a <at> gmx.us
To: Juri Linkov <juri <at> jurta.org>, Jambunathan K <kjambunathan <at> gmail.com>
Cc: 14979 <at> debbugs.gnu.org
Subject: Re: bug#14979: 24.3; Feature Request: query-replace-backward
Date: Tue, 30 Jul 2013 16:09:26 -0400
Thanks for your interest in my bug report.  The use case I had in mind
was changing a couple of instances of a word backwards from the point.
Typing M-- C-M-% is fewer keystrokes than pressing C-r a few times then
C-M-%.  It is also fewer keystrokes than setting the mark, then moving
through the document to find a proper starting point and finally pressing
C-M-%.

The best solution would be if the functions query-replace and
query-replace-regexp are changed so that they step backwards when
receiving a negative argument.

I have come across the website freedomsponsors.org and thought it would
be an interesting experiment to put a prize on fixing this bug.  If
anyone is interested in working on this please visit
http://www.freedomsponsors.org/core/issue/309/243-feature-request-query-replace-backward

Ben




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#14979; Package emacs. (Thu, 01 Aug 2013 21:18:01 GMT) Full text and rfc822 format available.

Message #26 received at 14979 <at> debbugs.gnu.org (full text, mbox):

From: Juri Linkov <juri <at> jurta.org>
To: ben.a <at> gmx.us
Cc: Jambunathan K <kjambunathan <at> gmail.com>, 14979 <at> debbugs.gnu.org
Subject: Re: bug#14979: 24.3; Feature Request: query-replace-backward
Date: Fri, 02 Aug 2013 00:13:06 +0300
> Thanks for your interest in my bug report.  The use case I had in mind
> was changing a couple of instances of a word backwards from the point.
> Typing M-- C-M-% is fewer keystrokes than pressing C-r a few times then
> C-M-%.  It is also fewer keystrokes than setting the mark, then moving
> through the document to find a proper starting point and finally pressing
> C-M-%.
>
> The best solution would be if the functions query-replace and
> query-replace-regexp are changed so that they step backwards when
> receiving a negative argument.

One unclear point: you said that you want to replace words backwards.
Currently a numeric argument or C-u means to replace words forwards
(delimited by word boundary characters).  If a negative argument
will mean normal non-word replacement backwards, then what a prefix
should mean word replacement backwards?  To summarize the question:

no prefix M-% - normal non-word replacement forwards
C-u M-% - word replacement forwards
M-- M-% (negative argument) - normal non-word replacement backwards
??? - word replacement backwards

The same question for regexp replacements:

no prefix C-M-% - regexp replacement forwards
C-u C-M-% - word replacement forwards
M-- C-M-% (negative argument) - regexp replacement backwards
??? - word replacement backwards

> I have come across the website freedomsponsors.org and thought it would
> be an interesting experiment to put a prize on fixing this bug.

Thank you.  It would be nice if your example will encourage more
people to donate money to more difficult tasks in Emacs development.
As I can see freedomsponsors.org is a very promising site since
it supports donations to the development of free software.
This is in stark contrast with freelance sites where
customers pay money for developing highly custom
Emacs packages that are useless for most users.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#14979; Package emacs. (Sat, 03 Aug 2013 20:46:01 GMT) Full text and rfc822 format available.

Message #29 received at 14979 <at> debbugs.gnu.org (full text, mbox):

From: Ben A. <ben.a <at> gmx.us>
To: Juri Linkov <juri <at> jurta.org>
Cc: Jambunathan K <kjambunathan <at> gmail.com>, 14979 <at> debbugs.gnu.org
Subject: Re: bug#14979: 24.3; Feature Request: query-replace-backward
Date: Sat, 03 Aug 2013 16:44:40 -0400
Juri Linkov <juri <at> jurta.org> writes:

> One unclear point: you said that you want to replace words backwards.
> Currently a numeric argument or C-u means to replace words forwards
> (delimited by word boundary characters).  If a negative argument
> will mean normal non-word replacement backwards, then what a prefix
> should mean word replacement backwards?  To summarize the question:
>
> no prefix M-% - normal non-word replacement forwards
> C-u M-% - word replacement forwards
> M-- M-% (negative argument) - normal non-word replacement backwards
> ??? - word replacement backwards
>

A possibility might be:

no prefix M-% - normal non-word replacement forwards
C-u M-% - word replacement forwards
M-0 M-% - normal non-word replacement backwards
M-- M-% - word replacement backwards

this is somewhat analogous to the behavior of kill-line.

Ben




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#14979; Package emacs. (Sat, 03 Aug 2013 21:25:02 GMT) Full text and rfc822 format available.

Message #32 received at 14979 <at> debbugs.gnu.org (full text, mbox):

From: Drew Adams <drew.adams <at> oracle.com>
To: "Ben A." <ben.a <at> gmx.us>, Juri Linkov <juri <at> jurta.org>
Cc: Jambunathan K <kjambunathan <at> gmail.com>, 14979 <at> debbugs.gnu.org
Subject: RE: bug#14979: 24.3; Feature Request: query-replace-backward
Date: Sat, 3 Aug 2013 14:24:35 -0700 (PDT)
> > no prefix M-% - normal non-word replacement forwards
> > C-u M-% - word replacement forwards
> > M-- M-% (negative argument) - normal non-word replacement backwards
> > ??? - word replacement backwards
> 
> no prefix M-% - normal non-word replacement forwards
> C-u M-% - word replacement forwards
> M-0 M-% - normal non-word replacement backwards
> M-- M-% - word replacement backwards

I vote against using a prefix arg for anything like this.  There are
better things to use a prefix arg for.

Instead, either choose a different key altogether for backward q-r or
do not bind a key for it at all.

Just one opinion.


FWIW, I bind `M-%' to a command that rolls together these q-r
behaviors:

* No prefix arg: string q-r (like vanilla `query-replace')
* Negative prefix arg: regexp q-r
* Non-negative prefix arg: word q-r (like vanilla `query-replace')




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#14979; Package emacs. (Tue, 17 Dec 2013 19:42:02 GMT) Full text and rfc822 format available.

Message #35 received at 14979 <at> debbugs.gnu.org (full text, mbox):

From: Juri Linkov <juri <at> jurta.org>
To: ben.a <at> gmx.us
Cc: 14979 <at> debbugs.gnu.org
Subject: Re: bug#14979: 24.3; Feature Request: query-replace-backward
Date: Tue, 17 Dec 2013 21:35:32 +0200
> Typing M-- C-M-% is fewer keystrokes than pressing C-r a few times then
> C-M-%.  It is also fewer keystrokes than setting the mark, then moving
> through the document to find a proper starting point and finally pressing
> C-M-%.
>
> The best solution would be if the functions query-replace and
> query-replace-regexp are changed so that they step backwards when
> receiving a negative argument.

The following patch implements replace backward with this UI:

M-- M-%         - replace string backward
M-- C-M-%       - replace regexp backward
M-s w M-- M-%   - replace words backward
M-s _ M-- M-%   - replace symbols backward

=== modified file 'lisp/replace.el'
--- lisp/replace.el	2013-11-30 08:42:28 +0000
+++ lisp/replace.el	2013-12-17 19:34:11 +0000
@@ -226,9 +226,11 @@ (defun query-replace-read-args (prompt r
   (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 @@ (defun query-replace (from-string to-str
 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.  The negative prefix arg `-'
+means replacing 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 @@ (defun query-replace (from-string to-str
    (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 @@ (defun query-replace (from-string to-str
 	   (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 @@ (defun query-replace-regexp (regexp to-s
 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.  The negative prefix arg `-'
+means replacing 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 @@ (defun query-replace-regexp (regexp to-s
    (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 @@ (defun query-replace-regexp (regexp to-s
 	   (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 @@ (defun map-query-replace-regexp (regexp
 		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 @@ (defun replace-string (from-string to-st
 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.  The negative prefix arg `-'
+means replacing 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 @@ (defun replace-string (from-string to-st
    (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 @@ (defun replace-string (from-string to-st
 	   (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 @@ (defun replace-regexp (regexp to-string
 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.  The negative prefix arg `-'
+means replacing 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 @@ (defun replace-regexp (regexp to-string
    (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 @@ (defun replace-regexp (regexp to-string
 	   (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.")
 
@@ -1847,7 +1868,7 @@ (defun replace-match-data (integers reus
 		  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
@@ -1871,6 +1892,9 @@ (defun replace-match-maybe-edit (newtext
 	    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
@@ -1886,7 +1910,7 @@ (defvar replace-re-search-function nil
 `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
@@ -1905,7 +1929,7 @@ (defun replace-search (search-string lim
 	 (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
@@ -1917,7 +1941,7 @@ (defvar replace-overlay nil)
 
 (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))
@@ -1933,7 +1957,7 @@ (defun replace-highlight (match-beg matc
 	    (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))))
@@ -1949,7 +1973,7 @@ (defun replace-dehighlight ()
 
 (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:
@@ -2003,10 +2027,15 @@ (defun perform-replace (from-string repl
                      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
@@ -2036,13 +2065,17 @@ (defun perform-replace (from-string repl
     (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
@@ -2051,22 +2084,26 @@ (defun perform-replace (from-string repl
 				 (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)
@@ -2087,7 +2124,9 @@ (defun perform-replace (from-string repl
 	  (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))))))
@@ -2124,11 +2163,11 @@ (defun perform-replace (from-string repl
 		    (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)
@@ -2143,7 +2182,7 @@ (defun perform-replace (from-string repl
 		  (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)
@@ -2173,6 +2212,7 @@ (defun perform-replace (from-string repl
 						 (get delimited-flag 'isearch-message-prefix))
 					    "word ") "")
 				    (if regexp-flag "regexp " "")
+				    (if backward "backward " "")
 				    from-string " with "
 				    next-replacement ".\n\n"
 				    (substitute-command-keys
@@ -2201,7 +2241,7 @@ (defun perform-replace (from-string repl
 			     (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)
@@ -2209,7 +2249,7 @@ (defun perform-replace (from-string repl
 			     (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))
@@ -2218,7 +2258,7 @@ (defun perform-replace (from-string repl
 			     (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)
@@ -2228,7 +2268,7 @@ (defun perform-replace (from-string repl
 			     (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)))
@@ -2272,7 +2312,7 @@ (defun perform-replace (from-string repl
 			   (setq noedit
 				 (replace-match-maybe-edit
 				  next-replacement nocasify literal noedit
-				  real-match-data)
+				  real-match-data backward)
 				 replaced t))
 			 (setq done t))
 

=== modified file 'lisp/isearch.el'
--- lisp/isearch.el	2013-12-16 20:32:15 +0000
+++ lisp/isearch.el	2013-12-17 19:34:05 +0000
@@ -1667,10 +1667,11 @@ (defun re-search-backward-lax-whitespace
     (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.  The negative prefix
+arg `-' means replacing 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 @@ (defun isearch-query-replace (&optional
 	 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 @@ (defun isearch-query-replace (&optional
     (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 @@ (defun isearch-query-replace (&optional
 		      " 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.





Reply sent to Juri Linkov <juri <at> jurta.org>:
You have taken responsibility. (Thu, 19 Dec 2013 00:32:02 GMT) Full text and rfc822 format available.

Notification sent to Ben A. <ben.a <at> gmx.us>:
bug acknowledged by developer. (Thu, 19 Dec 2013 00:32:03 GMT) Full text and rfc822 format available.

Message #40 received at 14979-done <at> debbugs.gnu.org (full text, mbox):

From: Juri Linkov <juri <at> jurta.org>
To: Ben A. <ben.a <at> gmx.us>
Cc: 14979-done <at> debbugs.gnu.org
Subject: Re: bug#14979: 24.3; Feature Request: query-replace-backward
Date: Thu, 19 Dec 2013 02:30:04 +0200
> A feature request: I often make use of query-replace and
> query-replace-regexp.  It would be useful to have a query-replace-backward
> and a query-replace-regexp-backward.  The backward variant would be
> executed when the forward variant is given a negative argument.

This feature is completely implemented now.  Thanks for the suggestion.




bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Thu, 16 Jan 2014 12:24:06 GMT) Full text and rfc822 format available.

This bug report was last modified 10 years and 123 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.