]> code.delx.au - gnu-emacs/blobdiff - lisp/replace.el
Add 2008 to copyright years.
[gnu-emacs] / lisp / replace.el
index f09868cc6d3b7632bd949bddc49199f717c32485..de57ddccff20e387940b4bb463078dc4463dd238 100644 (file)
@@ -1,7 +1,7 @@
 ;;; replace.el --- replace commands for Emacs
 
-;; Copyright (C) 1985, 1986, 1987, 1992, 1994, 1996, 1997, 2000, 2001, 2002,
-;;   2003, 2004  Free Software Foundation, Inc.
+;; Copyright (C) 1985, 1986, 1987, 1992, 1994, 1996, 1997, 2000, 2001,
+;;   2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
 
@@ -9,7 +9,7 @@
 
 ;; GNU Emacs is free software; you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
+;; the Free Software Foundation; either version 3, or (at your option)
 ;; any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
@@ -19,8 +19,8 @@
 
 ;; You should have received a copy of the GNU General Public License
 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
 
 ;;; Commentary:
 
 
 (defvar query-replace-history nil)
 
+(defvar query-replace-defaults nil
+  "Default values of FROM-STRING and TO-STRING for `query-replace'.
+This is a cons cell (FROM-STRING . TO-STRING), or nil if there is
+no default value.")
+
 (defvar query-replace-interactive nil
   "Non-nil means `query-replace' uses the last search string.
 That becomes the \"string to replace\".")
@@ -62,39 +67,58 @@ strings or patterns."
   "*Non-nil means `query-replace' and friends ignore read-only matches."
   :type 'boolean
   :group 'matching
-  :version "21.4")
+  :version "22.1")
+
+(defcustom query-replace-highlight t
+  "*Non-nil means to highlight matches during query replacement."
+  :type 'boolean
+  :group 'matching)
+
+(defcustom query-replace-lazy-highlight t
+  "*Controls the lazy-highlighting during query replacements.
+When non-nil, all text in the buffer matching the current match
+is highlighted lazily using isearch lazy highlighting (see
+`lazy-highlight-initial-delay' and `lazy-highlight-interval')."
+  :type 'boolean
+  :group 'lazy-highlight
+  :group 'matching
+  :version "22.1")
+
+(defface query-replace
+  '((t (:inherit isearch)))
+  "Face for highlighting query replacement matches."
+  :group 'matching
+  :version "22.1")
 
 (defun query-replace-descr (string)
   (mapconcat 'isearch-text-char-description string ""))
 
-(defun query-replace-read-from (string regexp-flag)
+(defun query-replace-read-from (prompt regexp-flag)
   "Query and return the `from' argument of a query-replace operation.
 The return value can also be a pair (FROM . TO) indicating that the user
 wants to replace FROM with TO."
   (if query-replace-interactive
       (car (if regexp-flag regexp-search-ring search-ring))
-    (let* ((lastfrom (car (symbol-value query-replace-from-history-variable)))
-          (lastto (car (symbol-value query-replace-to-history-variable)))
+    (let* ((history-add-new-input nil)
           (from
            ;; The save-excursion here is in case the user marks and copies
            ;; a region in order to specify the minibuffer input.
            ;; That should not clobber the region for the query-replace itself.
            (save-excursion
-             (when (equal lastfrom lastto)
-               ;; Typically, this is because the two histlists are shared.
-               (setq lastfrom (cadr (symbol-value
-                                     query-replace-from-history-variable))))
              (read-from-minibuffer
-              (if (and lastto lastfrom)
-                  (format "%s (default %s -> %s): " string
-                          (query-replace-descr lastfrom)
-                          (query-replace-descr lastto))
-                (format "%s: " string))
+              (if query-replace-defaults
+                  (format "%s (default %s -> %s): " prompt
+                          (query-replace-descr (car query-replace-defaults))
+                          (query-replace-descr (cdr query-replace-defaults)))
+                (format "%s: " prompt))
               nil nil nil
               query-replace-from-history-variable
               nil t))))
-      (if (and (zerop (length from)) lastto lastfrom)
-         (cons lastfrom lastto)
+      (if (and (zerop (length from)) query-replace-defaults)
+         (cons (car query-replace-defaults)
+               (query-replace-compile-replacement
+                (cdr query-replace-defaults) regexp-flag))
+       (add-to-history query-replace-from-history-variable from nil t)
        ;; Warn if user types \n or \t, but don't reject the input.
        (and regexp-flag
             (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\(\\\\[nt]\\)" from)
@@ -107,15 +131,12 @@ wants to replace FROM with TO."
               (sit-for 2)))
        from))))
 
-(defun query-replace-read-to (from string regexp-flag)
-  "Query and return the `from' argument of a query-replace operation."
-  (let ((to (save-excursion
-             (read-from-minibuffer
-              (format "%s %s with: " string (query-replace-descr from))
-              nil nil nil
-              query-replace-to-history-variable from t))))
-    (when (and regexp-flag
-              (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\\\[,#]" to))
+(defun query-replace-compile-replacement (to regexp-flag)
+  "Maybe convert a regexp replacement TO to Lisp.
+Returns a list suitable for `perform-replace' if necessary,
+the original string if not."
+  (if (and regexp-flag
+          (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\\\[,#]" to))
       (let (pos list char)
        (while
            (progn
@@ -142,20 +163,35 @@ wants to replace FROM with TO."
                              (cdr pos))))
                       (setq to (substring to end)))))
              (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\\\[,#]" to)))
-       (setq to (nreverse (delete "" (cons to list)))))
-      (replace-match-string-symbols to)
-      (setq to (cons 'replace-eval-replacement
-                    (if (> (length to) 1)
-                        (cons 'concat to)
-                      (car to)))))
+       (setq to (nreverse (delete "" (cons to list))))
+       (replace-match-string-symbols to)
+       (cons 'replace-eval-replacement
+             (if (cdr to)
+                 (cons 'concat to)
+               (car to))))
     to))
 
-(defun query-replace-read-args (string regexp-flag &optional noerror)
+
+(defun query-replace-read-to (from prompt regexp-flag)
+  "Query and return the `to' argument of a query-replace operation."
+  (query-replace-compile-replacement
+   (save-excursion
+     (let* ((history-add-new-input nil)
+           (to (read-from-minibuffer
+                (format "%s %s with: " prompt (query-replace-descr from))
+                nil nil nil
+                query-replace-to-history-variable from t)))
+       (add-to-history query-replace-to-history-variable to nil t)
+       (setq query-replace-defaults (cons from to))
+       to))
+   regexp-flag))
+
+(defun query-replace-read-args (prompt regexp-flag &optional noerror)
   (unless noerror
     (barf-if-buffer-read-only))
-  (let* ((from (query-replace-read-from string regexp-flag))
+  (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 string regexp-flag))))
+              (query-replace-read-to from prompt regexp-flag))))
     (list from to current-prefix-arg)))
 
 (defun query-replace (from-string to-string &optional delimited start end)
@@ -184,7 +220,11 @@ Fourth and fifth arg START and END specify the region to operate on.
 
 To customize possible responses, change the \"bindings\" in `query-replace-map'."
   (interactive (let ((common
-                     (query-replace-read-args "Query replace" nil)))
+                     (query-replace-read-args
+                      (if (and transient-mark-mode mark-active)
+                        "Query replace in region"
+                        "Query replace")
+                        nil)))
                 (list (nth 0 common) (nth 1 common) (nth 2 common)
                       ;; These are done separately here
                       ;; so that command-history will record these expressions
@@ -244,7 +284,11 @@ text, TO-STRING is actually made a list instead of a string.
 Use \\[repeat-complex-command] after this command for details."
   (interactive
    (let ((common
-         (query-replace-read-args "Query replace regexp" t)))
+         (query-replace-read-args
+          (if (and transient-mark-mode mark-active)
+              "Query replace regexp in region"
+            "Query replace regexp")
+          t)))
      (list (nth 0 common) (nth 1 common) (nth 2 common)
           ;; These are done separately here
           ;; so that command-history will record these expressions
@@ -259,6 +303,11 @@ Use \\[repeat-complex-command] after this command for details."
 
 (defun query-replace-regexp-eval (regexp to-expr &optional delimited start end)
   "Replace some things after point matching REGEXP with the result of TO-EXPR.
+
+Interactive use of this function is deprecated in favor of the
+`\\,' feature of `query-replace-regexp'.  For non-interactive use, a loop
+using `search-forward-regexp' and `replace-match' is preferred.
+
 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.
 
@@ -310,13 +359,16 @@ Fourth and fifth arg START and END specify the region to operate on."
   (perform-replace regexp (cons 'replace-eval-replacement to-expr)
                   t 'literal delimited nil nil start end))
 
+(make-obsolete 'query-replace-regexp-eval
+  "for interactive use, use the special `\\,' feature of
+`query-replace-regexp' instead.  Non-interactively, a loop
+using `search-forward-regexp' and `replace-match' is preferred." "22.1")
+
 (defun map-query-replace-regexp (regexp to-strings &optional n start end)
   "Replace some matches for REGEXP with various strings, in rotation.
-The second argument TO-STRINGS contains the replacement strings,
-separated by spaces.  Third arg DELIMITED (prefix arg if interactive),
-if non-nil, means replace only matches surrounded by word boundaries.
-This command works like `query-replace-regexp' except that each
-successive replacement uses the next successive replacement string,
+The second argument TO-STRINGS contains the replacement strings, separated
+by spaces.  This command works like `query-replace-regexp' except that
+each successive replacement uses the next successive replacement string,
 wrapping around from the last such string to the first.
 
 In Transient Mark mode, if the mark is active, operate on the contents
@@ -390,7 +442,11 @@ which will run faster and will not set the mark or print anything.
 and TO-STRING is also null.)"
   (interactive
    (let ((common
-         (query-replace-read-args "Replace string" nil)))
+         (query-replace-read-args
+          (if (and transient-mark-mode mark-active)
+              "Replace string in region"
+            "Replace string")
+          nil)))
      (list (nth 0 common) (nth 1 common) (nth 2 common)
           (if (and transient-mark-mode mark-active)
               (region-beginning))
@@ -419,10 +475,9 @@ at the given position for each replacement.
 In interactive calls, the replacement text may contain `\\,'
 followed by a Lisp expression used as part of the replacement
 text.  Inside of that expression, `\\&' is a string denoting the
-whole match, `\\N' a partial matches, `\\#&' and `\\#N' the
-respective numeric values from `string-to-number', and `\\#'
-itself for `replace-count', the number of replacements occured so
-far.
+whole match, `\\N' a partial match, `\\#&' and `\\#N' the respective
+numeric values from `string-to-number', and `\\#' itself for
+`replace-count', the number of replacements occurred so far.
 
 If your Lisp expression is an identifier and the next letter in
 the replacement string would be interpreted as part of it, you
@@ -444,7 +499,11 @@ What you probably want is a loop like this:
 which will run faster and will not set the mark or print anything."
   (interactive
    (let ((common
-         (query-replace-read-args "Replace regexp" t)))
+         (query-replace-read-args
+          (if (and transient-mark-mode mark-active)
+              "Replace regexp in region"
+            "Replace regexp")
+          t)))
      (list (nth 0 common) (nth 1 common) (nth 2 common)
           (if (and transient-mark-mode mark-active)
               (region-beginning))
@@ -454,7 +513,10 @@ which will run faster and will not set the mark or print anything."
 
 \f
 (defvar regexp-history nil
-  "History list for some commands that read regular expressions.")
+  "History list for some commands that read regular expressions.
+
+Maximum length of the history list is determined by the value
+of `history-length', which see.")
 
 
 (defalias 'delete-non-matching-lines 'keep-lines)
@@ -467,21 +529,32 @@ which will run faster and will not set the mark or print anything."
 Prompt for a regexp with PROMPT.
 Value is a list, (REGEXP)."
   (list (read-from-minibuffer prompt nil nil nil
-                             'regexp-history nil t)))
+                             'regexp-history nil t)
+       nil nil t))
 
-(defun keep-lines (regexp &optional rstart rend)
+(defun keep-lines (regexp &optional rstart rend interactive)
   "Delete all lines except those containing matches for REGEXP.
 A match split across lines preserves all the lines it lies in.
-Applies to all lines after point.
+When called from Lisp (and usually interactively as well, see below)
+applies to all lines starting after point.
 
 If REGEXP contains upper case characters (excluding those preceded by `\\'),
 the matching is case-sensitive.
 
 Second and third arg RSTART and REND specify the region to operate on.
+This command operates on (the accessible part of) all lines whose
+accessible part is entirely contained in the region determined by RSTART
+and REND.  (A newline ending a line counts as part of that line.)
 
 Interactively, in Transient Mark mode when the mark is active, operate
-on the contents of the region.  Otherwise, operate from point to the
-end of the buffer."
+on all lines whose accessible part is entirely contained in the region.
+Otherwise, the command applies to all lines starting after point.
+When calling this function from Lisp, you can pretend that it was
+called interactively by passing a non-nil INTERACTIVE argument.
+
+This function starts looking for the next match from the end of
+the previous match.  Hence, it ignores matches that overlap
+a previously found match."
 
   (interactive
    (progn
@@ -490,10 +563,20 @@ end of the buffer."
   (if rstart
       (progn
        (goto-char (min rstart rend))
-       (setq rend (copy-marker (max rstart rend))))
-    (if (and transient-mark-mode mark-active)
+       (setq rend
+             (progn
+               (save-excursion
+                 (goto-char (max rstart rend))
+                 (unless (or (bolp) (eobp))
+                   (forward-line 0))
+                 (point-marker)))))
+    (if (and interactive transient-mark-mode mark-active)
        (setq rstart (region-beginning)
-             rend (copy-marker (region-end)))
+             rend (progn
+                    (goto-char (region-end))
+                    (unless (or (bolp) (eobp))
+                      (forward-line 0))
+                    (point-marker)))
       (setq rstart (point)
            rend (point-max-marker)))
     (goto-char rstart))
@@ -507,7 +590,7 @@ end of the buffer."
        (if (not (re-search-forward regexp rend 'move))
            (delete-region start rend)
          (let ((end (save-excursion (goto-char (match-beginning 0))
-                                    (beginning-of-line)
+                                    (forward-line 0)
                                     (point))))
            ;; Now end is first char preserved by the new match.
            (if (< start end)
@@ -517,22 +600,34 @@ end of the buffer."
        ;; If the match was empty, avoid matching again at same place.
        (and (< (point) rend)
             (= (match-beginning 0) (match-end 0))
-            (forward-char 1))))))
+            (forward-char 1)))))
+  (set-marker rend nil)
+  nil)
 
 
-(defun flush-lines (regexp &optional rstart rend)
-  "Delete lines containing matches for REGEXP.
-If a match is split across lines, all the lines it lies in are deleted.
-Applies to lines after point.
+(defun flush-lines (regexp &optional rstart rend interactive)
+ "Delete lines containing matches for REGEXP.
+When called from Lisp (and usually when called interactively as
+well, see below), applies to the part of the buffer after point.
+The line point is in is deleted if and only if it contains a
+match for regexp starting after point.
 
 If REGEXP contains upper case characters (excluding those preceded by `\\'),
 the matching is case-sensitive.
 
 Second and third arg RSTART and REND specify the region to operate on.
+Lines partially contained in this region are deleted if and only if
+they contain a match entirely contained in it.
 
 Interactively, in Transient Mark mode when the mark is active, operate
 on the contents of the region.  Otherwise, operate from point to the
-end of the buffer."
+end of (the accessible portion of) the buffer.  When calling this function
+from Lisp, you can pretend that it was called interactively by passing
+a non-nil INTERACTIVE argument.
+
+If a match is split across lines, all the lines it lies in are deleted.
+They are deleted _before_ looking for the next match.  Hence, a match
+starting on the same line at which another match ended is ignored."
 
   (interactive
    (progn
@@ -542,7 +637,7 @@ end of the buffer."
       (progn
        (goto-char (min rstart rend))
        (setq rend (copy-marker (max rstart rend))))
-    (if (and transient-mark-mode mark-active)
+    (if (and interactive transient-mark-mode mark-active)
        (setq rstart (region-beginning)
              rend (copy-marker (region-end)))
       (setq rstart (point)
@@ -554,13 +649,18 @@ end of the buffer."
       (while (and (< (point) rend)
                  (re-search-forward regexp rend t))
        (delete-region (save-excursion (goto-char (match-beginning 0))
-                                      (beginning-of-line)
+                                      (forward-line 0)
                                       (point))
-                      (progn (forward-line 1) (point)))))))
+                      (progn (forward-line 1) (point))))))
+  (set-marker rend nil)
+  nil)
 
 
-(defun how-many (regexp &optional rstart rend)
-  "Print number of matches for REGEXP following point.
+(defun how-many (regexp &optional rstart rend interactive)
+  "Print and return number of matches for REGEXP following point.
+When called from Lisp and INTERACTIVE is omitted or nil, just return
+the number, do not print it; if INTERACTIVE is t, the function behaves
+in all respects has if it had been called interactively.
 
 If REGEXP contains upper case characters (excluding those preceded by `\\'),
 the matching is case-sensitive.
@@ -569,18 +669,24 @@ Second and third arg RSTART and REND specify the region to operate on.
 
 Interactively, in Transient Mark mode when the mark is active, operate
 on the contents of the region.  Otherwise, operate from point to the
-end of the buffer."
+end of (the accessible portion of) the buffer.
+
+This function starts looking for the next match from the end of
+the previous match.  Hence, it ignores matches that overlap
+a previously found match."
 
   (interactive
    (keep-lines-read-args "How many matches for (regexp): "))
   (save-excursion
     (if rstart
-       (goto-char (min rstart rend))
-      (if (and transient-mark-mode mark-active)
+       (progn
+         (goto-char (min rstart rend))
+         (setq rend (max rstart rend)))
+      (if (and interactive transient-mark-mode mark-active)
          (setq rstart (region-beginning)
-               rend (copy-marker (region-end)))
+               rend (region-end))
        (setq rstart (point)
-             rend (point-max-marker)))
+             rend (point-max)))
       (goto-char rstart))
     (let ((count 0)
          opoint
@@ -592,11 +698,15 @@ end of the buffer."
        (if (= opoint (point))
            (forward-char 1)
          (setq count (1+ count))))
-      (message "%d occurrences" count))))
+      (when interactive (message "%d occurrence%s"
+                                count
+                                (if (= count 1) "" "s")))
+      count)))
 
 \f
 (defvar occur-mode-map
   (let ((map (make-sparse-keymap)))
+    ;; We use this alternative name, so we can use \\[occur-mode-mouse-goto].
     (define-key map [mouse-2] 'occur-mode-mouse-goto)
     (define-key map "\C-c\C-c" 'occur-mode-goto-occurrence)
     (define-key map "\C-m" 'occur-mode-goto-occurrence)
@@ -609,6 +719,7 @@ end of the buffer."
     (define-key map "g" 'revert-buffer)
     (define-key map "q" 'quit-window)
     (define-key map "z" 'kill-this-buffer)
+    (define-key map "\C-c\C-f" 'next-error-follow-minor-mode)
     map)
   "Keymap for `occur-mode'.")
 
@@ -622,7 +733,7 @@ See `occur-revert-function'.")
   :group 'matching)
 
 (defcustom occur-hook nil
-  "Hook run when `occur' is called."
+  "Hook run by Occur when there are any matches."
   :type 'hook
   :group 'matching)
 
@@ -643,24 +754,12 @@ Alternatively, click \\[occur-mode-mouse-goto] on an item to go to it.
   (make-local-variable 'occur-revert-arguments)
   (add-hook 'change-major-mode-hook 'font-lock-defontify nil t)
   (setq next-error-function 'occur-next-error)
-  (run-hooks 'occur-mode-hook))
+  (run-mode-hooks 'occur-mode-hook))
 
 (defun occur-revert-function (ignore1 ignore2)
   "Handle `revert-buffer' for Occur mode buffers."
   (apply 'occur-1 (append occur-revert-arguments (list (buffer-name)))))
 
-(defun occur-mode-mouse-goto (event)
-  "In Occur mode, go to the occurrence whose line you click on."
-  (interactive "e")
-  (let (pos)
-    (save-excursion
-      (set-buffer (window-buffer (posn-window (event-end event))))
-      (save-excursion
-       (goto-char (posn-point (event-end event)))
-       (setq pos (occur-mode-find-occurrence))))
-    (pop-to-buffer (marker-buffer pos))
-    (goto-char pos)))
-
 (defun occur-mode-find-occurrence ()
   (let ((pos (get-text-property (point) 'occur-target)))
     (unless pos
@@ -669,10 +768,22 @@ Alternatively, click \\[occur-mode-mouse-goto] on an item to go to it.
       (error "Buffer for this occurrence was killed"))
     pos))
 
-(defun occur-mode-goto-occurrence ()
+(defalias 'occur-mode-mouse-goto 'occur-mode-goto-occurrence)
+(defun occur-mode-goto-occurrence (&optional event)
   "Go to the occurrence the current line describes."
-  (interactive)
-  (let ((pos (occur-mode-find-occurrence)))
+  (interactive (list last-nonmenu-event))
+  (let ((pos
+         (if (null event)
+             ;; Actually `event-end' works correctly with a nil argument as
+             ;; well, so we could dispense with this test, but let's not
+             ;; rely on this undocumented behavior.
+             (occur-mode-find-occurrence)
+           (with-current-buffer (window-buffer (posn-window (event-end event)))
+             (save-excursion
+               (goto-char (posn-point (event-end event)))
+               (occur-mode-find-occurrence)))))
+        same-window-buffer-names
+        same-window-regexps)
     (pop-to-buffer (marker-buffer pos))
     (goto-char pos)))
 
@@ -724,19 +835,46 @@ Alternatively, click \\[occur-mode-mouse-goto] on an item to go to it.
   "Move to the Nth (default 1) next match in an Occur mode buffer.
 Compatibility function for \\[next-error] invocations."
   (interactive "p")
-  (when reset
-    (occur-find-match 0 #'next-single-property-change "No first match"))
-  (occur-find-match
-   (prefix-numeric-value argp)
-   (if (> 0 (prefix-numeric-value argp))
-       #'previous-single-property-change
-     #'next-single-property-change)
-   "No more matches")
-  ;; In case the *Occur* buffer is visible in a nonselected window.
-  (set-window-point (get-buffer-window (current-buffer)) (point))
-  (occur-mode-goto-occurrence))
-
+  ;; we need to run occur-find-match from within the Occur buffer
+  (with-current-buffer
+      ;; Choose the buffer and make it current.
+      (if (next-error-buffer-p (current-buffer))
+         (current-buffer)
+       (next-error-find-buffer nil nil
+                               (lambda ()
+                                 (eq major-mode 'occur-mode))))
+
+    (goto-char (cond (reset (point-min))
+                    ((< argp 0) (line-beginning-position))
+                    ((> argp 0) (line-end-position))
+                    ((point))))
+    (occur-find-match
+     (abs argp)
+     (if (> 0 argp)
+        #'previous-single-property-change
+       #'next-single-property-change)
+     "No more matches")
+    ;; In case the *Occur* buffer is visible in a nonselected window.
+    (let ((win (get-buffer-window (current-buffer) t)))
+      (if win (set-window-point win (point))))
+    (occur-mode-goto-occurrence)))
 \f
+(defface match
+  '((((class color) (min-colors 88) (background light))
+     :background "yellow1")
+    (((class color) (min-colors 88) (background dark))
+     :background "RoyalBlue3")
+    (((class color) (min-colors 8) (background light))
+     :background "yellow" :foreground "black")
+    (((class color) (min-colors 8) (background dark))
+     :background "blue" :foreground "white")
+    (((type tty) (class mono))
+     :inverse-video t)
+    (t :background "gray"))
+  "Face used to highlight matches permanently."
+  :group 'matching
+  :version "22.1")
+
 (defcustom list-matching-lines-default-context-lines 0
   "*Default number of context lines included around `list-matching-lines' matches.
 A negative number means to include that many lines before the match.
@@ -746,7 +884,7 @@ A positive number means to include that many lines both before and after."
 
 (defalias 'list-matching-lines 'occur)
 
-(defcustom list-matching-lines-face 'bold
+(defcustom list-matching-lines-face 'match
   "*Face used by \\[list-matching-lines] to show the text that matches.
 If the value is nil, don't highlight the matching portions specially."
   :type 'face
@@ -758,21 +896,38 @@ If the value is nil, don't highlight the buffer names specially."
   :type 'face
   :group 'matching)
 
-(defun occur-accumulate-lines (count &optional no-props)
+(defcustom occur-excluded-properties
+  '(read-only invisible intangible field mouse-face help-echo local-map keymap
+    yank-handler follow-link)
+  "*Text properties to discard when copying lines to the *Occur* buffer.
+The value should be a list of text properties to discard or t,
+which means to discard all text properties."
+  :type '(choice (const :tag "All" t) (repeat symbol))
+  :group 'matching
+  :version "22.1")
+
+(defun occur-accumulate-lines (count &optional keep-props)
   (save-excursion
     (let ((forwardp (> count 0))
-         (result nil))
+         result beg end)
       (while (not (or (zerop count)
                      (if forwardp
                          (eobp)
                        (bobp))))
        (setq count (+ count (if forwardp -1 1)))
+       (setq beg (line-beginning-position)
+             end (line-end-position))
+       (if (and keep-props (if (boundp 'jit-lock-mode) jit-lock-mode)
+                (text-property-not-all beg end 'fontified t))
+           (if (fboundp 'jit-lock-fontify-now)
+               (jit-lock-fontify-now beg end)))
        (push
-        (funcall (if no-props
-                     #'buffer-substring-no-properties
-                   #'buffer-substring)
-         (line-beginning-position)
-         (line-end-position))
+        (if (and keep-props (not (eq occur-excluded-properties t)))
+            (let ((str (buffer-substring beg end)))
+              (remove-list-of-text-properties
+               0 (length str) occur-excluded-properties str)
+              str)
+          (buffer-substring-no-properties beg end))
         result)
        (forward-line (if forwardp 1 -1)))
       (nreverse result))))
@@ -782,7 +937,7 @@ If the value is nil, don't highlight the buffer names specially."
               (input
                (read-from-minibuffer
                 (if default
-                    (format "List lines matching regexp (default `%s'): "
+                    (format "List lines matching regexp (default %s): "
                             (query-replace-descr default))
                   "List lines matching regexp: ")
                 nil
@@ -796,26 +951,26 @@ If the value is nil, don't highlight the buffer names specially."
        (when current-prefix-arg
          (prefix-numeric-value current-prefix-arg))))
 
-(defun occur-rename-buffer (&optional unique-p)
+(defun occur-rename-buffer (&optional unique-p interactive-p)
   "Rename the current *Occur* buffer to *Occur: original-buffer-name*.
-Here `original-buffer-name' is the buffer name were occur was originally run.
-When given the prefix argument, the renaming will not clobber the existing
-buffer(s) of that name, but use `generate-new-buffer-name' instead.
-You can add this to `occur-hook' if you always want a separate *Occur*
-buffer for each buffer where you invoke `occur'."
-  (interactive "P")
+Here `original-buffer-name' is the buffer name were Occur was originally run.
+When given the prefix argument, or called non-interactively, the renaming
+will not clobber the existing buffer(s) of that name, but use
+`generate-new-buffer-name' instead.  You can add this to `occur-hook'
+if you always want a separate *Occur* buffer for each buffer where you
+invoke `occur'."
+  (interactive "P\np")
   (with-current-buffer
       (if (eq major-mode 'occur-mode) (current-buffer) (get-buffer "*Occur*"))
     (rename-buffer (concat "*Occur: "
                            (mapconcat #'buffer-name
                                       (car (cddr occur-revert-arguments)) "/")
                            "*")
-                   unique-p)))
+                   (or unique-p (not interactive-p)))))
 
 (defun occur (regexp &optional nlines)
   "Show all lines in the current buffer containing a match for REGEXP.
-
-If a match spreads across multiple lines, all those lines are shown.
+This function can not handle matches that span more than one line.
 
 Each line is displayed with NLINES lines before and after, or -NLINES
 before if NLINES is negative.
@@ -834,7 +989,8 @@ the matching is case-sensitive."
 (defun multi-occur (bufs regexp &optional nlines)
   "Show all lines in buffers BUFS containing a match for REGEXP.
 This function acts on multiple buffers; otherwise, it is exactly like
-`occur'."
+`occur'.  When you invoke this command interactively, you must specify
+the buffer names that you want, one by one."
   (interactive
    (cons
     (let* ((bufs (list (read-buffer "First buffer to search: "
@@ -854,15 +1010,19 @@ This function acts on multiple buffers; otherwise, it is exactly like
     (occur-read-primary-args)))
   (occur-1 regexp nlines bufs))
 
-(defun multi-occur-by-filename-regexp (bufregexp regexp &optional nlines)
-  "Show all lines matching REGEXP in buffers named by BUFREGEXP.
+(defun multi-occur-in-matching-buffers (bufregexp regexp &optional allbufs)
+  "Show all lines matching REGEXP in buffers specified by BUFREGEXP.
+Normally BUFREGEXP matches against each buffer's visited file name,
+but if you specify a prefix argument, it matches against the buffer name.
 See also `multi-occur'."
   (interactive
    (cons
     (let* ((default (car regexp-history))
           (input
            (read-from-minibuffer
-            "List lines in buffers whose filename matches regexp: "
+            (if current-prefix-arg
+                "List lines in buffers whose names match regexp: "
+              "List lines in buffers whose filenames match regexp: ")
             nil
             nil
             nil
@@ -872,62 +1032,65 @@ See also `multi-occur'."
        input))
     (occur-read-primary-args)))
   (when bufregexp
-    (occur-1 regexp nlines
+    (occur-1 regexp nil
             (delq nil
                   (mapcar (lambda (buf)
-                            (when (and (buffer-file-name buf)
-                                       (string-match bufregexp
-                                                     (buffer-file-name buf)))
+                            (when (if allbufs
+                                      (string-match bufregexp
+                                                    (buffer-name buf))
+                                    (and (buffer-file-name buf)
+                                         (string-match bufregexp
+                                                       (buffer-file-name buf))))
                               buf))
                           (buffer-list))))))
 
 (defun occur-1 (regexp nlines bufs &optional buf-name)
   (unless buf-name
     (setq buf-name "*Occur*"))
-  (let ((occur-buf (get-buffer-create buf-name))
-       (made-temp-buf nil)
+  (let (occur-buf
        (active-bufs (delq nil (mapcar #'(lambda (buf)
                                           (when (buffer-live-p buf) buf))
                                       bufs))))
     ;; Handle the case where one of the buffers we're searching is the
-    ;; *Occur* buffer itself.
-    (when (memq occur-buf bufs)
-      (setq occur-buf (with-current-buffer occur-buf
-                       (clone-buffer "*Occur-temp*"))
-           made-temp-buf t))
+    ;; output buffer.  Just rename it.
+    (when (member buf-name (mapcar 'buffer-name active-bufs))
+      (with-current-buffer (get-buffer buf-name)
+       (rename-uniquely)))
+
+    ;; Now find or create the output buffer.
+    ;; If we just renamed that buffer, we will make a new one here.
+    (setq occur-buf (get-buffer-create buf-name))
+
     (with-current-buffer occur-buf
-      (setq buffer-read-only nil)
       (occur-mode)
-      (erase-buffer)
-      (let ((count (occur-engine
-                   regexp active-bufs occur-buf
-                   (or nlines list-matching-lines-default-context-lines)
-                   (and case-fold-search
-                        (isearch-no-upper-case-p regexp t))
-                   list-matching-lines-buffer-name-face
-                   nil list-matching-lines-face nil)))
-       (let* ((bufcount (length active-bufs))
-              (diff (- (length bufs) bufcount)))
-         (message "Searched %d buffer%s%s; %s match%s for `%s'"
-                  bufcount (if (= bufcount 1) "" "s")
-                  (if (zerop diff) "" (format " (%d killed)" diff))
-                  (if (zerop count) "no" (format "%d" count))
-                  (if (= count 1) "" "es")
-                  regexp))
-       ;; If we had to make a temporary buffer, make it the *Occur*
-       ;; buffer now.
-       (when made-temp-buf
-         (with-current-buffer (get-buffer buf-name)
-           (kill-buffer (current-buffer)))
-         (rename-buffer buf-name))
-       (setq occur-revert-arguments (list regexp nlines bufs)
-             buffer-read-only t)
-       (if (> count 0)
-           (progn
-             (display-buffer occur-buf)
-             (setq next-error-last-buffer occur-buf))
-         (kill-buffer occur-buf)))
-      (run-hooks 'occur-hook))))
+      (let ((inhibit-read-only t)
+           ;; Don't generate undo entries for creation of the initial contents.
+           (buffer-undo-list t))
+       (erase-buffer)
+       (let ((count (occur-engine
+                     regexp active-bufs occur-buf
+                     (or nlines list-matching-lines-default-context-lines)
+                     (and case-fold-search
+                          (isearch-no-upper-case-p regexp t))
+                     list-matching-lines-buffer-name-face
+                     nil list-matching-lines-face
+                     (not (eq occur-excluded-properties t)))))
+         (let* ((bufcount (length active-bufs))
+                (diff (- (length bufs) bufcount)))
+           (message "Searched %d buffer%s%s; %s match%s for `%s'"
+                    bufcount (if (= bufcount 1) "" "s")
+                    (if (zerop diff) "" (format " (%d killed)" diff))
+                    (if (zerop count) "no" (format "%d" count))
+                    (if (= count 1) "" "es")
+                    regexp))
+         (setq occur-revert-arguments (list regexp nlines bufs))
+          (if (= count 0)
+              (kill-buffer occur-buf)
+            (display-buffer occur-buf)
+            (setq next-error-last-buffer occur-buf)
+            (setq buffer-read-only t)
+            (set-buffer-modified-p nil)
+            (run-hooks 'occur-hook)))))))
 
 (defun occur-engine-add-prefix (lines)
   (mapcar
@@ -938,7 +1101,6 @@ See also `multi-occur'."
 (defun occur-engine (regexp buffers out-buf nlines case-fold-search
                            title-face prefix-face match-face keep-props)
   (with-current-buffer out-buf
-    (setq buffer-read-only nil)
     (let ((globalcount 0)
          (coding nil))
       ;; Map over all the buffers
@@ -952,9 +1114,9 @@ See also `multi-occur'."
                (endpt nil)
                (marker nil)
                (curstring "")
+               (inhibit-field-text-motion t)
                (headerpt (with-current-buffer out-buf (point))))
-           (save-excursion
-             (set-buffer buf)
+           (with-current-buffer buf
              (or coding
                  ;; Set CODING only if the current buffer locally
                  ;; binds buffer-file-coding-system.
@@ -967,29 +1129,38 @@ See also `multi-occur'."
                  (when (setq endpt (re-search-forward regexp nil t))
                    (setq matches (1+ matches)) ;; increment match count
                    (setq matchbeg (match-beginning 0))
-                   (setq begpt (save-excursion
-                                 (goto-char matchbeg)
-                                 (line-beginning-position)))
                    (setq lines (+ lines (1- (count-lines origpt endpt))))
+                   (save-excursion
+                     (goto-char matchbeg)
+                     (setq begpt (line-beginning-position)
+                           endpt (line-end-position)))
                    (setq marker (make-marker))
                    (set-marker marker matchbeg)
-                   (setq curstring (buffer-substring begpt
-                                                     (line-end-position)))
-                   ;; Depropertize the string, and maybe
-                   ;; highlight the matches
+                   (if (and keep-props
+                            (if (boundp 'jit-lock-mode) jit-lock-mode)
+                            (text-property-not-all begpt endpt 'fontified t))
+                       (if (fboundp 'jit-lock-fontify-now)
+                           (jit-lock-fontify-now begpt endpt)))
+                   (if (and keep-props (not (eq occur-excluded-properties t)))
+                       (progn
+                         (setq curstring (buffer-substring begpt endpt))
+                         (remove-list-of-text-properties
+                          0 (length curstring) occur-excluded-properties curstring))
+                     (setq curstring (buffer-substring-no-properties begpt endpt)))
+                   ;; Highlight the matches
                    (let ((len (length curstring))
                          (start 0))
-                     (unless keep-props
-                       (set-text-properties 0 len nil curstring))
                      (while (and (< start len)
                                  (string-match regexp curstring start))
-                       (add-text-properties (match-beginning 0)
-                                            (match-end 0)
-                                            (append
-                                             `(occur-match t)
-                                             (when match-face
-                                               `(font-lock-face ,match-face)))
-                                            curstring)
+                       (add-text-properties
+                        (match-beginning 0) (match-end 0)
+                        (append
+                         `(occur-match t)
+                         (when match-face
+                           ;; Use `face' rather than `font-lock-face' here
+                           ;; so as to override faces copied from the buffer.
+                           `(face ,match-face)))
+                        curstring)
                        (setq start (match-end 0))))
                    ;; Generate the string to insert for this match
                    (let* ((out-line
@@ -999,9 +1170,19 @@ See also `multi-occur'."
                                    (append
                                     (when prefix-face
                                       `(font-lock-face prefix-face))
-                                    '(occur-prefix t)))
-                            curstring
-                            "\n"))
+                                    `(occur-prefix t mouse-face (highlight)
+                                      occur-target ,marker follow-link t
+                                      help-echo "mouse-2: go to this occurrence")))
+                            ;; We don't put `mouse-face' on the newline,
+                            ;; because that loses.  And don't put it
+                            ;; on context lines to reduce flicker.
+                            (propertize curstring 'mouse-face (list 'highlight)
+                                        'occur-target marker
+                                        'follow-link t
+                                        'help-echo
+                                        "mouse-2: go to this occurrence")
+                            ;; Add marker at eol, but no mouse props.
+                            (propertize "\n" 'occur-target marker)))
                           (data
                            (if (= nlines 0)
                                ;; The simple display style
@@ -1021,13 +1202,7 @@ See also `multi-occur'."
                        (let ((beg (point))
                              (end (progn (insert data) (point))))
                          (unless (= nlines 0)
-                           (insert "-------\n"))
-                         (add-text-properties
-                          beg end
-                          `(occur-target ,marker help-echo "mouse-2: go to this occurrence"))
-                         ;; We don't put `mouse-face' on the newline,
-                         ;; because that loses.
-                         (add-text-properties beg (1- end) '(mouse-face highlight)))))
+                           (insert "-------\n")))))
                    (goto-char endpt))
                  (if endpt
                      (progn
@@ -1074,42 +1249,43 @@ C-l to clear the screen, redisplay, and offer same replacement again,
 E to edit the replacement string"
   "Help message while in `query-replace'.")
 
-(defvar query-replace-map (make-sparse-keymap)
+(defvar query-replace-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map " " 'act)
+    (define-key map "\d" 'skip)
+    (define-key map [delete] 'skip)
+    (define-key map [backspace] 'skip)
+    (define-key map "y" 'act)
+    (define-key map "n" 'skip)
+    (define-key map "Y" 'act)
+    (define-key map "N" 'skip)
+    (define-key map "e" 'edit-replacement)
+    (define-key map "E" 'edit-replacement)
+    (define-key map "," 'act-and-show)
+    (define-key map "q" 'exit)
+    (define-key map "\r" 'exit)
+    (define-key map [return] 'exit)
+    (define-key map "." 'act-and-exit)
+    (define-key map "\C-r" 'edit)
+    (define-key map "\C-w" 'delete-and-edit)
+    (define-key map "\C-l" 'recenter)
+    (define-key map "!" 'automatic)
+    (define-key map "^" 'backup)
+    (define-key map "\C-h" 'help)
+    (define-key map [f1] 'help)
+    (define-key map [help] 'help)
+    (define-key map "?" 'help)
+    (define-key map "\C-g" 'quit)
+    (define-key map "\C-]" 'quit)
+    (define-key map "\e" 'exit-prefix)
+    (define-key map [escape] 'exit-prefix)
+    map)
   "Keymap that defines the responses to questions in `query-replace'.
 The \"bindings\" in this map are not commands; they are answers.
 The valid answers include `act', `skip', `act-and-show',
 `exit', `act-and-exit', `edit', `delete-and-edit', `recenter',
 `automatic', `backup', `exit-prefix', and `help'.")
 
-(define-key query-replace-map " " 'act)
-(define-key query-replace-map "\d" 'skip)
-(define-key query-replace-map [delete] 'skip)
-(define-key query-replace-map [backspace] 'skip)
-(define-key query-replace-map "y" 'act)
-(define-key query-replace-map "n" 'skip)
-(define-key query-replace-map "Y" 'act)
-(define-key query-replace-map "N" 'skip)
-(define-key query-replace-map "e" 'edit-replacement)
-(define-key query-replace-map "E" 'edit-replacement)
-(define-key query-replace-map "," 'act-and-show)
-(define-key query-replace-map "q" 'exit)
-(define-key query-replace-map "\r" 'exit)
-(define-key query-replace-map [return] 'exit)
-(define-key query-replace-map "." 'act-and-exit)
-(define-key query-replace-map "\C-r" 'edit)
-(define-key query-replace-map "\C-w" 'delete-and-edit)
-(define-key query-replace-map "\C-l" 'recenter)
-(define-key query-replace-map "!" 'automatic)
-(define-key query-replace-map "^" 'backup)
-(define-key query-replace-map "\C-h" 'help)
-(define-key query-replace-map [f1] 'help)
-(define-key query-replace-map [help] 'help)
-(define-key query-replace-map "?" 'help)
-(define-key query-replace-map "\C-g" 'quit)
-(define-key query-replace-map "\C-]" 'quit)
-(define-key query-replace-map "\e" 'exit-prefix)
-(define-key query-replace-map [escape] 'exit-prefix)
-
 (defun replace-match-string-symbols (n)
   "Process a list (and any sub-lists), expanding certain symbols.
 Symbol  Expands To
@@ -1120,8 +1296,8 @@ N     (match-string N)           (where N is a string of digits)
 #     replace-count
 
 Note that these symbols must be preceeded by a backslash in order to
-type them."
-  (while n
+type them using Lisp syntax."
+  (while (consp n)
     (cond
      ((consp (car n))
       (replace-match-string-symbols (car n))) ;Process sub-list
@@ -1178,7 +1354,7 @@ with the `noescape' argument set.
 
 (defun replace-match-data (integers reuse &optional new)
   "Like `match-data', but markers in REUSE get invalidated.
-If NEW is non-NIL, it is set and returned instead of fresh data,
+If NEW is non-nil, it is set and returned instead of fresh data,
 but coerced to the correct value of INTEGERS."
   (or (and new
           (progn
@@ -1186,34 +1362,29 @@ but coerced to the correct value of INTEGERS."
             (and (eq new reuse)
                  (eq (null integers) (markerp (car reuse)))
                  new)))
-      (match-data integers
-                 (prog1 reuse
-                   (while reuse
-                     (if (markerp (car reuse))
-                         (set-marker (car reuse) nil))
-                     (setq reuse (cdr reuse)))))))
+      (match-data integers reuse t)))
 
 (defun replace-match-maybe-edit (newtext fixedcase literal noedit match-data)
   "Make a replacement with `replace-match', editing `\\?'.
-NEXTEXT, FIXEDCASE, LITERAL are just passed on.  If NOEDIT is true, no
+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
 replacement.  In case editing is done, it is changed to use markers.
 
-The return value is non-NIL if there has been no `\\?' or NOEDIT was
+The return value is non-nil if there has been no `\\?' or NOEDIT was
 passed in.  If LITERAL is set, no checking is done, anyway."
   (unless (or literal noedit)
     (setq noedit t)
     (while (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\(\\\\\\?\\)"
                         newtext)
       (setq newtext
-           (read-input "Edit replacement string: "
-                       (prog1
-                           (cons
-                            (replace-match "" t t newtext 3)
-                            (1+ (match-beginning 3)))
-                         (setq match-data
-                               (replace-match-data
-                                nil match-data match-data))))
+           (read-string "Edit replacement string: "
+                         (prog1
+                             (cons
+                              (replace-match "" t t newtext 3)
+                              (1+ (match-beginning 3)))
+                           (setq match-data
+                                 (replace-match-data
+                                  nil match-data match-data))))
            noedit nil)))
   (set-match-data match-data)
   (replace-match newtext fixedcase literal)
@@ -1249,6 +1420,8 @@ make, or the user didn't cancel the call."
        (search-string from-string)
        (real-match-data nil)           ; the match data for the current match
        (next-replacement nil)
+       ;; This is non-nil if we know there is nothing for the user
+       ;; to edit in the replacement.
        (noedit nil)
        (keep-going t)
        (stack nil)
@@ -1264,8 +1437,10 @@ make, or the user didn't cancel the call."
 
        (message
         (if query-flag
-            (substitute-command-keys
-             "Query replacing %s with %s: (\\<query-replace-map>\\[help] for help) "))))
+            (apply 'propertize
+                   (substitute-command-keys
+                    "Query replacing %s with %s: (\\<query-replace-map>\\[help] for help) ")
+                   minibuffer-prompt-properties))))
 
     ;; If region is active, in Transient Mark mode, operate on region.
     (when start
@@ -1294,6 +1469,9 @@ make, or the user didn't cancel the call."
                                    (if regexp-flag from-string
                                      (regexp-quote from-string))
                                    "\\b")))
+    (when query-replace-lazy-highlight
+      (setq isearch-lazy-highlight-last-string nil))
+
     (push-mark)
     (undo-boundary)
     (unwind-protect
@@ -1304,66 +1482,76 @@ make, or the user didn't cancel the call."
                    ;; otherwise, search for a match after moving forward
                    ;; one char if progress is required.
                    (setq real-match-data
-                         (if (consp match-again)
-                             (progn (goto-char (nth 1 match-again))
-                                    (replace-match-data t
-                                     real-match-data
-                                     match-again))
-                           (and (or match-again
-                                    ;; MATCH-AGAIN non-nil means we
-                                    ;; accept an adjacent match.  If
-                                    ;; we don't, move one char to the
-                                    ;; right.  This takes us a
-                                    ;; character too far at the end,
-                                    ;; but this is undone after the
-                                    ;; while-loop.
-                                    (progn
-                                      (forward-char 1)
-                                      (not (or (eobp)
-                                               (and limit (>= (point) limit))))))
-                                (funcall search-function search-string limit t)
-                                ;; For speed, use only integers and
-                                ;; reuse the list used last time.
-                                (replace-match-data t real-match-data)))))
+                         (cond ((consp match-again)
+                                (goto-char (nth 1 match-again))
+                                (replace-match-data
+                                 t real-match-data match-again))
+                               ;; MATCH-AGAIN non-nil means accept an
+                               ;; adjacent match.
+                               (match-again
+                                (and
+                                 (funcall search-function search-string
+                                          limit t)
+                                 ;; For speed, use only integers and
+                                 ;; reuse the list used last time.
+                                 (replace-match-data t real-match-data)))
+                               ((and (< (1+ (point)) (point-max))
+                                     (or (null 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)
+                                  (if (funcall
+                                       search-function search-string
+                                       limit t)
+                                      (replace-match-data
+                                       t real-match-data)
+                                    (goto-char opoint)
+                                    nil))))))
+
+         ;; Record whether the match is nonempty, to avoid an infinite loop
+         ;; repeatedly matching the same empty string.
+         (setq nonempty-match
+               (/= (nth 0 real-match-data) (nth 1 real-match-data)))
+
+         ;; If the match is empty, record that the next one can't be
+         ;; adjacent.
+
+         ;; Otherwise, if matching a regular expression, do the next
+         ;; match now, since the replacement for this match may
+         ;; affect whether the next match is adjacent to this one.
+         ;; If that match is empty, don't use it.
+         (setq match-again
+               (and nonempty-match
+                    (or (not regexp-flag)
+                        (and (looking-at search-string)
+                             (let ((match (match-data)))
+                               (and (/= (nth 0 match) (nth 1 match))
+                                    match))))))
+
          ;; Optionally ignore matches that have a read-only property.
          (unless (and query-replace-skip-read-only
                       (text-property-not-all
-                       (match-beginning 0) (match-end 0)
+                       (nth 0 real-match-data) (nth 1 real-match-data)
                        'read-only nil))
 
-           ;; Record whether the match is nonempty, to avoid an infinite loop
-           ;; repeatedly matching the same empty string.
-           (setq nonempty-match
-                 (/= (nth 0 real-match-data) (nth 1 real-match-data)))
-
-           ;; If the match is empty, record that the next one can't be
-           ;; adjacent.
-
-           ;; Otherwise, if matching a regular expression, do the next
-           ;; match now, since the replacement for this match may
-           ;; affect whether the next match is adjacent to this one.
-           ;; If that match is empty, don't use it.
-           (setq match-again
-                 (and nonempty-match
-                      (or (not regexp-flag)
-                          (and (looking-at search-string)
-                               (let ((match (match-data)))
-                                 (and (/= (nth 0 match) (nth 1 match))
-                                      match))))))
-
            ;; Calculate the replacement string, if necessary.
            (when replacements
              (set-match-data real-match-data)
              (setq next-replacement
                    (funcall (car replacements) (cdr replacements)
-                            replace-count)
-                   noedit nil))
+                            replace-count)))
            (if (not query-flag)
                (let ((inhibit-read-only
                       query-replace-skip-read-only))
-                 (unless noedit
-                   (replace-highlight (nth 0 real-match-data)
-                                      (nth 1 real-match-data)))
+                 (unless (or literal noedit)
+                   (replace-highlight
+                    (nth 0 real-match-data) (nth 1 real-match-data)
+                    start end search-string
+                    (or delimited-flag regexp-flag) case-fold-search))
                  (setq noedit
                        (replace-match-maybe-edit
                         next-replacement nocasify literal
@@ -1379,11 +1567,16 @@ make, or the user didn't cancel the call."
                ;; `real-match-data'.
                (while (not done)
                  (set-match-data real-match-data)
-                 (replace-highlight (match-beginning 0) (match-end 0))
+                 (replace-highlight
+                  (match-beginning 0) (match-end 0)
+                  start end search-string
+                  (or delimited-flag regexp-flag) case-fold-search)
                  ;; Bind message-log-max so we don't fill up the message log
                  ;; with a bunch of identical messages.
                  (let ((message-log-max nil))
-                   (message message from-string next-replacement))
+                   (message message
+                             (query-replace-descr from-string)
+                             (query-replace-descr next-replacement)))
                  (setq key (read-event))
                  ;; Necessary in case something happens during read-event
                  ;; that clobbers the match data.
@@ -1479,8 +1672,8 @@ make, or the user didn't cancel the call."
                                                nil real-match-data
                                                real-match-data)
                               next-replacement
-                              (read-input "Edit replacement string: "
-                                          next-replacement)
+                              (read-string "Edit replacement string: "
+                                            next-replacement)
                               noedit nil)
                         (if replaced
                             (set-match-data real-match-data)
@@ -1507,20 +1700,24 @@ make, or the user didn't cancel the call."
                         (setq unread-command-events
                               (append (listify-key-sequence key)
                                       unread-command-events))
-                        (setq done t))))
+                        (setq done t)))
+                 (when query-replace-lazy-highlight
+                   ;; Force lazy rehighlighting only after replacements
+                   (if (not (memq def '(skip backup)))
+                       (setq isearch-lazy-highlight-last-string nil))))
                ;; Record previous position for ^ when we move on.
                ;; Change markers to numbers in the match data
                ;; since lots of markers slow down editing.
                (push (list (point) replaced
-;;; If the replacement has already happened, all we need is the
-;;; current match start and end.  We could get this with a trivial
-;;; match like
-;;; (save-excursion (goto-char (match-beginning 0))
-;;;                (search-forward (match-string 0))
-;;;                 (match-data t))
-;;; if we really wanted to avoid manually constructing match data.
-;;; Adding current-buffer is necessary so that match-data calls can
-;;; return markers which are appropriate for editing.
+;;;  If the replacement has already happened, all we need is the
+;;;  current match start and end.  We could get this with a trivial
+;;;  match like
+;;;  (save-excursion (goto-char (match-beginning 0))
+;;;                 (search-forward (match-string 0))
+;;;                  (match-data t))
+;;;  if we really wanted to avoid manually constructing match data.
+;;;  Adding current-buffer is necessary so that match-data calls can
+;;;  return markers which are appropriate for editing.
                            (if replaced
                                (list
                                 (match-beginning 0)
@@ -1529,12 +1726,6 @@ make, or the user didn't cancel the call."
                              (match-data t)))
                      stack)))))
 
-      ;; The code preventing adjacent regexp matches in the condition
-      ;; of the while-loop above will haven taken us one character
-      ;; beyond the last replacement.  Undo that.
-      (when (and regexp-flag (not match-again) (> replace-count 0))
-       (backward-char 1))
-
       (replace-dehighlight))
     (or unread-command-events
        (message "Replaced %d occurrence%s"
@@ -1542,27 +1733,29 @@ make, or the user didn't cancel the call."
                 (if (= replace-count 1) "" "s")))
     (and keep-going stack)))
 
-(defcustom query-replace-highlight t
-  "*Non-nil means to highlight words during query replacement."
-  :type 'boolean
-  :group 'matching)
-
 (defvar replace-overlay nil)
 
+(defun replace-highlight (match-beg match-end range-beg range-end
+                         string regexp case-fold)
+  (if query-replace-highlight
+      (if replace-overlay
+         (move-overlay replace-overlay match-beg match-end (current-buffer))
+       (setq replace-overlay (make-overlay match-beg match-end))
+       (overlay-put replace-overlay 'priority 1001) ;higher than lazy overlays
+       (overlay-put replace-overlay 'face 'query-replace)))
+  (if query-replace-lazy-highlight
+      (let ((isearch-string string)
+           (isearch-regexp regexp)
+           (search-whitespace-regexp nil)
+           (isearch-case-fold-search case-fold))
+       (isearch-lazy-highlight-new-loop range-beg range-end))))
+
 (defun replace-dehighlight ()
-  (and replace-overlay
-       (progn
-        (delete-overlay replace-overlay)
-        (setq replace-overlay nil))))
-
-(defun replace-highlight (start end)
-  (and query-replace-highlight
-       (if replace-overlay
-          (move-overlay replace-overlay start end (current-buffer))
-        (setq replace-overlay (make-overlay start end))
-        (overlay-put replace-overlay 'face
-                     (if (facep 'query-replace)
-                         'query-replace 'region)))))
+  (when replace-overlay
+    (delete-overlay replace-overlay))
+  (when query-replace-lazy-highlight
+    (lazy-highlight-cleanup lazy-highlight-cleanup)
+    (setq isearch-lazy-highlight-last-string nil)))
 
 ;; arch-tag: 16b4cd61-fd40-497b-b86f-b667c4cf88e4
 ;;; replace.el ends here