Subject: bug#10614: [EXPERIMENTAL PATCH] Extending Isearch-repeat-forward/backward to support a prefix argument following suggesion by Juri Linkov

From: Gideon Stupp <[email-masked]>
Date: Thu, 26 Jan 2012 19:27:06 +0200

Package: emacs
Tags: patch
Severity: wishlist

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 possible with this patch to
enter a prefix argument
which runs the command multiple times for you. If a negative argument
is given for
isearch-repeat-forward then 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.

Please try it out.

Thanks, Gideon.
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)