(symbol-file): Remove unused variable `functions'.
[bpt/emacs.git] / lisp / replace.el
index ad197ff..18817d3 100644 (file)
@@ -3,6 +3,8 @@
 ;; Copyright (C) 1985, 86, 87, 92, 94, 96, 1997, 2000, 2001, 2002
 ;;  Free Software Foundation, Inc.
 
+;; Maintainer: FSF
+
 ;; This file is part of GNU Emacs.
 
 ;; GNU Emacs is free software; you can redistribute it and/or modify
@@ -27,9 +29,6 @@
 
 ;;; Code:
 
-(eval-when-compile
-  (require 'cl))
-
 (defcustom case-replace t
   "*Non-nil means `query-replace' should preserve case in replacements."
   :type 'boolean
@@ -63,7 +62,7 @@ strings or patterns."
   "*Non-nil means `query-replace' and friends ignore read-only matches."
   :type 'boolean
   :group 'matching
-  :version "21.3")
+  :version "21.4")
 
 (defun query-replace-read-args (string regexp-flag &optional noerror)
   (unless noerror
@@ -104,11 +103,13 @@ 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.
@@ -132,8 +133,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.
@@ -333,9 +339,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)))
@@ -357,7 +367,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)
@@ -380,9 +390,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)))
@@ -441,15 +455,31 @@ end of the buffer."
     (define-key map [mouse-2] 'occur-mode-mouse-goto)
     (define-key map "\C-c\C-c" 'occur-mode-goto-occurrence)
     (define-key map "\C-m" 'occur-mode-goto-occurrence)
-    (define-key map "\o" 'occur-mode-goto-occurrence-other-window)
+    (define-key map "o" 'occur-mode-goto-occurrence-other-window)
     (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" 'quit-window)
+    (define-key map "z" 'kill-this-buffer)
     map)
   "Keymap for `occur-mode'.")
 
-(defvar occur-revert-properties nil)
+(defvar occur-revert-arguments nil
+  "Arguments to pass to `occur-1' to revert an Occur mode buffer.
+See `occur-revert-function'.")
+
+(defcustom occur-mode-hook '(turn-on-font-lock)
+  "Hook run when entering Occur mode."
+  :type 'hook
+  :group 'matching)
+
+(defcustom occur-hook nil
+  "Hook run when `occur' is called."
+  :type 'hook
+  :group 'matching)
 
 (put 'occur-mode 'mode-class 'special)
 (defun occur-mode ()
@@ -459,104 +489,90 @@ end of the buffer."
 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-defaults)
-       '(nil t nil nil nil
-            (font-lock-fontify-region-function . occur-fontify-region-function)
-            (font-lock-unfontify-region-function . occur-unfontify-region-function)))
-  (setq revert-buffer-function 'occur-revert-function)
   (set (make-local-variable 'revert-buffer-function) 'occur-revert-function)
-  (make-local-variable 'occur-revert-properties)
+  (make-local-variable 'occur-revert-arguments)
+  (add-hook 'change-major-mode-hook 'font-lock-defontify nil t)
   (run-hooks 'occur-mode-hook))
 
 (defun occur-revert-function (ignore1 ignore2)
-  "Handle `revert-buffer' for *Occur* buffers."
-  (apply 'occur-1 occur-revert-properties))
+  "Handle `revert-buffer' for Occur mode buffers."
+  (apply 'occur-1 (append occur-revert-arguments (list (buffer-name)))))
 
 (defun occur-mode-mouse-goto (event)
   "In Occur mode, go to the occurrence whose line you click on."
   (interactive "e")
-  (let ((buffer nil)
-       (pos nil))
+  (let (pos)
     (save-excursion
       (set-buffer (window-buffer (posn-window (event-end event))))
       (save-excursion
        (goto-char (posn-point (event-end event)))
-       (let ((props (occur-mode-find-occurrence)))
-         (setq buffer (car props))
-         (setq pos (cdr props)))))
-    (pop-to-buffer buffer)
-    (goto-char (marker-position pos))))
+       (setq pos (occur-mode-find-occurrence))))
+    (pop-to-buffer (marker-buffer pos))
+    (goto-char pos)))
 
 (defun occur-mode-find-occurrence ()
-  (let ((props (get-text-property (point) 'occur-target)))
-    (unless props
+  (let ((pos (get-text-property (point) 'occur-target)))
+    (unless pos
       (error "No occurrence on this line"))
-    (unless (buffer-live-p (car props))
-      (error "Buffer in which occurrence was found is deleted"))
-    props))
+    (unless (buffer-live-p (marker-buffer pos))
+      (error "Buffer for this occurrence was killed"))
+    pos))
 
 (defun occur-mode-goto-occurrence ()
   "Go to the occurrence the current line describes."
   (interactive)
-  (let ((target (occur-mode-find-occurrence)))
-    (pop-to-buffer (car target))
-    (goto-char (marker-position (cdr target)))))
+  (let ((pos (occur-mode-find-occurrence)))
+    (pop-to-buffer (marker-buffer pos))
+    (goto-char pos)))
 
 (defun occur-mode-goto-occurrence-other-window ()
   "Go to the occurrence the current line describes, in another window."
   (interactive)
-  (let ((target (occur-mode-find-occurrence)))
-    (switch-to-buffer-other-window (car target))
-    (goto-char (marker-position (cdr target)))))
+  (let ((pos (occur-mode-find-occurrence)))
+    (switch-to-buffer-other-window (marker-buffer pos))
+    (goto-char pos)))
 
 (defun occur-mode-display-occurrence ()
   "Display in another window the occurrence the current line describes."
   (interactive)
-  (let ((target (occur-mode-find-occurrence))
+  (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
-       window)
-    (setq window (display-buffer (car target)))
+       same-window-regexps)
+    (setq window (display-buffer (marker-buffer pos)))
     ;; This is the way to set point in the proper window.
     (save-selected-window
       (select-window window)
-      (goto-char (marker-position (cdr target))))))
+      (goto-char pos))))
 
-(defun occur-next (&optional n)
-  "Move to the Nth (default 1) next match in the *Occur* 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 the *Occur* buffer."
+  "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"))
 \f
 (defcustom list-matching-lines-default-context-lines 0
   "*Default number of context lines included around `list-matching-lines' matches.
@@ -579,7 +595,7 @@ If the value is nil, don't highlight the buffer names specially."
   :type 'face
   :group 'matching)
 
-(defun occur-accumulate-lines (count)
+(defun occur-accumulate-lines (count &optional no-props)
   (save-excursion
     (let ((forwardp (> count 0))
          (result nil))
@@ -587,11 +603,11 @@ If the value is nil, don't highlight the buffer names specially."
                      (if forwardp
                          (eobp)
                        (bobp))))
-       (if forwardp
-           (decf count)
-         (incf count))
+       (setq count (+ count (if forwardp -1 1)))
        (push
-        (buffer-substring
+        (funcall (if no-props
+                     #'buffer-substring-no-properties
+                   #'buffer-substring)
          (line-beginning-position)
          (line-end-position))
         result)
@@ -613,7 +629,24 @@ If the value is nil, don't highlight the buffer names specially."
          (if (equal input "")
              default
            input))
-       current-prefix-arg))
+       (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.
@@ -640,20 +673,25 @@ 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))
 
 (defun multi-occur-by-filename-regexp (bufregexp regexp &optional nlines)
-  "Show all lines in buffers containing REGEXP, named by BUFREGEXP.
+  "Show all lines matching REGEXP in buffers named by BUFREGEXP.
 See also `multi-occur'."
   (interactive
    (cons
@@ -679,175 +717,180 @@ See also `multi-occur'."
                               buf))
                           (buffer-list))))))
 
-(defun occur-1 (regexp nlines bufs)
-  (let ((occur-buf (get-buffer-create "*Occur*")))
+(defun occur-1 (regexp nlines bufs &optional buf-name)
+  (unless buf-name
+    (setq buf-name "*Occur*"))
+  (let ((occur-buf (get-buffer-create buf-name))
+       (made-temp-buf nil)
+       (active-bufs (delq nil (mapcar #'(lambda (buf)
+                                          (when (buffer-live-p buf) buf))
+                                      bufs))))
+    ;; Handle the case where one of the buffers we're searching is the
+    ;; *Occur* buffer itself.
+    (when (memq occur-buf bufs)
+      (setq occur-buf (with-current-buffer occur-buf
+                       (clone-buffer "*Occur-temp*"))
+           made-temp-buf t))
     (with-current-buffer occur-buf
       (setq buffer-read-only nil)
       (occur-mode)
       (erase-buffer)
       (let ((count (occur-engine
-                   regexp bufs occur-buf
+                   regexp active-bufs occur-buf
                    (or nlines list-matching-lines-default-context-lines)
                    (and case-fold-search
                         (isearch-no-upper-case-p regexp t))
-                   nil nil nil nil)))
-       (message "Searched %d buffers; %s matches for `%s'" (length bufs)
-                (if (zerop count)
-                    "no"
-                  (format "%d" count))
-                regexp)
+                   list-matching-lines-buffer-name-face
+                   nil list-matching-lines-face nil)))
+       (let* ((bufcount (length active-bufs))
+              (diff (- (length bufs) bufcount)))
+         (message "Searched %d buffer%s%s; %s match%s for `%s'"
+                  bufcount (if (= bufcount 1) "" "s")
+                  (if (zerop diff) "" (format " (%d killed)" diff))
+                  (if (zerop count) "no" (format "%d" count))
+                  (if (= count 1) "" "es")
+                  regexp))
+       ;; If we had to make a temporary buffer, make it the *Occur*
+       ;; buffer now.
+       (when made-temp-buf
+         (with-current-buffer (get-buffer buf-name)
+           (kill-buffer (current-buffer)))
+         (rename-buffer buf-name))
+       (setq occur-revert-arguments (list regexp nlines bufs)
+             buffer-read-only t)
        (if (> count 0)
            (display-buffer occur-buf)
          (kill-buffer occur-buf)))
-      (goto-char (point-min))
-      (setq occur-revert-properties (list regexp nlines bufs)
-           buffer-read-only t))))
-
-;; Most of these are macros becuase if we used `flet', it wouldn't
-;; create a closure, so things would blow up at run time.  Ugh. :(
-(macrolet ((insert-get-point (obj)
-            `(progn
-               (insert ,obj)
-               (point)))
-          (add-prefix (lines)
-            `(mapcar
-                #'(lambda (line)
-                    (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))
-       ;; Map over all the buffers
-       (dolist (buf buffers)
-         (when (buffer-live-p buf)
-           (let ((c 0) ;; count of matched lines
-                 (l 1) ;; line count
-                 (matchbeg 0)
-                 (matchend 0)
-                 (origpt nil)
-                 (begpt nil)
-                 (endpt nil)
-                 (marker nil)
-                 (curstring "")
-                 (headerpt (with-current-buffer out-buf (point))))
+      (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
+                           title-face prefix-face match-face keep-props)
+  (with-current-buffer out-buf
+    (setq buffer-read-only nil)
+    (let ((globalcount 0)
+         (coding nil))
+      ;; Map over all the buffers
+      (dolist (buf buffers)
+       (when (buffer-live-p buf)
+         (let ((matches 0)     ;; count of matched lines
+               (lines 1)       ;; line count
+               (matchbeg 0)
+               (matchend 0)
+               (origpt nil)
+               (begpt nil)
+               (endpt nil)
+               (marker nil)
+               (curstring "")
+               (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
-               (set-buffer buf)
-               (save-excursion
-                 (goto-char (point-min)) ;; begin searching in the buffer
-                 (while (not (eobp))
-                   (setq origpt (point))
-                   (when (setq endpt (re-search-forward regexp nil t))
-                       (incf c) ;; increment match count
-                       (incf globalcount)
-                       (setq matchbeg (match-beginning 0)
-                             matchend (match-end 0))
-                       (setq begpt (save-excursion
-                                     (goto-char matchbeg)
-                                     (line-beginning-position)))
-                       (incf l (1- (count-lines origpt endpt)))
-                       (setq marker (make-marker))
-                       (set-marker marker matchbeg)
-                       (setq curstring (buffer-substring begpt
-                                        (line-end-position)))
-                       ;; Depropertize the string, and maybe
-                       ;; highlight the matches
-                       (let ((len (length curstring))
-                                     (start 0))
-                                 (unless keep-props
-                                   (set-text-properties 0 len nil curstring))
-                                 (while (and (< start len)
-                                             (string-match regexp curstring start))
-                                   (add-text-properties (match-beginning 0)
-                                                        (match-end 0)
-                                                        (append
-                                                         '(occur-match t)
-                                                         (when match-face
-                                                           `(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:" l)
-                                       (append
-                                        (when prefix-face
-                                          `(face prefix-face))
-                                        '(occur-prefix t)))
-                                curstring
-                                "\n"))
-                              (data
-                               (if (= nlines 1)
-                                   ;; The simple display style
-                                   out-line
-                                ;; The complex multi-line display
-                                ;; style.  Generate a list of lines,
-                                ;; concatenate them all together.
-                                (apply #'concat
-                                       (nconc
-                                        (add-prefix (nreverse (cdr (occur-accumulate-lines (- nlines)))))
-                                        (list out-line)
-                                        (add-prefix (cdr (occur-accumulate-lines nlines))))))))
-                         ;; Actually insert the match display data
-                         (with-current-buffer out-buf
-                           (let ((beg (point))
-                                 (end (insert-get-point data)))
-                             (unless (= nlines 1)
-                               (insert-get-point "-------\n"))
-                             (add-text-properties
-                              beg (1- end)
-                              `(occur-target ,(cons buf marker)
-                                             mouse-face highlight help-echo
-                                             "mouse-2: go to this occurrence")))))
-                       (goto-char endpt))
-                   (incf l)
-                   ;; On to the next match...
-                   (forward-line 1))))
-             (when (not (zerop c)) ;; is the count zero?
-               (with-current-buffer out-buf
-                 (goto-char headerpt)
-                 (let ((beg (point))
-                       (end (insert-get-point
-                             (format "%d lines matching \"%s\" in buffer: %s\n"
-                                     c regexp (buffer-name buf)))))
-                   (add-text-properties beg end
-                                        (append
-                                         (when title-face
-                                           `(face ,title-face))
-                                         `(occur-title ,buf))))
-                 (goto-char (point-max)))))))
-       ;; Return the number of matches
-       globalcount))))
-
-(defun occur-fontify-on-property (prop face beg end)
-  (let ((prop-beg (or (and (get-text-property (point) prop) (point))
-                     (next-single-property-change (point) prop nil end))))
-    (when (and prop-beg (not (= prop-beg end)))
-      (let ((prop-end (next-single-property-change beg prop nil end)))
-       (when (and prop-end (not (= prop-end end)))
-         (put-text-property prop-beg prop-end 'face face)
-         prop-end)))))
-
-(defun occur-fontify-region-function (beg end &optional verbose)
-  (when verbose (message "Fontifying..."))
-  (let ((inhibit-read-only t))
-    (save-excursion
-      (dolist (e `((occur-title . ,list-matching-lines-buffer-name-face)
-                  (occur-match . ,list-matching-lines-face)))
-                  ; (occur-prefix . ,list-matching-lines-prefix-face)))
-       (goto-char beg)
-       (let ((change-end nil))
-         (while (setq change-end (occur-fontify-on-property (car e)
-                                                            (cdr e)
-                                                            (point)
-                                                            end))
-           (goto-char change-end))))))
-  (when verbose (message "Fontifying...done")))
-
-(defun occur-unfontify-region-function (beg end)
-  (let ((inhibit-read-only t))
-    (remove-text-properties beg end '(face nil))))
+               (goto-char (point-min)) ;; begin searching in the buffer
+               (while (not (eobp))
+                 (setq origpt (point))
+                 (when (setq endpt (re-search-forward regexp nil t))
+                   (setq matches (1+ matches)) ;; increment match count
+                   (setq matchbeg (match-beginning 0)
+                         matchend (match-end 0))
+                   (setq begpt (save-excursion
+                                 (goto-char matchbeg)
+                                 (line-beginning-position)))
+                   (setq lines (+ lines (1- (count-lines origpt endpt))))
+                   (setq marker (make-marker))
+                   (set-marker marker matchbeg)
+                   (setq curstring (buffer-substring begpt
+                                                     (line-end-position)))
+                   ;; Depropertize the string, and maybe
+                   ;; highlight the matches
+                   (let ((len (length curstring))
+                         (start 0))
+                     (unless keep-props
+                       (set-text-properties 0 len nil curstring))
+                     (while (and (< start len)
+                                 (string-match regexp curstring start))
+                       (add-text-properties (match-beginning 0)
+                                            (match-end 0)
+                                            (append
+                                             `(occur-match t)
+                                             (when match-face
+                                               `(font-lock-face ,match-face)))
+                                            curstring)
+                       (setq start (match-end 0))))
+                   ;; Generate the string to insert for this match
+                   (let* ((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)))
+                            curstring
+                            "\n"))
+                          (data
+                           (if (= nlines 0)
+                               ;; The simple display style
+                               out-line
+                             ;; The complex multi-line display
+                             ;; style.  Generate a list of lines,
+                             ;; concatenate them all together.
+                             (apply #'concat
+                                    (nconc
+                                     (occur-engine-add-prefix (nreverse (cdr (occur-accumulate-lines (- (1+ nlines)) keep-props))))
+                                     (list out-line)
+                                     (occur-engine-add-prefix (cdr (occur-accumulate-lines (1+ nlines) keep-props))))))))
+                     ;; Actually insert the match display data
+                     (with-current-buffer out-buf
+                       (let ((beg (point))
+                             (end (progn (insert data) (point))))
+                         (unless (= nlines 0)
+                           (insert "-------\n"))
+                         (add-text-properties
+                          beg end
+                          `(occur-target ,marker help-echo "mouse-2: go to this occurrence"))
+                         ;; We don't put `mouse-face' on the newline,
+                         ;; because that loses.
+                         (add-text-properties beg (1- end) '(mouse-face highlight)))))
+                   (goto-char endpt))
+                 (if endpt
+                     (progn
+                       (setq lines (1+ lines))
+                       ;; On to the next match...
+                       (forward-line 1))
+                   (goto-char (point-max))))))
+           (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 lines matching \"%s\" in buffer: %s\n"
+                                 matches regexp (buffer-name buf)))
+                 (setq end (point))
+                 (add-text-properties beg end
+                                      (append
+                                       (when title-face
+                                         `(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)))
 
 \f
 ;; It would be nice to use \\[...], but there is no reasonable way
@@ -948,7 +991,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.
@@ -960,7 +1003,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))))
@@ -1116,12 +1162,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)))
@@ -1171,7 +1216,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
@@ -1180,7 +1225,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
@@ -1210,7 +1255,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"