X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/02cbe062bee38a6705bafb1699d77e3c44cfafcf..d0fc47eda0459c486dd114eee8674df1a6e4bc6a:/lisp/replace.el diff --git a/lisp/replace.el b/lisp/replace.el index 3680d574e8..e54939ff8d 100644 --- a/lisp/replace.el +++ b/lisp/replace.el @@ -7,10 +7,10 @@ ;; This file is part of GNU Emacs. -;; GNU Emacs is free software; you can redistribute it and/or modify +;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 3, or (at your option) -;; any later version. +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -18,9 +18,7 @@ ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -;; Boston, MA 02110-1301, USA. +;; along with GNU Emacs. If not, see . ;;; Commentary: @@ -225,20 +223,21 @@ only matches surrounded by word boundaries. Fourth and fifth arg START and END specify the region to operate on. To customize possible responses, change the \"bindings\" in `query-replace-map'." - (interactive (let ((common - (query-replace-read-args - (if (and transient-mark-mode mark-active) - "Query replace in region" - "Query replace") - nil))) - (list (nth 0 common) (nth 1 common) (nth 2 common) - ;; These are done separately here - ;; so that command-history will record these expressions - ;; rather than the values they had this time. - (if (and transient-mark-mode mark-active) - (region-beginning)) - (if (and transient-mark-mode mark-active) - (region-end))))) + (interactive + (let ((common + (query-replace-read-args + (concat "Query replace" + (if current-prefix-arg " word" "") + (if (and transient-mark-mode mark-active) " in region" "")) + nil))) + (list (nth 0 common) (nth 1 common) (nth 2 common) + ;; These are done separately here + ;; so that command-history will record these expressions + ;; rather than the values they had this time. + (if (and transient-mark-mode mark-active) + (region-beginning)) + (if (and transient-mark-mode mark-active) + (region-end))))) (perform-replace from-string to-string t nil delimited nil nil start end)) (define-key esc-map "%" 'query-replace) @@ -291,9 +290,10 @@ Use \\[repeat-complex-command] after this command for details." (interactive (let ((common (query-replace-read-args - (if (and transient-mark-mode mark-active) - "Query replace regexp in region" - "Query replace regexp") + (concat "Query replace" + (if current-prefix-arg " word" "") + " regexp" + (if (and transient-mark-mode mark-active) " in region" "")) t))) (list (nth 0 common) (nth 1 common) (nth 2 common) ;; These are done separately here @@ -449,9 +449,10 @@ and TO-STRING is also null.)" (interactive (let ((common (query-replace-read-args - (if (and transient-mark-mode mark-active) - "Replace string in region" - "Replace string") + (concat "Replace" + (if current-prefix-arg " word" "") + " string" + (if (and transient-mark-mode mark-active) " in region" "")) nil))) (list (nth 0 common) (nth 1 common) (nth 2 common) (if (and transient-mark-mode mark-active) @@ -506,9 +507,10 @@ which will run faster and will not set the mark or print anything." (interactive (let ((common (query-replace-read-args - (if (and transient-mark-mode mark-active) - "Replace regexp in region" - "Replace regexp") + (concat "Replace" + (if current-prefix-arg " word" "") + " regexp" + (if (and transient-mark-mode mark-active) " in region" "")) t))) (list (nth 0 common) (nth 1 common) (nth 2 common) (if (and transient-mark-mode mark-active) @@ -524,6 +526,39 @@ which will run faster and will not set the mark or print anything." Maximum length of the history list is determined by the value of `history-length', which see.") +(defun read-regexp (prompt &optional default-value) + "Read regexp as a string using the regexp history and some useful defaults. +Prompt for a regular expression with PROMPT (without a colon and +space) in the minibuffer. The optional argument DEFAULT-VALUE +provides the value to display in the minibuffer prompt that is +returned if the user just types RET. +Values available via M-n are the string at point, the last isearch +regexp, the last isearch string, and the last replacement regexp." + (let* ((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)))) + ;; Don't add automatically the car of defaults for empty input + (history-add-new-input nil) + (input + (read-from-minibuffer + (if default-value + (format "%s (default %s): " prompt + (query-replace-descr default-value)) + (format "%s: " prompt)) + nil nil nil 'regexp-history defaults t))) + (if (equal input "") + default-value + (prog1 input + (add-to-history 'regexp-history input))))) + (defalias 'delete-non-matching-lines 'keep-lines) (defalias 'delete-matching-lines 'flush-lines) @@ -534,20 +569,7 @@ of `history-length', which see.") "Read arguments for `keep-lines' and friends. Prompt for a regexp with PROMPT. Value is a list, (REGEXP)." - (let* ((default (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)))) - (default (delete-dups (delq nil (delete "" default))))) - (list (read-from-minibuffer prompt nil nil nil - 'regexp-history default t) - nil nil t))) + (list (read-regexp prompt) nil nil t)) (defun keep-lines (regexp &optional rstart rend interactive) "Delete all lines except those containing matches for REGEXP. @@ -576,7 +598,7 @@ a previously found match." (interactive (progn (barf-if-buffer-read-only) - (keep-lines-read-args "Keep lines (containing match for regexp): "))) + (keep-lines-read-args "Keep lines containing match for regexp"))) (if rstart (progn (goto-char (min rstart rend)) @@ -651,7 +673,7 @@ starting on the same line at which another match ended is ignored." (interactive (progn (barf-if-buffer-read-only) - (keep-lines-read-args "Flush lines (containing match for regexp): "))) + (keep-lines-read-args "Flush lines containing match for regexp"))) (if rstart (progn (goto-char (min rstart rend)) @@ -697,7 +719,7 @@ the previous match. Hence, it ignores matches that overlap a previously found match." (interactive - (keep-lines-read-args "How many matches for (regexp): ")) + (keep-lines-read-args "How many matches for regexp")) (save-excursion (if rstart (progn @@ -752,26 +774,36 @@ a previously found match." "Display another occurrence when moving the cursor")) (define-key map [separator-1] '("--")) (define-key map [kill-this-buffer] - '("Kill occur buffer" . kill-this-buffer)) + '(menu-item "Kill occur buffer" kill-this-buffer + :help "Kill the current *Occur* buffer")) (define-key map [quit-window] - '("Quit occur window" . quit-window)) + '(menu-item "Quit occur window" quit-window + :help "Quit the current *Occur* buffer. Bury it, and maybe delete the selected frame")) (define-key map [revert-buffer] - '("Revert occur buffer" . revert-buffer)) + '(menu-item "Revert occur buffer" revert-buffer + :help "Replace the text in the *Occur* buffer with the results of rerunning occur")) (define-key map [clone-buffer] - '("Clone occur buffer" . clone-buffer)) + '(menu-item "Clone occur buffer" clone-buffer + :help "Create and return a twin copy of the current *Occur* buffer")) (define-key map [occur-rename-buffer] - '("Rename occur buffer" . occur-rename-buffer)) + '(menu-item "Rename occur buffer" occur-rename-buffer + :help "Rename the current *Occur* buffer to *Occur: original-buffer-name*.")) (define-key map [separator-2] '("--")) (define-key map [occur-mode-goto-occurrence-other-window] - '("Go To Occurrence Other Window" . occur-mode-goto-occurrence-other-window)) + '(menu-item "Go To Occurrence Other Window" occur-mode-goto-occurrence-other-window + :help "Go to the occurrence the current line describes, in another window")) (define-key map [occur-mode-goto-occurrence] - '("Go To Occurrence" . occur-mode-goto-occurrence)) + '(menu-item "Go To Occurrence" occur-mode-goto-occurrence + :help "Go to the occurrence the current line describes")) (define-key map [occur-mode-display-occurrence] - '("Display Occurrence" . occur-mode-display-occurrence)) + '(menu-item "Display Occurrence" occur-mode-display-occurrence + :help "Display in another window the occurrence the current line describes")) (define-key map [occur-next] - '("Move to next match" . occur-next)) + '(menu-item "Move to next match" occur-next + :help "Move to the Nth (default 1) next match in an Occur mode buffer")) (define-key map [occur-prev] - '("Move to previous match" . occur-prev)) + '(menu-item "Move to previous match" occur-prev + :help "Move to the Nth (default 1) previous match in an Occur mode buffer")) map) "Keymap for `occur-mode'.") @@ -995,29 +1027,10 @@ which means to discard all text properties." (nreverse result)))) (defun occur-read-primary-args () - (let* ((default - (list (and transient-mark-mode mark-active - (regexp-quote - (buffer-substring-no-properties - (region-beginning) (region-end)))) - (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)))) - (default (delete-dups (delq nil (delete "" default)))) - (input - (read-from-minibuffer - "List lines matching regexp: " - nil nil nil 'regexp-history default))) - (list input - (when current-prefix-arg - (prefix-numeric-value current-prefix-arg))))) + (list (read-regexp "List lines matching regexp" + (car regexp-history)) + (when current-prefix-arg + (prefix-numeric-value current-prefix-arg)))) (defun occur-rename-buffer (&optional unique-p interactive-p) "Rename the current *Occur* buffer to *Occur: original-buffer-name*. @@ -1113,6 +1126,8 @@ See also `multi-occur'." (buffer-list)))))) (defun occur-1 (regexp nlines bufs &optional buf-name) + (unless (and regexp (not (equal regexp ""))) + (error "Occur doesn't work with the empty regexp")) (unless buf-name (setq buf-name "*Occur*")) (let (occur-buf @@ -1363,6 +1378,20 @@ The valid answers include `act', `skip', `act-and-show', `exit', `act-and-exit', `edit', `delete-and-edit', `recenter', `automatic', `backup', `exit-prefix', and `help'.") +(defvar multi-query-replace-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map query-replace-map) + (define-key map "Y" 'automatic-all) + (define-key map "N" 'exit-current) + map) + "Keymap that defines additional bindings for multi-buffer replacements. +It extends its parent map `query-replace-map' with new bindings to +operate on a set of buffers/files. The difference with its parent map +is the additional answers `automatic-all' to replace all remaining +matches in all remaining buffers with no more questions, and +`exit-current' to skip remaining matches in the current buffer +and to continue with the next buffer in the sequence.") + (defun replace-match-string-symbols (n) "Process a list (and any sub-lists), expanding certain symbols. Symbol Expands To @@ -1467,6 +1496,18 @@ passed in. If LITERAL is set, no checking is done, anyway." (replace-match newtext fixedcase literal) noedit) +(defvar replace-search-function 'search-forward + "Function to use when searching for strings to replace. +It is used by `query-replace' and `replace-string', and is called +with three arguments, as if it were `search-forward'.") + +(defvar replace-re-search-function 're-search-forward + "Function to use when searching for regexps to replace. +It is used by `query-replace-regexp', `replace-regexp', +`query-replace-regexp-eval', and `map-query-replace-regexp'. +It is called with three arguments, as if it were +`re-search-forward'.") + (defun perform-replace (from-string replacements query-flag regexp-flag delimited-flag &optional repeat-count map start end) @@ -1492,7 +1533,10 @@ make, or the user didn't cancel the call." case-fold-search)) (nocasify (not (and case-replace case-fold-search))) (literal (or (not regexp-flag) (eq regexp-flag 'literal))) - (search-function (if regexp-flag 're-search-forward 'search-forward)) + (search-function + (if regexp-flag + replace-re-search-function + replace-search-function)) (search-string from-string) (real-match-data nil) ; The match data for the current match. (next-replacement nil) @@ -1503,6 +1547,7 @@ make, or the user didn't cancel the call." (stack nil) (replace-count 0) (nonempty-match nil) + (multi-buffer nil) ;; If non-nil, it is marker saying where in the buffer to stop. (limit nil) @@ -1524,6 +1569,11 @@ make, or the user didn't cancel the call." (goto-char (min start end)) (deactivate-mark)) + ;; If last typed key in previous call of multi-buffer perform-replace + ;; was `automatic-all', don't ask more questions in next files + (when (eq (lookup-key map (vector last-input-char)) 'automatic-all) + (setq query-flag nil multi-buffer t)) + ;; REPLACEMENTS is either a string, a list of strings, or a cons cell ;; containing a function and its first argument. The function is ;; called to generate each replacement like this: @@ -1621,8 +1671,7 @@ make, or the user didn't cancel the call." (funcall (car replacements) (cdr replacements) replace-count))) (if (not query-flag) - (let ((inhibit-read-only - query-replace-skip-read-only)) + (progn (unless (or literal noedit) (replace-highlight (nth 0 real-match-data) (nth 1 real-match-data) @@ -1671,6 +1720,7 @@ make, or the user didn't cancel the call." (with-output-to-temp-buffer "*Help*" (princ (concat "Query replacing " + (if delimited-flag "word " "") (if regexp-flag "regexp " "") from-string " with " next-replacement ".\n\n" @@ -1681,6 +1731,8 @@ make, or the user didn't cancel the call." ((eq def 'exit) (setq keep-going nil) (setq done t)) + ((eq def 'exit-current) + (setq multi-buffer t keep-going nil done t)) ((eq def 'backup) (if stack (let ((elt (pop stack))) @@ -1720,14 +1772,15 @@ make, or the user didn't cancel the call." real-match-data (replace-match-data t real-match-data) replaced t))) - ((eq def 'automatic) + ((or (eq def 'automatic) (eq def 'automatic-all)) (or replaced (setq noedit (replace-match-maybe-edit next-replacement nocasify literal noedit real-match-data) replace-count (1+ replace-count))) - (setq done t query-flag nil replaced t)) + (setq done t query-flag nil replaced t) + (if (eq def 'automatic-all) (setq multi-buffer t))) ((eq def 'skip) (setq done t)) ((eq def 'recenter) @@ -1814,7 +1867,7 @@ make, or the user didn't cancel the call." (message "Replaced %d occurrence%s" replace-count (if (= replace-count 1) "" "s"))) - (and keep-going stack))) + (or (and keep-going stack) multi-buffer))) (defvar replace-overlay nil)