]> code.delx.au - gnu-emacs/blobdiff - lisp/replace.el
(normal-splash-screen, fancy-splash-screens-1): Add a reference to the Lisp
[gnu-emacs] / lisp / replace.el
index 97b05cbc80ab2883192c57f10f6673ade61f055e..f1792b499fcd93f1ef446e3bff9f4bf88b7fd889 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 Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
 
@@ -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:
 
@@ -62,12 +62,33 @@ 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."
@@ -86,16 +107,19 @@ wants to replace FROM with TO."
                                      query-replace-from-history-variable))))
              (read-from-minibuffer
               (if (and lastto lastfrom)
-                  (format "%s (default %s -> %s): " string
+                  (format "%s (default %s -> %s): " prompt
                           (query-replace-descr lastfrom)
                           (query-replace-descr lastto))
-                (format "%s: " string))
+                (format "%s: " prompt))
               nil nil nil
               query-replace-from-history-variable
               nil t t))))
       (if (and (zerop (length from)) lastto lastfrom)
-         (cons lastfrom
-               (query-replace-compile-replacement lastto regexp-flag))
+         (progn
+           (set query-replace-from-history-variable
+                (cdr (symbol-value query-replace-from-history-variable)))
+           (cons lastfrom
+                 (query-replace-compile-replacement lastto regexp-flag)))
        ;; Warn if user types \n or \t, but don't reject the input.
        (and regexp-flag
             (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\(\\\\[nt]\\)" from)
@@ -149,22 +173,22 @@ the original string if not."
     to))
 
 
-(defun query-replace-read-to (from string regexp-flag)
+(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
      (read-from-minibuffer
-      (format "%s %s with: " string (query-replace-descr from))
+      (format "%s %s with: " prompt (query-replace-descr from))
       nil nil nil
       query-replace-to-history-variable from t t))
    regexp-flag))
 
-(defun query-replace-read-args (string regexp-flag &optional noerror)
+(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)
@@ -193,7 +217,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
@@ -253,7 +281,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
@@ -399,7 +431,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))
@@ -453,7 +489,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))
@@ -476,21 +516,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
@@ -499,10 +550,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))
@@ -516,7 +577,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)
@@ -526,22 +587,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
@@ -551,7 +624,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)
@@ -563,13 +636,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.
@@ -578,18 +656,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
@@ -601,11 +685,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)
@@ -632,7 +720,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)
 
@@ -653,24 +741,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
@@ -679,10 +755,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)))
 
@@ -734,19 +822,43 @@ 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.
+    (set-window-point (get-buffer-window (current-buffer)) (point))
+    (occur-mode-goto-occurrence)))
 \f
+(defface match
+  '((((class color) (min-colors 88) (background light))
+     :background "Tan")
+    (((class color) (min-colors 88) (background dark))
+     :background "RoyalBlue3")
+    (((class color) (min-colors 8))
+     :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.
@@ -756,7 +868,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
@@ -768,21 +880,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))))
@@ -792,7 +921,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
@@ -806,26 +935,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.
@@ -844,7 +973,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: "
@@ -864,15 +994,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
@@ -882,62 +1016,63 @@ 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))
+       (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
@@ -948,8 +1083,9 @@ 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)
+         ;; Don't generate undo entries for creation of the initial contents.
+         (buffer-undo-list t)
          (coding nil))
       ;; Map over all the buffers
       (dolist (buf buffers)
@@ -962,9 +1098,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.
@@ -977,29 +1113,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
@@ -1009,9 +1154,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
@@ -1031,13 +1186,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
@@ -1084,42 +1233,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
@@ -1196,16 +1346,11 @@ 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.
 
@@ -1216,14 +1361,14 @@ passed in.  If LITERAL is set, no checking is done, anyway."
     (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)
@@ -1304,6 +1449,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
@@ -1335,32 +1483,33 @@ make, or the user didn't cancel the call."
                                 ;; For speed, use only integers and
                                 ;; reuse the list used last time.
                                 (replace-match-data t real-match-data)))))
+         
+         ;; 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)
@@ -1371,9 +1520,11 @@ make, or the user didn't cancel the call."
            (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
@@ -1389,7 +1540,10 @@ 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))
@@ -1491,8 +1645,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)
@@ -1519,20 +1673,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)
@@ -1554,27 +1712,28 @@ 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)
+           (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