Subject: bug#54733: Match again in perform-replace

From: Juri Linkov <[email-masked]>
Date: Tue, 03 May 2022 10:10:10 +0300

>>> So now we have two cases that require fixing perform-replace.
>>
>> To be able to redesign the match-again part of perform-replace,
>> there is a need to have a test suite that will confirm nothing
>> is broken after redesign.  So I pushed a new test in replace-tests.el.
>
> The need to have `looking-at` in `perform-replace` is explained
> in the commit message of 5632eb272c7.  So now added it to replace-tests.

The only way to fix all reported problems is to always use
search functions in perform-replace:

diff --git a/lisp/replace.el b/lisp/replace.el
index 81282deb14..7fbaa93ead 100644
--- a/lisp/replace.el
+++ b/lisp/replace.el
@@ -3013,9 +3013,10 @@ perform-replace
 	  (setq match-again
 		(and nonempty-match
 		     (or (not regexp-flag)
-			 (and (if backward
-				  (looking-back search-string nil)
-				(looking-at search-string))
+			 (and (save-excursion
+				(replace-search search-string limit
+						regexp-flag delimited-flag
+						case-fold-search backward))
 			      (let ((match (match-data)))
 				(and (/= (nth 0 match) (nth 1 match))
 				     match))))))
@@ -3298,8 +3299,12 @@ perform-replace
 			 ;; decide whether the search string
 			 ;; can match again just after this match.
 			 (if (and regexp-flag nonempty-match)
-			     (setq match-again (and (looking-at search-string)
-						    (match-data)))))
+			     (setq match-again
+				   (and (save-window-excursion
+					  (replace-search search-string limit
+						          regexp-flag delimited-flag
+						          case-fold-search backward))
+					(match-data)))))
 			;; Edit replacement.
 			((eq def 'edit-replacement)
 			 (setq real-match-data (replace-match-data
diff --git a/test/lisp/replace-tests.el b/test/lisp/replace-tests.el
index ef1e5c3eaf..f7a2e043ff 100644
--- a/test/lisp/replace-tests.el
+++ b/test/lisp/replace-tests.el
@@ -472,8 +472,7 @@ query-replace-search-function-tests
               found)))
          (tests
           '(
-            ;; FIXME: this test should pass after fixing bug#54733:
-            ;; ("aaaa" "C-M-% .* RET 1 RET !" "1a1a")
+            ("aaaa" "C-M-% .* RET 1 RET !" "1a1a")
             )))
     (query-replace--run-tests tests)))
 
@@ -485,8 +484,7 @@ perform-replace-tests
     ;; Test case from commit 5632eb272c7
     ("a a a " "\\ba " "c" nil t nil nil nil nil nil nil nil "ccc") ; not "ca c"
     ;; The same with region inside the second match
-    ;; FIXME: this test should pass after fixing bug#54733:
-    ;; ("a a a " "\\ba " "c" nil t nil nil nil 1 4 nil nil "ca a ")
+    ("a a a " "\\ba " "c" nil t nil nil nil 1 4 nil nil "ca a ")
     ))
 
 (defun perform-replace--run-tests (tests)
diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el
index 6e763eef01..981f51c30a 100644
--- a/lisp/progmodes/xref.el
+++ b/lisp/progmodes/xref.el
@@ -856,14 +856,6 @@ xref--query-replace-1
          (continue t)
          did-it-once buf-pairs pairs
          current-beg current-end
-         ;; Counteract the "do the next match now" hack in
-         ;; `perform-replace'.  And still, it'll report that those
-         ;; matches were "filtered out" at the end.
-         (isearch-filter-predicate
-          (lambda (beg end)
-            (and current-beg
-                 (>= beg current-beg)
-                 (<= end current-end))))
          (replace-re-search-function
           (lambda (from &optional _bound noerror)
             (let (found pair)