]> code.delx.au - gnu-emacs/blobdiff - lisp/replace.el
Revision: miles@gnu.org--gnu-2004/emacs--unicode--0--patch-15
[gnu-emacs] / lisp / replace.el
index 6b1d0309bb6ec45a73e65ba7524552944e3dad88..89f55c2829e9cecf370c423ca85252d14b94d14d 100644 (file)
@@ -1,7 +1,7 @@
 ;;; replace.el --- replace commands for Emacs
 
-;; Copyright (C) 1985, 86, 87, 92, 94, 96, 1997, 2000, 2001, 2002
-;;  Free Software Foundation, Inc.
+;; Copyright (C) 1985, 86, 87, 92, 94, 96, 1997, 2000, 2001, 2002,
+;;   2003, 2004 Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
 
 
 (defvar query-replace-history nil)
 
-(defvar query-replace-interactive nil
+(defcustom query-replace-interactive nil
   "Non-nil means `query-replace' uses the last search string.
-That becomes the \"string to replace\".")
+That becomes the \"string to replace\"."
+  :type 'boolean
+  :group 'matching)
 
 (defcustom query-replace-from-history-variable 'query-replace-history
   "History list to use for the FROM argument of `query-replace' commands.
@@ -70,26 +72,30 @@ strings or patterns."
   (let (from to)
     (if query-replace-interactive
        (setq from (car (if regexp-flag regexp-search-ring search-ring)))
-      (setq from (read-from-minibuffer (format "%s: " string)
-                                      nil nil nil
-                                      query-replace-from-history-variable
-                                      nil t))
+      ;; The save-excursion here is in case the user marks and copies
+      ;; a region in order to specify the minibuffer input.
+      ;; That should not clobber the region for the query-replace itself.
+      (save-excursion
+       (setq from (read-from-minibuffer (format "%s: " string)
+                                        nil nil nil
+                                        query-replace-from-history-variable
+                                        nil t)))
       ;; Warn if user types \n or \t, but don't reject the input.
-      (if (string-match "\\\\[nt]" from)
-         (let ((match (match-string 0 from)))
-           (cond
-            ((string= match "\\n")
-             (message "Note: `\\n' here doesn't match a newline; to do that, type C-q C-j instead"))
-            ((string= match "\\t")
-             (message "Note: `\\t' here doesn't match a tab; to do that, just type TAB")))
-           (sit-for 2))))
-
-    (setq to (read-from-minibuffer (format "%s %s with: " string from)
-                                  nil nil nil
-                                  query-replace-to-history-variable from t))
-    (if (and transient-mark-mode mark-active)
-       (list from to current-prefix-arg (region-beginning) (region-end))
-      (list from to current-prefix-arg nil nil))))
+      (and regexp-flag
+          (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\(\\\\[nt]\\)" from)
+          (let ((match (match-string 3 from)))
+            (cond
+             ((string= match "\\n")
+              (message "Note: `\\n' here doesn't match a newline; to do that, type C-q C-j instead"))
+             ((string= match "\\t")
+              (message "Note: `\\t' here doesn't match a tab; to do that, just type TAB")))
+            (sit-for 2))))
+
+    (save-excursion
+      (setq to (read-from-minibuffer (format "%s %s with: " string from)
+                                    nil nil nil
+                                    query-replace-to-history-variable from t)))
+    (list from to current-prefix-arg)))
 
 (defun query-replace (from-string to-string &optional delimited start end)
   "Replace some occurrences of FROM-STRING with TO-STRING.
@@ -103,18 +109,29 @@ If `query-replace-interactive' is non-nil, the last incremental search
 string is used as FROM-STRING--you don't have to specify it with the
 minibuffer.
 
-Replacement transfers the case of the old text to the new text,
-if `case-replace' and `case-fold-search'
-are non-nil and FROM-STRING has no uppercase letters.
-\(Preserving case means that if the string matched is all caps, or capitalized,
-then its replacement is upcased or capitalized.)
+Matching is independent of case if `case-fold-search' is non-nil and
+FROM-STRING has no uppercase letters.  Replacement transfers the case
+pattern of the old text to the new text, if `case-replace' and
+`case-fold-search' are non-nil and FROM-STRING has no uppercase
+letters.  \(Transferring the case pattern means that if the old text
+matched is all caps, or capitalized, then its replacement is upcased
+or capitalized.)
 
 Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
 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 (query-replace-read-args "Query replace" nil))
+  (interactive (let ((common
+                     (query-replace-read-args "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)))))
   (perform-replace from-string to-string t nil delimited nil nil start end))
 
 (define-key esc-map "%" 'query-replace)
@@ -131,8 +148,13 @@ If `query-replace-interactive' is non-nil, the last incremental search
 regexp is used as REGEXP--you don't have to specify it with the
 minibuffer.
 
-Preserves case in each replacement if `case-replace' and `case-fold-search'
-are non-nil and REGEXP has no uppercase letters.
+Matching is independent of case if `case-fold-search' is non-nil and
+REGEXP has no uppercase letters.  Replacement transfers the case
+pattern of the old text to the new text, if `case-replace' and
+`case-fold-search' are non-nil and REGEXP has no uppercase letters.
+\(Transferring the case pattern means that if the old text matched is
+all caps, or capitalized, then its replacement is upcased or
+capitalized.)
 
 Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
 only matches surrounded by word boundaries.
@@ -140,9 +162,62 @@ Fourth and fifth arg START and END specify the region to operate on.
 
 In TO-STRING, `\\&' stands for whatever matched the whole of REGEXP,
 and `\\=\\N' (where N is a digit) stands for
- whatever what matched the Nth `\\(...\\)' in REGEXP."
-  (interactive (query-replace-read-args "Query replace regexp" t))
+whatever what matched the Nth `\\(...\\)' in REGEXP.
+
+When this function is called interactively, the replacement text
+can also contain `\\,' followed by a Lisp expression.  The escaped
+shorthands for `query-replace-regexp-eval' are also valid
+here: within the Lisp expression, you can use `\\&' for the whole
+match string, `\\N' for partial matches, `\\#&' and `\\#N' for
+the respective numeric values, and `\\#' for `replace-count'.
+
+If your Lisp expression is an identifier and the next
+letter in the replacement string would be interpreted as part of it,
+you can wrap it with an expression like `\\,(or \\#)'.  Incidentally,
+for this particular case you may also enter `\\#' in the replacement
+text directly.
+
+When you use `\\,' or `\\#' in the replacement, TO-STRING actually
+becomes a list with expanded shorthands.
+Use \\[repeat-complex-command] after this command to see details."
+  (interactive
+   (let ((common
+         (query-replace-read-args "Query replace regexp" t)))
+     (list
+      (nth 0 common)
+      (if (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\\\[,#]"
+                       (nth 1 common))
+         (let ((to-string (nth 1 common)) pos to-expr char prompt)
+           (while (string-match
+                   "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\\\[,#]"
+                   to-string)
+             (setq pos (match-end 0))
+             (push (substring to-string 0 (- pos 2)) to-expr)
+             (setq char (aref to-string (1- pos))
+                   to-string (substring to-string pos))
+             (cond ((eq char ?\#)
+                    (push '(number-to-string replace-count) to-expr))
+                   ((eq char ?\,)
+                    (setq pos (read-from-string to-string))
+                    (push `(replace-quote ,(car pos)) to-expr)
+                    (setq to-string (substring to-string (cdr pos))))))
+           (setq to-expr (nreverse (delete "" (cons to-string to-expr))))
+           (replace-match-string-symbols to-expr)
+           (cons 'replace-eval-replacement 
+                 (if (> (length to-expr) 1)
+                     (cons 'concat to-expr)
+                   (car to-expr))))
+       (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 regexp to-string t t delimited nil nil start end))
+
 (define-key esc-map [?\C-%] 'query-replace-regexp)
 
 (defun query-replace-regexp-eval (regexp to-expr &optional delimited start end)
@@ -159,6 +234,7 @@ For convenience, when entering TO-EXPR interactively, you can use `\\&' or
 `\\0' to stand for whatever matched the whole of REGEXP, and `\\N' (where
 N is a digit) to stand for whatever matched the Nth `\\(...\\)' in REGEXP.
 Use `\\#&' or `\\#N' if you want a number instead of a string.
+In interactive use, `\\#' in itself stands for `replace-count'.
 
 In Transient Mark mode, if the mark is active, operate on the contents
 of the region.  Otherwise, operate from point to the end of the buffer.
@@ -174,10 +250,7 @@ Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
 only matches that are surrounded by word boundaries.
 Fourth and fifth arg START and END specify the region to operate on."
   (interactive
-   (let (from to start end)
-     (when (and transient-mark-mode mark-active)
-       (setq start (region-beginning)
-            end (region-end)))
+   (let (from to)
      (if query-replace-interactive
          (setq from (car regexp-search-ring))
        (setq from (read-from-minibuffer "Query replace regexp: "
@@ -190,9 +263,13 @@ Fourth and fifth arg START and END specify the region to operate on."
      ;; We make TO a list because replace-match-string-symbols requires one,
      ;; and the user might enter a single token.
      (replace-match-string-symbols to)
-     (list from (car to) current-prefix-arg start end)))
+     (list from (car to) current-prefix-arg
+          (if (and transient-mark-mode mark-active)
+              (region-beginning))
+          (if (and transient-mark-mode mark-active)
+              (region-end)))))
   (perform-replace regexp (cons 'replace-eval-replacement to-expr)
-                  t t delimited nil nil start end))
+                  t 'literal delimited nil nil start end))
 
 (defun map-query-replace-regexp (regexp to-strings &optional n start end)
   "Replace some matches for REGEXP with various strings, in rotation.
@@ -215,10 +292,7 @@ A prefix argument N says to use each replacement string N times
 before rotating to the next.
 Fourth and fifth arg START and END specify the region to operate on."
   (interactive
-   (let (from to start end)
-     (when (and transient-mark-mode mark-active)
-       (setq start (region-beginning)
-            end (region-end)))
+   (let (from to)
      (setq from (if query-replace-interactive
                    (car regexp-search-ring)
                  (read-from-minibuffer "Map query replace (regexp): "
@@ -229,7 +303,13 @@ Fourth and fifth arg START and END specify the region to operate on."
                       from)
               nil nil nil
               'query-replace-history from t))
-     (list from to start end current-prefix-arg)))
+     (list from to
+          (and current-prefix-arg
+               (prefix-numeric-value current-prefix-arg))
+          (if (and transient-mark-mode mark-active)
+              (region-beginning))
+          (if (and transient-mark-mode mark-active)
+              (region-end)))))
   (let (replacements)
     (if (listp to-strings)
        (setq replacements to-strings)
@@ -270,7 +350,14 @@ What you probably want is a loop like this:
 which will run faster and will not set the mark or print anything.
 \(You may need a more complex loop if FROM-STRING can match the null string
 and TO-STRING is also null.)"
-  (interactive (query-replace-read-args "Replace string" nil))
+  (interactive
+   (let ((common
+         (query-replace-read-args "Replace string" nil)))
+     (list (nth 0 common) (nth 1 common) (nth 2 common)
+          (if (and transient-mark-mode mark-active)
+              (region-beginning))
+          (if (and transient-mark-mode mark-active)
+              (region-end)))))
   (perform-replace from-string to-string nil nil delimited nil nil start end))
 
 (defun replace-regexp (regexp to-string &optional delimited start end)
@@ -297,7 +384,14 @@ What you probably want is a loop like this:
   (while (re-search-forward REGEXP nil t)
     (replace-match TO-STRING nil nil))
 which will run faster and will not set the mark or print anything."
-  (interactive (query-replace-read-args "Replace regexp" t))
+  (interactive
+   (let ((common
+         (query-replace-read-args "Replace regexp" t)))
+     (list (nth 0 common) (nth 1 common) (nth 2 common)
+          (if (and transient-mark-mode mark-active)
+              (region-beginning))
+          (if (and transient-mark-mode mark-active)
+              (region-end)))))
   (perform-replace regexp to-string nil t delimited nil nil start end))
 
 \f
@@ -332,9 +426,13 @@ on the contents of the region.  Otherwise, operate from point to the
 end of the buffer."
 
   (interactive
-   (keep-lines-read-args "Keep lines (containing match for regexp): "))
+   (progn
+     (barf-if-buffer-read-only)
+     (keep-lines-read-args "Keep lines (containing match for regexp): ")))
   (if rstart
-      (goto-char (min rstart rend))
+      (progn
+       (goto-char (min rstart rend))
+       (setq rend (copy-marker (max rstart rend))))
     (if (and transient-mark-mode mark-active)
        (setq rstart (region-beginning)
              rend (copy-marker (region-end)))
@@ -356,7 +454,7 @@ end of the buffer."
            ;; Now end is first char preserved by the new match.
            (if (< start end)
                (delete-region start end))))
-       
+
        (setq start (save-excursion (forward-line 1) (point)))
        ;; If the match was empty, avoid matching again at same place.
        (and (< (point) rend)
@@ -379,9 +477,13 @@ on the contents of the region.  Otherwise, operate from point to the
 end of the buffer."
 
   (interactive
-   (keep-lines-read-args "Flush lines (containing match for regexp): "))
+   (progn
+     (barf-if-buffer-read-only)
+     (keep-lines-read-args "Flush lines (containing match for regexp): ")))
   (if rstart
-      (goto-char (min rstart rend))
+      (progn
+       (goto-char (min rstart rend))
+       (setq rend (copy-marker (max rstart rend))))
     (if (and transient-mark-mode mark-active)
        (setq rstart (region-beginning)
              rend (copy-marker (region-end)))
@@ -444,8 +546,11 @@ end of the buffer."
     (define-key map "\C-o" 'occur-mode-display-occurrence)
     (define-key map "\M-n" 'occur-next)
     (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" 'delete-window)
+    (define-key map "q" 'quit-window)
+    (define-key map "z" 'kill-this-buffer)
     map)
   "Keymap for `occur-mode'.")
 
@@ -454,7 +559,12 @@ end of the buffer."
 See `occur-revert-function'.")
 
 (defcustom occur-mode-hook '(turn-on-font-lock)
-  "Hooks run when `occur' is called."
+  "Hook run when entering Occur mode."
+  :type 'hook
+  :group 'matching)
+
+(defcustom occur-hook nil
+  "Hook run when `occur' is called."
   :type 'hook
   :group 'matching)
 
@@ -466,16 +576,15 @@ See `occur-revert-function'.")
 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")
-  (make-local-variable 'revert-buffer-function)
-  (set (make-local-variable 'font-lock-category-alist)
-       `((,(make-symbol "occur-match") . bold)
-        (,(make-symbol "occur-title") . underline)))
   (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-hooks 'occur-mode-hook))
 
 (defun occur-revert-function (ignore1 ignore2)
@@ -530,36 +639,43 @@ Alternatively, click \\[occur-mode-mouse-goto] on an item to go to it.
       (select-window window)
       (goto-char pos))))
 
-(defun occur-next (&optional n)
-  "Move to the Nth (default 1) next match in an Occur mode buffer."
-  (interactive "p")
+(defun occur-find-match (n search message)
   (if (not n) (setq n 1))
   (let ((r))
     (while (> n 0)
-      (if (get-text-property (point) 'occur-point)
-         (forward-char 1))
-      (setq r (next-single-property-change (point) 'occur-point))
+      (setq r (funcall search (point) 'occur-match))
+      (and r
+           (get-text-property r 'occur-match)
+           (setq r (funcall search r 'occur-match)))
       (if r
-         (goto-char r)
-       (error "No more matches"))
+          (goto-char r)
+        (error message))
       (setq n (1- n)))))
 
+(defun occur-next (&optional n)
+  "Move to the Nth (default 1) next match in an Occur mode buffer."
+  (interactive "p")
+  (occur-find-match n #'next-single-property-change "No more matches"))
+
 (defun occur-prev (&optional n)
   "Move to the Nth (default 1) previous match in an Occur mode buffer."
   (interactive "p")
-  (if (not n) (setq n 1))
-  (let ((r))
-    (while (> n 0)
-    
-      (setq r (get-text-property (point) 'occur-point))
-      (if r (forward-char -1))
-      
-      (setq r (previous-single-property-change (point) 'occur-point))
-      (if r
-         (goto-char (- r 1))
-       (error "No earlier matches"))
-      
-      (setq n (1- n)))))
+  (occur-find-match n #'previous-single-property-change "No earlier matches"))
+
+(defun occur-next-error (&optional argp reset)
+  "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")
+  (occur-mode-goto-occurrence))
+
 \f
 (defcustom list-matching-lines-default-context-lines 0
   "*Default number of context lines included around `list-matching-lines' matches.
@@ -619,6 +735,22 @@ 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)
+  "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")
+  (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)))
+
 (defun occur (regexp &optional nlines)
   "Show all lines in the current buffer containing a match for REGEXP.
 
@@ -644,14 +776,19 @@ This function acts on multiple buffers; otherwise, it is exactly like
 `occur'."
   (interactive
    (cons
-    (let ((bufs (list (read-buffer "First buffer to search: "
-                                  (current-buffer) t)))
-         (buf nil))
+    (let* ((bufs (list (read-buffer "First buffer to search: "
+                                   (current-buffer) t)))
+          (buf nil)
+          (ido-ignore-item-temp-list bufs))
       (while (not (string-equal
-                  (setq buf (read-buffer "Next buffer to search (RET to end): "
-                                         nil t))
+                  (setq buf (read-buffer
+                             (if (eq read-buffer-function 'ido-read-buffer)
+                                 "Next buffer to search (C-j to end): "
+                               "Next buffer to search (RET to end): ")
+                             nil t))
                   ""))
-       (push buf bufs))
+       (add-to-list 'bufs buf)
+       (setq ido-ignore-item-temp-list bufs))
       (nreverse (mapcar #'get-buffer bufs)))
     (occur-read-primary-args)))
   (occur-1 regexp nlines bufs))
@@ -706,23 +843,16 @@ See also `multi-occur'."
                    (or nlines list-matching-lines-default-context-lines)
                    (and case-fold-search
                         (isearch-no-upper-case-p regexp t))
-                   nil nil nil nil)))
-       (let* ((diff (- (length bufs) (length active-bufs)))
-              (bufcount (- (length bufs) diff))
-              (msg (concat
-                    (format "Searched %d buffer%s" bufcount (if (= bufcount 1) "" "s"))
-                    "%s; "
-                    (format "%s match%s for `%s'"
-                            (if (zerop count)
-                                "no"
-                              (format "%d" count))
-                            (if (= count 1)
-                                ""
-                              "es")
-                            regexp))))
-         (message msg (if (zerop diff)
-                          ""
-                        (format " (%d killed)" diff))))
+                   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
@@ -732,20 +862,24 @@ See also `multi-occur'."
        (setq occur-revert-arguments (list regexp nlines bufs)
              buffer-read-only t)
        (if (> count 0)
-           (display-buffer occur-buf)
-         (kill-buffer occur-buf))))))
+           (progn
+             (display-buffer occur-buf)
+             (setq next-error-last-buffer occur-buf))
+         (kill-buffer occur-buf)))
+      (run-hooks 'occur-hook))))
 
 (defun occur-engine-add-prefix (lines)
   (mapcar
    #'(lambda (line)
-       (concat "      :" line "\n"))
+       (concat "       :" line "\n"))
    lines))
 
 (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))
+    (let ((globalcount 0)
+         (coding nil))
       ;; Map over all the buffers
       (dolist (buf buffers)
        (when (buffer-live-p buf)
@@ -761,6 +895,11 @@ See also `multi-occur'."
                (headerpt (with-current-buffer out-buf (point))))
            (save-excursion
              (set-buffer buf)
+             (or coding
+                 ;; Set CODING only if the current buffer locally
+                 ;; binds buffer-file-coding-system.
+                 (not (local-variable-p 'buffer-file-coding-system))
+                 (setq coding buffer-file-coding-system))
              (save-excursion
                (goto-char (point-min)) ;; begin searching in the buffer
                (while (not (eobp))
@@ -780,8 +919,6 @@ See also `multi-occur'."
                    ;; Depropertize the string, and maybe
                    ;; highlight the matches
                    (let ((len (length curstring))
-                         (match-category (with-current-buffer out-buf
-                                           (car (nth 0 font-lock-category-alist))))
                          (start 0))
                      (unless keep-props
                        (set-text-properties 0 len nil curstring))
@@ -790,18 +927,19 @@ See also `multi-occur'."
                        (add-text-properties (match-beginning 0)
                                             (match-end 0)
                                             (append
-                                             `(occur-match t category ,match-category)
+                                             `(occur-match t)
                                              (when match-face
-                                               `(face ,match-face)))
+                                               `(font-lock-face ,match-face)))
                                             curstring)
                        (setq start (match-end 0))))
                    ;; Generate the string to insert for this match
                    (let* ((out-line
                            (concat
-                            (apply #'propertize (format "%6d:" lines)
+                            ;; Using 7 digits aligns tabs properly.
+                            (apply #'propertize (format "%7d:" lines)
                                    (append
                                     (when prefix-face
-                                      `(face prefix-face))
+                                      `(font-lock-face prefix-face))
                                     '(occur-prefix t)))
                             curstring
                             "\n"))
@@ -824,10 +962,11 @@ See also `multi-occur'."
                          (unless (= nlines 0)
                            (insert "-------\n"))
                          (add-text-properties
-                          beg (1- end)
-                          `(occur-target ,marker
-                                         mouse-face highlight help-echo
-                                         "mouse-2: go to this occurrence")))))
+                          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)))))
                    (goto-char endpt))
                  (if endpt
                      (progn
@@ -841,17 +980,21 @@ See also `multi-occur'."
                (goto-char headerpt)
                (let ((beg (point))
                      end)
-                 (insert (format "%d lines matching \"%s\" in buffer: %s\n"
-                                 matches regexp (buffer-name buf)))
+                 (insert (format "%d match%s for \"%s\" in buffer: %s\n"
+                                 matches (if (= matches 1) "" "es")
+                                 regexp (buffer-name buf)))
                  (setq end (point))
                  (add-text-properties beg end
                                       (append
                                        (when title-face
-                                         `(face ,title-face))
-                                       `(occur-title
-                                         ,buf category
-                                         ,(car (nth 1 font-lock-category-alist))))))
+                                         `(font-lock-face ,title-face))
+                                       `(occur-title ,buf))))
                (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
+         ;; buffer.
+         (set-buffer-file-coding-system coding))
       ;; Return the number of matches
       globalcount)))
 
@@ -913,6 +1056,7 @@ N     (match-string N)           (where N is a string of digits)
 #N    (string-to-number (match-string N))
 &     (match-string 0)
 #&    (string-to-number (match-string 0))
+#     replace-count
 
 Note that these symbols must be preceeded by a backslash in order to
 type them."
@@ -932,7 +1076,9 @@ type them."
          ((string= "&" name)
           (setcar n '(match-string 0)))
          ((string= "#&" name)
-          (setcar n '(string-to-number (match-string 0))))))))
+          (setcar n '(string-to-number (match-string 0))))
+        ((string= "#" name)
+         (setcar n 'replace-count))))))
     (setq n (cdr n))))
 
 (defun replace-eval-replacement (expression replace-count)
@@ -941,6 +1087,21 @@ type them."
         replacement
       (prin1-to-string replacement t))))
 
+(defun replace-quote (replacement)
+  "Quote a replacement string.
+This just doubles all backslashes in REPLACEMENT and
+returns the resulting string.  If REPLACEMENT is not
+a string, it is first passed through `prin1-to-string'
+with the `noescape' argument set.
+
+`match-data' is preserved across the call."
+  (save-match-data
+    (replace-regexp-in-string "\\\\" "\\\\"
+                             (if (stringp replacement)
+                                 replacement
+                               (prin1-to-string replacement t))
+                             t t)))
+
 (defun replace-loop-through-replacements (data replace-count)
   ;; DATA is a vector contaning the following values:
   ;;   0 next-rotate-count
@@ -954,7 +1115,7 @@ type them."
           (aset data 2 (if (consp next) next (aref data 3))))))
   (car (aref data 2)))
 
-(defun perform-replace (from-string replacements 
+(defun perform-replace (from-string replacements
                        query-flag regexp-flag delimited-flag
                        &optional repeat-count map start end)
   "Subroutine of `query-replace'.  Its complexity handles interactive queries.
@@ -966,7 +1127,10 @@ just as `query-replace' does.  Instead, write a simple loop like this:
 
 which will run faster and probably do exactly what you want.  Please
 see the documentation of `replace-match' to find out how to simulate
-`case-replace'."
+`case-replace'.
+
+This function returns nil if and only if there were no matches to
+make, or the user didn't cancel the call."
   (or map (setq map query-replace-map))
   (and query-flag minibuffer-auto-raise
        (raise-frame (window-frame (minibuffer-window))))
@@ -976,7 +1140,7 @@ see the documentation of `replace-match' to find out how to simulate
        (case-fold-search (and case-fold-search
                               (string-equal from-string
                                             (downcase from-string))))
-       (literal (not regexp-flag))
+       (literal (or (not regexp-flag) (eq regexp-flag 'literal)))
        (search-function (if regexp-flag 're-search-forward 'search-forward))
        (search-string from-string)
        (real-match-data nil)           ; the match data for the current match
@@ -1030,7 +1194,7 @@ see the documentation of `replace-match' to find out how to simulate
     (unwind-protect
        ;; Loop finding occurrences that perhaps should be replaced.
        (while (and keep-going
-                   (not (eobp))
+                   (not (or (eobp) (and limit (>= (point) limit))))
                    ;; Use the next match if it is already known;
                    ;; otherwise, search for a match after moving forward
                    ;; one char if progress is required.
@@ -1046,7 +1210,10 @@ see the documentation of `replace-match' to find out how to simulate
                                     ;; character too far at the end,
                                     ;; but this is undone after the
                                     ;; while-loop.
-                                    (progn (forward-char 1) (not (eobp))))
+                                    (progn
+                                      (forward-char 1)
+                                      (not (or (eobp)
+                                               (and limit (>= (point) limit))))))
                                 (funcall search-function search-string limit t)
                                 ;; For speed, use only integers and
                                 ;; reuse the list used last time.
@@ -1122,12 +1289,11 @@ see the documentation of `replace-match' to find out how to simulate
                         (setq done t))
                        ((eq def 'backup)
                         (if stack
-                            (let ((elt (car stack)))
+                            (let ((elt (pop stack)))
                               (goto-char (car elt))
                               (setq replaced (eq t (cdr elt)))
                               (or replaced
-                                  (set-match-data (cdr elt)))
-                              (setq stack (cdr stack)))
+                                  (set-match-data (cdr elt))))
                           (message "No previous match")
                           (ding 'no-terminate)
                           (sit-for 1)))
@@ -1177,7 +1343,7 @@ see the documentation of `replace-match' to find out how to simulate
                         (if (and regexp-flag nonempty-match)
                             (setq match-again (and (looking-at search-string)
                                                    (match-data)))))
-                     
+
                        ;; Edit replacement.
                        ((eq def 'edit-replacement)
                         (setq next-replacement
@@ -1186,7 +1352,7 @@ see the documentation of `replace-match' to find out how to simulate
                         (or replaced
                             (replace-match next-replacement nocasify literal))
                         (setq done t))
-                     
+
                        ((eq def 'delete-and-edit)
                         (delete-region (match-beginning 0) (match-end 0))
                         (set-match-data
@@ -1216,7 +1382,7 @@ see the documentation of `replace-match' to find out how to simulate
       ;; beyond the last replacement.  Undo that.
       (when (and regexp-flag (not match-again) (> replace-count 0))
        (backward-char 1))
-      
+
       (replace-dehighlight))
     (or unread-command-events
        (message "Replaced %d occurrence%s"
@@ -1248,4 +1414,5 @@ see the documentation of `replace-match' to find out how to simulate
                                'query-replace 'region))))
         (move-overlay replace-overlay start end (current-buffer)))))
 
+;;; arch-tag: 16b4cd61-fd40-497b-b86f-b667c4cf88e4
 ;;; replace.el ends here