]> code.delx.au - gnu-emacs/blobdiff - lisp/replace.el
lisp/gnus/gnus-icalendar.el (gnus-icalendar-identities): Make changing the value...
[gnu-emacs] / lisp / replace.el
index 7757426cf958b7a257432f5c7d61edabf5851fb9..2c6b02364b2ec5a7d3a8d5d7827bf578a7731e02 100644 (file)
@@ -1,7 +1,7 @@
 ;;; replace.el --- replace commands for Emacs
 
 ;;; replace.el --- replace commands for Emacs
 
-;; Copyright (C) 1985-1987, 1992, 1994, 1996-1997, 2000-2013 Free
-;; Software Foundation, Inc.
+;; Copyright (C) 1985-1987, 1992, 1994, 1996-1997, 2000-2013
+;;   Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
 ;; Package: emacs
 
 ;; Maintainer: FSF
 ;; Package: emacs
@@ -246,10 +246,14 @@ 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
 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
+letters.  (Transferring the case pattern means that if the old text
 matched is all caps, or capitalized, then its replacement is upcased
 or capitalized.)
 
 matched is all caps, or capitalized, then its replacement is upcased
 or capitalized.)
 
+Ignore read-only matches if `query-replace-skip-read-only' is non-nil,
+ignore hidden matches if `search-invisible' is nil, and ignore more
+matches using `isearch-filter-predicate'.
+
 If `replace-lax-whitespace' is non-nil, a space or spaces in the string
 to be replaced will match a sequence of whitespace chars defined by the
 regexp in `search-whitespace-regexp'.
 If `replace-lax-whitespace' is non-nil, a space or spaces in the string
 to be replaced will match a sequence of whitespace chars defined by the
 regexp in `search-whitespace-regexp'.
@@ -300,6 +304,10 @@ pattern of the old text to the new text, if `case-replace' and
 all caps, or capitalized, then its replacement is upcased or
 capitalized.)
 
 all caps, or capitalized, then its replacement is upcased or
 capitalized.)
 
+Ignore read-only matches if `query-replace-skip-read-only' is non-nil,
+ignore hidden matches if `search-invisible' is nil, and ignore more
+matches using `isearch-filter-predicate'.
+
 If `replace-regexp-lax-whitespace' is non-nil, a space or spaces in the regexp
 to be replaced will match a sequence of whitespace chars defined by the
 regexp in `search-whitespace-regexp'.
 If `replace-regexp-lax-whitespace' is non-nil, a space or spaces in the regexp
 to be replaced will match a sequence of whitespace chars defined by the
 regexp in `search-whitespace-regexp'.
@@ -380,6 +388,10 @@ that reads REGEXP.
 Preserves case in each replacement if `case-replace' and `case-fold-search'
 are non-nil and REGEXP has no uppercase letters.
 
 Preserves case in each replacement if `case-replace' and `case-fold-search'
 are non-nil and REGEXP has no uppercase letters.
 
+Ignore read-only matches if `query-replace-skip-read-only' is non-nil,
+ignore hidden matches if `search-invisible' is nil, and ignore more
+matches using `isearch-filter-predicate'.
+
 If `replace-regexp-lax-whitespace' is non-nil, a space or spaces in the regexp
 to be replaced will match a sequence of whitespace chars defined by the
 regexp in `search-whitespace-regexp'.
 If `replace-regexp-lax-whitespace' is non-nil, a space or spaces in the regexp
 to be replaced will match a sequence of whitespace chars defined by the
 regexp in `search-whitespace-regexp'.
@@ -470,16 +482,21 @@ 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.)
 
 \(Preserving case means that if the string matched is all caps, or capitalized,
 then its replacement is upcased or capitalized.)
 
+Ignore read-only matches if `query-replace-skip-read-only' is non-nil,
+ignore hidden matches if `search-invisible' is nil, and ignore more
+matches using `isearch-filter-predicate'.
+
 If `replace-lax-whitespace' is non-nil, a space or spaces in the string
 to be replaced will match a sequence of whitespace chars defined by the
 regexp in `search-whitespace-regexp'.
 
 If `replace-lax-whitespace' is non-nil, a space or spaces in the string
 to be replaced will match a sequence of whitespace chars defined by the
 regexp in `search-whitespace-regexp'.
 
-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.
-
 Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
 only matches surrounded by word boundaries.
 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.
+
+Operates on the region between START and END (if both are nil, from point
+to the end of the buffer).  Interactively, if Transient Mark mode is
+enabled and the mark is active, operates on the contents of the region;
+otherwise from point to the end of the buffer.
 
 Use \\<minibuffer-local-map>\\[next-history-element] \
 to pull the last incremental search string to the minibuffer
 
 Use \\<minibuffer-local-map>\\[next-history-element] \
 to pull the last incremental search string to the minibuffer
@@ -506,12 +523,18 @@ and TO-STRING is also null.)"
           (if (and transient-mark-mode mark-active)
               (region-end)))))
   (perform-replace from-string to-string nil nil delimited nil nil start end))
           (if (and transient-mark-mode mark-active)
               (region-end)))))
   (perform-replace from-string to-string nil nil delimited nil nil start end))
