diff options
Diffstat (limited to 'lisp/isearch.el')
-rw-r--r-- | lisp/isearch.el | 310 |
1 files changed, 147 insertions, 163 deletions
diff --git a/lisp/isearch.el b/lisp/isearch.el index eb4f9a43f6..5fd9654660 100644 --- a/lisp/isearch.el +++ b/lisp/isearch.el @@ -593,33 +593,33 @@ is treated as a regexp. See \\[isearch-forward] for more info." ;; Called after each command to update the display. (if (null unread-command-events) (progn - (if (not (input-pending-p)) - (isearch-message)) - (if (and isearch-slow-terminal-mode - (not (or isearch-small-window - (pos-visible-in-window-p)))) - (let ((found-point (point))) - (setq isearch-small-window t) - (move-to-window-line 0) - (let ((window-min-height 1)) - (split-window nil (if (< search-slow-window-lines 0) - (1+ (- search-slow-window-lines)) - (- (window-height) - (1+ search-slow-window-lines))))) - (if (< search-slow-window-lines 0) - (progn (vertical-motion (- 1 search-slow-window-lines)) - (set-window-start (next-window) (point)) - (set-window-hscroll (next-window) - (window-hscroll)) - (set-window-hscroll (selected-window) 0)) - (other-window 1)) - (goto-char found-point))) - (if isearch-other-end - (if (< isearch-other-end (point)) ; isearch-forward? - (isearch-highlight isearch-other-end (point)) - (isearch-highlight (point) isearch-other-end)) - (isearch-dehighlight nil)) - )) + (if (not (input-pending-p)) + (isearch-message)) + (if (and isearch-slow-terminal-mode + (not (or isearch-small-window + (pos-visible-in-window-p)))) + (let ((found-point (point))) + (setq isearch-small-window t) + (move-to-window-line 0) + (let ((window-min-height 1)) + (split-window nil (if (< search-slow-window-lines 0) + (1+ (- search-slow-window-lines)) + (- (window-height) + (1+ search-slow-window-lines))))) + (if (< search-slow-window-lines 0) + (progn (vertical-motion (- 1 search-slow-window-lines)) + (set-window-start (next-window) (point)) + (set-window-hscroll (next-window) + (window-hscroll)) + (set-window-hscroll (selected-window) 0)) + (other-window 1)) + (goto-char found-point))) + (if isearch-other-end + (if (< isearch-other-end (point)) ; isearch-forward? + (isearch-highlight isearch-other-end (point)) + (isearch-highlight (point) isearch-other-end)) + (isearch-dehighlight nil)) + )) (setq ;; quit-flag nil not for isearch-mode isearch-adjusted nil isearch-yank-flag nil) @@ -638,7 +638,6 @@ is treated as a regexp. See \\[isearch-forward] for more info." (remove-hook 'mouse-leave-buffer-hook 'isearch-done) (remove-hook 'kbd-macro-termination-hook 'isearch-done) - (setq isearch-lazy-highlight-start nil) ;; Called by all commands that terminate isearch-mode. @@ -1583,7 +1582,7 @@ If there is no completion possible, say so and continue searching." isearch-invalid-regexp) (setq isearch-invalid-regexp "incomplete input"))) (error - ;; Stack overflow in regexp search, for instance. + ;; stack overflow in regexp search. (setq isearch-invalid-regexp (format "%s" lossage)))) (if isearch-success @@ -1740,45 +1739,21 @@ If there is no completion possible, say so and continue searching." (defvar isearch-overlay nil) -(defsubst isearch-set-lazy-highlight-faces-at (pos face) - "Set the face property of isearch lazy highlight overlays at POS to FACE. -If POS is nil, nothing is done." - (unless (null pos) - (dolist (ov (overlays-at pos)) - (when (and (not (eq ov isearch-overlay)) - (memq ov isearch-lazy-highlight-overlays) - (not (eq (overlay-get ov 'face) face))) - (overlay-put ov 'face face))))) - (defun isearch-highlight (beg end) (unless (or (null search-highlight) (null (display-color-p))) (cond (isearch-overlay ;; Overlay already exists, just move it. - - ;; Check to see if there are any lazy-isearch overlays at - ;; the same position with their face property suppressed - ;; (to avoid face clashes), and if so, give them their face - ;; back. - (isearch-set-lazy-highlight-faces-at (overlay-start isearch-overlay) - isearch-lazy-highlight-face) - (move-overlay isearch-overlay beg end (current-buffer))) (t ;; Overlay doesn't exist, create it. (setq isearch-overlay (make-overlay beg end)) - (overlay-put isearch-overlay 'face isearch))) - - ;; Suppress the faces of any lazy-isearch overlays at the new position - (isearch-set-lazy-highlight-faces-at beg nil))) + (overlay-put isearch-overlay 'face isearch) + (overlay-put isearch-overlay 'priority 1) ;higher than lazy overlays + )))) (defun isearch-dehighlight (totally) (when isearch-overlay - ;; Check to see if there are any lazy-isearch overlays at the same - ;; position with their face property suppressed (to avoid face - ;; clashes), and if so, give them their face back. - (isearch-set-lazy-highlight-faces-at (overlay-start isearch-overlay) - isearch-lazy-highlight-face) (delete-overlay isearch-overlay))) @@ -1820,23 +1795,19 @@ since they have special meaning in a regexp." ;;; When active, *every* match for the current search string is ;;; highlighted: the current one using the normal isearch match color -;;; and all the others using the unobtrusive `secondary-selection' -;;; color. The extra highlighting makes it easier to anticipate where -;;; the cursor will land each time you press C-s or C-r to repeat a -;;; pending search. Highlighting of these additional matches happens -;;; in a deferred fashion using "idle timers," so the cycles needed do -;;; not rob isearch of its usual snappy response. +;;; and all the others using `isearch-lazy-highlight-face'. The extra +;;; highlighting makes it easier to anticipate where the cursor will +;;; land each time you press C-s or C-r to repeat a pending search. +;;; Highlighting of these additional matches happens in a deferred +;;; fashion using "idle timers," so the cycles needed do not rob +;;; isearch of its usual snappy response. ;;; IMPLEMENTATION NOTE: This depends on some isearch internals. ;;; Specifically: ;;; - `isearch-update' is expected to be called (at least) every time -;;; the search string changes; +;;; the search string or window-start changes; ;;; - `isearch-string' is expected to contain the current search ;;; string as entered by the user; -;;; - `isearch-overlay' is expected to contain the overlay used for -;;; primary isearch match-highlighting; -;;; - `isearch-opoint' is expected to contain the location where the -;;; current search began; ;;; - the type of the current search is expected to be given by ;;; `isearch-word' and `isearch-regexp'; ;;; - the direction of the current search is expected to be given by @@ -1877,8 +1848,11 @@ If this is nil, extra highlighting can be \"manually\" removed with :type 'number :group 'isearch-lazy-highlight) -(defcustom isearch-lazy-highlight-max 20 - "*Maximum number of matches to highlight." +(defcustom isearch-lazy-highlight-max-at-a-time 20 + "*Maximum matches to highlight at a time (for `isearch-lazy-highlight'). +Larger values may reduce isearch's responsiveness to user input; +smaller values make matches highlight slowly. +A value of nil means highlight all matches." :type '(choice (const :tag "All" nil) (integer :tag "Some")) :group 'isearch-lazy-highlight) @@ -1913,114 +1887,124 @@ If this is nil, extra highlighting can be \"manually\" removed with (defvar isearch-lazy-highlight-face 'isearch-lazy-highlight-face) (defvar isearch-lazy-highlight-overlays nil) -(defvar isearch-lazy-highlight-window nil) +(defvar isearch-lazy-highlight-wrapped nil) (defvar isearch-lazy-highlight-start nil) (defvar isearch-lazy-highlight-end nil) (defvar isearch-lazy-highlight-timer nil) (defvar isearch-lazy-highlight-last-string nil) +(defvar isearch-lazy-highlight-window nil) +(defvar isearch-lazy-highlight-window-start nil) -(defun isearch-lazy-highlight-cleanup (&optional remove) - "Stop lazy highlighting and maybe remove existing highlighting. -REMOVE non-nil means remove all the existing lazy highlighting. - -This function is called when exiting an incremental search." +(defun isearch-lazy-highlight-cleanup (&optional force) + "Stop lazy highlighting and remove extra highlighting from current buffer. +FORCE non-nil means do it whether or not `isearch-lazy-highlight-cleanup' +is nil. This function is called when exiting an incremental search if +`isearch-lazy-highlight-cleanup' is non-nil." (interactive '(t)) - (if remove - (isearch-lazy-highlight-remove-overlays)) - (if isearch-lazy-highlight-timer - (progn - (cancel-timer isearch-lazy-highlight-timer) - (setq isearch-lazy-highlight-timer nil)))) - -(defun isearch-lazy-highlight-remove-overlays (&optional keep-start keep-end) - "Remove lazy highlight overlays from the current buffer. -With optional arguments KEEP-START and KEEP-END, -preserve any overlays in that range." - (let ((tem isearch-lazy-highlight-overlays)) - (while tem - (if (or (null keep-start) - (let ((pos (overlay-start (car tem)))) - (or (< pos keep-start) (> pos keep-end)))) - (progn - (delete-overlay (car tem)) - (setq isearch-lazy-highlight-overlays - (delq (car tem) isearch-lazy-highlight-overlays)))) - (setq tem (cdr tem))))) + (if (or force isearch-lazy-highlight-cleanup) + (while isearch-lazy-highlight-overlays + (delete-overlay (car isearch-lazy-highlight-overlays)) + (setq isearch-lazy-highlight-overlays + (cdr isearch-lazy-highlight-overlays)))) + (when isearch-lazy-highlight-timer + (cancel-timer isearch-lazy-highlight-timer) + (setq isearch-lazy-highlight-timer nil))) (defun isearch-lazy-highlight-new-loop () - "Clear obsolete highlighting, and queue up to do new highlighting. + "Cleanup any previous `isearch-lazy-highlight' loop and begin a new one. This happens when `isearch-update' is invoked (which can cause the -search string to change)." +search string to change or the window to scroll)." (when (and isearch-lazy-highlight - (not isearch-invalid-regexp) - (not (equal isearch-string ""))) - - ;; If the search string has changed, remove all old overlays. - (unless (equal isearch-string isearch-lazy-highlight-last-string) - (isearch-lazy-highlight-remove-overlays) - (setq isearch-lazy-highlight-window nil)) - - (if (and isearch-overlay - (not (overlay-get isearch-overlay 'priority))) - ;; Make sure the isearch-overlay takes priority - ;; over any other matches. - (overlay-put isearch-overlay 'priority 1)) - - ;; Queue up to display other matches after a short pause. - (setq isearch-lazy-highlight-timer - (run-with-idle-timer isearch-lazy-highlight-initial-delay nil - 'isearch-lazy-highlight-update)))) + (sit-for 0) ;make sure (window-start) is credible + (or (not (equal isearch-string + isearch-lazy-highlight-last-string)) + (not (eq (selected-window) + isearch-lazy-highlight-window)) + (not (= (window-start) + isearch-lazy-highlight-window-start)))) + ;; something important did indeed change + (isearch-lazy-highlight-cleanup t) ;kill old loop & remove overlays + (when (not isearch-invalid-regexp) + (setq isearch-lazy-highlight-window (selected-window) + isearch-lazy-highlight-window-start (window-start) + isearch-lazy-highlight-start (point) + isearch-lazy-highlight-end (point) + isearch-lazy-highlight-last-string isearch-string + isearch-lazy-highlight-wrapped nil) + (setq isearch-lazy-highlight-timer + (run-with-idle-timer isearch-lazy-highlight-initial-delay nil + 'isearch-lazy-highlight-update))))) + +(defun isearch-lazy-highlight-search () + "Search ahead for the next or previous match, for lazy highlighting. +Attempt to do the search exactly the way the pending isearch would." + (let ((case-fold-search isearch-case-fold-search) + (choices (cond (isearch-word + '(word-search-forward . word-search-backward)) + (isearch-regexp + '(re-search-forward . re-search-backward)) + (t + '(search-forward . search-backward))))) + (funcall (if isearch-forward + (car choices) + (cdr choices)) + isearch-string + (if isearch-forward + (if isearch-lazy-highlight-wrapped + isearch-lazy-highlight-start + (window-end)) + (if isearch-lazy-highlight-wrapped + isearch-lazy-highlight-end + (window-start))) + t))) (defun isearch-lazy-highlight-update () - "Update highlighting of possible other matches for isearch." - (unless (and (eq isearch-lazy-highlight-window (selected-window)) - (equal isearch-lazy-highlight-start (window-start))) - - ;; The search string or the visible window has changed. - - (setq isearch-lazy-highlight-window (selected-window) - isearch-lazy-highlight-start (window-start) - isearch-lazy-highlight-end (window-end nil t) - isearch-lazy-highlight-last-string isearch-string) - - ;; If the string is the same, the old overlays are still usable - ;; if they are still visible in the window. - (isearch-lazy-highlight-remove-overlays (window-start) - (window-end nil t)) - + "Update highlighting of other matches for current search." + (let ((max isearch-lazy-highlight-max-at-a-time) + (looping t) + nomore) (save-excursion (save-match-data - (let (found) - (goto-char isearch-lazy-highlight-start) - (while (and (or (null isearch-lazy-highlight-max) - (< (length isearch-lazy-highlight-overlays) - isearch-lazy-highlight-max)) - (< (point) isearch-lazy-highlight-end) - (let ((case-fold-search isearch-case-fold-search)) - (funcall (cond (isearch-word 'word-search-forward) - (isearch-regexp 're-search-forward) - (t 'search-forward)) - isearch-string - isearch-lazy-highlight-end - t))) - ;; Found the next match. - ;; If it is empty, ignore it and move on. - (if (= (match-beginning 0) (match-end 0)) - (forward-char 1) - (let ((ov (make-overlay (match-beginning 0) - (match-end 0)))) - ;; If OV overlaps the current isearch overlay, suppress - ;; its face property; otherwise, we sometimes get odd - ;; looking face combinations. - (unless (memq isearch-overlay - (overlays-in (match-beginning 0) (match-end 0))) - (overlay-put ov 'face isearch-lazy-highlight-face)) - - (overlay-put ov 'priority 0) - ;; Don't highlight on any other windows. - (overlay-put ov 'window isearch-lazy-highlight-window) - - (push ov isearch-lazy-highlight-overlays))))))))) + (goto-char (if isearch-forward + isearch-lazy-highlight-end + isearch-lazy-highlight-start)) + (while looping + (let ((found (isearch-lazy-highlight-search))) + (when max + (setq max (1- max)) + (if (<= max 0) + (setq looping nil))) + (if found + (let ((mb (match-beginning 0)) + (me (match-end 0))) + (if (= mb me) ;zero-length match + (forward-char 1) + + ;; non-zero-length match + (let ((ov (make-overlay mb me))) + (overlay-put ov 'face isearch-lazy-highlight-face) + (overlay-put ov 'priority 0) ;lower than main overlay + (overlay-put ov 'window (selected-window)) + (push ov isearch-lazy-highlight-overlays))) + (if isearch-forward + (setq isearch-lazy-highlight-end (point)) + (setq isearch-lazy-highlight-start (point)))) + + ;; not found + (if isearch-lazy-highlight-wrapped + (setq looping nil + nomore t) + (setq isearch-lazy-highlight-wrapped t) + (if isearch-forward + (progn + (setq isearch-lazy-highlight-end (window-start)) + (goto-char (window-start))) + (setq isearch-lazy-highlight-start (window-end)) + (goto-char (window-end))))))) + (unless nomore + (setq isearch-lazy-highlight-timer + (run-at-time isearch-lazy-highlight-interval nil + 'isearch-lazy-highlight-update))))))) (defun isearch-resume (search regexp word forward message case-fold) "Resume an incremental search. |