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

From: Juri Linkov <[email-masked]>
Date: Sun, 20 May 2012 03:15:33 +0300

> Regarding the addition of symbol search functions, I hesitate to
> define 7 more functions for every search type.  If now add
> symbol-search-forward, symbol-search-backward,
> symbol-search-forward-lax, symbol-search-backward-lax,
> and two functions to activate the symbol search
> isearch-forward-symbol, isearch-backward-symbol,
> then later someone might ask to add a subword search type
> with subword-search-forward, subword-search-backward,
> subword-search-forward-lax, subword-search-backward-lax,
> isearch-forward-subword, isearch-backward-subword,
> and so on.  This will grow isearch.el unnecessarily.
>
> When trying to achieve simplicity, all what is needed to define
> for every regexp-based search type is just:
>
> 1. Define a string-to-regexp conversion function
>    (like `word-search-regexp').
>
> 2. Define a key sequence to activate this search type.
>
> This is a minimal set of requirements to define a new regexp-based
> search type.

There are more limitations that affect the design of this feature:
it would be desirable to reuse the existing search type variable
`isearch-word' because it's used in too many places like:
recording the search status in the `isearch-cmds' stack, lazy-highlighting,
external custom search functions.  And also the aim is to to reuse
4 existing functions word-search-{for,back}ward{-lax,} because they are
used in `isearch-search-fun' and some other packages that override the
default search function.

So the minimal set of changes would be like in the following patch.
It also allows adding more regexp-based search types with less changes
due to `funcall'.

=== modified file 'lisp/isearch.el'
--- lisp/isearch.el	2012-05-15 21:30:58 +0000
+++ lisp/isearch.el	2012-05-20 00:14:00 +0000
@@ -512,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)
@@ -626,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.
 
@@ -732,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) 'symbol-search-regexp))
+
 (defun isearch-backward (&optional regexp-p no-recursive-edit)
   "\
 Do incremental search backward.
@@ -1379,6 +1392,13 @@ (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 'symbol-search-regexp) 'symbol-search-regexp))
+  (setq isearch-success t isearch-adjusted t)
+  (isearch-update))
+
 (defun isearch-toggle-case-fold ()
   "Toggle case folding in searching on or off."
   (interactive)
@@ -1425,7 +1445,11 @@ (defun word-search-backward (string &opt
 of words in STRING to a regexp used to search words without regard
 to punctuation."
   (interactive "sWord search backward: ")
-  (re-search-backward (word-search-regexp string nil) bound noerror count))
+  (re-search-backward
+   (if (functionp isearch-word)
+       (funcall isearch-word string nil)
+     (word-search-regexp string nil))
+   bound noerror count))
 
 (defun word-search-forward (string &optional bound noerror count)
   "Search forward from point for STRING, ignoring differences in punctuation.
@@ -1440,7 +1464,11 @@ (defun word-search-forward (string &opti
 of words in STRING to a regexp used to search words without regard
 to punctuation."
   (interactive "sWord search: ")
-  (re-search-forward (word-search-regexp string nil) bound noerror count))
+  (re-search-forward
+   (if (functionp isearch-word)
+       (funcall isearch-word string nil)
+     (word-search-regexp string nil))
+   bound noerror count))
 
 (defun word-search-backward-lax (string &optional bound noerror count)
   "Search backward from point for STRING, ignoring differences in punctuation.
@@ -1459,7 +1487,11 @@ (defun word-search-backward-lax (string
 of words in STRING to a regexp used to search words without regard
 to punctuation."
   (interactive "sWord search backward: ")
-  (re-search-backward (word-search-regexp string t) bound noerror count))
+  (re-search-backward
+   (if (functionp isearch-word)
+       (funcall isearch-word string t)
+     (word-search-regexp string t))
+   bound noerror count))
 
 (defun word-search-forward-lax (string &optional bound noerror count)
   "Search forward from point for STRING, ignoring differences in punctuation.
@@ -1478,7 +1510,23 @@ (defun word-search-forward-lax (string &
 of words in STRING to a regexp used to search words without regard
 to punctuation."
   (interactive "sWord search: ")
-  (re-search-forward (word-search-regexp string t) bound noerror count))
+  (re-search-forward
+   (if (functionp isearch-word)
+       (funcall isearch-word string t)
+     (word-search-regexp string t))
+   bound noerror count))
+
+;; Symbol search
+
+(defun symbol-search-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 unless it ends in whitespace."
+  (concat
+   "\\_<"
+   (regexp-quote string)
+   (if (or (not lax) (string-match-p "\\W$" string)) "\\_>")))
 
 
 (defun isearch-query-replace (&optional delimited regexp-flag)
@@ -1546,6 +1594,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)))))
@@ -1763,6 +1813,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)))))
@@ -2348,7 +2400,9 @@ (defun isearch-message-prefix (&optional
 			      (< (point) isearch-opoint)))
 		       "over")
 		   (if isearch-wrapped "wrapped ")
-		   (if isearch-word "word " "")
+		   (if (eq isearch-word 'symbol-search-regexp)
+		       "symbol "
+		     (if isearch-word "word " ""))
 		   (if isearch-regexp "regexp " "")
 		   (if multi-isearch-next-buffer-current-function "multi " "")
 		   (or isearch-message-prefix-add "")