+(put 'replace-string 'interactive-only
+     "use `search-forward' and `replace-match' instead.")
 
 (defun replace-regexp (regexp to-string &optional delimited start end)
   "Replace things after point matching REGEXP with TO-STRING.
 Preserve case in each match if `case-replace' and `case-fold-search'
 are non-nil and REGEXP has no uppercase letters.
 
 
 (defun replace-regexp (regexp to-string &optional delimited start end)
   "Replace things after point matching REGEXP with TO-STRING.
 Preserve case in each match if `case-replace' and `case-fold-search'
 are non-nil and REGEXP has no uppercase letters.
 
+Ignore read-only matches if `query-replace-skip-read-only' is non-nil,
+ignore hidden matches if `search-invisible' is nil, and ignore more
+matches using `isearch-filter-predicate'.
+
 If `replace-regexp-lax-whitespace' is non-nil, a space or spaces in the regexp
 to be replaced will match a sequence of whitespace chars defined by the
 regexp in `search-whitespace-regexp'.
 If `replace-regexp-lax-whitespace' is non-nil, a space or spaces in the regexp
 to be replaced will match a sequence of whitespace chars defined by the
 regexp in `search-whitespace-regexp'.
@@ -569,6 +592,8 @@ which will run faster and will not set the mark or print anything."
           (if (and transient-mark-mode mark-active)
               (region-end)))))
   (perform-replace regexp to-string nil t delimited nil nil start end))
           (if (and transient-mark-mode mark-active)
               (region-end)))))
   (perform-replace regexp to-string nil t delimited nil nil start end))
+(put 'replace-regexp 'interactive-only
+     "use `re-search-forward' and `replace-match' instead.")
 
 \f
 (defvar regexp-history nil
 
 \f
 (defvar regexp-history nil
@@ -583,29 +608,31 @@ of `history-length', which see.")
 (defun read-regexp (prompt &optional defaults history)
   "Read and return a regular expression as a string.
 When PROMPT doesn't end with a colon and space, it adds a final \": \".
 (defun read-regexp (prompt &optional defaults history)
   "Read and return a regular expression as a string.
 When PROMPT doesn't end with a colon and space, it adds a final \": \".
-If DEFAULTS is non-nil, it displays the first default in the prompt.
-
-Non-nil optional arg DEFAULTS is a string or a list of strings that
-are prepended to a list of standard default values, which include the
-string at point, the last isearch regexp, the last isearch string, and
-the last replacement regexp.
-
-Non-nil HISTORY is a symbol to use for the history list.
+If the first element of DEFAULTS is non-nil, it's added to the prompt.
+
+Optional arg DEFAULTS has the form (DEFAULT . SUGGESTIONS)
+or simply DEFAULT where DEFAULT, if non-nil, should be a string that
+is returned as the default value when the user enters empty input.
+SUGGESTIONS is a list of strings that can be inserted into
+the minibuffer using \\<minibuffer-local-map>\\[next-history-element].  \
+The values supplied in SUGGESTIONS
+are prepended to the list of standard suggestions that include
+the tag at point, the last isearch regexp, the last isearch string,
+and the last replacement regexp.
+
+Optional arg HISTORY is a symbol to use for the history list.
 If HISTORY is nil, `regexp-history' is used."
 If HISTORY is nil, `regexp-history' is used."
-  (let* ((default (if (consp defaults) (car defaults) defaults))
-        (defaults
-          (append
-           (if (listp defaults) defaults (list 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))))
+  (let* ((default     (if (consp defaults) (car defaults) defaults))
+        (suggestions (if (listp defaults) defaults (list defaults)))
+        (suggestions
+         (append
+          suggestions
+          (list
+           (find-tag-default-as-regexp)
+           (car regexp-search-ring)
+           (regexp-quote (or (car search-ring) ""))
+           (car (symbol-value query-replace-from-history-variable)))))
+        (suggestions (delete-dups (delq nil (delete "" suggestions))))
         ;; Do not automatically add default to the history for empty input.
         (history-add-new-input nil)
         (input (read-from-minibuffer
         ;; Do not automatically add default to the history for empty input.
         (history-add-new-input nil)
         (input (read-from-minibuffer
@@ -616,9 +643,11 @@ If HISTORY is nil, `regexp-history' is used."
                                 (query-replace-descr default)))
                       (t
                        (format "%s: " prompt)))
                                 (query-replace-descr default)))
                       (t
                        (format "%s: " prompt)))
