diff options
Diffstat (limited to 'lisp/replace.el')
| -rw-r--r-- | lisp/replace.el | 137 |
1 files changed, 111 insertions, 26 deletions
diff --git a/lisp/replace.el b/lisp/replace.el index 2d26cb5cc66..89f55c2829e 100644 --- a/lisp/replace.el +++ b/lisp/replace.el @@ -1,7 +1,7 @@ ;;; replace.el --- replace commands for Emacs -;; Copyright (C) 1985, 86, 87, 92, 94, 96, 1997, 2000, 2001, 2002 -;; Free Software Foundation, Inc. +;; Copyright (C) 1985, 86, 87, 92, 94, 96, 1997, 2000, 2001, 2002, +;; 2003, 2004 Free Software Foundation, Inc. ;; Maintainer: FSF @@ -36,9 +36,11 @@ (defvar query-replace-history nil) -(defvar query-replace-interactive nil +(defcustom query-replace-interactive nil "Non-nil means `query-replace' uses the last search string. -That becomes the \"string to replace\".") +That becomes the \"string to replace\"." + :type 'boolean + :group 'matching) (defcustom query-replace-from-history-variable 'query-replace-history "History list to use for the FROM argument of `query-replace' commands. @@ -79,14 +81,15 @@ strings or patterns." query-replace-from-history-variable nil t))) ;; Warn if user types \n or \t, but don't reject the input. - (if (string-match "\\\\[nt]" from) - (let ((match (match-string 0 from))) - (cond - ((string= match "\\n") - (message "Note: `\\n' here doesn't match a newline; to do that, type C-q C-j instead")) - ((string= match "\\t") - (message "Note: `\\t' here doesn't match a tab; to do that, just type TAB"))) - (sit-for 2)))) + (and regexp-flag + (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\(\\\\[nt]\\)" from) + (let ((match (match-string 3 from))) + (cond + ((string= match "\\n") + (message "Note: `\\n' here doesn't match a newline; to do that, type C-q C-j instead")) + ((string= match "\\t") + (message "Note: `\\t' here doesn't match a tab; to do that, just type TAB"))) + (sit-for 2)))) (save-excursion (setq to (read-from-minibuffer (format "%s %s with: " string from) @@ -159,20 +162,62 @@ Fourth and fifth arg START and END specify the region to operate on. In TO-STRING, `\\&' stands for whatever matched the whole of REGEXP, and `\\=\\N' (where N is a digit) stands for - whatever what matched the Nth `\\(...\\)' in REGEXP." +whatever what matched the Nth `\\(...\\)' in REGEXP. + +When this function is called interactively, the replacement text +can also contain `\\,' followed by a Lisp expression. The escaped +shorthands for `query-replace-regexp-eval' are also valid +here: within the Lisp expression, you can use `\\&' for the whole +match string, `\\N' for partial matches, `\\#&' and `\\#N' for +the respective numeric values, and `\\#' for `replace-count'. + +If your Lisp expression is an identifier and the next +letter in the replacement string would be interpreted as part of it, +you can wrap it with an expression like `\\,(or \\#)'. Incidentally, +for this particular case you may also enter `\\#' in the replacement +text directly. + +When you use `\\,' or `\\#' in the replacement, TO-STRING actually +becomes a list with expanded shorthands. +Use \\[repeat-complex-command] after this command to see details." (interactive (let ((common (query-replace-read-args "Query replace regexp" t))) - (list (nth 0 common) (nth 1 common) (nth 2 common) - ;; These are done separately here - ;; so that command-history will record these expressions - ;; rather than the values they had this time. - (if (and transient-mark-mode mark-active) - (region-beginning)) - (if (and transient-mark-mode mark-active) - (region-end))))) - + (list + (nth 0 common) + (if (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\\\[,#]" + (nth 1 common)) + (let ((to-string (nth 1 common)) pos to-expr char prompt) + (while (string-match + "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\\\[,#]" + to-string) + (setq pos (match-end 0)) + (push (substring to-string 0 (- pos 2)) to-expr) + (setq char (aref to-string (1- pos)) + to-string (substring to-string pos)) + (cond ((eq char ?\#) + (push '(number-to-string replace-count) to-expr)) + ((eq char ?\,) + (setq pos (read-from-string to-string)) + (push `(replace-quote ,(car pos)) to-expr) + (setq to-string (substring to-string (cdr pos)))))) + (setq to-expr (nreverse (delete "" (cons to-string to-expr)))) + (replace-match-string-symbols to-expr) + (cons 'replace-eval-replacement + (if (> (length to-expr) 1) + (cons 'concat to-expr) + (car to-expr)))) + (nth 1 common)) + (nth 2 common) + ;; These are done separately here + ;; so that command-history will record these expressions + ;; rather than the values they had this time. + (if (and transient-mark-mode mark-active) + (region-beginning)) + (if (and transient-mark-mode mark-active) + (region-end))))) (perform-replace regexp to-string t t delimited nil nil start end)) + (define-key esc-map [?\C-%] 'query-replace-regexp) (defun query-replace-regexp-eval (regexp to-expr &optional delimited start end) @@ -189,6 +234,7 @@ For convenience, when entering TO-EXPR interactively, you can use `\\&' or `\\0' to stand for whatever matched the whole of REGEXP, and `\\N' (where N is a digit) to stand for whatever matched the Nth `\\(...\\)' in REGEXP. Use `\\#&' or `\\#N' if you want a number instead of a string. +In interactive use, `\\#' in itself stands for `replace-count'. In Transient Mark mode, if the mark is active, operate on the contents of the region. Otherwise, operate from point to the end of the buffer. @@ -538,6 +584,7 @@ Alternatively, click \\[occur-mode-mouse-goto] on an item to go to it. (set (make-local-variable 'revert-buffer-function) 'occur-revert-function) (make-local-variable 'occur-revert-arguments) (add-hook 'change-major-mode-hook 'font-lock-defontify nil t) + (setq next-error-function 'occur-next-error) (run-hooks 'occur-mode-hook)) (defun occur-revert-function (ignore1 ignore2) @@ -614,6 +661,21 @@ Alternatively, click \\[occur-mode-mouse-goto] on an item to go to it. "Move to the Nth (default 1) previous match in an Occur mode buffer." (interactive "p") (occur-find-match n #'previous-single-property-change "No earlier matches")) + +(defun occur-next-error (&optional argp reset) + "Move to the Nth (default 1) next match in an Occur mode buffer. +Compatibility function for \\[next-error] invocations." + (interactive "p") + (when reset + (occur-find-match 0 #'next-single-property-change "No first match")) + (occur-find-match + (prefix-numeric-value argp) + (if (> 0 (prefix-numeric-value argp)) + #'previous-single-property-change + #'next-single-property-change) + "No more matches") + (occur-mode-goto-occurrence)) + (defcustom list-matching-lines-default-context-lines 0 "*Default number of context lines included around `list-matching-lines' matches. @@ -800,7 +862,9 @@ See also `multi-occur'." (setq occur-revert-arguments (list regexp nlines bufs) buffer-read-only t) (if (> count 0) - (display-buffer occur-buf) + (progn + (display-buffer occur-buf) + (setq next-error-last-buffer occur-buf)) (kill-buffer occur-buf))) (run-hooks 'occur-hook)))) @@ -992,6 +1056,7 @@ N (match-string N) (where N is a string of digits) #N (string-to-number (match-string N)) & (match-string 0) #& (string-to-number (match-string 0)) +# replace-count Note that these symbols must be preceeded by a backslash in order to type them." @@ -1011,7 +1076,9 @@ type them." ((string= "&" name) (setcar n '(match-string 0))) ((string= "#&" name) - (setcar n '(string-to-number (match-string 0)))))))) + (setcar n '(string-to-number (match-string 0)))) + ((string= "#" name) + (setcar n 'replace-count)))))) (setq n (cdr n)))) (defun replace-eval-replacement (expression replace-count) @@ -1020,6 +1087,21 @@ type them." replacement (prin1-to-string replacement t)))) +(defun replace-quote (replacement) + "Quote a replacement string. +This just doubles all backslashes in REPLACEMENT and +returns the resulting string. If REPLACEMENT is not +a string, it is first passed through `prin1-to-string' +with the `noescape' argument set. + +`match-data' is preserved across the call." + (save-match-data + (replace-regexp-in-string "\\\\" "\\\\" + (if (stringp replacement) + replacement + (prin1-to-string replacement t)) + t t))) + (defun replace-loop-through-replacements (data replace-count) ;; DATA is a vector contaning the following values: ;; 0 next-rotate-count @@ -1112,7 +1194,7 @@ make, or the user didn't cancel the call." (unwind-protect ;; Loop finding occurrences that perhaps should be replaced. (while (and keep-going - (not (eobp)) + (not (or (eobp) (and limit (>= (point) limit)))) ;; Use the next match if it is already known; ;; otherwise, search for a match after moving forward ;; one char if progress is required. @@ -1128,7 +1210,10 @@ make, or the user didn't cancel the call." ;; character too far at the end, ;; but this is undone after the ;; while-loop. - (progn (forward-char 1) (not (eobp)))) + (progn + (forward-char 1) + (not (or (eobp) + (and limit (>= (point) limit)))))) (funcall search-function search-string limit t) ;; For speed, use only integers and ;; reuse the list used last time. |
