diff options
Diffstat (limited to 'lisp/replace.el')
-rw-r--r-- | lisp/replace.el | 539 |
1 files changed, 359 insertions, 180 deletions
diff --git a/lisp/replace.el b/lisp/replace.el index eb7a439b54a..69bdfe1331d 100644 --- a/lisp/replace.el +++ b/lisp/replace.el @@ -213,7 +213,7 @@ wants to replace FROM with TO." (when query-replace-from-to-separator ;; Check if the first non-whitespace char is displayable (if (char-displayable-p - (string-to-char (replace-regexp-in-string + (string-to-char (string-replace " " "" query-replace-from-to-separator))) query-replace-from-to-separator " -> "))) @@ -310,7 +310,7 @@ the original string if not." ;; but not after (quote foo). (and (eq (car-safe (car pos)) 'quote) (not (= ?\( (aref to 0))))) - (eq (string-match " " to (cdr pos)) + (eq (string-search " " to (cdr pos)) (cdr pos))) (1+ (cdr pos)) (cdr pos)))) @@ -432,6 +432,9 @@ 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's accessible portion. +When invoked interactively, matching a newline with `\\n' will not work; +use `C-q C-j' instead. To match a tab character (`\\t'), just press `TAB'. + Use \\<minibuffer-local-map>\\[next-history-element] \ to pull the last incremental search regexp to the minibuffer that reads REGEXP, or invoke replacements from @@ -630,13 +633,13 @@ Arguments REGEXP, START, END, and REGION-NONCONTIGUOUS-P are passed to (if (listp to-strings) (setq replacements to-strings) (while (/= (length to-strings) 0) - (if (string-match " " to-strings) + (if (string-search " " to-strings) (setq replacements (append replacements (list (substring to-strings 0 - (string-match " " to-strings)))) + (string-search " " to-strings)))) to-strings (substring to-strings - (1+ (string-match " " to-strings)))) + (1+ (string-search " " to-strings)))) (setq replacements (append replacements (list to-strings)) to-strings "")))) (perform-replace regexp replacements t t nil n nil start end nil region-noncontiguous-p))) @@ -789,12 +792,8 @@ 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.") -(defvar occur-highlight-regexp t - "Regexp matching part of visited source lines to highlight temporarily. -Highlight entire line if t; don't highlight source lines if nil.") - -(defvar occur-highlight-overlay nil - "Overlay used to temporarily highlight occur matches.") +(defvar occur-highlight-overlays nil + "Overlays used to temporarily highlight occur matches.") (defvar occur-collect-regexp-history '("\\1") "History of regexp for occur's collect operation") @@ -1051,6 +1050,130 @@ also print the number." count)) count)) +(defun kill-matching-lines (regexp &optional rstart rend interactive) + "Kill lines containing matches for REGEXP. + +When called from Lisp (and usually when called interactively as +well, see below), applies to the part of the buffer after point. +The line point is in is killed if and only if it contains a match +for REGEXP starting after point. + +If REGEXP contains upper case characters (excluding those +preceded by `\\') and `search-upper-case' is non-nil, the +matching is case-sensitive. + +Second and third args RSTART and REND specify the region to +operate on. Lines partially contained in this region are killed +if and only if they contain a match entirely contained in the +region. + +Interactively, in Transient Mark mode when the mark is active, +operate on the contents of the region. Otherwise, operate from +point to the end of (the accessible portion of) the buffer. + +If a match is split across lines, all the lines it lies in are +killed. They are killed _before_ looking for the next match. +Hence, a match starting on the same line at which another match +ended is ignored. + +Return the number of killed matching lines. When called +interactively, also print the number." + (interactive + (progn + (barf-if-buffer-read-only) + (keep-lines-read-args "Kill lines containing match for regexp"))) + (if rstart + (progn + (goto-char (min rstart rend)) + (setq rend (copy-marker (max rstart rend)))) + (if (and interactive (use-region-p)) + (setq rstart (region-beginning) + rend (copy-marker (region-end))) + (setq rstart (point) + rend (point-max-marker))) + (goto-char rstart)) + (let ((count 0) + (case-fold-search + (if (and case-fold-search search-upper-case) + (isearch-no-upper-case-p regexp t) + case-fold-search))) + (save-excursion + (while (and (< (point) rend) + (re-search-forward regexp rend t)) + (unless (zerop count) + (setq last-command 'kill-region)) + (kill-region (save-excursion (goto-char (match-beginning 0)) + (forward-line 0) + (point)) + (progn (forward-line 1) (point))) + (setq count (1+ count)))) + (set-marker rend nil) + (when interactive (message (ngettext "Killed %d matching line" + "Killed %d matching lines" + count) + count)) + count)) + +(defun copy-matching-lines (regexp &optional rstart rend interactive) + "Copy lines containing matches for REGEXP to the kill ring. + +When called from Lisp (and usually when called interactively as +well, see below), applies to the part of the buffer after point. +The line point is in is copied if and only if it contains a match +for REGEXP starting after point. + +If REGEXP contains upper case characters (excluding those +preceded by `\\') and `search-upper-case' is non-nil, the +matching is case-sensitive. + +Second and third args RSTART and REND specify the region to +operate on. Lines partially contained in this region are copied +if and only if they contain a match entirely contained in the +region. + +Interactively, in Transient Mark mode when the mark is active, +operate on the contents of the region. Otherwise, operate from +point to the end of (the accessible portion of) the buffer. + +If a match is split across lines, all the lines it lies in are +copied. + +Return the number of copied matching lines. When called +interactively, also print the number." + (interactive + (keep-lines-read-args "Copy lines containing match for regexp")) + (if rstart + (progn + (goto-char (min rstart rend)) + (setq rend (copy-marker (max rstart rend)))) + (if (and interactive (use-region-p)) + (setq rstart (region-beginning) + rend (copy-marker (region-end))) + (setq rstart (point) + rend (point-max-marker))) + (goto-char rstart)) + (let ((count 0) + (case-fold-search + (if (and case-fold-search search-upper-case) + (isearch-no-upper-case-p regexp t) + case-fold-search))) + (save-excursion + (while (and (< (point) rend) + (re-search-forward regexp rend t)) + (unless (zerop count) + (setq last-command 'kill-region)) + (copy-region-as-kill (save-excursion (goto-char (match-beginning 0)) + (forward-line 0) + (point)) + (progn (forward-line 1) (point))) + (setq count (1+ count)))) + (set-marker rend nil) + (when interactive (message (ngettext "Copied %d matching line" + "Copied %d matching lines" + count) + count)) + count)) + (defun how-many (regexp &optional rstart rend interactive) "Print and return number of matches for REGEXP following point. When called from Lisp and INTERACTIVE is omitted or nil, just return @@ -1086,17 +1209,17 @@ a previously found match." rend (point-max))) (goto-char rstart)) (let ((count 0) - opoint (case-fold-search (if (and case-fold-search search-upper-case) (isearch-no-upper-case-p regexp t) case-fold-search))) (while (and (< (point) rend) - (progn (setq opoint (point)) - (re-search-forward regexp rend t))) - (if (= opoint (point)) - (forward-char 1) - (setq count (1+ count)))) + (re-search-forward regexp rend t)) + ;; Ensure forward progress on zero-length matches like "^$". + (when (and (= (match-beginning 0) (match-end 0)) + (not (eobp))) + (forward-char 1)) + (setq count (1+ count))) (when interactive (message (ngettext "%d occurrence" "%d occurrences" count) @@ -1104,51 +1227,39 @@ a previously found match." count))) -(defvar occur-menu-map - (let ((map (make-sparse-keymap))) - (bindings--define-key map [next-error-follow-minor-mode] - '(menu-item "Auto Occurrence Display" - next-error-follow-minor-mode - :help "Display another occurrence when moving the cursor" - :button (:toggle . (and (boundp 'next-error-follow-minor-mode) - next-error-follow-minor-mode)))) - (bindings--define-key map [separator-1] menu-bar-separator) - (bindings--define-key map [kill-this-buffer] - '(menu-item "Kill Occur Buffer" kill-this-buffer - :help "Kill the current *Occur* buffer")) - (bindings--define-key map [quit-window] - '(menu-item "Quit Occur Window" quit-window - :help "Quit the current *Occur* buffer. Bury it, and maybe delete the selected frame")) - (bindings--define-key map [revert-buffer] - '(menu-item "Revert Occur Buffer" revert-buffer - :help "Replace the text in the *Occur* buffer with the results of rerunning occur")) - (bindings--define-key map [clone-buffer] - '(menu-item "Clone Occur Buffer" clone-buffer - :help "Create and return a twin copy of the current *Occur* buffer")) - (bindings--define-key map [occur-rename-buffer] - '(menu-item "Rename Occur Buffer" occur-rename-buffer - :help "Rename the current *Occur* buffer to *Occur: original-buffer-name*.")) - (bindings--define-key map [occur-edit-buffer] - '(menu-item "Edit Occur Buffer" occur-edit-mode - :help "Edit the *Occur* buffer and apply changes to the original buffers.")) - (bindings--define-key map [separator-2] menu-bar-separator) - (bindings--define-key map [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")) - (bindings--define-key map [occur-mode-goto-occurrence] - '(menu-item "Go To Occurrence" occur-mode-goto-occurrence - :help "Go to the occurrence the current line describes")) - (bindings--define-key map [occur-mode-display-occurrence] - '(menu-item "Display Occurrence" occur-mode-display-occurrence - :help "Display in another window the occurrence the current line describes")) - (bindings--define-key map [occur-next] - '(menu-item "Move to Next Match" occur-next - :help "Move to the Nth (default 1) next match in an Occur mode buffer")) - (bindings--define-key map [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) - "Menu keymap for `occur-mode'.") +(easy-menu-define occur-menu-map nil + "Menu for `occur-mode'." + '("Occur" + ["Move to Previous Match" occur-prev + :help "Move to the Nth (default 1) previous match in an Occur mode buffer"] + ["Move to Next Match" occur-next + :help "Move to the Nth (default 1) next match in an Occur mode buffer"] + ["Display Occurrence" occur-mode-display-occurrence + :help "Display in another window the occurrence the current line describes"] + ["Go To Occurrence" occur-mode-goto-occurrence + :help "Go to the occurrence the current line describes"] + ["Go To Occurrence Other Window" occur-mode-goto-occurrence-other-window + :help "Go to the occurrence the current line describes, in another window"] + "---" + ["Edit Occur Buffer" occur-edit-mode + :help "Edit the *Occur* buffer and apply changes to the original buffers."] + ["Rename Occur Buffer" occur-rename-buffer + :help "Rename the current *Occur* buffer to *Occur: original-buffer-name*."] + ["Clone Occur Buffer" clone-buffer + :help "Create and return a twin copy of the current *Occur* buffer"] + ["Revert Occur Buffer" revert-buffer + :help "Replace the text in the *Occur* buffer with the results of rerunning occur"] + ["Quit Occur Window" quit-window + :help "Quit the current *Occur* buffer. Bury it, and maybe delete the selected frame"] + ["Kill Occur Buffer" kill-this-buffer + :help "Kill the current *Occur* buffer"] + "---" + ["Auto Occurrence Display" + next-error-follow-minor-mode + :help "Display another occurrence when moving the cursor" + :style toggle + :selected (and (boundp 'next-error-follow-minor-mode) + next-error-follow-minor-mode)])) (defvar occur-mode-map (let ((map (make-sparse-keymap))) @@ -1242,18 +1353,27 @@ To return to ordinary Occur mode, use \\[occur-cease-edit]." (occur-mode) (message "Switching to Occur mode."))) +(defun occur--targets-start (targets) + "First marker of the `occur-target' property value TARGETS." + (if (consp targets) + (caar targets) + ;; Tolerate an `occur-target' value that is a single marker for + ;; compatibility. + targets)) + (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)) + (targets (get-text-property line-beg 'occur-target)) + (m (occur--targets-start targets)) (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) + (put-text-property beg end 'occur-target targets) ;; Did we insert a newline? Occur Edit mode can't create new ;; Occur entries; just discard everything after the newline. (save-excursion @@ -1278,8 +1398,27 @@ To return to ordinary Occur mode, use \\[occur-cease-edit]." (recenter line) (if readonly (message "Buffer `%s' is read only." buf) - (delete-region (line-beginning-position) (line-end-position)) - (insert text)) + ;; Replace the line, but make the change as small as + ;; possible by shrink-wrapping. That way, we avoid + ;; disturbing markers unnecessarily. + (let* ((beg-pos (line-beginning-position)) + (end-pos (line-end-position)) + (buf-str (buffer-substring-no-properties beg-pos end-pos)) + (common-prefix + (lambda (s1 s2) + (let ((c (compare-strings s1 nil nil s2 nil nil))) + (if (numberp c) + (1- (abs c)) + (length s1))))) + (prefix-len (funcall common-prefix buf-str text)) + (suffix-len (funcall common-prefix + (reverse buf-str) (reverse text)))) + (setq beg-pos (+ beg-pos prefix-len)) + (setq end-pos (- end-pos suffix-len)) + (setq text (substring text prefix-len (- suffix-len))) + (delete-region beg-pos end-pos) + (goto-char beg-pos) + (insert text))) (move-to-column col))))))) @@ -1287,35 +1426,56 @@ To return to ordinary Occur mode, use \\[occur-cease-edit]." "Handle `revert-buffer' for Occur mode buffers." (apply #'occur-1 (append occur-revert-arguments (list (buffer-name))))) +;; Retained for compatibility. (defun occur-mode-find-occurrence () - (let ((pos (get-text-property (point) 'occur-target))) - (unless pos + "Return a marker to the first match of the line at point." + (occur--targets-start (occur-mode--find-occurrences))) + +(defun occur-mode--find-occurrences () + ;; The `occur-target' property value is a list of (BEG . END) for each + ;; match on the line, or (for compatibility) a single marker to the start + ;; of the first match. + (let* ((targets (get-text-property (point) 'occur-target)) + (start (occur--targets-start targets))) + (unless targets (error "No occurrence on this line")) - (unless (buffer-live-p (marker-buffer pos)) + (unless (buffer-live-p (marker-buffer start)) (error "Buffer for this occurrence was killed")) - pos)) + targets)) + +(defun occur--set-arrow () + "Set the overlay arrow at the first line of the occur match at point." + (save-excursion + (let ((target (get-text-property (point) 'occur-target)) + ;; Find the start of the occur match, in case it's multi-line. + (prev (previous-single-property-change (point) 'occur-target))) + (when (and prev (eq (get-text-property prev 'occur-target) target)) + (goto-char prev)) + (setq overlay-arrow-position + (set-marker (or overlay-arrow-position (make-marker)) + (line-beginning-position)))))) (defalias 'occur-mode-mouse-goto 'occur-mode-goto-occurrence) (defun occur-mode-goto-occurrence (&optional event) "Go to the occurrence specified by EVENT, a mouse click. If not invoked by a mouse click, go to occurrence on the current line." (interactive (list last-nonmenu-event)) - (let ((buffer (when event (current-buffer))) - (pos - (if (null event) - ;; Actually `event-end' works correctly with a nil argument as - ;; well, so we could dispense with this test, but let's not - ;; rely on this undocumented behavior. - (occur-mode-find-occurrence) - (with-current-buffer (window-buffer (posn-window (event-end event))) - (save-excursion - (goto-char (posn-point (event-end event))) - (occur-mode-find-occurrence))))) - (regexp occur-highlight-regexp)) + (let* ((buffer (when event (current-buffer))) + (targets + (if (null event) + ;; Actually `event-end' works correctly with a nil argument as + ;; well, so we could dispense with this test, but let's not + ;; rely on this undocumented behavior. + (occur-mode--find-occurrences) + (with-current-buffer (window-buffer (posn-window (event-end event))) + (save-excursion + (goto-char (posn-point (event-end event))) + (occur-mode--find-occurrences))))) + (pos (occur--targets-start targets))) + (occur--set-arrow) (pop-to-buffer (marker-buffer pos)) (goto-char pos) - (let ((end-mk (save-excursion (re-search-forward regexp nil t)))) - (occur--highlight-occurrence pos end-mk)) + (occur--highlight-occurrences targets) (when buffer (next-error-found buffer (current-buffer))) (run-hooks 'occur-mode-find-occurrence-hook))) @@ -1323,15 +1483,16 @@ If not invoked by a mouse click, go to occurrence on the current line." "Go to the occurrence the current line describes, in another window." (interactive) (let ((buffer (current-buffer)) - (pos (occur-mode-find-occurrence))) + (pos (occur--targets-start (occur-mode--find-occurrences)))) + (occur--set-arrow) (switch-to-buffer-other-window (marker-buffer pos)) (goto-char pos) (next-error-found buffer (current-buffer)) (run-hooks 'occur-mode-find-occurrence-hook))) -;; Stolen from compile.el (defun occur-goto-locus-delete-o () - (delete-overlay occur-highlight-overlay) + (mapc #'delete-overlay occur-highlight-overlays) + (setq occur-highlight-overlays nil) ;; Get rid of timer and hook that would try to do this again. (if (timerp next-error-highlight-timer) (cancel-timer next-error-highlight-timer)) @@ -1339,64 +1500,56 @@ If not invoked by a mouse click, go to occurrence on the current line." #'occur-goto-locus-delete-o)) ;; Highlight the current visited occurrence. -;; Adapted from `compilation-goto-locus'. -(defun occur--highlight-occurrence (mk end-mk) - (let ((highlight-regexp occur-highlight-regexp)) - (if (timerp next-error-highlight-timer) - (cancel-timer next-error-highlight-timer)) - (unless occur-highlight-overlay - (setq occur-highlight-overlay - (make-overlay (point-min) (point-min))) - (overlay-put occur-highlight-overlay 'face 'next-error)) - (with-current-buffer (marker-buffer mk) - (save-excursion - (if end-mk (goto-char end-mk) (end-of-line)) - (let ((end (point))) - (if mk (goto-char mk) (beginning-of-line)) - (if (and (stringp highlight-regexp) - (re-search-forward highlight-regexp end t)) - (progn - (goto-char (match-beginning 0)) - (move-overlay occur-highlight-overlay - (match-beginning 0) (match-end 0) - (current-buffer))) - (move-overlay occur-highlight-overlay - (point) end (current-buffer))) - (if (or (eq next-error-highlight t) - (numberp next-error-highlight)) - ;; We want highlighting: delete overlay on next input. - (add-hook 'pre-command-hook - #'occur-goto-locus-delete-o) - ;; We don't want highlighting: delete overlay now. - (delete-overlay occur-highlight-overlay)) - ;; We want highlighting for a limited time: - ;; set up a timer to delete it. - (when (numberp next-error-highlight) - (setq next-error-highlight-timer - (run-at-time next-error-highlight nil - 'occur-goto-locus-delete-o)))))) - (when (eq next-error-highlight 'fringe-arrow) - ;; We want a fringe arrow (instead of highlighting). - (setq next-error-overlay-arrow-position - (copy-marker (line-beginning-position)))))) +(defun occur--highlight-occurrences (targets) + (let ((start-marker (occur--targets-start targets))) + (occur-goto-locus-delete-o) + (with-current-buffer (marker-buffer start-marker) + (when (or (eq next-error-highlight t) + (numberp next-error-highlight)) + (setq occur-highlight-overlays + (mapcar (lambda (target) + (let ((o (make-overlay (car target) (cdr target)))) + (overlay-put o 'face 'next-error) + o)) + (if (listp targets) + targets + ;; `occur-target' compatibility: when we only + ;; have a single starting point, highlight the + ;; rest of the line. + (let ((end-pos (save-excursion + (goto-char start-marker) + (line-end-position)))) + (list (cons start-marker end-pos)))))) + (add-hook 'pre-command-hook #'occur-goto-locus-delete-o) + (when (numberp next-error-highlight) + ;; We want highlighting for a limited time: + ;; set up a timer to delete it. + (setq next-error-highlight-timer + (run-at-time next-error-highlight nil + 'occur-goto-locus-delete-o)))) + + (when (eq next-error-highlight 'fringe-arrow) + ;; We want a fringe arrow (instead of highlighting). + (setq next-error-overlay-arrow-position + (copy-marker (line-beginning-position))))))) (defun occur-mode-display-occurrence () "Display in another window the occurrence the current line describes." (interactive) - (let ((buffer (current-buffer)) - (pos (occur-mode-find-occurrence)) - (regexp occur-highlight-regexp) - (next-error-highlight next-error-highlight-no-select) - (display-buffer-overriding-action - '(nil (inhibit-same-window . t))) - window) + (let* ((buffer (current-buffer)) + (targets (occur-mode--find-occurrences)) + (pos (occur--targets-start targets)) + (next-error-highlight next-error-highlight-no-select) + (display-buffer-overriding-action + '(nil (inhibit-same-window . t))) + window) (setq window (display-buffer (marker-buffer pos) t)) + (occur--set-arrow) ;; This is the way to set point in the proper window. (save-selected-window (select-window window) (goto-char pos) - (let ((end-mk (save-excursion (re-search-forward regexp nil t)))) - (occur--highlight-occurrence pos end-mk)) + (occur--highlight-occurrences targets) (next-error-found buffer (current-buffer)) (run-hooks 'occur-mode-find-occurrence-hook)))) @@ -1445,7 +1598,7 @@ This is a compatibility function for \\[next-error] invocations." (defface match '((((class color) (min-colors 88) (background light)) - :background "yellow1") + :background "khaki1") (((class color) (min-colors 88) (background dark)) :background "RoyalBlue3") (((class color) (min-colors 8) (background light)) @@ -1489,15 +1642,22 @@ If the value is nil, don't highlight the buffer names specially." (defcustom list-matching-lines-jump-to-current-line nil "If non-nil, \\[list-matching-lines] shows the current line highlighted. -Set the point right after such line when there are matches after it." +The current line for this purpose is the line of the original buffer +which was current when \\[list-matching-lines] was invoked. +Point in the `*Occur*' buffer will be set right after such line when +there are matches after it." :type 'boolean :group 'matching :version "26.1") (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." +The prefix column is the part of display that precedes the actual +contents of the line; it normally shows the line number. \(For +multiline matches, the prefix column shows the line number for the +first line and whitespace for the rest of the lines.\) +If this face will display the same as the default face, the prefix +column will not be highlighted speciall." :type 'face :group 'matching :version "24.4") @@ -1577,11 +1737,24 @@ REGION must be a list of (START . END) positions as returned by `region-bounds'. The lines are shown in a buffer named `*Occur*'. -It serves as a menu to find any of the occurrences in this buffer. +That buffer can serve as a menu for finding any of the matches for REGEXP +in the current buffer. \\<occur-mode-map>\\[describe-mode] in that buffer will explain how. -If `list-matching-lines-jump-to-current-line' is non-nil, then show -the current line highlighted with `list-matching-lines-current-line-face' -and set point at the first match after such line. + +Matches for REGEXP are shown in the face determined by the +variable `list-matching-lines-face'. +Names of buffers with matched lines are shown in the face determined +by the variable `list-matching-lines-buffer-name-face'. +The line numbers of the matching lines are shown in the face +determined by the variable `list-matching-lines-prefix-face'. + +If `list-matching-lines-jump-to-current-line' is non-nil, then the +line in the current buffer which was current when the command was +invoked will be shown in the `*Occur*' buffer highlighted with +the `list-matching-lines-current-line-face', with point at the end +of that line. (If the current line doesn't match REGEXP, it will +nonetheless be inserted into the `*Occur*' buffer between the 2 +closest lines that do match REGEXP.) If REGEXP contains upper case characters (excluding those preceded by `\\') and `search-upper-case' is non-nil, the matching is case-sensitive. @@ -1725,6 +1898,7 @@ See also `multi-occur'." ;; Make the default-directory of the *Occur* buffer match that of ;; the buffer where the occurrences come from (setq default-directory source-buffer-default-directory) + (setq overlay-arrow-position nil) (if (stringp nlines) (fundamental-mode) ;; This is for collect operation. (occur-mode)) @@ -1733,7 +1907,6 @@ See also `multi-occur'." (buffer-undo-list t) (occur--final-pos nil)) (erase-buffer) - (setq-local occur-highlight-regexp regexp) (let ((count (if (stringp nlines) ;; Treat nlines as a regexp to collect. @@ -1833,7 +2006,7 @@ See also `multi-occur'." (origpt nil) (begpt nil) (endpt nil) - (marker nil) + markers ; list of (BEG-MARKER . END-MARKER) (curstring "") (ret nil) ;; The following binding is for when case-fold-search @@ -1859,8 +2032,7 @@ See also `multi-occur'." (setq endpt (line-end-position))) ;; Sum line numbers up to the first match line. (setq curr-line (+ curr-line (count-lines origpt begpt))) - (setq marker (make-marker)) - (set-marker marker matchbeg) + (setq markers nil) (setq curstring (occur-engine-line begpt endpt keep-props)) ;; Highlight the matches (let ((len (length curstring)) @@ -1882,6 +2054,11 @@ See also `multi-occur'." (setq orig-line-shown-p t))) (while (and (< start len) (string-match regexp curstring start)) + (push (cons (set-marker (make-marker) + (+ begpt (match-beginning 0))) + (set-marker (make-marker) + (+ begpt (match-end 0)))) + markers) (setq matches (1+ matches)) (add-text-properties (match-beginning 0) (match-end 0) @@ -1894,6 +2071,7 @@ See also `multi-occur'." ;; Avoid infloop (Bug#7593). (let ((end (match-end 0))) (setq start (if (= start end) (1+ start) end))))) + (setq markers (nreverse markers)) ;; Generate the string to insert for this match (let* ((match-prefix ;; Using 7 digits aligns tabs properly. @@ -1907,7 +2085,7 @@ See also `multi-occur'." ;; (for Occur Edit mode). front-sticky t rear-nonsticky t - occur-target ,marker + occur-target ,markers follow-link t help-echo "mouse-2: go to this occurrence")))) (match-str @@ -1915,7 +2093,7 @@ See also `multi-occur'." ;; because that loses. And don't put it ;; on context lines to reduce flicker. (propertize curstring - 'occur-target marker + 'occur-target markers 'follow-link t 'help-echo "mouse-2: go to this occurrence")) @@ -1923,19 +2101,21 @@ See also `multi-occur'." ;; Add non-numeric prefix to all non-first lines ;; of multi-line matches. (concat - (replace-regexp-in-string + (string-replace "\n" (if prefix-face (propertize - "\n :" 'font-lock-face prefix-face) - "\n :") + "\n :" 'font-lock-face prefix-face + 'occur-target markers) + (propertize + "\n :" 'occur-target markers)) ;; Add mouse face in one section to ;; ensure the prefix and the string ;; get a contiguous highlight. (propertize (concat match-prefix match-str) 'mouse-face 'highlight)) - ;; Add marker at eol, but no mouse props. - (propertize "\n" 'occur-target marker))) + ;; Add markers at eol, but no mouse props. + (propertize "\n" 'occur-target markers))) (data (if (= nlines 0) ;; The simple display style @@ -2326,12 +2506,10 @@ 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))) + (string-replace "\\" "\\\\" + (if (stringp replacement) + replacement + (prin1-to-string replacement t)))) (defun replace-loop-through-replacements (data count) ;; DATA is a vector containing the following values: @@ -2587,9 +2765,7 @@ characters." ;; If non-nil, it is marker saying where in the buffer to stop. (limit nil) - ;; Use local binding in add-function below. - (isearch-filter-predicate isearch-filter-predicate) - (region-bounds nil) + (region-filter nil) ;; Data for the next match. If a cons, it has the same format as ;; (match-data); otherwise it is t if a match is possible at point. @@ -2613,21 +2789,22 @@ characters." ;; Unless a single contiguous chunk is selected, operate on multiple chunks. (when region-noncontiguous-p - (setq region-bounds - (mapcar (lambda (position) - (cons (copy-marker (car position)) - (copy-marker (cdr position)))) - (funcall region-extract-function 'bounds))) - (add-function :after-while isearch-filter-predicate - (lambda (start end) - (delq nil (mapcar - (lambda (bounds) - (and - (>= start (car bounds)) - (<= start (cdr bounds)) - (>= end (car bounds)) - (<= end (cdr bounds)))) - region-bounds))))) + (let ((region-bounds + (mapcar (lambda (position) + (cons (copy-marker (car position)) + (copy-marker (cdr position)))) + (funcall region-extract-function 'bounds)))) + (setq region-filter + (lambda (start end) + (delq nil (mapcar + (lambda (bounds) + (and + (>= start (car bounds)) + (<= start (cdr bounds)) + (>= end (car bounds)) + (<= end (cdr bounds)))) + region-bounds)))) + (add-function :after-while isearch-filter-predicate region-filter))) ;; If region is active, in Transient Mark mode, operate on region. (if backward @@ -3060,7 +3237,9 @@ characters." (setq next-replacement-replaced nil search-string-replaced nil last-was-act-and-show nil)))))) - (replace-dehighlight)) + (replace-dehighlight) + (when region-filter + (remove-function isearch-filter-predicate region-filter))) (or unread-command-events (message (ngettext "Replaced %d occurrence%s" "Replaced %d occurrences%s" |