-                nil nil nil (or history 'regexp-history) defaults t)))
+                nil nil nil (or history 'regexp-history) suggestions t)))
     (if (equal input "")
     (if (equal input "")
+       ;; Return the default value when the user enters empty input.
        (or default input)
        (or default input)
+      ;; Otherwise, add non-empty input to the history and return input.
       (prog1 input
        (add-to-history (or history 'regexp-history) input)))))
 
       (prog1 input
        (add-to-history (or history 'regexp-history) input)))))
 
@@ -785,9 +814,12 @@ a previously found match."
    (keep-lines-read-args "How many matches for regexp"))
   (save-excursion
     (if rstart
    (keep-lines-read-args "How many matches for regexp"))
   (save-excursion
     (if rstart
-       (progn
-         (goto-char (min rstart rend))
-         (setq rend (max rstart rend)))
+        (if rend
+            (progn
+              (goto-char (min rstart rend))
+              (setq rend (max rstart rend)))
+          (goto-char rstart)
+          (setq rend (point-max)))
       (if (and interactive transient-mark-mode mark-active)
          (setq rstart (region-beginning)
                rend (region-end))
       (if (and interactive transient-mark-mode mark-active)
          (setq rstart (region-beginning)
                rend (region-end))
@@ -1121,6 +1153,14 @@ If the value is nil, don't highlight the buffer names specially."
   :type 'face
   :group 'matching)
 
   :type 'face
   :group 'matching)
 
+(defcustom list-matching-lines-prefix-face 'shadow
+  "Face used by \\[list-matching-lines] to show the prefix column.
+If the face doesn't differ from the default face,
+don't highlight the prefix with line numbers specially."
+  :type 'face
+  :group 'matching
+  :version "24.4")
+
 (defcustom occur-excluded-properties
   '(read-only invisible intangible field mouse-face help-echo local-map keymap
     yank-handler follow-link)
 (defcustom occur-excluded-properties
   '(read-only invisible intangible field mouse-face help-echo local-map keymap
     yank-handler follow-link)
@@ -1131,12 +1171,32 @@ which means to discard all text properties."
   :group 'matching
   :version "22.1")
 
   :group 'matching
   :version "22.1")
 
+(defvar occur-read-regexp-defaults-function
+  'occur-read-regexp-defaults
+  "Function that provides default regexp(s) for occur commands.
+This function should take no arguments and return one of nil, a
+regexp or a list of regexps for use with occur commands -
+`occur', `multi-occur' and `multi-occur-in-matching-buffers'.
+The return value of this function is used as DEFAULTS param of
+`read-regexp' while executing the occur command.  This function
+is called only during interactive use.
+
+For example, to check for occurrence of symbol at point use
+
+    (setq occur-read-regexp-defaults-function
+         'find-tag-default-as-regexp).")
+
+(defun occur-read-regexp-defaults ()
+  "Return the latest regexp from `regexp-history'.
+See `occur-read-regexp-defaults-function' for details."
+  (car regexp-history))
+
 (defun occur-read-primary-args ()
   (let* ((perform-collect (consp current-prefix-arg))
          (regexp (read-regexp (if perform-collect
                                   "Collect strings matching regexp"
                                 "List lines matching regexp")
 (defun occur-read-primary-args ()
   (let* ((perform-collect (consp current-prefix-arg))
          (regexp (read-regexp (if perform-collect
                                   "Collect strings matching regexp"
                                 "List lines matching regexp")
-                              (car regexp-history))))
+                              (funcall occur-read-regexp-defaults-function))))
     (list regexp
          (if perform-collect
              ;; Perform collect operation
     (list regexp
          (if perform-collect
              ;; Perform collect operation
@@ -1310,7 +1370,9 @@ See also `multi-occur'."
                      (isearch-no-upper-case-p regexp t)
                    case-fold-search)
                  list-matching-lines-buffer-name-face
                      (isearch-no-upper-case-p regexp t)
                    case-fold-search)
                  list-matching-lines-buffer-name-face
-                 nil list-matching-lines-face
+                 (if (face-differs-from-default-p list-matching-lines-prefix-face)
+                     list-matching-lines-prefix-face)
+                 list-matching-lines-face
                  (not (eq occur-excluded-properties t))))))
          (let* ((bufcount (length active-bufs))
                 (diff (- (length bufs) bufcount)))
                  (not (eq occur-excluded-properties t))))))
          (let* ((bufcount (length active-bufs))
                 (diff (- (length bufs) bufcount)))
@@ -1335,16 +1397,18 @@ See also `multi-occur'."
 (defun occur-engine (regexp buffers out-buf nlines case-fold
                            title-face prefix-face match-face keep-props)
   (with-current-buffer out-buf
 (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)
+    (let ((global-lines 0)    ;; total count of matching lines
+         (global-matches 0)  ;; total count of matches
          (coding nil)
          (case-fold-search case-fold))
       ;; Map over all the buffers
       (dolist (buf buffers)
        (when (buffer-live-p buf)
          (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
+         (let ((lines 0)               ;; count of matching lines
+               (matches 0)             ;; count of matches
+               (curr-line 1)           ;; line count
+               (prev-line nil)         ;; line number of prev match endpt
+               (prev-after-lines nil)  ;; context lines of prev match
                (matchbeg 0)
                (origpt nil)
                (begpt nil)
                (matchbeg 0)
                (origpt nil)
                (begpt nil)
@@ -1365,7 +1429,7 @@ See also `multi-occur'."
                (while (not (eobp))
                  (setq origpt (point))
                  (when (setq endpt (re-search-forward regexp nil t))
                (while (not (eobp))
                  (setq origpt (point))
                  (when (setq endpt (re-search-forward regexp nil t))
-                   (setq matches (1+ matches)) ;; increment match count
+                   (setq lines (1+ lines)) ;; increment matching lines count
                    (setq matchbeg (match-beginning 0))
                    ;; Get beginning of first match line and end of the last.
                    (save-excursion
                    (setq matchbeg (match-beginning 0))
                    ;; Get beginning of first match line and end of the last.
                    (save-excursion
@@ -1374,7 +1438,7 @@ See also `multi-occur'."
                      (goto-char endpt)
                      (setq endpt (line-end-position)))
                    ;; Sum line numbers up to the first match line.
                      (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 curr-line (+ curr-line (count-lines origpt begpt)))
                    (setq marker (make-marker))
                    (set-marker marker matchbeg)
                    (setq curstring (occur-engine-line begpt endpt keep-props))
                    (setq marker (make-marker))
                    (set-marker marker matchbeg)
                    (setq curstring (occur-engine-line begpt endpt keep-props))
@@ -1383,6 +1447,7 @@ See also `multi-occur'."
                          (start 0))
                      (while (and (< start len)
                                  (string-match regexp curstring start))
                          (start 0))
                      (while (and (< start len)
                                  (string-match regexp curstring start))
+                       (setq matches (1+ matches))
                        (add-text-properties
                         (match-beginning 0) (match-end 0)
                         (append
                        (add-text-properties
                         (match-beginning 0) (match-end 0)
                         (append
@@ -1392,14 +1457,16 @@ See also `multi-occur'."
                            ;; so as to override faces copied from the buffer.
                            `(face ,match-face)))
                         curstring)
                            ;; so as to override faces copied from the buffer.
                            `(face ,match-face)))
                         curstring)
-                       (setq start (match-end 0))))
+                       ;; Avoid infloop (Bug#7593).
+                       (let ((end (match-end 0)))
+                         (setq start (if (= start end) (1+ start) end)))))
                    ;; Generate the string to insert for this match
                    (let* ((match-prefix
                            ;; Using 7 digits aligns tabs properly.
                    ;; Generate the string to insert for this match
                    (let* ((match-prefix
                            ;; Using 7 digits aligns tabs properly.
-                           (apply #'propertize (format "%7d:" lines)
+                           (apply #'propertize (format "%7d:" curr-line)
                                   (append
                                    (when prefix-face
                                   (append
                                    (when prefix-face
-                                     `(font-lock-face 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-prefix t mouse-face (highlight)
                                      ;; Allow insertion of text at
                                      ;; the end of the prefix (for
@@ -1423,7 +1490,9 @@ See also `multi-occur'."
                             ;; of multi-line matches.
                             (replace-regexp-in-string
                              "\n"
                             ;; of multi-line matches.
                             (replace-regexp-in-string
                              "\n"
-                             "\n       :"
+                             (if prefix-face
+                                 (propertize "\n       :" 'font-lock-face prefix-face)
+                               "\n       :")
                              match-str)
                             ;; Add marker at eol, but no mouse props.
                             (propertize "\n" 'occur-target marker)))
                              match-str)
                             ;; Add marker at eol, but no mouse props.
                             (propertize "\n" 'occur-target marker)))
@@ -1434,7 +1503,8 @@ See also `multi-occur'."
                              ;; The complex multi-line display style.
                              (setq ret (occur-context-lines
                                         out-line nlines keep-props begpt endpt
                              ;; The complex multi-line display style.
                              (setq ret (occur-context-lines
                                         out-line nlines keep-props begpt endpt
-                                        lines prev-lines prev-after-lines))
+                                        curr-line prev-line prev-after-lines
+                                        prefix-face))
                              ;; Set first elem of the returned list to `data',
                              ;; and the second elem to `prev-after-lines'.
                              (setq prev-after-lines (nth 1 ret))
                              ;; Set first elem of the returned list to `data',
                              ;; and the second elem to `prev-after-lines'.
                              (setq prev-after-lines (nth 1 ret))
@@ -1446,28 +1516,34 @@ See also `multi-occur'."
                  (if endpt
                      (progn
                        ;; Sum line numbers between first and last match lines.
                  (if endpt
                      (progn
                        ;; 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)))
+                       (setq curr-line (+ curr-line (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)))
                        ;; On to the next match...
                        (forward-line 1))
                    (goto-char (point-max)))
-                 (setq prev-lines (1- lines)))
+                 (setq prev-line (1- curr-line)))
                ;; Flush remaining context after-lines.
                (when prev-after-lines
                  (with-current-buffer out-buf
                    (insert (apply #'concat (occur-engine-add-prefix
                ;; 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))
+                                            prev-after-lines prefix-face)))))))
+           (when (not (zerop lines)) ;; is the count zero?
+             (setq global-lines (+ global-lines lines)
+                   global-matches (+ global-matches matches))
              (with-current-buffer out-buf
                (goto-char headerpt)
                (let ((beg (point))
                      end)
                  (insert (propertize
              (with-current-buffer out-buf
                (goto-char headerpt)
                (let ((beg (point))
                      end)
                  (insert (propertize
-                          (format "%d match%s%s in buffer: %s\n"
+                          (format "%d match%s%s%s in buffer: %s\n"
                                   matches (if (= matches 1) "" "es")
                                   matches (if (= matches 1) "" "es")
+                                  ;; Don't display the same number of lines
+                                  ;; and matches in case of 1 match per line.
+                                  (if (= lines matches)
+                                      "" (format " in %d line%s"
+                                                 lines (if (= lines 1) "" "s")))
                                   ;; Don't display regexp for multi-buffer.
                                   (if (> (length buffers) 1)
                                       "" (format " for \"%s\""
                                   ;; Don't display regexp for multi-buffer.
                                   (if (> (length buffers) 1)
                                       "" (format " for \"%s\""
@@ -1482,12 +1558,17 @@ See also `multi-occur'."
                                        `(occur-title ,buf))))
                (goto-char (point-min)))))))
       ;; Display total match count and regexp for multi-buffer.
                                        `(occur-title ,buf))))
                (goto-char (point-min)))))))
       ;; Display total match count and regexp for multi-buffer.
-      (when (and (not (zerop globalcount)) (> (length buffers) 1))
+      (when (and (not (zerop global-lines)) (> (length buffers) 1))
        (goto-char (point-min))
        (let ((beg (point))
              end)
        (goto-char (point-min))
        (let ((beg (point))
              end)
-         (insert (format "%d match%s total for \"%s\":\n"
-                         globalcount (if (= globalcount 1) "" "es")
+         (insert (format "%d match%s%s total for \"%s\":\n"
+                         global-matches (if (= global-matches 1) "" "es")
+                         ;; Don't display the same number of lines
+                         ;; and matches in case of 1 match per line.
+                         (if (= global-lines global-matches)
+                             "" (format " in %d line%s"
+                                        global-lines (if (= global-lines 1) "" "s")))
                          (query-replace-descr regexp)))
          (setq end (point))
          (add-text-properties beg end (when title-face
                          (query-replace-descr regexp)))
          (setq end (point))
          (add-text-properties beg end (when title-face
@@ -1499,7 +1580,7 @@ See also `multi-occur'."
          ;; buffer.
          (set-buffer-file-coding-system coding))
       ;; Return the number of matches
          ;; buffer.
          (set-buffer-file-coding-system coding))
       ;; Return the number of matches
-      globalcount)))
+      global-matches)))
 
 (defun occur-engine-line (beg end &optional keep-props)
   (if (and keep-props (if (boundp 'jit-lock-mode) jit-lock-mode)
 
 (defun occur-engine-line (beg end &optional keep-props)
   (if (and keep-props (if (boundp 'jit-lock-mode) jit-lock-mode)
@@ -1513,10 +1594,13 @@ See also `multi-occur'."
        str)
     (buffer-substring-no-properties beg end)))
 
        str)
     (buffer-substring-no-properties beg end)))
 
-(defun occur-engine-add-prefix (lines)
+(defun occur-engine-add-prefix (lines &optional prefix-face)
   (mapcar
    #'(lambda (line)
   (mapcar
    #'(lambda (line)
-       (concat "       :" line "\n"))
+       (concat (if prefix-face
+                  (propertize "       :" 'font-lock-face prefix-face)
+                "       :")
+              line "\n"))
    lines))
 
 (defun occur-accumulate-lines (count &optional keep-props pt)
    lines))
 
 (defun occur-accumulate-lines (count &optional keep-props pt)
@@ -1539,13 +1623,14 @@ See also `multi-occur'."
 ;; Generate context display for occur.
 ;; OUT-LINE is the line where the match is.
 ;; NLINES and KEEP-PROPS are args to occur-engine.
 ;; 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,
+;; CURR-LINE is line count of the current match,
+;; PREV-LINE 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 begpt endpt
 ;; 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 begpt endpt
-                                    lines prev-lines prev-after-lines)
+                                    curr-line prev-line prev-after-lines
+                                    &optional prefix-face)
   ;; Find after- and before-context lines of the current match.
   (let ((before-lines
         (nreverse (cdr (occur-accumulate-lines
   ;; Find after- and before-context lines of the current match.
   (let ((before-lines
         (nreverse (cdr (occur-accumulate-lines
@@ -1560,22 +1645,22 @@ See also `multi-occur'."
 
     (when prev-after-lines
       ;; Don't overlap prev after-lines with current before-lines.
 
     (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)))
+      (if (>= (+ prev-line (length prev-after-lines))
+             (- curr-line      (length before-lines)))
          (setq prev-after-lines
                (butlast prev-after-lines
                         (- (length prev-after-lines)
          (setq prev-after-lines
                (butlast prev-after-lines
                         (- (length prev-after-lines)
-                           (- lines prev-lines (length before-lines) 1))))
+                           (- curr-line prev-line (length before-lines) 1))))
        ;; Separate non-overlapping context lines with a dashed line.
        (setq separator "-------\n")))
 
        ;; Separate non-overlapping context lines with a dashed line.
        (setq separator "-------\n")))
 
-    (when prev-lines
+    (when prev-line
       ;; Don't overlap current before-lines with previous match line.
       ;; Don't overlap current before-lines with previous match line.
-      (if (<= (- lines (length before-lines))
-             prev-lines)
+      (if (<= (- curr-line (length before-lines))
+             prev-line)
          (setq before-lines
                (nthcdr (- (length before-lines)
          (setq before-lines
                (nthcdr (- (length before-lines)
-                          (- lines prev-lines 1))
+                          (- curr-line prev-line 1))
                        before-lines))
        ;; Separate non-overlapping before-context lines.
        (unless (> nlines 0)
                        before-lines))
        ;; Separate non-overlapping before-context lines.
        (unless (> nlines 0)
@@ -1585,10 +1670,13 @@ See also `multi-occur'."
      ;; Return a list where the first element is the output line.
      (apply #'concat
            (append
      ;; 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)
+            (if prev-after-lines
+                (occur-engine-add-prefix prev-after-lines prefix-face))
+            (if separator
+                (list (if prefix-face
+                          (propertize separator 'font-lock-face prefix-face)
+                        separator)))
+            (occur-engine-add-prefix before-lines prefix-face)
             (list out-line)))
      ;; And the second element is the list of context after-lines.
      (if (> nlines 0) after-lines))))
             (list out-line)))
      ;; And the second element is the list of context after-lines.
      (if (> nlines 0) after-lines))))
@@ -1711,7 +1799,12 @@ type them using Lisp syntax."
 
 (defun replace-eval-replacement (expression count)
   (let* ((replace-count count)
 
 (defun replace-eval-replacement (expression count)
   (let* ((replace-count count)
-         (replacement (eval expression)))
+         err
+         (replacement
+          (condition-case err
+              (eval expression)
+            (error
+             (error "Error evaluating replacement expression: %S" err)))))
     (if (stringp replacement)
         replacement
       (prin1-to-string replacement t))))
     (if (stringp replacement)
         replacement
       (prin1-to-string replacement t))))
@@ -1794,6 +1887,68 @@ It is used by `query-replace-regexp', `replace-regexp',
 It is called with three arguments, as if it were
 `re-search-forward'.")
 
 It is called with three arguments, as if it were
 `re-search-forward'.")
 
+(defun replace-search (search-string limit regexp-flag delimited-flag
+                                    case-fold-search)
+  "Search for the next occurrence of SEARCH-STRING to replace."
+  ;; Let-bind global isearch-* variables to values used
+  ;; to search the next replacement.  These let-bindings
+  ;; should be effective both at the time of calling
+  ;; `isearch-search-fun-default' and also at the
+  ;; time of funcalling `search-function'.
+  ;; These isearch-* bindings can't be placed higher
+  ;; outside of this function because then another I-search
+  ;; used after `recursive-edit' might override them.
+  (let* ((isearch-regexp regexp-flag)
+        (isearch-word delimited-flag)
+        (isearch-lax-whitespace
+         replace-lax-whitespace)
+        (isearch-regexp-lax-whitespace
+         replace-regexp-lax-whitespace)
+        (isearch-case-fold-search case-fold-search)
+        (isearch-adjusted nil)
+        (isearch-nonincremental t)     ; don't use lax word mode
+        (isearch-forward t)
+        (search-function
+         (or (if regexp-flag
+                 replace-re-search-function
+               replace-search-function)
+             (isearch-search-fun-default))))
+    (funcall search-function search-string limit t)))
+
+(defvar replace-overlay nil)
+
+(defun replace-highlight (match-beg match-end range-beg range-end
+                         search-string regexp-flag delimited-flag
+                         case-fold-search)
+  (if query-replace-highlight
+      (if replace-overlay
+         (move-overlay replace-overlay match-beg match-end (current-buffer))
+       (setq replace-overlay (make-overlay match-beg match-end))
+       (overlay-put replace-overlay 'priority 1001) ;higher than lazy overlays
+       (overlay-put replace-overlay 'face 'query-replace)))
+  (if query-replace-lazy-highlight
+      (let ((isearch-string search-string)
+           (isearch-regexp regexp-flag)
+           (isearch-word delimited-flag)
+           (isearch-lax-whitespace
+            replace-lax-whitespace)
+           (isearch-regexp-lax-whitespace
+            replace-regexp-lax-whitespace)
+           (isearch-case-fold-search case-fold-search)
+           (isearch-forward t)
+           (isearch-other-end match-beg)
+           (isearch-error nil))
+       (isearch-lazy-highlight-new-loop range-beg range-end))))
+
+(defun replace-dehighlight ()
+  (when replace-overlay
+    (delete-overlay replace-overlay))
+  (when query-replace-lazy-highlight
+    (lazy-highlight-cleanup lazy-highlight-cleanup)
+    (setq isearch-lazy-highlight-last-string nil))
+  ;; Close overlays opened by `isearch-range-invisible' in `perform-replace'.
+  (isearch-clean-overlays))
+
 (defun perform-replace (from-string replacements
                        query-flag regexp-flag delimited-flag
                        &optional repeat-count map start end)
 (defun perform-replace (from-string replacements
                        query-flag regexp-flag delimited-flag
                        &optional repeat-count map start end)
@@ -1828,6 +1983,9 @@ make, or the user didn't cancel the call."
          (keep-going t)
          (stack nil)
          (replace-count 0)
          (keep-going t)
          (stack nil)
          (replace-count 0)
+         (skip-read-only-count 0)
+         (skip-filtered-count 0)
+         (skip-invisible-count 0)
          (nonempty-match nil)
         (multi-buffer nil)
         (recenter-last-op nil) ; Start cycling order with initial position.
          (nonempty-match nil)
         (multi-buffer nil)
         (recenter-last-op nil) ; Start cycling order with initial position.
@@ -1881,62 +2039,40 @@ make, or the user didn't cancel the call."
        ;; Loop finding occurrences that perhaps should be replaced.
        (while (and keep-going
                    (not (or (eobp) (and limit (>= (point) limit))))
        ;; Loop finding occurrences that perhaps should be replaced.
        (while (and keep-going
                    (not (or (eobp) (and limit (>= (point) limit))))
-                   ;; Let-bind global isearch-* variables to values used
-                   ;; to search the next replacement.  These let-bindings
-                   ;; should be effective both at the time of calling
-                   ;; `isearch-search-fun-default' and also at the
-                   ;; time of funcalling `search-function'.
-                   ;; These isearch-* bindings can't be placed higher
-                   ;; outside of this loop because then another I-search
-                   ;; used after `recursive-edit' might override them.
-                   (let* ((isearch-regexp regexp-flag)
-                          (isearch-word delimited-flag)
-                          (isearch-lax-whitespace
-                           replace-lax-whitespace)
-                          (isearch-regexp-lax-whitespace
-                           replace-regexp-lax-whitespace)
-                          (isearch-case-fold-search case-fold-search)
-                          (isearch-adjusted nil)
-                          (isearch-nonincremental t) ; don't use lax word mode
-                          (isearch-forward t)
-                          (search-function
-                           (or (if regexp-flag
-                                   replace-re-search-function
-                                 replace-search-function)
-                               (isearch-search-fun-default))))
-                     ;; Use the next match if it is already known;
-                     ;; otherwise, search for a match after moving forward
-                     ;; one char if progress is required.
-                     (setq real-match-data
-                           (cond ((consp match-again)
-                                  (goto-char (nth 1 match-again))
-                                  (replace-match-data
-                                   t real-match-data match-again))
-                                 ;; MATCH-AGAIN non-nil means accept an
-                                 ;; adjacent match.
-                                 (match-again
-                                  (and
-                                   (funcall search-function search-string
-                                            limit t)
-                                   ;; For speed, use only integers and
-                                   ;; reuse the list used last time.
-                                   (replace-match-data t real-match-data)))
-                                 ((and (< (1+ (point)) (point-max))
-                                       (or (null limit)
-                                           (< (1+ (point)) limit)))
-                                  ;; If not accepting adjacent matches,
-                                  ;; move one char to the right before
-                                  ;; searching again.  Undo the motion
-                                  ;; if the search fails.
-                                  (let ((opoint (point)))
-                                    (forward-char 1)
-                                    (if (funcall
-                                         search-function search-string
-                                         limit t)
-                                        (replace-match-data
-                                         t real-match-data)
-                                      (goto-char opoint)
-                                      nil)))))))
+                   ;; Use the next match if it is already known;
+                   ;; otherwise, search for a match after moving forward
+                   ;; one char if progress is required.
+                   (setq real-match-data
+                         (cond ((consp match-again)
+                                (goto-char (nth 1 match-again))
+                                (replace-match-data
+                                 t real-match-data match-again))
+                               ;; MATCH-AGAIN non-nil means accept an
+                               ;; adjacent match.
+                               (match-again
+                                (and
+                                 (replace-search search-string limit
+                                                 regexp-flag delimited-flag
+                                                 case-fold-search)
+                                 ;; For speed, use only integers and
+                                 ;; reuse the list used last time.
+                                 (replace-match-data t real-match-data)))
+                               ((and (< (1+ (point)) (point-max))
+                                     (or (null limit)
+                                         (< (1+ (point)) limit)))
+                                ;; If not accepting adjacent matches,
+                                ;; move one char to the right before
+                                ;; searching again.  Undo the motion
+                                ;; if the search fails.
+                                (let ((opoint (point)))
+                                  (forward-char 1)
+                                  (if (replace-search search-string limit
+                                                      regexp-flag delimited-flag
+                                                      case-fold-search)
+                                      (replace-match-data
+                                       t real-match-data)
+                                    (goto-char opoint)
+                                    nil))))))
 
          ;; Record whether the match is nonempty, to avoid an infinite loop
          ;; repeatedly matching the same empty string.
 
          ;; Record whether the match is nonempty, to avoid an infinite loop
          ;; repeatedly matching the same empty string.
@@ -1958,12 +2094,26 @@ make, or the user didn't cancel the call."
                                (and (/= (nth 0 match) (nth 1 match))
                                     match))))))
 
                                (and (/= (nth 0 match) (nth 1 match))
                                     match))))))
 
-         ;; Optionally ignore matches that have a read-only property.
-         (unless (and query-replace-skip-read-only
-                      (text-property-not-all
-                       (nth 0 real-match-data) (nth 1 real-match-data)
-                       'read-only nil))
-
+         (cond
+          ;; Optionally ignore matches that have a read-only property.
+          ((not (or (not query-replace-skip-read-only)
+                    (not (text-property-not-all
+                          (nth 0 real-match-data) (nth 1 real-match-data)
+                          'read-only nil))))
+           (setq skip-read-only-count (1+ skip-read-only-count)))
+          ;; Optionally filter out matches.
+          ((not (funcall isearch-filter-predicate
+                          (nth 0 real-match-data) (nth 1 real-match-data)))
+           (setq skip-filtered-count (1+ skip-filtered-count)))
+          ;; Optionally ignore invisible matches.
+          ((not (or (eq search-invisible t)
+                    ;; Don't open overlays for automatic replacements.
+                    (and (not query-flag) search-invisible)
+                    ;; Open hidden overlays for interactive replacements.
+                    (not (isearch-range-invisible
+                          (nth 0 real-match-data) (nth 1 real-match-data)))))
+           (setq skip-invisible-count (1+ skip-invisible-count)))
+          (t
            ;; Calculate the replacement string, if necessary.
            (when replacements
              (set-match-data real-match-data)
            ;; Calculate the replacement string, if necessary.
            (when replacements
              (set-match-data real-match-data)
@@ -2020,7 +2170,10 @@ make, or the user didn't cancel the call."
                         (with-output-to-temp-buffer "*Help*"
                           (princ
                            (concat "Query replacing "
                         (with-output-to-temp-buffer "*Help*"
                           (princ
                            (concat "Query replacing "
-                                   (if delimited-flag "word " "")
+                                   (if delimited-flag
+                                       (or (and (symbolp delimited-flag)
+                                                (get delimited-flag 'isearch-message-prefix))
+                                           "word ") "")
                                    (if regexp-flag "regexp " "")
                                    from-string " with "
                                    next-replacement ".\n\n"
                                    (if regexp-flag "regexp " "")
                                    from-string " with "
                                    next-replacement ".\n\n"
@@ -2168,44 +2321,31 @@ make, or the user didn't cancel the call."
                                 (match-end 0)
                                 (current-buffer))
                              (match-data t)))
                                 (match-end 0)
                                 (current-buffer))
                              (match-data t)))
-                     stack)))))
+                     stack))))))
 
       (replace-dehighlight))
     (or unread-command-events
 
       (replace-dehighlight))
     (or unread-command-events
-       (message "Replaced %d occurrence%s"
+       (message "Replaced %d occurrence%s%s"
                 replace-count
                 replace-count
-                (if (= replace-count 1) "" "s")))
+                (if (= replace-count 1) "" "s")
+                (if (> (+ skip-read-only-count
+                          skip-filtered-count
+                          skip-invisible-count) 0)
+                    (format " (skipped %s)"
+                            (mapconcat
+                             'identity
+                             (delq nil (list
+                                        (if (> skip-read-only-count 0)
+                                            (format "%s read-only"
+                                                    skip-read-only-count))
+                                        (if (> skip-invisible-count 0)
+                                            (format "%s invisible"
+                                                    skip-invisible-count))
+                                        (if (> skip-filtered-count 0)
+                                            (format "%s filtered out"
+                                                    skip-filtered-count))))
+                             ", "))
+                  "")))
     (or (and keep-going stack) multi-buffer)))
 
     (or (and keep-going stack) multi-buffer)))
 
-(defvar replace-overlay nil)
-
-(defun replace-highlight (match-beg match-end range-beg range-end
-                         search-string regexp-flag delimited-flag
-                         case-fold-search)
-  (if query-replace-highlight
-      (if replace-overlay
-         (move-overlay replace-overlay match-beg match-end (current-buffer))
-       (setq replace-overlay (make-overlay match-beg match-end))
-       (overlay-put replace-overlay 'priority 1001) ;higher than lazy overlays
-       (overlay-put replace-overlay 'face 'query-replace)))
-  (if query-replace-lazy-highlight
-      (let ((isearch-string search-string)
-           (isearch-regexp regexp-flag)
-           (isearch-word delimited-flag)
-           (isearch-lax-whitespace
-            replace-lax-whitespace)
-           (isearch-regexp-lax-whitespace
-            replace-regexp-lax-whitespace)
-           (isearch-case-fold-search case-fold-search)
-           (isearch-forward t)
-           (isearch-error nil))
-       (isearch-lazy-highlight-new-loop range-beg range-end))))
-
-(defun replace-dehighlight ()
-  (when replace-overlay
-    (delete-overlay replace-overlay))
-  (when query-replace-lazy-highlight
-    (lazy-highlight-cleanup lazy-highlight-cleanup)
-    (setq isearch-lazy-highlight-last-string nil)))
-
 ;;; replace.el ends here
 ;;; replace.el ends here