Subject: Re: bug#14563: Add prefix arg to more isearch commands

From: Juri Linkov <[email-masked]>
Date: Sat, 15 Jun 2013 01:30:15 +0300

The remaining command that could benefit from a prefix arg is
`isearch-repeat-forward'.  There are 4 places on the call stack
that can process the count argument: `isearch-repeat-forward',
`isearch-repeat', `isearch-search', `isearch-search-string'.

`isearch-repeat-forward' is too high-level, so repeating
the call to the more low-level `isearch-repeat' COUNT times
has such bad effects as pushing to the search stack all
intermediate positions (because `isearch-repeat' calls
`isearch-push-state' on every invocation), so e.g.
`C-s str C-u 42 C-s DEL DEL DEL ...' requires 42 DELs
to return to the first match instead of just 1 `DEL',
And it also flickers because `isearch-update' quickly
highlights all visited matches.

OTOH, `isearch-search-string' is too low-level because passing
the COUNT argument to search functions like `search-forward'
doesn't skip invisible matches and other additional processing
in `isearch-search'.

So the most appropriate place to implement a prefix arg
is `isearch-repeat':

=== modified file 'lisp/isearch.el'
--- lisp/isearch.el	2013-06-13 22:08:45 +0000
+++ lisp/isearch.el	2013-06-14 22:28:27 +0000
@@ -1393,8 +1462,10 @@ (defun isearch-abort ()
       (isearch-pop-state))
     (isearch-update)))
 
-(defun isearch-repeat (direction)
+(defun isearch-repeat (direction &optional arg)
   ;; Utility for isearch-repeat-forward and -backward.
+  (unless arg (setq arg 1))
+
   (if (eq isearch-forward (eq direction 'forward))
       ;; C-s in forward or C-r in reverse.
       (if (equal isearch-string "")
@@ -1410,21 +1481,25 @@ (defun isearch-repeat (direction)
 	  isearch-success t))
 
   (setq isearch-barrier (point)) ; For subsequent \| if regexp.
+  (if (< arg 0) (setq arg (abs arg) isearch-forward (not isearch-forward)))
 
   (if (equal isearch-string "")
       (setq isearch-success t)
+    (let ((count arg)
+	  ;; Don't remember intermediate states in global `isearch-cmds'.
+	  (isearch-cmds isearch-cmds))
+      (while (> count 0)
     (if (and isearch-success
 	     (equal (point) isearch-other-end)
 	     (not isearch-just-started))
@@ -1437,20 +1512,33 @@ (defun isearch-repeat (direction)
 	      (ding))
 	  (forward-char (if isearch-forward 1 -1))
 	  (isearch-search))
-      (isearch-search)))
+	  (isearch-search))
+	(when (> count 1)
+	  ;; For the next iteration, add the current state to `isearch-cmds',
+	  ;; so that next failed `isearch-search' could restore old position.
+	  (isearch-push-state)
+	  (unless isearch-success
+	    ;; Wrap the search with code like above.
+	    (setq isearch-wrapped t)
+	    (if isearch-wrap-function
+		(funcall isearch-wrap-function)
+	      (goto-char (if isearch-forward (point-min) (point-max))))))
+	(setq count (1- count)))))
 
   (isearch-push-state)
   (isearch-update))
 
-(defun isearch-repeat-forward ()
-  "Repeat incremental search forwards."
-  (interactive)
-  (isearch-repeat 'forward))
+(defun isearch-repeat-forward (&optional arg)
+  "Repeat incremental search forwards.
+With argument, repeat search ARG times."
+  (interactive "p")
+  (isearch-repeat 'forward arg))
 
-(defun isearch-repeat-backward ()
-  "Repeat incremental search backwards."
-  (interactive)
-  (isearch-repeat 'backward))
+(defun isearch-repeat-backward (&optional arg)
+  "Repeat incremental search backwards.
+With argument, repeat search ARG times."
+  (interactive "p")
+  (isearch-repeat 'backward arg))
 
 (defun isearch-toggle-regexp ()
   "Toggle regexp searching on or off."