Subject: bug#11378: 24.1.50; Suggestion: Let M-i in isearch cycle `search-invisible'

From: Juri Linkov <[email-masked]>
Date: Tue, 29 May 2012 19:40:19 +0300

> It would be more useful to allow multiple filters by transforming
> `isearch-filter-predicate' from the variable defining a predicate to the
> hook-like list defining a set of predicates all of which should satisfy
> for the search hit.

This is implemented by the following patch that also simplifies
the filter usage in other packages and adds two useful filters for
comments/strings:

=== modified file 'lisp/isearch.el'
--- lisp/isearch.el	2012-05-29 09:46:06 +0000
+++ lisp/isearch.el	2012-05-29 16:35:09 +0000
@@ -179,12 +179,13 @@
   "Function to save a function restoring the mode-specific Isearch state
 to the search status stack.")
 
-(defvar isearch-filter-predicate 'isearch-filter-visible
-  "Predicate that filters the search hits that would normally be available.
-Search hits that dissatisfy the predicate are skipped.  The function
-has two arguments: the positions of start and end of text matched by
-the search.  If this function returns nil, continue searching without
-stopping at this match.")
+(defvar isearch-filter-predicate '(isearch-filter-visible)
+  "Predicates that filter the search hits that would normally be available.
+Search hits that dissatisfy the list of predicates are skipped.
+Each function in this list has two arguments: the positions of
+start and end of text matched by the search.
+If `run-hook-with-args-until-failure' returns nil for all predicates,
+continue searching without stopping at this match.")
 
 ;; Search ring.
 
