Subject: bug#11381: 23.3; isearch-search-and-update issue?

From: Juri Linkov <[email-masked]>
Date: Mon, 28 May 2012 11:55:20 +0300

> Do we really need those 4?  I think we can just get away with
> symbol-search-regexp (whose name also needs to start with "isearch-").

In the next patch these functions are removed and symbol-search-regexp
is renamed to isearch-symbol-regexp.

>> Also it splits the standard default part of `isearch-search-fun'
>
> You could actually set isearch-search-fun-function's default to
> isearch-search-fun-default so we can just unconditionally call
> isearch-search-fun-function's.

It still needs protection against such cases as currently existing in
several places in internal and probably also in external packages:

    (let ((isearch-search-fun-function nil))
      (isearch-search-fun))

So it requires a call like:

    (funcall (or isearch-search-fun-function 'isearch-search-fun-default))

> and then
>
>         (lambda (string &optional bound noerror count)
>           (funcall
>            (if isearch-forward #'re-search-forward #'re-search-backward)
>            (if (functionp isearch-word)
>                (funcall isearch-word string nil)
>              (word-search-regexp string nil))
>            bound noerror count))

This is used in a complete patch that implements the symbol search
and fixes the word search (everything is in one patch below
but it will be installed in separate commits):

=== modified file 'lisp/isearch.el'
--- lisp/isearch.el	2012-05-17 00:03:49 +0000
+++ lisp/isearch.el	2012-05-28 08:38:31 +0000
@@ -503,6 +512,7 @@ (defvar isearch-mode-map
 
     (define-key map "\M-sr" 'isearch-toggle-regexp)
     (define-key map "\M-sw" 'isearch-toggle-word)
+    (define-key map "\M-s_" 'isearch-toggle-symbol)
 
     (define-key map [?\M-%] 'isearch-query-replace)
     (define-key map [?\C-\M-%] 'isearch-query-replace-regexp)
@@ -614,6 +627,7 @@ (define-key esc-map "\C-s" 'isearch-forw
 (define-key global-map "\C-r" 'isearch-backward)
 (define-key esc-map "\C-r" 'isearch-backward-regexp)
 (define-key search-map "w" 'isearch-forward-word)
+(define-key search-map "_" 'isearch-forward-symbol)
 
 ;; Entry points to isearch-mode.
 
@@ -653,6 +667,7 @@ (defun isearch-forward (&optional regexp
 Type \\[isearch-toggle-case-fold] to toggle search case-sensitivity.
 Type \\[isearch-toggle-regexp] to toggle regular-expression mode.
 Type \\[isearch-toggle-word] to toggle word mode.
+Type \\[isearch-toggle-symbol] to toggle symbol mode.
 Type \\[isearch-edit-string] to edit the search string in the minibuffer.
 
 Also supported is a search ring of the previous 16 search strings.
@@ -720,6 +735,16 @@ (defun isearch-forward-word (&optional n
   (interactive "P\np")
   (isearch-mode t nil nil (not no-recursive-edit) (null not-word)))
 
+(defun isearch-forward-symbol (&optional not-symbol no-recursive-edit)
+  "\
+Do incremental search forward for a symbol.
+The prefix argument is currently unused.
+Like ordinary incremental search except that your input is treated
+as a symbol surrounded by symbol boundary constructs \\_< and \\_>.
+See the command `isearch-forward' for more information."
+  (interactive "P\np")
+  (isearch-mode t nil nil (not no-recursive-edit) 'isearch-symbol-regexp))
+
 (defun isearch-backward (&optional regexp-p no-recursive-edit)
   "\
 Do incremental search backward.
@@ -747,14 +772,14 @@ (defun isearch-backward-regexp (&optiona
 ;;  "List of commands for which isearch-mode does not recursive-edit.")

-(defun isearch-mode (forward &optional regexp op-fun recursive-edit word-p)
+(defun isearch-mode (forward &optional regexp op-fun recursive-edit word)
   "Start Isearch minor mode.
 It is called by the function `isearch-forward' and other related functions."
 
   ;; Initialize global vars.
   (setq isearch-forward forward
 	isearch-regexp regexp
-	isearch-word word-p
+	isearch-word word
 	isearch-op-fun op-fun
 	isearch-last-case-fold-search isearch-case-fold-search
 	isearch-case-fold-search case-fold-search
@@ -1367,6 +1392,14 @@ (defun isearch-toggle-word ()
   (setq isearch-success t isearch-adjusted t)
   (isearch-update))
 
+(defun isearch-toggle-symbol ()
+  "Toggle symbol searching on or off."
+  (interactive)
+  (setq isearch-word (unless (eq isearch-word 'isearch-symbol-regexp)
+		       'isearch-symbol-regexp))
+  (setq isearch-success t isearch-adjusted t)
+  (isearch-update))
+
 (defun isearch-toggle-case-fold ()
   "Toggle case folding in searching on or off."
   (interactive)
@@ -1468,6 +1501,20 @@ (defun word-search-forward-lax (string &
   (interactive "sWord search: ")
   (re-search-forward (word-search-regexp string t) bound noerror count))
 
+;; Symbol search
+
+(defun isearch-symbol-regexp (string &optional lax)
+  "Return a regexp which matches STRING as a symbol.
+Creates a regexp where STRING is surrounded by symbol delimiters \\_< and \\_>.
+If LAX is non-nil, the end of the string need not match a symbol boundary."
+  (concat "\\_<" (regexp-quote string) (unless lax "\\_>")))
+
+(put 'isearch-symbol-regexp 'isearch-message-prefix "symbol ")
+
 
 (defun isearch-query-replace (&optional delimited regexp-flag)
   "Start `query-replace' with string to replace from last search string.
@@ -1534,6 +1581,8 @@ (defun isearch-occur (regexp &optional n
   (interactive
    (let* ((perform-collect (consp current-prefix-arg))
 	  (regexp (cond
+		   ((functionp isearch-word)
+		    (funcall isearch-word isearch-string))
 		   (isearch-word (word-search-regexp isearch-string))
 		   (isearch-regexp isearch-string)
 		   (t (regexp-quote isearch-string)))))
@@ -1749,6 +1800,8 @@ (defun isearch-search-and-update ()
 		       (setq case-fold-search
 			     (isearch-no-upper-case-p isearch-string isearch-regexp)))
 		   (looking-at (cond
+				((functionp isearch-word)
+				 (funcall isearch-word isearch-string t))
 				(isearch-word (word-search-regexp isearch-string t))
 				(isearch-regexp isearch-string)
 				(t (regexp-quote isearch-string)))))
@@ -2329,7 +2387,11 @@ (defun isearch-message-prefix (&optional
 			      (< (point) isearch-opoint)))
 		       "over")
 		   (if isearch-wrapped "wrapped ")
-		   (if isearch-word "word " "")
+		   (if isearch-word
+		       (or (and (symbolp isearch-word)
+				(get isearch-word 'isearch-message-prefix))
+			   "word ")
+		     "")
 		   (if isearch-regexp "regexp " "")
 		   (if multi-isearch-next-buffer-current-function "multi " "")
 		   (or isearch-message-prefix-add "")
@@ -2356,8 +2418,8 @@ (defun isearch-message-suffix (&optional
 
 ;; Searching
 
-(defvar isearch-search-fun-function nil
-  "Overrides the default `isearch-search-fun' behavior.
+(defvar isearch-search-fun-function 'isearch-search-fun-default
+  "Non-default value overrides the behavior of `isearch-search-fun-default'.
 This variable's value should be a function, which will be called
 with no arguments, and should return a function that takes three
 arguments: STRING, BOUND, and NOERROR.
@@ -2368,22 +2430,29 @@ (defvar isearch-search-fun-function nil
 (defun isearch-search-fun ()
   "Return the function to use for the search.
 Can be changed via `isearch-search-fun-function' for special needs."
-  (if isearch-search-fun-function
-      (funcall isearch-search-fun-function)
+  (funcall (or isearch-search-fun-function 'isearch-search-fun-default)))
+
+(defun isearch-search-fun-default ()
+  "Return default functions to use for the search."
     (cond
      (isearch-word
+    (lambda (string &optional bound noerror count)
       ;; Use lax versions to not fail at the end of the word while
       ;; the user adds and removes characters in the search string
       ;; (or when using nonincremental word isearch)
-      (if (or isearch-nonincremental
+      (let ((lax (not (or isearch-nonincremental
 	      (eq (length isearch-string)
-		  (length (isearch-string-state (car isearch-cmds)))))
-	  (if isearch-forward 'word-search-forward 'word-search-backward)
-	(if isearch-forward 'word-search-forward-lax 'word-search-backward-lax)))
+			      (length (isearch-string-state (car isearch-cmds))))))))
+	(funcall
+	 (if isearch-forward #'re-search-forward #'re-search-backward)
+	 (if (functionp isearch-word)
+	     (funcall isearch-word string lax)
+	   (word-search-regexp string lax))
+	 bound noerror count))))
      (isearch-regexp
       (if isearch-forward 're-search-forward 're-search-backward))
      (t
-      (if isearch-forward 'search-forward 'search-backward)))))
+    (if isearch-forward 'search-forward 'search-backward))))
 
 (defun isearch-search-string (string bound noerror)
   "Search for the first occurrence of STRING or its translation.

=== modified file 'lisp/comint.el'
--- lisp/comint.el	2012-05-15 16:58:35 +0000
+++ lisp/comint.el	2012-05-28 08:50:41 +0000
@@ -1463,18 +1463,10 @@ (defun comint-goto-input (pos)
 
 (defun comint-history-isearch-search ()
   "Return the proper search function, for Isearch in input history."
-  (cond
-   (isearch-word
-    (if isearch-forward 'word-search-forward 'word-search-backward))
-   (t
     (lambda (string bound noerror)
       (let ((search-fun
 	     ;; Use standard functions to search within comint text
-             (cond
-              (isearch-regexp
-               (if isearch-forward 're-search-forward 're-search-backward))
-              (t
-               (if isearch-forward 'search-forward 'search-backward))))
+	   (isearch-search-fun-default))
 	    found)
 	;; Avoid lazy-highlighting matches in the comint prompt and in the
 	;; output when searching forward.  Lazy-highlight calls this lambda
@@ -1523,7 +1515,7 @@ (defun comint-history-isearch-search ()
 		 ;; Return point of the new search result
 		 (point))
 	     ;; Return nil on the error "no next/preceding item"
-	     (error nil)))))))))
+	   (error nil)))))))
 
 (defun comint-history-isearch-message (&optional c-q-hack ellipsis)
   "Display the input history search prompt.
@@ -1556,14 +1548,13 @@ (defun comint-history-isearch-wrap ()
   "Wrap the input history search when search fails.
 Move point to the first history element for a forward search,
 or to the last history element for a backward search."
-  (unless isearch-word
     ;; When `comint-history-isearch-search' fails on reaching the
     ;; beginning/end of the history, wrap the search to the first/last
     ;; input history element.
     (if isearch-forward
 	(comint-goto-input (1- (ring-length comint-input-ring)))
       (comint-goto-input nil))
-    (setq isearch-success t))
+  (setq isearch-success t)
   (goto-char (if isearch-forward (comint-line-beginning-position) (point-max))))
 
 (defun comint-history-isearch-push-state ()

=== modified file 'lisp/info.el'
--- lisp/info.el	2012-05-22 03:31:34 +0000
+++ lisp/info.el	2012-05-28 08:27:29 +0000
@@ -1913,26 +1916,23 @@ (defun Info-search-backward (regexp &opt
 (defun Info-isearch-search ()
   (if Info-isearch-search
       (lambda (string &optional bound noerror count)
-	(if isearch-word
-	    (Info-search (concat "\\b" (replace-regexp-in-string
-					"\\W+" "\\W+"
-					(replace-regexp-in-string
-					 "^\\W+\\|\\W+$" "" string)
-					nil t)
+	(Info-search
+	 (cond
+	  (isearch-word
 				 ;; Lax version of word search
-				 (if (or isearch-nonincremental
+	   (let ((lax (not (or isearch-nonincremental
 					 (eq (length string)
 					     (length (isearch-string-state
-						      (car isearch-cmds)))))
-				     "\\b"))
+					    (car isearch-cmds))))))))
+	     (if (functionp isearch-word)
+		 (funcall isearch-word string lax)
+	       (word-search-regexp string lax))))
+	  (isearch-regexp string)
+	  (t (regexp-quote string)))
 			 bound noerror count
 			 (unless isearch-forward 'backward))
-	  (Info-search (if isearch-regexp string (regexp-quote string))
-		       bound noerror count
-		       (unless isearch-forward 'backward)))
 	(point))
-    (let ((isearch-search-fun-function nil))
-      (isearch-search-fun))))
+    (isearch-search-fun-default)))
 
 (defun Info-isearch-wrap ()
   (if Info-isearch-search

=== modified file 'lisp/misearch.el'
--- lisp/misearch.el	2012-01-19 07:21:25 +0000
+++ lisp/misearch.el	2012-05-28 08:43:16 +0000
@@ -130,13 +130,7 @@ (defun multi-isearch-search-fun ()
   (lambda (string bound noerror)
     (let ((search-fun
 	   ;; Use standard functions to search within one buffer
-	   (cond
-	    (isearch-word
-	     (if isearch-forward 'word-search-forward 'word-search-backward))
-	    (isearch-regexp
-	     (if isearch-forward 're-search-forward 're-search-backward))
-	    (t
-	     (if isearch-forward 'search-forward 'search-backward))))
+	   (isearch-search-fun-default))
 	  found buffer)
       (or
        ;; 1. First try searching in the initial buffer

=== modified file 'lisp/simple.el'
--- lisp/simple.el	2012-05-04 23:16:47 +0000
+++ lisp/simple.el	2012-05-28 08:29:25 +0000
@@ -1699,18 +1771,10 @@ (defun minibuffer-history-isearch-end ()
 
 (defun minibuffer-history-isearch-search ()
   "Return the proper search function, for isearch in minibuffer history."
-  (cond
-   (isearch-word
-    (if isearch-forward 'word-search-forward 'word-search-backward))
-   (t
     (lambda (string bound noerror)
       (let ((search-fun
 	     ;; Use standard functions to search within minibuffer text
-             (cond
-              (isearch-regexp
-               (if isearch-forward 're-search-forward 're-search-backward))
-              (t
-               (if isearch-forward 'search-forward 'search-backward))))
+	   (isearch-search-fun-default))
 	    found)
 	;; Avoid lazy-highlighting matches in the minibuffer prompt when
 	;; searching forward.  Lazy-highlight calls this lambda with the
@@ -1750,7 +1814,7 @@ (defun minibuffer-history-isearch-search
 		 ;; Return point of the new search result
 		 (point))
 	     ;; Return nil when next(prev)-history-element fails
-	     (error nil)))))))))
+	   (error nil)))))))
 
 (defun minibuffer-history-isearch-message (&optional c-q-hack ellipsis)
   "Display the minibuffer history search prompt.
@@ -1781,14 +1845,13 @@ (defun minibuffer-history-isearch-wrap (
   "Wrap the minibuffer history search when search fails.
 Move point to the first history element for a forward search,
 or to the last history element for a backward search."
-  (unless isearch-word
     ;; When `minibuffer-history-isearch-search' fails on reaching the
     ;; beginning/end of the history, wrap the search to the first/last
     ;; minibuffer history element.
     (if isearch-forward
 	(goto-history-element (length (symbol-value minibuffer-history-variable)))
       (goto-history-element 0))
-    (setq isearch-success t))
+  (setq isearch-success t)
   (goto-char (if isearch-forward (minibuffer-prompt-end) (point-max))))
 
 (defun minibuffer-history-isearch-push-state ()

=== modified file 'lisp/textmodes/reftex-global.el'
--- lisp/textmodes/reftex-global.el	2012-01-19 07:21:25 +0000
+++ lisp/textmodes/reftex-global.el	2012-05-28 08:49:52 +0000
@@ -350,9 +350,8 @@ (defun reftex-ensure-write-access (files
 ;; variable `multi-isearch-next-buffer-function'.
 
 (defun reftex-isearch-wrap-function ()
-  (if (not isearch-word)
       (switch-to-buffer
-       (funcall isearch-next-buffer-function (current-buffer) t)))
+   (funcall isearch-next-buffer-function (current-buffer) t))
   (goto-char (if isearch-forward (point-min) (point-max))))
 
 (defun reftex-isearch-push-state-function ()
@@ -364,14 +363,7 @@ (defun reftex-isearch-pop-state-function
 
 (defun reftex-isearch-isearch-search (string bound noerror)
   (let ((nxt-buff nil)
-	(search-fun
-	 (cond
-	  (isearch-word
-	   (if isearch-forward 'word-search-forward 'word-search-backward))
-	  (isearch-regexp
-	   (if isearch-forward 're-search-forward 're-search-backward))
-	  (t
-	   (if isearch-forward 'search-forward 'search-backward)))))
+	(search-fun (isearch-search-fun-default)))
     (or
      (funcall search-fun string bound noerror)
      (unless bound