]> code.delx.au - gnu-emacs/blobdiff - lisp/replace.el
* lisp/loadup.el: Count byte-code functions as well.
[gnu-emacs] / lisp / replace.el
index d62da35f082b65d66182605b2c0351b7d54d60ee..ad87d474b8b46ce3874d60d46eb01ffb42a791d0 100644 (file)
@@ -1,16 +1,17 @@
 ;;; replace.el --- replace commands for Emacs
 
-;; Copyright (C) 1985, 1986, 1987, 1992, 1994, 1996, 1997, 2000, 2001,
-;;   2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+;; Copyright (C) 1985-1987, 1992, 1994, 1996-1997, 2000-2012
+;;   Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
+;; Package: emacs
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; 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 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -18,9 +19,7 @@
 ;; GNU General Public License for more details.
 
 ;; 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., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
 ;;; Code:
 
 (defcustom case-replace t
-  "*Non-nil means `query-replace' should preserve case in replacements."
+  "Non-nil means `query-replace' should preserve case in replacements."
   :type 'boolean
   :group 'matching)
 
-(defvar query-replace-history nil)
+(defvar query-replace-history nil
+  "Default history list for query-replace commands.
+See `query-replace-from-history-variable' and
+`query-replace-to-history-variable'.")
 
 (defvar query-replace-defaults nil
   "Default values of FROM-STRING and TO-STRING for `query-replace'.
@@ -64,24 +66,24 @@ strings or patterns."
   :version "20.3")
 
 (defcustom query-replace-skip-read-only nil
-  "*Non-nil means `query-replace' and friends ignore read-only matches."
+  "Non-nil means `query-replace' and friends ignore read-only matches."
   :type 'boolean
   :group 'matching
   :version "22.1")
 
 (defcustom query-replace-show-replacement t
-  "*Non-nil means to show what actual replacement text will be."
+  "Non-nil means to show what actual replacement text will be."
   :type 'boolean
   :group 'matching
   :version "23.1")
 
 (defcustom query-replace-highlight t
-  "*Non-nil means to highlight matches during query replacement."
+  "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.
+  "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')."
@@ -96,6 +98,10 @@ is highlighted lazily using isearch lazy highlighting (see
   :group 'matching
   :version "22.1")
 
+(defvar replace-count 0
+  "Number of replacements done so far.
+See `replace-regexp' and `query-replace-regexp-eval'.")
+
 (defun query-replace-descr (string)
   (mapconcat 'isearch-text-char-description string ""))
 
@@ -225,20 +231,21 @@ only matches surrounded by word boundaries.
 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
-                      (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
-                      ;; rather than the values they had this time.
-                      (if (and transient-mark-mode mark-active)
-                          (region-beginning))
-                      (if (and transient-mark-mode mark-active)
-                          (region-end)))))
+  (interactive
+   (let ((common
+         (query-replace-read-args
+          (concat "Query replace"
+                  (if current-prefix-arg " word" "")
+                  (if (and transient-mark-mode mark-active) " in region" ""))
+          nil)))
+     (list (nth 0 common) (nth 1 common) (nth 2 common)
+          ;; These are done separately here
+          ;; so that command-history will record these expressions
+          ;; rather than the values they had this time.
+          (if (and transient-mark-mode mark-active)
+              (region-beginning))
+          (if (and transient-mark-mode mark-active)
+              (region-end)))))
   (perform-replace from-string to-string t nil delimited nil nil start end))
 
 (define-key esc-map "%" 'query-replace)
@@ -291,9 +298,10 @@ Use \\[repeat-complex-command] after this command for details."
   (interactive
    (let ((common
          (query-replace-read-args
-          (if (and transient-mark-mode mark-active)
-              "Query replace regexp in region"
-            "Query replace regexp")
+          (concat "Query replace"
+                  (if current-prefix-arg " word" "")
+                  " regexp"
+                  (if (and transient-mark-mode mark-active) " in region" ""))
           t)))
      (list (nth 0 common) (nth 1 common) (nth 2 common)
           ;; These are done separately here
@@ -393,12 +401,13 @@ Fourth and fifth arg START and END specify the region to operate on."
                    (car regexp-search-ring)
                  (read-from-minibuffer "Map query replace (regexp): "
                                        nil nil nil
-                                       'query-replace-history nil t)))
+                                       query-replace-from-history-variable
+                                       nil t)))
          (to (read-from-minibuffer
               (format "Query replace %s with (space-separated strings): "
                       (query-replace-descr from))
               nil nil nil
-              'query-replace-history from t)))
+              query-replace-to-history-variable from t)))
      (list from to
           (and current-prefix-arg
                (prefix-numeric-value current-prefix-arg))
@@ -449,9 +458,10 @@ and TO-STRING is also null.)"
   (interactive
    (let ((common
          (query-replace-read-args
-          (if (and transient-mark-mode mark-active)
-              "Replace string in region"
-            "Replace string")
+          (concat "Replace"
+                  (if current-prefix-arg " word" "")
+                  " string"
+                  (if (and transient-mark-mode mark-active) " in region" ""))
           nil)))
      (list (nth 0 common) (nth 1 common) (nth 2 common)
           (if (and transient-mark-mode mark-active)
@@ -506,9 +516,10 @@ which will run faster and will not set the mark or print anything."
   (interactive
    (let ((common
          (query-replace-read-args
-          (if (and transient-mark-mode mark-active)
-              "Replace regexp in region"
-            "Replace regexp")
+          (concat "Replace"
+                  (if current-prefix-arg " word" "")
+                  " regexp"
+                  (if (and transient-mark-mode mark-active) " in region" ""))
           t)))
      (list (nth 0 common) (nth 1 common) (nth 2 common)
           (if (and transient-mark-mode mark-active)
@@ -524,6 +535,42 @@ which will run faster and will not set the mark or print anything."
 Maximum length of the history list is determined by the value
 of `history-length', which see.")
 
+(defvar occur-collect-regexp-history '("\\1")
+  "History of regexp for occur's collect operation")
+
+(defun read-regexp (prompt &optional default-value)
+  "Read regexp as a string using the regexp history and some useful defaults.
+Prompt for a regular expression with PROMPT (without a colon and
+space) in the minibuffer.  The optional argument DEFAULT-VALUE
+provides the value to display in the minibuffer prompt that is
+returned if the user just types RET.
+Values available via M-n are the string at point, the last isearch
+regexp, the last isearch string, and the last replacement regexp."
+  (let* ((defaults
+          (list (regexp-quote
+                 (or (funcall (or find-tag-default-function
+                                  (get major-mode 'find-tag-default-function)
+                                  'find-tag-default))
+                     ""))
+                (car regexp-search-ring)
+                (regexp-quote (or (car search-ring) ""))
+                (car (symbol-value
+                      query-replace-from-history-variable))))
+        (defaults (delete-dups (delq nil (delete "" defaults))))
+        ;; Don't add automatically the car of defaults for empty input
+        (history-add-new-input nil)
+        (input
+         (read-from-minibuffer
+          (if default-value
+              (format "%s (default %s): " prompt
+                      (query-replace-descr default-value))
+            (format "%s: " prompt))
+          nil nil nil 'regexp-history defaults t)))
+    (if (equal input "")
+       (or default-value input)
+      (prog1 input
+       (add-to-history 'regexp-history input)))))
+
 
 (defalias 'delete-non-matching-lines 'keep-lines)
 (defalias 'delete-matching-lines 'flush-lines)
@@ -534,20 +581,7 @@ of `history-length', which see.")
   "Read arguments for `keep-lines' and friends.
 Prompt for a regexp with PROMPT.
 Value is a list, (REGEXP)."
-  (let* ((default (list
-                  (regexp-quote
-                   (or (funcall (or find-tag-default-function
-                                    (get major-mode 'find-tag-default-function)
-                                    'find-tag-default))
-                       ""))
-                  (car regexp-search-ring)
-                  (regexp-quote (or (car search-ring) ""))
-                  (car (symbol-value
-                        query-replace-from-history-variable))))
-        (default (delete-dups (delq nil (delete "" default)))))
-    (list (read-from-minibuffer prompt nil nil nil
-                               'regexp-history default t)
-         nil nil t)))
+  (list (read-regexp prompt) nil nil t))
 
 (defun keep-lines (regexp &optional rstart rend interactive)
   "Delete all lines except those containing matches for REGEXP.
@@ -576,7 +610,7 @@ a previously found match."
   (interactive
    (progn
      (barf-if-buffer-read-only)
-     (keep-lines-read-args "Keep lines (containing match for regexp): ")))
+     (keep-lines-read-args "Keep lines containing match for regexp")))
   (if rstart
       (progn
        (goto-char (min rstart rend))
@@ -651,7 +685,7 @@ starting on the same line at which another match ended is ignored."
   (interactive
    (progn
      (barf-if-buffer-read-only)
-     (keep-lines-read-args "Flush lines (containing match for regexp): ")))
+     (keep-lines-read-args "Flush lines containing match for regexp")))
   (if rstart
       (progn
        (goto-char (min rstart rend))
@@ -681,7 +715,7 @@ starting on the same line at which another match ended is ignored."
   "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.
+in all respects as if it had been called interactively.
 
 If REGEXP contains upper case characters (excluding those preceded by `\\')
 and `search-upper-case' is non-nil, the matching is case-sensitive.
@@ -697,7 +731,7 @@ the previous match.  Hence, it ignores matches that overlap
 a previously found match."
 
   (interactive
-   (keep-lines-read-args "How many matches for (regexp): "))
+   (keep-lines-read-args "How many matches for regexp"))
   (save-excursion
     (if rstart
        (progn
@@ -727,11 +761,59 @@ a previously found match."
       count)))
 
 \f
+(defvar occur-menu-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map [next-error-follow-minor-mode]
+      `(menu-item ,(purecopy "Auto Occurrence Display")
+                 next-error-follow-minor-mode
+                 :help ,(purecopy
+                         "Display another occurrence when moving the cursor")
+                 :button (:toggle . (and (boundp 'next-error-follow-minor-mode)
+                                         next-error-follow-minor-mode))))
+    (define-key map [separator-1] menu-bar-separator)
+    (define-key map [kill-this-buffer]
+      `(menu-item ,(purecopy "Kill Occur Buffer") kill-this-buffer
+                 :help ,(purecopy "Kill the current *Occur* buffer")))
+    (define-key map [quit-window]
+      `(menu-item ,(purecopy "Quit Occur Window") quit-window
+                 :help ,(purecopy "Quit the current *Occur* buffer.  Bury it, and maybe delete the selected frame")))
+    (define-key map [revert-buffer]
+      `(menu-item ,(purecopy "Revert Occur Buffer") revert-buffer
+                 :help ,(purecopy "Replace the text in the *Occur* buffer with the results of rerunning occur")))
+    (define-key map [clone-buffer]
+      `(menu-item ,(purecopy "Clone Occur Buffer") clone-buffer
+                 :help ,(purecopy "Create and return a twin copy of the current *Occur* buffer")))
+    (define-key map [occur-rename-buffer]
+      `(menu-item ,(purecopy "Rename Occur Buffer") occur-rename-buffer
+                 :help ,(purecopy "Rename the current *Occur* buffer to *Occur: original-buffer-name*.")))
+    (define-key map [occur-edit-buffer]
+      `(menu-item ,(purecopy "Edit Occur Buffer") occur-edit-mode
+                 :help ,(purecopy "Edit the *Occur* buffer and apply changes to the original buffers.")))
+    (define-key map [separator-2] menu-bar-separator)
+    (define-key map [occur-mode-goto-occurrence-other-window]
+      `(menu-item ,(purecopy "Go To Occurrence Other Window") occur-mode-goto-occurrence-other-window
+                 :help ,(purecopy "Go to the occurrence the current line describes, in another window")))
+    (define-key map [occur-mode-goto-occurrence]
+      `(menu-item ,(purecopy "Go To Occurrence") occur-mode-goto-occurrence
+                 :help ,(purecopy "Go to the occurrence the current line describes")))
+    (define-key map [occur-mode-display-occurrence]
+      `(menu-item ,(purecopy "Display Occurrence") occur-mode-display-occurrence
+                 :help ,(purecopy "Display in another window the occurrence the current line describes")))
+    (define-key map [occur-next]
+      `(menu-item ,(purecopy "Move to Next Match") occur-next
+                 :help ,(purecopy "Move to the Nth (default 1) next match in an Occur mode buffer")))
+    (define-key map [occur-prev]
+      `(menu-item ,(purecopy "Move to Previous Match") occur-prev
+                 :help ,(purecopy "Move to the Nth (default 1) previous match in an Occur mode buffer")))
+    map)
+  "Menu keymap for `occur-mode'.")
+
 (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 "e" 'occur-edit-mode)
     (define-key map "\C-m" 'occur-mode-goto-occurrence)
     (define-key map "o" 'occur-mode-goto-occurrence-other-window)
     (define-key map "\C-o" 'occur-mode-display-occurrence)
@@ -739,55 +821,16 @@ a previously found match."
     (define-key map "\M-p" 'occur-prev)
     (define-key map "r" 'occur-rename-buffer)
     (define-key map "c" 'clone-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)
-    (define-key map [menu-bar] (make-sparse-keymap))
-    (define-key map [menu-bar occur]
-      (cons "Occur" map))
-    (define-key map [next-error-follow-minor-mode]
-      (menu-bar-make-mm-toggle next-error-follow-minor-mode
-                              "Auto Occurrence Display"
-                              "Display another occurrence when moving the cursor"))
-    (define-key map [separator-1] '("--"))
-    (define-key map [kill-this-buffer] 
-      '(menu-item "Kill occur buffer" kill-this-buffer
-                 :help "Kill the current *Occur* buffer"))
-    (define-key map [quit-window] 
-      '(menu-item "Quit occur window" quit-window
-                 :help "Quit the current *Occur* buffer.  Bury it, and maybe delete the selected frame"))
-    (define-key map [revert-buffer] 
-      '(menu-item "Revert occur buffer" revert-buffer
-                 :help "Replace the text in the *Occur* buffer with the results of rerunning occur"))
-    (define-key map [clone-buffer] 
-      '(menu-item "Clone occur buffer" clone-buffer
-                 :help "Create and return a twin copy of the current *Occur* buffer"))
-    (define-key map [occur-rename-buffer] 
-      '(menu-item "Rename occur buffer" occur-rename-buffer
-                 :help "Rename the current *Occur* buffer to *Occur: original-buffer-name*."))
-    (define-key map [separator-2] '("--"))
-    (define-key map [occur-mode-goto-occurrence-other-window]
-      '(menu-item "Go To Occurrence Other Window" occur-mode-goto-occurrence-other-window
-                 :help "Go to the occurrence the current line describes, in another window"))
-    (define-key map [occur-mode-goto-occurrence]
-      '(menu-item "Go To Occurrence" occur-mode-goto-occurrence
-                 :help "Go to the occurrence the current line describes"))
-    (define-key map [occur-mode-display-occurrence]
-      '(menu-item "Display Occurrence" occur-mode-display-occurrence
-                 :help "Display in another window the occurrence the current line describes"))
-    (define-key map [occur-next] 
-      '(menu-item "Move to next match" occur-next
-                 :help "Move to the Nth (default 1) next match in an Occur mode buffer"))
-    (define-key map [occur-prev] 
-      '(menu-item "Move to previous match" occur-prev
-                 :help "Move to the Nth (default 1) previous match in an Occur mode buffer"))
+    (define-key map [menu-bar occur] (cons (purecopy "Occur") occur-menu-map))
     map)
   "Keymap for `occur-mode'.")
 
 (defvar occur-revert-arguments nil
   "Arguments to pass to `occur-1' to revert an Occur mode buffer.
 See `occur-revert-function'.")
+(make-variable-buffer-local 'occur-revert-arguments)
+(put 'occur-revert-arguments 'permanent-local t)
 
 (defcustom occur-mode-hook '(turn-on-font-lock)
   "Hook run when entering Occur mode."
@@ -807,25 +850,88 @@ for this is to reveal context in an outline-mode when the occurrence is hidden."
   :group 'matching)
 
 (put 'occur-mode 'mode-class 'special)
-(defun occur-mode ()
+(define-derived-mode occur-mode special-mode "Occur"
   "Major mode for output from \\[occur].
 \\<occur-mode-map>Move point to one of the items in this buffer, then use
 \\[occur-mode-goto-occurrence] to go to the occurrence that the item refers to.
 Alternatively, click \\[occur-mode-mouse-goto] on an item to go to it.
 
 \\{occur-mode-map}"
-  (interactive)
-  (kill-all-local-variables)
-  (use-local-map occur-mode-map)
-  (setq major-mode 'occur-mode)
-  (setq mode-name "Occur")
   (set (make-local-variable 'revert-buffer-function) 'occur-revert-function)
-  (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-mode-hooks 'occur-mode-hook))
+  (setq next-error-function 'occur-next-error))
 
-(defun occur-revert-function (ignore1 ignore2)
+\f
+;;; Occur Edit mode
+
+(defvar occur-edit-mode-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map text-mode-map)
+    (define-key map [mouse-2] 'occur-mode-mouse-goto)
+    (define-key map "\C-c\C-c" 'occur-cease-edit)
+    (define-key map "\C-o" 'occur-mode-display-occurrence)
+    (define-key map "\C-c\C-f" 'next-error-follow-minor-mode)
+    (define-key map [menu-bar occur] (cons (purecopy "Occur") occur-menu-map))
+    map)
+  "Keymap for `occur-edit-mode'.")
+
+(define-derived-mode occur-edit-mode occur-mode "Occur-Edit"
+  "Major mode for editing *Occur* buffers.
+In this mode, changes to the *Occur* buffer are also applied to
+the originating buffer.
+
+To return to ordinary Occur mode, use \\[occur-cease-edit]."
+  (setq buffer-read-only nil)
+  (add-hook 'after-change-functions 'occur-after-change-function nil t)
+  (message (substitute-command-keys
+           "Editing: Type \\[occur-cease-edit] to return to Occur mode.")))
+
+(defun occur-cease-edit ()
+  "Switch from Occur Edit mode to Occur mode."
+  (interactive)
+  (when (derived-mode-p 'occur-edit-mode)
+    (occur-mode)
+    (message "Switching to Occur mode.")))
+
+(defun occur-after-change-function (beg end length)
+  (save-excursion
+    (goto-char beg)
+    (let* ((line-beg (line-beginning-position))
+          (m (get-text-property line-beg 'occur-target))
+          (buf (marker-buffer m))
+          col)
+      (when (and (get-text-property line-beg 'occur-prefix)
+                (not (get-text-property end 'occur-prefix)))
+       (when (= length 0)
+         ;; Apply occur-target property to inserted (e.g. yanked) text.
+         (put-text-property beg end 'occur-target m)
+         ;; Did we insert a newline?  Occur Edit mode can't create new
+         ;; Occur entries; just discard everything after the newline.
+         (save-excursion
+           (and (search-forward "\n" end t)
+                (delete-region (1- (point)) end))))
+       (let* ((line (- (line-number-at-pos)
+                       (line-number-at-pos (window-start))))
+              (readonly (with-current-buffer buf buffer-read-only))
+              (win (or (get-buffer-window buf)
+                       (display-buffer buf t)))
+              (line-end (line-end-position))
+              (text (save-excursion
+                      (goto-char (next-single-property-change
+                                  line-beg 'occur-prefix nil
+                                  line-end))
+                      (setq col (- (point) line-beg))
+                      (buffer-substring-no-properties (point) line-end))))
+         (with-selected-window win
+           (goto-char m)
+           (recenter line)
+           (if readonly
+               (message "Buffer `%s' is read only." buf)
+             (delete-region (line-beginning-position) (line-end-position))
+             (insert text))
+           (move-to-column col)))))))
+
+\f
+(defun occur-revert-function (_ignore1 _ignore2)
   "Handle `revert-buffer' for Occur mode buffers."
   (apply 'occur-1 (append occur-revert-arguments (list (buffer-name)))))
 
@@ -839,7 +945,7 @@ Alternatively, click \\[occur-mode-mouse-goto] on an item to go to it.
 
 (defalias 'occur-mode-mouse-goto 'occur-mode-goto-occurrence)
 (defun occur-mode-goto-occurrence (&optional event)
-  "Go to the occurrence the current line describes."
+  "Go to the occurrence on the current line."
   (interactive (list last-nonmenu-event))
   (let ((pos
          (if (null event)
@@ -850,9 +956,7 @@ Alternatively, click \\[occur-mode-mouse-goto] on an item to go to it.
            (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)
+               (occur-mode-find-occurrence))))))
     (pop-to-buffer (marker-buffer pos))
     (goto-char pos)
     (run-hooks 'occur-mode-find-occurrence-hook)))
@@ -869,11 +973,8 @@ Alternatively, click \\[occur-mode-mouse-goto] on an item to go to it.
   "Display in another window the occurrence the current line describes."
   (interactive)
   (let ((pos (occur-mode-find-occurrence))
-       window
-       ;; Bind these to ensure `display-buffer' puts it in another window.
-       same-window-buffer-names
-       same-window-regexps)
-    (setq window (display-buffer (marker-buffer pos)))
+       window)
+    (setq window (display-buffer (marker-buffer pos) t))
     ;; This is the way to set point in the proper window.
     (save-selected-window
       (select-window window)
@@ -948,7 +1049,7 @@ Compatibility function for \\[next-error] invocations."
   :version "22.1")
 
 (defcustom list-matching-lines-default-context-lines 0
-  "*Default number of context lines included around `list-matching-lines' matches.
+  "Default number of context lines included around `list-matching-lines' matches.
 A negative number means to include that many lines before the match.
 A positive number means to include that many lines both before and after."
   :type 'integer
@@ -957,13 +1058,13 @@ A positive number means to include that many lines both before and after."
 (defalias 'list-matching-lines 'occur)
 
 (defcustom list-matching-lines-face 'match
-  "*Face used by \\[list-matching-lines] to show the text that matches.
+  "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
   :group 'matching)
 
 (defcustom list-matching-lines-buffer-name-face 'underline
-  "*Face used by \\[list-matching-lines] to show the names of buffers.
+  "Face used by \\[list-matching-lines] to show the names of buffers.
 If the value is nil, don't highlight the buffer names specially."
   :type 'face
   :group 'matching)
@@ -971,67 +1072,37 @@ If the value is nil, don't highlight the buffer names specially."
 (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.
+  "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 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
-        (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))))
-
 (defun occur-read-primary-args ()
-  (let* ((default
-          (list (and transient-mark-mode mark-active
-                     (regexp-quote
-                      (buffer-substring-no-properties
-                       (region-beginning) (region-end))))
-                (regexp-quote
-                 (or (funcall
-                      (or find-tag-default-function
-                          (get major-mode 'find-tag-default-function)
-                          'find-tag-default))
-                     ""))
-                (car regexp-search-ring)
-                (regexp-quote (or (car search-ring) ""))
-                (car (symbol-value
-                      query-replace-from-history-variable))))
-        (default (delete-dups (delq nil (delete "" default))))
-        (input
-         (read-from-minibuffer
-          "List lines matching regexp: "
-          nil nil nil 'regexp-history default)))
-    (list input
-         (when current-prefix-arg
-           (prefix-numeric-value current-prefix-arg)))))
+  (let* ((perform-collect (consp current-prefix-arg))
+         (regexp (read-regexp (if perform-collect
+                                  "Collect strings matching regexp"
+                                "List lines matching regexp")
+                              (car regexp-history))))
+    (list regexp
+         (if perform-collect
+             ;; Perform collect operation
+             (if (zerop (regexp-opt-depth regexp))
+                 ;; No subexpression so collect the entire match.
+                 "\\&"
+               ;; Get the regexp for collection pattern.
+               (let ((default (car occur-collect-regexp-history)))
+                 (read-string
+                  (format "Regexp to collect (default %s): " default)
+                  nil 'occur-collect-regexp-history default)))
+           ;; Otherwise normal occur takes numerical prefix argument.
+           (when current-prefix-arg
+             (prefix-numeric-value current-prefix-arg))))))
 
 (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.
+Here `original-buffer-name' is the buffer name where 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'
@@ -1048,7 +1119,7 @@ invoke `occur'."
 
 (defun occur (regexp &optional nlines)
   "Show all lines in the current buffer containing a match for REGEXP.
-This function can not handle matches that span more than one line.
+If a match spreads across multiple lines, all those lines are shown.
 
 Each line is displayed with NLINES lines before and after, or -NLINES
 before if NLINES is negative.
@@ -1060,15 +1131,29 @@ It serves as a menu to find any of the occurrences in this buffer.
 \\<occur-mode-map>\\[describe-mode] in that buffer will explain how.
 
 If REGEXP contains upper case characters (excluding those preceded by `\\')
-and `search-upper-case' is non-nil, the matching is case-sensitive."
+and `search-upper-case' is non-nil, the matching is case-sensitive.
+
+When NLINES is a string or when the function is called
+interactively with prefix argument without a number (`C-u' alone
+as prefix) the matching strings are collected into the `*Occur*'
+buffer by using NLINES as a replacement regexp.  NLINES may
+contain \\& and \\N which convention follows `replace-match'.
+For example, providing \"defun\\s +\\(\\S +\\)\" for REGEXP and
+\"\\1\" for NLINES collects all the function names in a lisp
+program.  When there is no parenthesized subexpressions in REGEXP
+the entire match is collected.  In any case the searched buffers
+are not modified."
   (interactive (occur-read-primary-args))
   (occur-1 regexp nlines (list (current-buffer))))
 
+(defvar ido-ignore-item-temp-list)
+
 (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'.  When you invoke this command interactively, you must specify
-the buffer names that you want, one by one."
+the buffer names that you want, one by one.
+See also `multi-occur-in-matching-buffers'."
   (interactive
    (cons
     (let* ((bufs (list (read-buffer "First buffer to search: "
@@ -1123,6 +1208,8 @@ See also `multi-occur'."
                           (buffer-list))))))
 
 (defun occur-1 (regexp nlines bufs &optional buf-name)
+  (unless (and regexp (not (equal regexp "")))
+    (error "Occur doesn't work with the empty regexp"))
   (unless buf-name
     (setq buf-name "*Occur*"))
   (let (occur-buf
@@ -1140,28 +1227,54 @@ See also `multi-occur'."
     (setq occur-buf (get-buffer-create buf-name))
 
     (with-current-buffer occur-buf
-      (occur-mode)
+      (if (stringp nlines)
+         (fundamental-mode) ;; This is for collect operation.
+       (occur-mode))
       (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)
-                     (if (and case-fold-search search-upper-case)
-                         (isearch-no-upper-case-p regexp t)
-                       case-fold-search)
-                     list-matching-lines-buffer-name-face
-                     nil list-matching-lines-face
-                     (not (eq occur-excluded-properties t)))))
+       (let ((count
+              (if (stringp nlines)
+                   ;; Treat nlines as a regexp to collect.
+                  (let ((bufs active-bufs)
+                        (count 0))
+                    (while bufs
+                      (with-current-buffer (car bufs)
+                        (save-excursion
+                          (goto-char (point-min))
+                          (while (re-search-forward regexp nil t)
+                             ;; Insert the replacement regexp.
+                            (let ((str (match-substitute-replacement nlines)))
+                              (if str
+                                  (with-current-buffer occur-buf
+                                    (insert str)
+                                    (setq count (1+ count))
+                                    (or (zerop (current-column))
+                                        (insert "\n"))))))))
+                       (setq bufs (cdr bufs)))
+                     count)
+                ;; Perform normal occur.
+                (occur-engine
+                 regexp active-bufs occur-buf
+                 (or nlines list-matching-lines-default-context-lines)
+                 (if (and case-fold-search search-upper-case)
+                     (isearch-no-upper-case-p regexp t)
+                   case-fold-search)
+                 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'"
+           (message "Searched %d buffer%s%s; %s match%s%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))
+                    ;; Don't display regexp if with remaining text
+                    ;; it is longer than window-width.
+                    (if (> (+ (length regexp) 42) (window-width))
+                        "" (format " for `%s'" (query-replace-descr regexp)))))
          (setq occur-revert-arguments (list regexp nlines bufs))
           (if (= count 0)
               (kill-buffer occur-buf)
@@ -1171,28 +1284,26 @@ See also `multi-occur'."
             (set-buffer-modified-p nil)
             (run-hooks 'occur-hook)))))))
 
-(defun occur-engine-add-prefix (lines)
-  (mapcar
-   #'(lambda (line)
-       (concat "       :" line "\n"))
-   lines))
-
-(defun occur-engine (regexp buffers out-buf nlines case-fold-search
+(defun occur-engine (regexp buffers out-buf nlines case-fold
                            title-face prefix-face match-face keep-props)
   (with-current-buffer out-buf
     (let ((globalcount 0)
-         (coding nil))
+         (coding nil)
+         (case-fold-search case-fold))
       ;; Map over all the buffers
       (dolist (buf buffers)
        (when (buffer-live-p buf)
          (let ((matches 0)     ;; count of matched lines
                (lines 1)       ;; line count
+               (prev-after-lines nil)  ;; context lines of prev match
+               (prev-lines nil)        ;; line number of prev match endpt
                (matchbeg 0)
                (origpt nil)
                (begpt nil)
                (endpt nil)
                (marker nil)
                (curstring "")
+               (ret nil)
                (inhibit-field-text-motion t)
                (headerpt (with-current-buffer out-buf (point))))
            (with-current-buffer buf
@@ -1208,24 +1319,17 @@ 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 lines (+ lines (1- (count-lines origpt endpt))))
+                   ;; Get beginning of first match line and end of the last.
                    (save-excursion
                      (goto-char matchbeg)
-                     (setq begpt (line-beginning-position)
-                           endpt (line-end-position)))
+                     (setq begpt (line-beginning-position))
+                     (goto-char endpt)
+                     (setq endpt (line-end-position)))
+                   ;; Sum line numbers up to the first match line.
+                   (setq lines (+ lines (count-lines origpt begpt)))
                    (setq marker (make-marker))
                    (set-marker marker matchbeg)
-                   (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)))
+                   (setq curstring (occur-engine-line begpt endpt keep-props))
                    ;; Highlight the matches
                    (let ((len (length curstring))
                          (start 0))
@@ -1242,24 +1346,37 @@ See also `multi-occur'."
                         curstring)
                        (setq start (match-end 0))))
                    ;; Generate the string to insert for this match
-                   (let* ((out-line
+                   (let* ((match-prefix
+                           ;; Using 7 digits aligns tabs properly.
+                           (apply #'propertize (format "%7d:" lines)
+                                  (append
+                                   (when prefix-face
+                                     `(font-lock-face prefix-face))
+                                   `(occur-prefix t mouse-face (highlight)
+                                     ;; Allow insertion of text at
+                                     ;; the end of the prefix (for
+                                     ;; Occur Edit mode).
+                                     front-sticky t rear-nonsticky t
+                                     occur-target ,marker follow-link t
+                                     help-echo "mouse-2: go to this occurrence"))))
+                          (match-str
+                           ;; 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"))
+                          (out-line
                            (concat
-                            ;; Using 7 digits aligns tabs properly.
-                            (apply #'propertize (format "%7d:" lines)
-                                   (append
-                                    (when prefix-face
-                                      `(font-lock-face prefix-face))
-                                    `(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")
+                            match-prefix
+                            ;; Add non-numeric prefix to all non-first lines
+                            ;; of multi-line matches.
+                            (replace-regexp-in-string
+                             "\n"
+                             "\n       :"
+                             match-str)
                             ;; Add marker at eol, but no mouse props.
                             (propertize "\n" 'occur-target marker)))
                           (data
@@ -1267,30 +1384,48 @@ See also `multi-occur'."
                                ;; The simple display style
                                out-line
                              ;; The complex multi-line display style.
-                             (occur-context-lines out-line nlines keep-props)
-                             )))
+                             (setq ret (occur-context-lines
+                                        out-line nlines keep-props begpt endpt
+                                        lines prev-lines prev-after-lines))
+                             ;; Set first elem of the returned list to `data',
+                             ;; and the second elem to `prev-after-lines'.
+                             (setq prev-after-lines (nth 1 ret))
+                             (nth 0 ret))))
                      ;; Actually insert the match display data
                      (with-current-buffer out-buf
-                       (let ((beg (point))
-                             (end (progn (insert data) (point))))
-                         (unless (= nlines 0)
-                           (insert "-------\n")))))
+                       (insert data)))
                    (goto-char endpt))
                  (if endpt
                      (progn
-                       (setq lines (1+ lines))
+                       ;; Sum line numbers between first and last match lines.
+                       (setq lines (+ lines (count-lines begpt endpt)
+                                      ;; Add 1 for empty last match line since
+                                      ;; count-lines returns 1 line less.
+                                      (if (and (bolp) (eolp)) 1 0)))
                        ;; On to the next match...
                        (forward-line 1))
-                   (goto-char (point-max))))))
+                   (goto-char (point-max)))
+                 (setq prev-lines (1- lines)))
+               ;; Flush remaining context after-lines.
+               (when prev-after-lines
+                 (with-current-buffer out-buf
+                   (insert (apply #'concat (occur-engine-add-prefix
+                                            prev-after-lines)))))))
            (when (not (zerop matches)) ;; is the count zero?
              (setq globalcount (+ globalcount matches))
              (with-current-buffer out-buf
                (goto-char headerpt)
                (let ((beg (point))
                      end)
-                 (insert (format "%d match%s for \"%s\" in buffer: %s\n"
-                                 matches (if (= matches 1) "" "es")
-                                 regexp (buffer-name buf)))
+                 (insert (propertize
+                          (format "%d match%s%s in buffer: %s\n"
+                                  matches (if (= matches 1) "" "es")
+                                  ;; Don't display regexp for multi-buffer.
+                                  (if (> (length buffers) 1)
+                                      "" (format " for \"%s\""
+                                                 (query-replace-descr regexp)))
+                                  (buffer-name buf))
+                          'read-only t))
                  (setq end (point))
                  (add-text-properties beg end
                                       (append
@@ -1298,6 +1433,18 @@ See also `multi-occur'."
                                          `(font-lock-face ,title-face))
                                        `(occur-title ,buf))))
                (goto-char (point-min)))))))
+      ;; Display total match count and regexp for multi-buffer.
+      (when (and (not (zerop globalcount)) (> (length buffers) 1))
+       (goto-char (point-min))
+       (let ((beg (point))
+             end)
+         (insert (format "%d match%s total for \"%s\":\n"
+                         globalcount (if (= globalcount 1) "" "es")
+                         (query-replace-descr regexp)))
+         (setq end (point))
+         (add-text-properties beg end (when title-face
+                                        `(font-lock-face ,title-face))))
+       (goto-char (point-min)))
       (if coding
          ;; CODING is buffer-file-coding-system of the first buffer
          ;; that locally binds it.  Let's use it also for the output
@@ -1306,21 +1453,98 @@ See also `multi-occur'."
       ;; Return the number of matches
       globalcount)))
 
+(defun occur-engine-line (beg end &optional keep-props)
+  (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)))
+  (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)))
+
+(defun occur-engine-add-prefix (lines)
+  (mapcar
+   #'(lambda (line)
+       (concat "       :" line "\n"))
+   lines))
+
+(defun occur-accumulate-lines (count &optional keep-props pt)
+  (save-excursion
+    (when pt
+      (goto-char pt))
+    (let ((forwardp (> count 0))
+         result beg end moved)
+      (while (not (or (zerop count)
+                     (if forwardp
+                         (eobp)
+                       (and (bobp) (not moved)))))
+       (setq count (+ count (if forwardp -1 1)))
+       (setq beg (line-beginning-position)
+             end (line-end-position))
+       (push (occur-engine-line beg end keep-props) result)
+       (setq moved (= 0 (forward-line (if forwardp 1 -1)))))
+      (nreverse result))))
+
 ;; Generate context display for occur.
 ;; OUT-LINE is the line where the match is.
 ;; NLINES and KEEP-PROPS are args to occur-engine.
+;; LINES is line count of the current match,
+;; PREV-LINES is line count of the previous match,
+;; PREV-AFTER-LINES is a list of after-context lines of the previous match.
 ;; Generate a list of lines, add prefixes to all but OUT-LINE,
 ;; then concatenate them all together.
-(defun occur-context-lines (out-line nlines keep-props)
-  (apply #'concat
-        (nconc
-         (occur-engine-add-prefix
-          (nreverse (cdr (occur-accumulate-lines
-                          (- (1+ (abs nlines))) keep-props))))
-         (list out-line)
-         (if (> nlines 0)
-             (occur-engine-add-prefix
-              (cdr (occur-accumulate-lines (1+ nlines) keep-props)))))))
+(defun occur-context-lines (out-line nlines keep-props begpt endpt
+                                    lines prev-lines prev-after-lines)
+  ;; Find after- and before-context lines of the current match.
+  (let ((before-lines
+        (nreverse (cdr (occur-accumulate-lines
+                        (- (1+ (abs nlines))) keep-props begpt))))
+       (after-lines
+        (cdr (occur-accumulate-lines
+              (1+ nlines) keep-props endpt)))
+       separator)
+
+    ;; Combine after-lines of the previous match
+    ;; with before-lines of the current match.
+
+    (when prev-after-lines
+      ;; Don't overlap prev after-lines with current before-lines.
+      (if (>= (+ prev-lines (length prev-after-lines))
+             (- lines      (length before-lines)))
+         (setq prev-after-lines
+               (butlast prev-after-lines
+                        (- (length prev-after-lines)
+                           (- lines prev-lines (length before-lines) 1))))
+       ;; Separate non-overlapping context lines with a dashed line.
+       (setq separator "-------\n")))
+
+    (when prev-lines
+      ;; Don't overlap current before-lines with previous match line.
+      (if (<= (- lines (length before-lines))
+             prev-lines)
+         (setq before-lines
+               (nthcdr (- (length before-lines)
+                          (- lines prev-lines 1))
+                       before-lines))
+       ;; Separate non-overlapping before-context lines.
+       (unless (> nlines 0)
+         (setq separator "-------\n"))))
+
+    (list
+     ;; Return a list where the first element is the output line.
+     (apply #'concat
+           (append
+            (and prev-after-lines
+                 (occur-engine-add-prefix prev-after-lines))
+            (and separator (list separator))
+            (occur-engine-add-prefix before-lines)
+            (list out-line)))
+     ;; And the second element is the list of context after-lines.
+     (if (> nlines 0) after-lines))))
+
 \f
 ;; It would be nice to use \\[...], but there is no reasonable way
 ;; to make that display both SPC and Y.
@@ -1370,8 +1594,22 @@ E to edit the replacement string"
   "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'.")
+`exit', `act-and-exit', `edit', `edit-replacement', `delete-and-edit',
+`recenter', `automatic', `backup', `exit-prefix', `quit', and `help'.")
+
+(defvar multi-query-replace-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map query-replace-map)
+    (define-key map "Y" 'automatic-all)
+    (define-key map "N" 'exit-current)
+    map)
+  "Keymap that defines additional bindings for multi-buffer replacements.
+It extends its parent map `query-replace-map' with new bindings to
+operate on a set of buffers/files.  The difference with its parent map
+is the additional answers `automatic-all' to replace all remaining
+matches in all remaining buffers with no more questions, and
+`exit-current' to skip remaining matches in the current buffer
+and to continue with the next buffer in the sequence.")
 
 (defun replace-match-string-symbols (n)
   "Process a list (and any sub-lists), expanding certain symbols.
@@ -1382,7 +1620,7 @@ N     (match-string N)           (where N is a string of digits)
 #&    (string-to-number (match-string 0))
 #     replace-count
 
-Note that these symbols must be preceeded by a backslash in order to
+Note that these symbols must be preceded by a backslash in order to
 type them using Lisp syntax."
   (while (consp n)
     (cond
@@ -1405,8 +1643,9 @@ type them using Lisp syntax."
          (setcar n 'replace-count))))))
     (setq n (cdr n))))
 
-(defun replace-eval-replacement (expression replace-count)
-  (let ((replacement (eval expression)))
+(defun replace-eval-replacement (expression count)
+  (let* ((replace-count count)
+         (replacement (eval expression)))
     (if (stringp replacement)
         replacement
       (prin1-to-string replacement t))))
@@ -1426,15 +1665,15 @@ with the `noescape' argument set.
                                (prin1-to-string replacement t))
                              t t)))
 
-(defun replace-loop-through-replacements (data replace-count)
-  ;; DATA is a vector contaning the following values:
+(defun replace-loop-through-replacements (data count)
+  ;; DATA is a vector containing the following values:
   ;;   0 next-rotate-count
   ;;   1 repeat-count
   ;;   2 next-replacement
   ;;   3 replacements
-  (if (= (aref data 0) replace-count)
+  (if (= (aref data 0) count)
       (progn
-        (aset data 0 (+ replace-count (aref data 1)))
+        (aset data 0 (+ count (aref data 1)))
         (let ((next (cdr (aref data 2))))
           (aset data 2 (if (consp next) next (aref data 3))))))
   (car (aref data 2)))
@@ -1477,6 +1716,18 @@ passed in.  If LITERAL is set, no checking is done, anyway."
   (replace-match newtext fixedcase literal)
   noedit)
 
+(defvar replace-search-function 'search-forward
+  "Function to use when searching for strings to replace.
+It is used by `query-replace' and `replace-string', and is called
+with three arguments, as if it were `search-forward'.")
+
+(defvar replace-re-search-function 're-search-forward
+  "Function to use when searching for regexps to replace.
+It is used by `query-replace-regexp', `replace-regexp',
+`query-replace-regexp-eval', and `map-query-replace-regexp'.
+It is called with three arguments, as if it were
+`re-search-forward'.")
+
 (defun perform-replace (from-string replacements
                        query-flag regexp-flag delimited-flag
                        &optional repeat-count map start end)
@@ -1502,7 +1753,10 @@ make, or the user didn't cancel the call."
            case-fold-search))
          (nocasify (not (and case-replace case-fold-search)))
          (literal (or (not regexp-flag) (eq regexp-flag 'literal)))
-         (search-function (if regexp-flag 're-search-forward 'search-forward))
+         (search-function
+         (if regexp-flag
+             replace-re-search-function
+           replace-search-function))
          (search-string from-string)
          (real-match-data nil)       ; The match data for the current match.
          (next-replacement nil)
@@ -1513,6 +1767,8 @@ make, or the user didn't cancel the call."
          (stack nil)
          (replace-count 0)
          (nonempty-match nil)
+        (multi-buffer nil)
+        (recenter-last-op nil) ; Start cycling order with initial position.
 
          ;; If non-nil, it is marker saying where in the buffer to stop.
          (limit nil)
@@ -1534,6 +1790,11 @@ make, or the user didn't cancel the call."
       (goto-char (min start end))
       (deactivate-mark))
 
+    ;; If last typed key in previous call of multi-buffer perform-replace
+    ;; was `automatic-all', don't ask more questions in next files
+    (when (eq (lookup-key map (vector last-input-event)) 'automatic-all)
+      (setq query-flag nil multi-buffer t))
+
     ;; REPLACEMENTS is either a string, a list of strings, or a cons cell
     ;; containing a function and its first argument.  The function is
     ;; called to generate each replacement like this:
@@ -1631,8 +1892,7 @@ make, or the user didn't cancel the call."
                    (funcall (car replacements) (cdr replacements)
                             replace-count)))
            (if (not query-flag)
-               (let ((inhibit-read-only
-                      query-replace-skip-read-only))
+               (progn
                  (unless (or literal noedit)
                    (replace-highlight
                     (nth 0 real-match-data) (nth 1 real-match-data)
@@ -1681,6 +1941,7 @@ make, or the user didn't cancel the call."
                         (with-output-to-temp-buffer "*Help*"
                           (princ
                            (concat "Query replacing "
+                                   (if delimited-flag "word " "")
                                    (if regexp-flag "regexp " "")
                                    from-string " with "
                                    next-replacement ".\n\n"
@@ -1691,6 +1952,8 @@ make, or the user didn't cancel the call."
                        ((eq def 'exit)
                         (setq keep-going nil)
                         (setq done t))
+                       ((eq def 'exit-current)
+                        (setq multi-buffer t keep-going nil done t))
                        ((eq def 'backup)
                         (if stack
                             (let ((elt (pop stack)))
@@ -1730,18 +1993,24 @@ make, or the user didn't cancel the call."
                                   real-match-data (replace-match-data
                                                    t real-match-data)
                                   replaced t)))
-                       ((eq def 'automatic)
+                       ((or (eq def 'automatic) (eq def 'automatic-all))
                         (or replaced
                             (setq noedit
                                   (replace-match-maybe-edit
                                    next-replacement nocasify literal
                                    noedit real-match-data)
                                   replace-count (1+ replace-count)))
-                        (setq done t query-flag nil replaced t))
+                        (setq done t query-flag nil replaced t)
+                        (if (eq def 'automatic-all) (setq multi-buffer t)))
                        ((eq def 'skip)
                         (setq done t))
                        ((eq def 'recenter)
-                        (recenter nil))
+                        ;; `this-command' has the value `query-replace',
+                        ;; so we need to bind it to `recenter-top-bottom'
+                        ;; to allow it to detect a sequence of `C-l'.
+                        (let ((this-command 'recenter-top-bottom)
+                              (last-command 'recenter-top-bottom))
+                          (recenter-top-bottom)))
                        ((eq def 'edit)
                         (let ((opos (point-marker)))
                           (setq real-match-data (replace-match-data
@@ -1795,9 +2064,12 @@ make, or the user didn't cancel the call."
                                       unread-command-events))
                         (setq done t)))
                  (when query-replace-lazy-highlight
-                   ;; Force lazy rehighlighting only after replacements
+                   ;; Force lazy rehighlighting only after replacements.
                    (if (not (memq def '(skip backup)))
-                       (setq isearch-lazy-highlight-last-string nil))))
+                       (setq isearch-lazy-highlight-last-string nil)))
+                 (unless (eq def 'recenter)
+                   ;; Reset recenter cycling order to initial position.
+                   (setq recenter-last-op nil)))
                ;; Record previous position for ^ when we move on.
                ;; Change markers to numbers in the match data
                ;; since lots of markers slow down editing.
@@ -1824,7 +2096,12 @@ make, or the user didn't cancel the call."
        (message "Replaced %d occurrence%s"
                 replace-count
                 (if (= replace-count 1) "" "s")))
-    (and keep-going stack)))
+    (or (and keep-going stack) multi-buffer)))
+
+(defvar isearch-error)
+(defvar isearch-forward)
+(defvar isearch-case-fold-search)
+(defvar isearch-string)
 
 (defvar replace-overlay nil)
 
@@ -1839,8 +2116,13 @@ make, or the user didn't cancel the call."
   (if query-replace-lazy-highlight
       (let ((isearch-string string)
            (isearch-regexp regexp)
+           ;; Set isearch-word to nil because word-replace is regexp-based,
+           ;; so `isearch-search-fun' should not use `word-search-forward'.
+           (isearch-word nil)
            (search-whitespace-regexp nil)
-           (isearch-case-fold-search case-fold))
+           (isearch-case-fold-search case-fold)
+           (isearch-forward t)
+           (isearch-error nil))
        (isearch-lazy-highlight-new-loop range-beg range-end))))
 
 (defun replace-dehighlight ()
@@ -1850,5 +2132,4 @@ make, or the user didn't cancel the call."
     (lazy-highlight-cleanup lazy-highlight-cleanup)
     (setq isearch-lazy-highlight-last-string nil)))
 
-;; arch-tag: 16b4cd61-fd40-497b-b86f-b667c4cf88e4
 ;;; replace.el ends here