X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/233ba4d924933cb56129bd7511e6137b7c0b8e3e..ef62b23df5a7007c3d8c74dbca87ba83e9da682e:/lisp/replace.el diff --git a/lisp/replace.el b/lisp/replace.el index 924622b692..ad87d474b8 100644 --- a/lisp/replace.el +++ b/lisp/replace.el @@ -1,6 +1,6 @@ ;;; replace.el --- replace commands for Emacs -;; Copyright (C) 1985-1987, 1992, 1994, 1996-1997, 2000-2011 +;; Copyright (C) 1985-1987, 1992, 1994, 1996-1997, 2000-2012 ;; Free Software Foundation, Inc. ;; Maintainer: FSF @@ -98,6 +98,10 @@ is highlighted lazily using isearch lazy highlighting (see :group 'matching :version "22.1") +(defvar replace-count 0 + "Number of replacements done so far. +See `replace-regexp' and `query-replace-regexp-eval'.") + (defun query-replace-descr (string) (mapconcat 'isearch-text-char-description string "")) @@ -757,10 +761,76 @@ a previously found match." count))) +(defvar occur-menu-map + (let ((map (make-sparse-keymap))) + (define-key map [next-error-follow-minor-mode] + `(menu-item ,(purecopy "Auto Occurrence Display") + next-error-follow-minor-mode + :help ,(purecopy + "Display another occurrence when moving the cursor") + :button (:toggle . (and (boundp 'next-error-follow-minor-mode) + next-error-follow-minor-mode)))) + (define-key map [separator-1] menu-bar-separator) + (define-key map [kill-this-buffer] + `(menu-item ,(purecopy "Kill Occur Buffer") kill-this-buffer + :help ,(purecopy "Kill the current *Occur* buffer"))) + (define-key map [quit-window] + `(menu-item ,(purecopy "Quit Occur Window") quit-window + :help ,(purecopy "Quit the current *Occur* buffer. Bury it, and maybe delete the selected frame"))) + (define-key map [revert-buffer] + `(menu-item ,(purecopy "Revert Occur Buffer") revert-buffer + :help ,(purecopy "Replace the text in the *Occur* buffer with the results of rerunning occur"))) + (define-key map [clone-buffer] + `(menu-item ,(purecopy "Clone Occur Buffer") clone-buffer + :help ,(purecopy "Create and return a twin copy of the current *Occur* buffer"))) + (define-key map [occur-rename-buffer] + `(menu-item ,(purecopy "Rename Occur Buffer") occur-rename-buffer + :help ,(purecopy "Rename the current *Occur* buffer to *Occur: original-buffer-name*."))) + (define-key map [occur-edit-buffer] + `(menu-item ,(purecopy "Edit Occur Buffer") occur-edit-mode + :help ,(purecopy "Edit the *Occur* buffer and apply changes to the original buffers."))) + (define-key map [separator-2] menu-bar-separator) + (define-key map [occur-mode-goto-occurrence-other-window] + `(menu-item ,(purecopy "Go To Occurrence Other Window") occur-mode-goto-occurrence-other-window + :help ,(purecopy "Go to the occurrence the current line describes, in another window"))) + (define-key map [occur-mode-goto-occurrence] + `(menu-item ,(purecopy "Go To Occurrence") occur-mode-goto-occurrence + :help ,(purecopy "Go to the occurrence the current line describes"))) + (define-key map [occur-mode-display-occurrence] + `(menu-item ,(purecopy "Display Occurrence") occur-mode-display-occurrence + :help ,(purecopy "Display in another window the occurrence the current line describes"))) + (define-key map [occur-next] + `(menu-item ,(purecopy "Move to Next Match") occur-next + :help ,(purecopy "Move to the Nth (default 1) next match in an Occur mode buffer"))) + (define-key map [occur-prev] + `(menu-item ,(purecopy "Move to Previous Match") occur-prev + :help ,(purecopy "Move to the Nth (default 1) previous match in an Occur mode buffer"))) + map) + "Menu keymap for `occur-mode'.") + +(defvar occur-mode-map + (let ((map (make-sparse-keymap))) + ;; We use this alternative name, so we can use \\[occur-mode-mouse-goto]. + (define-key map [mouse-2] 'occur-mode-mouse-goto) + (define-key map "\C-c\C-c" 'occur-mode-goto-occurrence) + (define-key map "e" 'occur-edit-mode) + (define-key map "\C-m" 'occur-mode-goto-occurrence) + (define-key map "o" 'occur-mode-goto-occurrence-other-window) + (define-key map "\C-o" 'occur-mode-display-occurrence) + (define-key map "\M-n" 'occur-next) + (define-key map "\M-p" 'occur-prev) + (define-key map "r" 'occur-rename-buffer) + (define-key map "c" 'clone-buffer) + (define-key map "\C-c\C-f" 'next-error-follow-minor-mode) + (define-key map [menu-bar occur] (cons (purecopy "Occur") occur-menu-map)) + map) + "Keymap for `occur-mode'.") (defvar occur-revert-arguments nil "Arguments to pass to `occur-1' to revert an Occur mode buffer. See `occur-revert-function'.") +(make-variable-buffer-local 'occur-revert-arguments) +(put 'occur-revert-arguments 'permanent-local t) (defcustom occur-mode-hook '(turn-on-font-lock) "Hook run when entering Occur mode." @@ -788,59 +858,80 @@ Alternatively, click \\[occur-mode-mouse-goto] on an item to go to it. \\{occur-mode-map}" (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)) -(define-key occur-mode-map [mouse-2] 'occur-mode-mouse-goto) -(define-key occur-mode-map "\C-c\C-c" 'occur-mode-goto-occurrence) -(define-key occur-mode-map "\C-m" 'occur-mode-goto-occurrence) -(define-key occur-mode-map "o" 'occur-mode-goto-occurrence-other-window) -(define-key occur-mode-map "\C-o" 'occur-mode-display-occurrence) -(define-key occur-mode-map "\M-n" 'occur-next) -(define-key occur-mode-map "\M-p" 'occur-prev) -(define-key occur-mode-map "r" 'occur-rename-buffer) -(define-key occur-mode-map "c" 'clone-buffer) -(define-key occur-mode-map "\C-c\C-f" 'next-error-follow-minor-mode) -(define-key occur-mode-map [menu-bar] (make-sparse-keymap)) -(define-key occur-mode-map [menu-bar occur] `(cons ,(purecopy "Occur") map)) -(define-key occur-mode-map [next-error-follow-minor-mode] - (menu-bar-make-mm-toggle next-error-follow-minor-mode - "Auto Occurrence Display" - "Display another occurrence when moving the cursor")) -(define-key occur-mode-map [separator-1] menu-bar-separator) -(define-key occur-mode-map [kill-this-buffer] - `(menu-item ,(purecopy "Kill occur buffer") kill-this-buffer - :help ,(purecopy "Kill the current *Occur* buffer"))) -(define-key occur-mode-map [quit-window] - `(menu-item ,(purecopy "Quit occur window") quit-window - :help ,(purecopy "Quit the current *Occur* buffer. Bury it, and maybe delete the selected frame"))) -(define-key occur-mode-map [revert-buffer] - `(menu-item ,(purecopy "Revert occur buffer") revert-buffer - :help ,(purecopy "Replace the text in the *Occur* buffer with the results of rerunning occur"))) -(define-key occur-mode-map [clone-buffer] - `(menu-item ,(purecopy "Clone occur buffer") clone-buffer - :help ,(purecopy "Create and return a twin copy of the current *Occur* buffer"))) -(define-key occur-mode-map [occur-rename-buffer] - `(menu-item ,(purecopy "Rename occur buffer") occur-rename-buffer - :help ,(purecopy "Rename the current *Occur* buffer to *Occur: original-buffer-name*."))) -(define-key occur-mode-map [separator-2] menu-bar-separator) -(define-key occur-mode-map [occur-mode-goto-occurrence-other-window] - `(menu-item ,(purecopy "Go To Occurrence Other Window") occur-mode-goto-occurrence-other-window - :help ,(purecopy "Go to the occurrence the current line describes, in another window"))) -(define-key occur-mode-map [occur-mode-goto-occurrence] - `(menu-item ,(purecopy "Go To Occurrence") occur-mode-goto-occurrence - :help ,(purecopy "Go to the occurrence the current line describes"))) -(define-key occur-mode-map [occur-mode-display-occurrence] - `(menu-item ,(purecopy "Display Occurrence") occur-mode-display-occurrence - :help ,(purecopy "Display in another window the occurrence the current line describes"))) -(define-key occur-mode-map [occur-next] - `(menu-item ,(purecopy "Move to next match") occur-next - :help ,(purecopy "Move to the Nth (default 1) next match in an Occur mode buffer"))) -(define-key occur-mode-map [occur-prev] - `(menu-item ,(purecopy "Move to previous match") occur-prev - :help ,(purecopy "Move to the Nth (default 1) previous match in an Occur mode buffer"))) -(defun occur-revert-function (ignore1 ignore2) + +;;; Occur Edit mode + +(defvar occur-edit-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map text-mode-map) + (define-key map [mouse-2] 'occur-mode-mouse-goto) + (define-key map "\C-c\C-c" 'occur-cease-edit) + (define-key map "\C-o" 'occur-mode-display-occurrence) + (define-key map "\C-c\C-f" 'next-error-follow-minor-mode) + (define-key map [menu-bar occur] (cons (purecopy "Occur") occur-menu-map)) + map) + "Keymap for `occur-edit-mode'.") + +(define-derived-mode occur-edit-mode occur-mode "Occur-Edit" + "Major mode for editing *Occur* buffers. +In this mode, changes to the *Occur* buffer are also applied to +the originating buffer. + +To return to ordinary Occur mode, use \\[occur-cease-edit]." + (setq buffer-read-only nil) + (add-hook 'after-change-functions 'occur-after-change-function nil t) + (message (substitute-command-keys + "Editing: Type \\[occur-cease-edit] to return to Occur mode."))) + +(defun occur-cease-edit () + "Switch from Occur Edit mode to Occur mode." + (interactive) + (when (derived-mode-p 'occur-edit-mode) + (occur-mode) + (message "Switching to Occur mode."))) + +(defun occur-after-change-function (beg end length) + (save-excursion + (goto-char beg) + (let* ((line-beg (line-beginning-position)) + (m (get-text-property line-beg 'occur-target)) + (buf (marker-buffer m)) + col) + (when (and (get-text-property line-beg 'occur-prefix) + (not (get-text-property end 'occur-prefix))) + (when (= length 0) + ;; Apply occur-target property to inserted (e.g. yanked) text. + (put-text-property beg end 'occur-target m) + ;; Did we insert a newline? Occur Edit mode can't create new + ;; Occur entries; just discard everything after the newline. + (save-excursion + (and (search-forward "\n" end t) + (delete-region (1- (point)) end)))) + (let* ((line (- (line-number-at-pos) + (line-number-at-pos (window-start)))) + (readonly (with-current-buffer buf buffer-read-only)) + (win (or (get-buffer-window buf) + (display-buffer buf t))) + (line-end (line-end-position)) + (text (save-excursion + (goto-char (next-single-property-change + line-beg 'occur-prefix nil + line-end)) + (setq col (- (point) line-beg)) + (buffer-substring-no-properties (point) line-end)))) + (with-selected-window win + (goto-char m) + (recenter line) + (if readonly + (message "Buffer `%s' is read only." buf) + (delete-region (line-beginning-position) (line-end-position)) + (insert text)) + (move-to-column col))))))) + + +(defun occur-revert-function (_ignore1 _ignore2) "Handle `revert-buffer' for Occur mode buffers." (apply 'occur-1 (append occur-revert-arguments (list (buffer-name))))) @@ -854,7 +945,7 @@ Alternatively, click \\[occur-mode-mouse-goto] on an item to go to it. (defalias 'occur-mode-mouse-goto 'occur-mode-goto-occurrence) (defun occur-mode-goto-occurrence (&optional event) - "Go to the occurrence the current line describes." + "Go to the occurrence on the current line." (interactive (list last-nonmenu-event)) (let ((pos (if (null event) @@ -865,9 +956,7 @@ Alternatively, click \\[occur-mode-mouse-goto] on an item to go to it. (with-current-buffer (window-buffer (posn-window (event-end event))) (save-excursion (goto-char (posn-point (event-end event))) - (occur-mode-find-occurrence))))) - same-window-buffer-names - same-window-regexps) + (occur-mode-find-occurrence)))))) (pop-to-buffer (marker-buffer pos)) (goto-char pos) (run-hooks 'occur-mode-find-occurrence-hook))) @@ -884,11 +973,8 @@ Alternatively, click \\[occur-mode-mouse-goto] on an item to go to it. "Display in another window the occurrence the current line describes." (interactive) (let ((pos (occur-mode-find-occurrence)) - window - ;; Bind these to ensure `display-buffer' puts it in another window. - same-window-buffer-names - same-window-regexps) - (setq window (display-buffer (marker-buffer pos))) + window) + (setq window (display-buffer (marker-buffer pos) t)) ;; This is the way to set point in the proper window. (save-selected-window (select-window window) @@ -1060,11 +1146,14 @@ are not modified." (interactive (occur-read-primary-args)) (occur-1 regexp nlines (list (current-buffer)))) +(defvar ido-ignore-item-temp-list) + (defun multi-occur (bufs regexp &optional nlines) "Show all lines in buffers BUFS containing a match for REGEXP. This function acts on multiple buffers; otherwise, it is exactly like `occur'. When you invoke this command interactively, you must specify -the buffer names that you want, one by one." +the buffer names that you want, one by one. +See also `multi-occur-in-matching-buffers'." (interactive (cons (let* ((bufs (list (read-buffer "First buffer to search: " @@ -1139,7 +1228,7 @@ See also `multi-occur'." (with-current-buffer occur-buf (if (stringp nlines) - (fundamental-mode) ;; This is for collect opeartion. + (fundamental-mode) ;; This is for collect operation. (occur-mode)) (let ((inhibit-read-only t) ;; Don't generate undo entries for creation of the initial contents. @@ -1195,11 +1284,12 @@ See also `multi-occur'." (set-buffer-modified-p nil) (run-hooks 'occur-hook))))))) -(defun occur-engine (regexp buffers out-buf nlines case-fold-search +(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) - (coding nil)) + (coding nil) + (case-fold-search case-fold)) ;; Map over all the buffers (dolist (buf buffers) (when (buffer-live-p buf) @@ -1263,8 +1353,12 @@ See also `multi-occur'." (when prefix-face `(font-lock-face prefix-face)) `(occur-prefix t mouse-face (highlight) - occur-target ,marker follow-link t - help-echo "mouse-2: go to this occurrence")))) + ;; Allow insertion of text at + ;; the end of the prefix (for + ;; Occur Edit mode). + front-sticky t rear-nonsticky t + occur-target ,marker follow-link t + help-echo "mouse-2: go to this occurrence")))) (match-str ;; We don't put `mouse-face' on the newline, ;; because that loses. And don't put it @@ -1299,8 +1393,7 @@ See also `multi-occur'." (nth 0 ret)))) ;; Actually insert the match display data (with-current-buffer out-buf - (let ((beg (point)) - (end (progn (insert data) (point))))))) + (insert data))) (goto-char endpt)) (if endpt (progn @@ -1324,13 +1417,15 @@ See also `multi-occur'." (goto-char headerpt) (let ((beg (point)) end) - (insert (format "%d match%s%s in buffer: %s\n" - matches (if (= matches 1) "" "es") - ;; Don't display regexp for multi-buffer. - (if (> (length buffers) 1) - "" (format " for \"%s\"" - (query-replace-descr regexp))) - (buffer-name buf))) + (insert (propertize + (format "%d match%s%s in buffer: %s\n" + matches (if (= matches 1) "" "es") + ;; Don't display regexp for multi-buffer. + (if (> (length buffers) 1) + "" (format " for \"%s\"" + (query-replace-descr regexp))) + (buffer-name buf)) + 'read-only t)) (setq end (point)) (add-text-properties beg end (append @@ -1499,8 +1594,8 @@ E to edit the replacement string" "Keymap that defines the responses to questions in `query-replace'. The \"bindings\" in this map are not commands; they are answers. The valid answers include `act', `skip', `act-and-show', -`exit', `act-and-exit', `edit', `delete-and-edit', `recenter', -`automatic', `backup', `exit-prefix', and `help'.") +`exit', `act-and-exit', `edit', `edit-replacement', `delete-and-edit', +`recenter', `automatic', `backup', `exit-prefix', `quit', and `help'.") (defvar multi-query-replace-map (let ((map (make-sparse-keymap))) @@ -1525,7 +1620,7 @@ N (match-string N) (where N is a string of digits) #& (string-to-number (match-string 0)) # replace-count -Note that these symbols must be preceeded by a backslash in order to +Note that these symbols must be preceded by a backslash in order to type them using Lisp syntax." (while (consp n) (cond @@ -1548,8 +1643,9 @@ type them using Lisp syntax." (setcar n 'replace-count)))))) (setq n (cdr n)))) -(defun replace-eval-replacement (expression replace-count) - (let ((replacement (eval expression))) +(defun replace-eval-replacement (expression count) + (let* ((replace-count count) + (replacement (eval expression))) (if (stringp replacement) replacement (prin1-to-string replacement t)))) @@ -1569,15 +1665,15 @@ with the `noescape' argument set. (prin1-to-string replacement t)) t t))) -(defun replace-loop-through-replacements (data replace-count) - ;; DATA is a vector contaning the following values: +(defun replace-loop-through-replacements (data count) + ;; DATA is a vector containing the following values: ;; 0 next-rotate-count ;; 1 repeat-count ;; 2 next-replacement ;; 3 replacements - (if (= (aref data 0) replace-count) + (if (= (aref data 0) count) (progn - (aset data 0 (+ replace-count (aref data 1))) + (aset data 0 (+ count (aref data 1))) (let ((next (cdr (aref data 2)))) (aset data 2 (if (consp next) next (aref data 3)))))) (car (aref data 2))) @@ -2002,6 +2098,11 @@ make, or the user didn't cancel the call." (if (= replace-count 1) "" "s"))) (or (and keep-going stack) multi-buffer))) +(defvar isearch-error) +(defvar isearch-forward) +(defvar isearch-case-fold-search) +(defvar isearch-string) + (defvar replace-overlay nil) (defun replace-highlight (match-beg match-end range-beg range-end @@ -2015,13 +2116,13 @@ make, or the user didn't cancel the call." (if query-replace-lazy-highlight (let ((isearch-string string) (isearch-regexp regexp) + ;; Set isearch-word to nil because word-replace is regexp-based, + ;; so `isearch-search-fun' should not use `word-search-forward'. + (isearch-word nil) (search-whitespace-regexp nil) (isearch-case-fold-search case-fold) (isearch-forward t) (isearch-error nil)) - ;; Set isearch-word to nil because word-replace is regexp-based, - ;; so `isearch-search-fun' should not use `word-search-forward'. - (if (and isearch-word isearch-regexp) (setq isearch-word nil)) (isearch-lazy-highlight-new-loop range-beg range-end)))) (defun replace-dehighlight ()