This experimental patch implements a suggestion by Juri Linkov to extend isearch-repeat-forward/backward to support a prefix argument. Instead of pressing C-s / C-r multiple times it is now possible to enter a prefix argument which runs the command multiple times for you. If a negative argument is given for isearch-repeat-forward than isearch-repeat-backward is run and visa versa. Visual hints are added to the matched strings to help figure out what argument should be given. diff --git a/lisp/isearch.el b/lisp/isearch.el index ce75911..fce6fd5 100644 --- a/lisp/isearch.el +++ b/lisp/isearch.el @@ -333,6 +333,21 @@ A value of nil means highlight all matches." 'lazy-highlight-face "22.1") (defvar lazy-highlight-face 'lazy-highlight) + +(defface lazy-highlight-hint + '((((class color) (min-colors 88) (background light)) + (:background "paleturquoise" :bold t)) + (((class color) (min-colors 88) (background dark)) + (:background "paleturquoise4" :bold t)) + (((class color) (min-colors 16)) + (:background "turquoise3" :bold t)) + (((class color) (min-colors 8)) + (:background "turquoise3" :bold t)) + (t (:underline t))) + "Face for lazy highlighting hints." + :group 'lazy-highlight + :group 'basic-faces) + ;; Define isearch help map. @@ -417,10 +432,25 @@ This is like `describe-bindings', but displays only Isearch keys." ;; We need these explicit definitions because, in a dense keymap, ;; the binding for t does not affect characters. ;; We use a dense keymap to save space. + (while (< i ?\s) (define-key map (make-string 1 i) 'isearch-other-control-char) (setq i (1+ i))) + ;; Bring universal-argument and friends back in + (define-key map "\C-u" 'universal-argument) + (define-key map (kbd "C--") 'negative-argument) + (define-key map (kbd "C-0") 'digit-argument) + (define-key map (kbd "C-1") 'digit-argument) + (define-key map (kbd "C-2") 'digit-argument) + (define-key map (kbd "C-3") 'digit-argument) + (define-key map (kbd "C-4") 'digit-argument) + (define-key map (kbd "C-5") 'digit-argument) + (define-key map (kbd "C-6") 'digit-argument) + (define-key map (kbd "C-7") 'digit-argument) + (define-key map (kbd "C-8") 'digit-argument) + (define-key map (kbd "C-9") 'digit-argument) + ;; Single-byte printing chars extend the search string by default. (setq i ?\s) (while (< i 256) @@ -434,7 +464,20 @@ This is like `describe-bindings', but displays only Isearch keys." ;; default local key binding for any key not otherwise bound. (let ((meta-map (make-sparse-keymap))) (define-key map (char-to-string meta-prefix-char) meta-map) - (define-key map [escape] meta-map)) + (define-key map [escape] meta-map) + (define-key meta-map (kbd "-") 'negative-argument) + (define-key meta-map (kbd "C--") 'negative-argument) + (define-key meta-map (kbd "C-0") 'digit-argument) + (define-key meta-map (kbd "C-1") 'digit-argument) + (define-key meta-map (kbd "C-2") 'digit-argument) + (define-key meta-map (kbd "C-3") 'digit-argument) + (define-key meta-map (kbd "C-4") 'digit-argument) + (define-key meta-map (kbd "C-5") 'digit-argument) + (define-key meta-map (kbd "C-6") 'digit-argument) + (define-key meta-map (kbd "C-7") 'digit-argument) + (define-key meta-map (kbd "C-8") 'digit-argument) + (define-key meta-map (kbd "C-9") 'digit-argument)) + (define-key map (vector meta-prefix-char t) 'isearch-other-meta-char) ;; Several non-printing chars change the searching behavior. @@ -528,6 +571,7 @@ This is like `describe-bindings', but displays only Isearch keys." ;; These are all set with setq while isearching ;; and bound locally while editing the search string. +(defvar isearch-hint-count 0) (defvar isearch-forward nil) ; Searching in the forward direction. (defvar isearch-regexp nil) ; Searching for a regexp. (defvar isearch-word nil) ; Searching for words. @@ -1340,15 +1384,30 @@ Use `isearch-exit' to quit without signaling." (isearch-push-state) (isearch-update)) -(defun isearch-repeat-forward () +(defun isearch-repeat-forward (arg) "Repeat incremental search forwards." - (interactive) - (isearch-repeat 'forward)) + (interactive "p") + (while (> arg 0) + (isearch-repeat 'forward) + (setq arg (1- arg)) + (setq isearch-hint-count (1+ isearch-hint-count))) + (while (< arg 0) + (isearch-repeat 'backward) + (setq arg (1+ arg)) + (setq isearch-hint-count (1+ isearch-hint-count))) + (isearch-update)) -(defun isearch-repeat-backward () +(defun isearch-repeat-backward (arg) "Repeat incremental search backwards." - (interactive) - (isearch-repeat 'backward)) + (interactive "p") + (while (> arg 0) + (isearch-repeat 'backward) + (setq arg (1- arg)) + (setq isearch-hint-count (1+ isearch-hint-count))) + (while (< arg 0) + (isearch-repeat 'forward) + (setq arg (1+ arg))) + (setq isearch-hint-count (1+ isearch-hint-count))) (defun isearch-toggle-regexp () "Toggle regexp searching on or off." @@ -2627,6 +2686,7 @@ since they have special meaning in a regexp." (defvar isearch-lazy-highlight-word nil) (defvar isearch-lazy-highlight-forward nil) (defvar isearch-lazy-highlight-error nil) +(defvar isearch-lazy-highlight-hint-count nil) (defun lazy-highlight-cleanup (&optional force) "Stop lazy highlighting and remove extra highlighting from current buffer. @@ -2671,6 +2731,9 @@ by other Emacs features." isearch-lazy-highlight-window-end)) (not (eq isearch-forward isearch-lazy-highlight-forward)) + (not (eq isearch-lazy-highlight-hint-count + isearch-hint-count)) + ;; In case we are recovering from an error. (not (equal isearch-error isearch-lazy-highlight-error)))) @@ -2693,6 +2756,7 @@ by other Emacs features." isearch-lazy-highlight-regexp isearch-regexp isearch-lazy-highlight-space-regexp search-whitespace-regexp isearch-lazy-highlight-word isearch-word + isearch-lazy-highlight-hint-count isearch-hint-count isearch-lazy-highlight-forward isearch-forward) (unless (equal isearch-string "") (setq isearch-lazy-highlight-timer @@ -2739,7 +2803,8 @@ Attempt to do the search exactly the way the pending Isearch would." "Update highlighting of other matches for current search." (let ((max lazy-highlight-max-at-a-time) (looping t) - nomore) + nomore + (count 1)) (with-local-quit (save-selected-window (if (and (window-live-p isearch-lazy-highlight-window) @@ -2773,8 +2838,14 @@ Attempt to do the search exactly the way the pending Isearch would." (forward-char -1))) ;; non-zero-length match - (let ((ov (make-overlay mb me))) + (let ((ov (make-overlay mb me)) hint) (push ov isearch-lazy-highlight-overlays) + (if (not isearch-lazy-highlight-wrapped) + (progn + (setq hint (number-to-string count)) + (setq count (1+ count)) + (set-text-properties 0 (length hint) '(face lazy-highlight-hint) hint) + (overlay-put ov 'before-string hint))) ;; 1000 is higher than ediff's 100+, ;; but lower than isearch main overlay's 1001 (overlay-put ov 'priority 1000)