]> code.delx.au - gnu-emacs/blobdiff - lisp/replace.el
Merge from origin/emacs-25
[gnu-emacs] / lisp / replace.el
index a671a98b7a039b78258ea91a95fafc9917d082d0..0b25200be575cb1a3623c2a7607ffd2caad8a2dc 100644 (file)
@@ -1,6 +1,6 @@
 ;;; replace.el --- replace commands for Emacs
 
-;; Copyright (C) 1985-1987, 1992, 1994, 1996-1997, 2000-2015 Free
+;; Copyright (C) 1985-1987, 1992, 1994, 1996-1997, 2000-2016 Free
 ;; Software Foundation, Inc.
 
 ;; Maintainer: emacs-devel@gnu.org
   :group 'matching)
 
 (defcustom replace-character-fold nil
-  "Non-nil means `query-replace' should do character folding in matches.
+  "Non-nil means replacement commands should do character folding in matches.
 This means, for instance, that \\=' will match a large variety of
-unicode quotes."
+unicode quotes.
+This variable affects `query-replace' and `replace-string', but not
+`replace-regexp'."
   :type 'boolean
   :group 'matching
   :version "25.1")
@@ -83,7 +85,7 @@ from Isearch by using a key sequence like `C-s C-s M-%'." "24.3")
   ;; while preparing to dump, also stops customize-rogue listing this.
   :initialize 'custom-initialize-delay
   :group 'matching
-  :type 'sexp
+  :type '(choice string (sexp :tag "Display specification"))
   :version "25.1")
 
 (defcustom query-replace-from-history-variable 'query-replace-history
@@ -165,8 +167,6 @@ wants to replace FROM with TO."
     ;; unavailable while preparing to dump.
     (custom-reevaluate-setting 'query-replace-from-to-separator)
     (let* ((history-add-new-input nil)
-          (text-property-default-nonsticky
-           (cons '(separator . t) text-property-default-nonsticky))
           (separator
            (when query-replace-from-to-separator
              (propertize "\0"
@@ -191,11 +191,15 @@ wants to replace FROM with TO."
            ;; a region in order to specify the minibuffer input.
            ;; That should not clobber the region for the query-replace itself.
            (save-excursion
-             (if regexp-flag
-                 (read-regexp prompt nil 'query-replace-from-to-history)
-               (read-from-minibuffer
-                prompt nil nil nil 'query-replace-from-to-history
-                (car (if regexp-flag regexp-search-ring search-ring)) t))))
+              (minibuffer-with-setup-hook
+                  (lambda ()
+                    (setq-local text-property-default-nonsticky
+                                (cons '(separator . t) text-property-default-nonsticky)))
+                (if regexp-flag
+                    (read-regexp prompt nil 'query-replace-from-to-history)
+                  (read-from-minibuffer
+                   prompt nil nil nil 'query-replace-from-to-history
+                   (car (if regexp-flag regexp-search-ring search-ring)) t)))))
            (to))
       (if (and (zerop (length from)) query-replace-defaults)
          (cons (caar query-replace-defaults)
@@ -291,7 +295,12 @@ As each match is found, the user must type a character saying
 what to do with it.  For directions, type \\[help-command] at that time.
 
 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.
+of the region.  Otherwise, operate from point to the end of the buffer's
+accessible portion.
+
+In interactive use, the prefix arg (non-nil DELIMITED in
+non-interactive use), means replace only matches surrounded by
+word boundaries.  A negative prefix arg means replace backward.
 
 Use \\<minibuffer-local-map>\\[next-history-element] \
 to pull the last incremental search string to the minibuffer
@@ -315,9 +324,9 @@ 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'.
 
-Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
-only matches surrounded by word boundaries.  A negative prefix arg means
-replace backward.
+If `replace-character-fold' is non-nil, matching uses character folding,
+i.e. it ignores diacritics and other differences between equivalent
+character strings.
 
 Fourth and fifth arg START and END specify the region to operate on.
 
@@ -349,7 +358,8 @@ As each match is found, the user must type a character saying
 what to do with it.  For directions, type \\[help-command] at that time.
 
 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.
+of the region.  Otherwise, operate from point to the end of the buffer's
+accessible portion.
 
 Use \\<minibuffer-local-map>\\[next-history-element] \
 to pull the last incremental search regexp to the minibuffer
@@ -373,6 +383,8 @@ 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'.
 
+This function is not affected by `replace-character-fold'.
+
 Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
 only matches surrounded by word boundaries.  A negative prefix arg means
 replace backward.
@@ -444,7 +456,8 @@ 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.
+of the region.  Otherwise, operate from point to the end of the buffer's
+accessible portion.
 
 Use \\<minibuffer-local-map>\\[next-history-element] \
 to pull the last incremental search regexp to the minibuffer
@@ -461,6 +474,8 @@ 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'.
 
+This function is not affected by `replace-character-fold'.
+
 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."
@@ -497,7 +512,8 @@ each successive replacement uses the next successive replacement string,
 wrapping around from the last such string to the first.
 
 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.
+of the region.  Otherwise, operate from point to the end of the buffer's
+accessible portion.
 
 Non-interactively, TO-STRINGS may be a list of replacement strings.
 
@@ -552,6 +568,10 @@ 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-character-fold' is non-nil, matching uses character folding,
+i.e. it ignores diacritics and other differences between equivalent
+character strings.
+
 Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
 only matches surrounded by word boundaries.  A negative prefix arg means
 replace backward.
@@ -559,7 +579,7 @@ replace backward.
 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.
+otherwise from point to the end of the buffer's accessible portion.
 
 Use \\<minibuffer-local-map>\\[next-history-element] \
 to pull the last incremental search string to the minibuffer
@@ -603,8 +623,11 @@ 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'.
 
+This function is not affected by `replace-character-fold'
+
 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.
+of the region.  Otherwise, operate from point to the end of the buffer's
+accessible portion.
 
 Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
 only matches surrounded by word boundaries.  A negative prefix arg means
@@ -1252,6 +1275,7 @@ Compatibility function for \\[next-error] invocations."
     (t :background "gray"))
   "Face used to highlight matches permanently."
   :group 'matching
+  :group 'basic-faces
   :version "22.1")
 
 (defcustom list-matching-lines-default-context-lines 0
@@ -1561,6 +1585,9 @@ See also `multi-occur'."
                    ;; Highlight the matches
                    (let ((len (length curstring))
                          (start 0))
+                     ;; Count empty lines that don't use next loop (Bug#22062).
+                     (when (zerop len)
+                       (setq matches (1+ matches)))
                      (while (and (< start len)
                                  (string-match regexp curstring start))
                        (setq matches (1+ matches))
@@ -1805,6 +1832,8 @@ C-w to delete match and recursive edit,
 C-l to clear the screen, redisplay, and offer same replacement again,
 ! to replace all remaining matches in this buffer with no more questions,
 ^ to move point back to previous match,
+u to undo previous replacement,
+U to undo all replacements,
 E to edit the replacement string.
 In multi-buffer replacements type `Y' to replace all remaining
 matches in all remaining buffers with no more questions,
@@ -1834,6 +1863,8 @@ in the current buffer."
     (define-key map "\C-l" 'recenter)
     (define-key map "!" 'automatic)
     (define-key map "^" 'backup)
+    (define-key map "u" 'undo)
+    (define-key map "U" 'undo-all)
     (define-key map "\C-h" 'help)
     (define-key map [f1] 'help)
     (define-key map [help] 'help)
@@ -1859,7 +1890,7 @@ The valid answers include `act', `skip', `act-and-show',
 `act-and-exit', `exit', `exit-prefix', `recenter', `scroll-up',
 `scroll-down', `scroll-other-window', `scroll-other-window-down',
 `edit', `edit-replacement', `delete-and-edit', `automatic',
-`backup', `quit', and `help'.
+`backup', `undo', `undo-all', `quit', and `help'.
 
 This keymap is used by `y-or-n-p' as well as `query-replace'.")
 
@@ -1961,13 +1992,17 @@ but coerced to the correct value of INTEGERS."
                  new)))
       (match-data integers reuse t)))
 
-(defun replace-match-maybe-edit (newtext fixedcase literal noedit match-data backward)
+(defun replace-match-maybe-edit (newtext fixedcase literal noedit match-data
+                                 &optional backward)
   "Make a replacement with `replace-match', editing `\\?'.
 FIXEDCASE, LITERAL are passed to `replace-match' (which see).
 After possibly editing it (if `\\?' is present), NEWTEXT is also
 passed to `replace-match'.  If NOEDIT is true, no check for `\\?'
-is made (to save time).  MATCH-DATA is used for the replacement.
-In case editing is done, it is changed to use markers.
+is made (to save time).
+MATCH-DATA is used for the replacement, and is a data structure
+as returned from the `match-data' function.
+In case editing is done, it is changed to use markers.  BACKWARD is
+used to reverse the replacement direction.
 
 The return value is non-nil if there has been no `\\?' or NOEDIT was
 passed in.  If LITERAL is set, no checking is done, anyway."
@@ -1992,6 +2027,9 @@ passed in.  If LITERAL is set, no checking is done, anyway."
   (when backward (goto-char (nth 0 match-data)))
   noedit)
 
+(defvar replace-update-post-hook nil
+  "Function(s) to call after query-replace has found a match in the buffer.")
+
 (defvar replace-search-function nil
   "Function to use when searching for strings to replace.
 It is used by `query-replace' and `replace-string', and is called
@@ -2005,7 +2043,7 @@ 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 backward)
+                      case-fold-search &optional backward)
   "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
@@ -2039,7 +2077,7 @@ It is called with three arguments, as if it were
 
 (defun replace-highlight (match-beg match-end range-beg range-end
                          search-string regexp-flag delimited-flag
-                         case-fold-search backward)
+                         case-fold-search &optional backward)
   (if query-replace-highlight
       (if replace-overlay
          (move-overlay replace-overlay match-beg match-end (current-buffer))
@@ -2108,6 +2146,10 @@ It must return a string."
          (noedit nil)
          (keep-going t)
          (stack nil)
+         (search-string-replaced nil)    ; last string matching `from-string'
+         (next-replacement-replaced nil) ; replacement string
+                                         ; (substituted regexp)
+         (last-was-undo)
          (replace-count 0)
          (skip-read-only-count 0)
          (skip-filtered-count 0)
@@ -2245,7 +2287,7 @@ It must return a string."
                (and nonempty-match
                     (or (not regexp-flag)
                         (and (if backward
-                                 (looking-back search-string)
+                                 (looking-back search-string nil)
                                (looking-at search-string))
                              (let ((match (match-data)))
                                (and (/= (nth 0 match) (nth 1 match))
@@ -2299,12 +2341,33 @@ It must return a string."
                ;; `real-match-data'.
                (while (not done)
                  (set-match-data real-match-data)
-                 (replace-highlight
+                  (run-hooks 'replace-update-post-hook) ; Before `replace-highlight'.
+                  (replace-highlight
                   (match-beginning 0) (match-end 0)
                   start end search-string
                   regexp-flag delimited-flag case-fold-search backward)
-                 ;; Bind message-log-max so we don't fill up the message log
-                 ;; with a bunch of identical messages.
+                  ;; Obtain the matched groups: needed only when
+                  ;; regexp-flag non nil.
+                  (when (and last-was-undo regexp-flag)
+                    (setq last-was-undo nil
+                          real-match-data
+                          (save-excursion
+                            (goto-char (match-beginning 0))
+                            (looking-at search-string)
+                            (match-data t real-match-data))))
+                  ;; Matched string and next-replacement-replaced
+                  ;; stored in stack.
+                  (setq search-string-replaced (buffer-substring-no-properties
+                                                (match-beginning 0)
+                                                (match-end 0))
+                        next-replacement-replaced
+                        (query-replace-descr
+                         (save-match-data
+                           (set-match-data real-match-data)
+                           (match-substitute-replacement
+                            next-replacement nocasify literal))))
+                 ;; Bind message-log-max so we don't fill up the
+                 ;; message log with a bunch of identical messages.
                  (let ((message-log-max nil)
                        (replacement-presentation
                         (if query-replace-show-replacement
@@ -2317,8 +2380,8 @@ It must return a string."
                              (query-replace-descr from-string)
                              (query-replace-descr replacement-presentation)))
                  (setq key (read-event))
-                 ;; Necessary in case something happens during read-event
-                 ;; that clobbers the match data.
+                 ;; Necessary in case something happens during
+                 ;; read-event that clobbers the match data.
                  (set-match-data real-match-data)
                  (setq key (vector key))
                  (setq def (lookup-key map key))
@@ -2329,7 +2392,8 @@ It must return a string."
                            (concat "Query replacing "
                                    (if delimited-flag
                                        (or (and (symbolp delimited-flag)
-                                                (get delimited-flag 'isearch-message-prefix))
+                                                (get delimited-flag
+                                                      'isearch-message-prefix))
                                            "word ") "")
                                    (if regexp-flag "regexp " "")
                                    (if backward "backward " "")
@@ -2356,6 +2420,73 @@ It must return a string."
                           (message "No previous match")
                           (ding 'no-terminate)
                           (sit-for 1)))
+                       ((or (eq def 'undo) (eq def 'undo-all))
+                        (if (null stack)
+                             (progn
+                               (message "Nothing to undo")
+                               (ding 'no-terminate)
+                               (sit-for 1))
+                          (let ((stack-idx         0)
+                                 (stack-len         (length stack))
+                                 (num-replacements  0)
+                                 search-string
+                                 next-replacement)
+                             (while (and (< stack-idx stack-len)
+                                         stack
+                                         (null replaced))
+                               (let* ((elt (nth stack-idx stack)))
+                                 (setq
+                                  stack-idx (1+ stack-idx)
+                                  replaced (nth 1 elt)
+                                  ;; Bind swapped values
+                                  ;; (search-string <--> replacement)
+                                  search-string (nth (if replaced 4 3) elt)
+                                  next-replacement (nth (if replaced 3 4) elt)
+                                  search-string-replaced search-string
+                                  next-replacement-replaced next-replacement)
+
+                                 (when (and (= stack-idx stack-len)
+                                            (null replaced)
+                                            (zerop num-replacements))
+                                          (message "Nothing to undo")
+                                          (ding 'no-terminate)
+                                          (sit-for 1))
+
+                                 (when replaced
+                                   (setq stack (nthcdr stack-idx stack))
+                                   (goto-char (nth 0 elt))
+                                   (set-match-data (nth 2 elt))
+                                   (setq real-match-data
+                                         (save-excursion
+                                           (goto-char (match-beginning 0))
+                                           (looking-at search-string)
+                                           (match-data t (nth 2 elt)))
+                                         noedit
+                                         (replace-match-maybe-edit
+                                          next-replacement nocasify literal
+                                          noedit real-match-data backward)
+                                         replace-count (1- replace-count)
+                                         real-match-data
+                                         (save-excursion
+                                           (goto-char (match-beginning 0))
+                                           (looking-at next-replacement)
+                                           (match-data t (nth 2 elt))))
+                                   ;; Set replaced nil to keep in loop
+                                   (when (eq def 'undo-all)
+                                     (setq replaced nil
+                                           stack-len (- stack-len stack-idx)
+                                           stack-idx 0
+                                           num-replacements
+                                           (1+ num-replacements))))))
+                             (when (and (eq def 'undo-all)
+                                        (null (zerop num-replacements)))
+                               (message "Undid %d %s" num-replacements
+                                        (if (= num-replacements 1)
+                                            "replacement"
+                                          "replacements"))
+                               (ding 'no-terminate)
+                               (sit-for 1)))
+                          (setq replaced nil last-was-undo t)))
                        ((eq def 'act)
                         (or replaced
                             (setq noedit
@@ -2478,9 +2609,12 @@ It must return a string."
                                 (match-beginning 0)
                                 (match-end 0)
                                 (current-buffer))
-                             (match-data t)))
-                     stack))))))
-
+                             (match-data t))
+                               search-string-replaced
+                               next-replacement-replaced)
+                     stack)
+                (setq next-replacement-replaced nil
+                      search-string-replaced    nil))))))
       (replace-dehighlight))
     (or unread-command-events
        (message "Replaced %d occurrence%s%s"