@@ -506,6 +516,9 @@ (defvar isearch-mode-map
     (define-key map "\M-sw" 'isearch-toggle-word)
     (define-key map "\M-s_" 'isearch-toggle-symbol)
 
+    (define-key map "\M-sfs" 'isearch-toggle-filter-strings)
+    (define-key map "\M-sfc" 'isearch-toggle-filter-comments)
+
     (define-key map [?\M-%] 'isearch-query-replace)
     (define-key map [?\C-\M-%] 'isearch-query-replace-regexp)
     (define-key map "\M-so" 'isearch-occur)
@@ -665,6 +681,10 @@ (defun isearch-forward (&optional regexp
 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-toggle-filter-strings] to toggle searching inside strings.
+Type \\[isearch-toggle-filter-comments] to toggle searching inside comments.
+
 Type \\[isearch-edit-string] to edit the search string in the minibuffer.
 
 Also supported is a search ring of the previous 16 search strings.
@@ -1036,6 +1056,9 @@ (defsubst isearch-case-fold-search-state
 (defsubst isearch-pop-fun-state (frame)
   "Return the function restoring the mode-specific Isearch state in FRAME."
   (aref frame 11))
+(defsubst isearch-filter-predicate-state (frame)
+  "Return the filter predicates in FRAME."
+  (aref frame 12))
 
 (defun isearch-top-state ()
   (let ((cmd (car isearch-cmds)))
@@ -1048,7 +1071,8 @@ (defun isearch-top-state ()
 	  isearch-error (isearch-error-state cmd)
 	  isearch-wrapped (isearch-wrapped-state cmd)
 	  isearch-barrier (isearch-barrier-state cmd)
-	  isearch-case-fold-search (isearch-case-fold-search-state cmd))
+	  isearch-case-fold-search (isearch-case-fold-search-state cmd)
+	  isearch-filter-predicate (isearch-filter-predicate-state cmd))
     (if (functionp (isearch-pop-fun-state cmd))
 	(funcall (isearch-pop-fun-state cmd) cmd))
     (goto-char (isearch-point-state cmd))))
@@ -1065,7 +1089,8 @@ (defun isearch-push-state ()
 		      isearch-error isearch-wrapped isearch-barrier
 		      isearch-case-fold-search
 		      (if isearch-push-state-function
-			  (funcall isearch-push-state-function)))
+			  (funcall isearch-push-state-function))
+		      isearch-filter-predicate)
 	      isearch-cmds)))
 
 
@@ -1411,6 +1436,40 @@ (defun isearch-toggle-case-fold ()
   (sit-for 1)
   (isearch-update))
 
+(defun isearch-toggle-filter-strings ()
+  "Toggle searching inside strings on or off."
+  (interactive)
+  (setq isearch-filter-predicate
+	(if (memq 'isearch-filter-strings isearch-filter-predicate)
+	    (delq 'isearch-filter-strings isearch-filter-predicate)
+	  (cons 'isearch-filter-strings isearch-filter-predicate)))
+  (setq isearch-success t isearch-adjusted t)
+  (isearch-update))
+
+(defun isearch-filter-strings (beg end)
+  "Test whether the current search hit is inside strings.
+Return non-nil if the text from BEG to END is inside strings."
+  (nth 3 (parse-partial-sexp (point-min) (point))))
+
+(put 'isearch-filter-strings 'isearch-message-prefix "string ")
+
+(defun isearch-toggle-filter-comments ()
+  "Toggle searching inside comments on or off."
+  (interactive)
+  (setq isearch-filter-predicate
+	(if (memq 'isearch-filter-comments isearch-filter-predicate)
+	    (delq 'isearch-filter-comments isearch-filter-predicate)
+	  (cons 'isearch-filter-comments isearch-filter-predicate)))
+  (setq isearch-success t isearch-adjusted t)
+  (isearch-update))
+
+(defun isearch-filter-comments (beg end)
+  "Test whether the current search hit is inside comments.
+Return non-nil if the text from BEG to END is inside comments."
+  (nth 4 (parse-partial-sexp (point-min) (point))))
+
+(put 'isearch-filter-comments 'isearch-message-prefix "comment ")
+
 
 ;; Word search
 
@@ -2373,6 +2439,11 @@ (defun isearch-message-prefix (&optional
 			      (< (point) isearch-opoint)))
 		       "over")
 		   (if isearch-wrapped "wrapped ")
+		   (mapconcat (lambda (s)
+				(and (symbolp s)
+				     (get s 'isearch-message-prefix)))
+			      isearch-filter-predicate
+			      "")
 		   (if isearch-word
 		       (or (and (symbolp isearch-word)
 				(get isearch-word 'isearch-message-prefix))
@@ -2489,13 +2560,11 @@ (defun isearch-search ()
       (setq isearch-case-fold-search
 	    (isearch-no-upper-case-p isearch-string isearch-regexp)))
   (condition-case lossage
-      (let ((inhibit-point-motion-hooks
-	     (and (eq isearch-filter-predicate 'isearch-filter-visible)
-		  search-invisible))
+      (let ((inhibit-point-motion-hooks search-invisible)
 	    (inhibit-quit nil)
 	    (case-fold-search isearch-case-fold-search)
 	    (search-spaces-regexp search-whitespace-regexp)
@@ -2509,8 +2578,9 @@ (defun isearch-search ()
 	  (if (or (not isearch-success)
 		  (bobp) (eobp)
 		  (= (match-beginning 0) (match-end 0))
-		  (funcall isearch-filter-predicate
-			   (match-beginning 0) (match-end 0)))
+		  (run-hook-with-args-until-failure
+		   'isearch-filter-predicate
+		   (match-beginning 0) (match-end 0)))
 	      (setq retry nil)))
 	(setq isearch-just-started nil)
 	(if isearch-success
@@ -2895,8 +3005,9 @@ (defun isearch-lazy-highlight-search ()
 	  (if (or (not success)
 		  (= (point) bound) ; like (bobp) (eobp) in `isearch-search'.
 		  (= (match-beginning 0) (match-end 0))
-		  (funcall isearch-filter-predicate
-			   (match-beginning 0) (match-end 0)))
+		  (run-hook-with-args-until-failure
+		   'isearch-filter-predicate
+		   (match-beginning 0) (match-end 0)))
 	      (setq retry nil)))
 	success)
     (error nil)))

=== modified file 'lisp/dired-aux.el'
--- lisp/dired-aux.el	2012-04-17 01:52:00 +0000
+++ lisp/dired-aux.el	2012-05-29 16:35:30 +0000
@@ -2424,8 +2425,6 @@ (defcustom dired-isearch-filenames nil
   :group 'dired
   :version "23.1")
 
-(defvar dired-isearch-filter-predicate-orig nil)
-
 (defun dired-isearch-filenames-toggle ()
   "Toggle file names searching on or off.
 When on, Isearch skips matches outside file names using the predicate
@@ -2433,9 +2432,9 @@ (defun dired-isearch-filenames-toggle ()
 When off, it uses the original predicate."
   (interactive)
   (setq isearch-filter-predicate
-	(if (eq isearch-filter-predicate 'dired-isearch-filter-filenames)
-	    dired-isearch-filter-predicate-orig
-	  'dired-isearch-filter-filenames))
+	(if (memq 'dired-isearch-filter-filenames isearch-filter-predicate)
+	    (delq 'dired-isearch-filter-filenames isearch-filter-predicate)
+	  (cons 'dired-isearch-filter-filenames isearch-filter-predicate)))
   (setq isearch-success t isearch-adjusted t)
   (isearch-update))
 
@@ -2446,29 +2445,29 @@ (defun dired-isearch-filenames-setup ()
   (when (or (eq dired-isearch-filenames t)
 	    (and (eq dired-isearch-filenames 'dwim)
 		 (get-text-property (point) 'dired-filename)))
-    (setq isearch-message-prefix-add "filename ")
-    (define-key isearch-mode-map "\M-sf" 'dired-isearch-filenames-toggle)
-    (setq dired-isearch-filter-predicate-orig
-	  (default-value 'isearch-filter-predicate))
-    (setq-default isearch-filter-predicate 'dired-isearch-filter-filenames)
+    (define-key isearch-mode-map "\M-sff" 'dired-isearch-filenames-toggle)
+    (setq isearch-filter-predicate
+	  (cons 'dired-isearch-filter-filenames isearch-filter-predicate))
     (add-hook 'isearch-mode-end-hook 'dired-isearch-filenames-end nil t)))
 
 (defun dired-isearch-filenames-end ()
   "Clean up the Dired file name search after terminating isearch."
   (setq isearch-message-prefix-add nil)
-  (define-key isearch-mode-map "\M-sf" nil)
-  (setq-default isearch-filter-predicate dired-isearch-filter-predicate-orig)
+  (define-key isearch-mode-map "\M-sff" nil)
+  (setq isearch-filter-predicate
+	(delq 'dired-isearch-filter-filenames isearch-filter-predicate))
   (remove-hook 'isearch-mode-end-hook 'dired-isearch-filenames-end t))
 
 (defun dired-isearch-filter-filenames (beg end)
-  "Test whether the current search hit is a visible file name.
+  "Test whether the current search hit is a file name.
 Return non-nil if the text from BEG to END is part of a file
-name (has the text property `dired-filename') and is visible."
-  (and (isearch-filter-visible beg end)
-       (if dired-isearch-filenames
-	   (text-property-not-all (min beg end) (max beg end)
-				  'dired-filename nil)
-	 t)))
+name (has the text property `dired-filename')."
+  (if dired-isearch-filenames
+      (text-property-not-all (min beg end) (max beg end)
+			     'dired-filename nil)
+    t))
+
+(put 'dired-isearch-filter-filenames 'isearch-message-prefix "filename ")
 
 ;;;###autoload
 (defun dired-isearch-filenames ()

=== modified file 'lisp/info.el'
--- lisp/info.el	2012-05-29 09:09:38 +0000
+++ lisp/info.el	2012-05-29 16:39:00 +0000
@@ -1772,7 +1775,8 @@ (defun Info-search (regexp &optional bou
 			      (point-max)))
 	  (while (and (not give-up)
 		      (or (null found)
-			  (not (funcall isearch-filter-predicate beg-found found))))
+			  (not (run-hook-with-args-until-failure
+				'isearch-filter-predicate beg-found found))))
 	    (let ((search-spaces-regexp
 		   (if (or (not isearch-mode) isearch-regexp)
 		       Info-search-whitespace-regexp)))
@@ -1854,7 +1858,8 @@ (defun Info-search (regexp &optional bou
 		(setq give-up nil found nil)
 		(while (and (not give-up)
 			    (or (null found)
-				(not (funcall isearch-filter-predicate beg-found found))))
+				(not (run-hook-with-args-until-failure
+				      'isearch-filter-predicate beg-found found))))
 		  (let ((search-spaces-regexp
 			 (if (or (not isearch-mode) isearch-regexp)
 			     Info-search-whitespace-regexp)))
@@ -4049,7 +4054,7 @@ (define-derived-mode Info-mode nil "Info
   (set (make-local-variable 'isearch-push-state-function)
        'Info-isearch-push-state)
   (set (make-local-variable 'isearch-filter-predicate)
-       'Info-isearch-filter)
+       '(Info-isearch-filter))
   (set (make-local-variable 'search-whitespace-regexp)
        Info-search-whitespace-regexp)
   (set (make-local-variable 'revert-buffer-function)

=== modified file 'lisp/wdired.el'
--- lisp/wdired.el	2012-01-19 07:21:25 +0000
+++ lisp/wdired.el	2012-05-29 16:35:52 +0000
@@ -216,8 +216,7 @@ (defun wdired-change-to-wdired-mode ()
        (buffer-substring (point-min) (point-max)))
   (set (make-local-variable 'wdired-old-point) (point))
   (set (make-local-variable 'query-replace-skip-read-only) t)
-  (set (make-local-variable 'isearch-filter-predicate)
-       'wdired-isearch-filter-read-only)
+  (add-hook 'isearch-filter-predicate 'wdired-isearch-filter-read-only nil t)
   (use-local-map wdired-mode-map)
   (force-mode-line-update)
   (setq buffer-read-only nil)
@@ -245,9 +244,8 @@ (defun wdired-change-to-wdired-mode ()
 
 (defun wdired-isearch-filter-read-only (beg end)
   "Skip matches that have a read-only property."
-  (and (isearch-filter-visible beg end)
-       (not (text-property-not-all (min beg end) (max beg end)
-				   'read-only nil))))
+  (not (text-property-not-all (min beg end) (max beg end)
+			      'read-only nil)))
 
 ;; Protect the buffer so only the filenames can be changed, and put
 ;; properties so filenames (old and new) can be easily found.