+\f
+;; isearch-lazy-highlight feature
+;; by Bob Glickstein <http://www.zanshin.com/~bobg/>
+
+;; 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 `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 or window-start changes;
+;; - `isearch-string' is expected to contain the current search
+;; string as entered by the user;
+;; - 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
+;; `isearch-forward';
+;; - the variable `isearch-invalid-regexp' is expected to be true
+;; iff `isearch-string' is an invalid regexp.
+
+(defgroup isearch-lazy-highlight nil
+ "Lazy highlighting feature for incremental search."
+ :prefix "isearch-lazy-highlight-"
+ :version "21.1"
+ :group 'isearch)
+
+(defcustom isearch-lazy-highlight t
+ "*Controls the lazy-highlighting during incremental searches.
+When non-nil, all text in the buffer matching the current search
+string is highlighted lazily (see `isearch-lazy-highlight-initial-delay'
+and `isearch-lazy-highlight-interval')."
+ :type 'boolean
+ :group 'isearch-lazy-highlight)
+
+(defcustom isearch-lazy-highlight-cleanup t
+ "*Controls whether to remove extra highlighting after a search.
+If this is nil, extra highlighting can be \"manually\" removed with
+\\[isearch-lazy-highlight-cleanup]."
+ :type 'boolean
+ :group 'isearch-lazy-highlight)
+
+(defcustom isearch-lazy-highlight-initial-delay 0.25
+ "*Seconds to wait before beginning to lazily highlight all matches."
+ :type 'number
+ :group 'isearch-lazy-highlight)
+
+(defcustom isearch-lazy-highlight-interval 0 ; 0.0625
+ "*Seconds between lazily highlighting successive matches."
+ :type 'number
+ :group 'isearch-lazy-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)
+
+(defgroup isearch-faces nil
+ "Lazy highlighting feature for incremental search."
+ :version "21.1"
+ :group 'isearch)
+
+(defface isearch
+ '((((type tty pc) (class color))
+ (:background "magenta4" :foreground "cyan1"))
+ (((class color) (background light))
+ ;; The background must not be too dark, for that means
+ ;; the character is hard to see when the cursor is there.
+ (:background "magenta2" :foreground "lightskyblue1"))
+ (((class color) (background dark))
+ (:background "palevioletred2" :foreground "brown4"))
+ (t (:inverse-video t)))
+ "Face for highlighting Isearch matches."
+ :group 'isearch-faces)
+(defvar isearch 'isearch)
+
+(defface isearch-lazy-highlight-face
+ '((((type tty pc) (class color))
+ (:background "turquoise3"))
+ (((class color) (background light))
+ (:background "paleturquoise"))
+ (((class color) (background dark))
+ (:background "paleturquoise4"))
+ (t (:underline t)))
+ "Face for lazy highlighting of Isearch matches other than the current one."
+ :group 'isearch-faces)
+(defvar isearch-lazy-highlight-face 'isearch-lazy-highlight-face)
+
+(defvar isearch-lazy-highlight-overlays 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)
+(defvar isearch-lazy-highlight-case-fold-search nil)
+(defvar isearch-lazy-highlight-regexp nil)
+
+(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 (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 ()
+ "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 or the window to scroll)."
+ (when (and isearch-lazy-highlight
+ (null executing-kbd-macro)
+ (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 (eq isearch-lazy-highlight-case-fold-search
+ isearch-case-fold-search))
+ (not (eq isearch-lazy-highlight-regexp
+ isearch-regexp))
+ (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-case-fold-search isearch-case-fold-search
+ isearch-lazy-highlight-regexp isearch-regexp
+ 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))
+ (funcall (isearch-search-fun)
+ 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 other matches for current search."
+ (let ((max isearch-lazy-highlight-max-at-a-time)
+ (looping t)
+ nomore)
+ (save-excursion
+ (save-match-data
+ (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.
+SEARCH is the string or regexp searched for.
+REGEXP non-nil means the resumed search was a regexp search.
+WORD non-nil means resume a word search.
+FORWARD non-nil means resume a forward search.
+MESSAGE is the echo-area message recorded for the search resumed.
+CASE-FOLD non-nil means the search was case-insensitive."
+ (isearch-mode forward regexp nil nil word)
+ (setq isearch-string search
+ isearch-message message
+ isearch-case-fold-search case-fold)
+ (isearch-search))