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

From: Stefan Kangas <[email-masked]>
Date: Fri, 01 Nov 2019 19:54:20 +0100

Juri Linkov <[email-masked]> writes:

>>>> I was thinking that maybe a cycling behavior would be better than
>>>> toggling for these: off / foo-only / foo-excluded.
>>>
>>> Maybe something like this:
>>
>> Sounds about right, thank you,
>
> Now that text proprieties on the search string can be saved in the desktop file,
> I tried to save the isearch-filter-predicate with advice-functions, and
> set it back to the search string, but this fails.
>
> An example of the search string saved in the search ring in the desktop file:
>
> #("string" 0 6 (isearch-filter-predicate #1=#[128 #2="\300\301\2\"\205\13\0\300\302\2\"\207"
>               [apply isearch-filter-textual #[128 #3="\301\302\300!\2\"\207"
>               [isearch-filter-predicate apply default-value] 4 #4="\n\n(fn &rest ARGS)"]
>               #5=((isearch-message-prefix . "textual "))] 4 nil]))
>
> After it's restored by desktop.el using `load' and set from the restored string
> with (setq isearch-filter-predicate ...) it goes into an infinite loop:
>
> Debugger entered--Lisp error: (error "Lisp nesting exceeds ‘max-lisp-eval-depth’")
>   ...
>   apply(#f(compiled-function (&rest args) #<bytecode 0x2958aa5>) (12237 12244))
>   #f(advice-wrapper :before-while #f(compiled-function (&rest args) #<bytecode 0x2958aa5>) isearch-filter-nontextual ((isearch-message-prefix . "nontextual ")))(12237 12244)
>   apply(#f(advice-wrapper :before-while #f(compiled-function
>   #f(compiled-function (&rest args) #<bytecode 0x2958aa5>)(12237 12244)
>   apply(#f(compiled-function (&rest args) #<bytecode 0x2958aa5>) (12237 12244))
>   #f(advice-wrapper :before-while #f(compiled-function (&rest args) #<bytecode 0x2958aa5>) isearch-filter-nontextual ((isearch-message-prefix . "nontextual ")))(12237 12244)
>   apply(#f(advice-wrapper :before-while #f(compiled-function
>   #f(compiled-function (&rest args) #<bytecode 0x2958aa5>)(12237 12244)
>   apply(#f(compiled-function (&rest args) #<bytecode 0x2958aa5>) (12237 12244))
>   #f(advice-wrapper :before-while #f(compiled-function (&rest args) #<bytecode 0x2958aa5>) isearch-filter-nontextual ((isearch-message-prefix . "nontextual ")))(12237 12244)
>   funcall(#f(advice-wrapper :before-while #f(compiled-function
>   (not (funcall isearch-filter-predicate (nth 0 real-match-data) (nth 1 real-match-data)))
>
> If it's impossible to restore its value, then maybe better to save only
> function names, and on restoring explicitly call add-function with restored
> function names as symbols?

Was the below patch ever applied?  Is it still relevant?

Best regards,
Stefan Kangas

> A complete patch is here:
>
> diff --git a/lisp/isearch.el b/lisp/isearch.el
> index 5cbb4c9..70e3e44 100644
> --- a/lisp/isearch.el
> +++ b/lisp/isearch.el
> @@ -1136,7 +1141,8 @@ isearch-update-ring
>  (defun isearch-string-propertize (string &optional properties)
>    "Add isearch properties to the isearch string."
>    (unless properties
> -    (setq properties `(isearch-case-fold-search ,isearch-case-fold-search))
> +    (setq properties `(isearch-case-fold-search ,isearch-case-fold-search
> +                       isearch-filter-predicate ,isearch-filter-predicate))
>      (unless isearch-regexp
>        (setq properties (append properties `(isearch-regexp-function ,isearch-regexp-function)))))
>    (apply 'propertize string properties))
> @@ -1146,6 +1152,9 @@ isearch-update-from-string-properties
>    (when (plist-member (text-properties-at 0 string) 'isearch-case-fold-search)
>      (setq isearch-case-fold-search
>  	  (get-text-property 0 'isearch-case-fold-search string)))
> +  (when (plist-member (text-properties-at 0 string) 'isearch-filter-predicate)
> +    (setq isearch-filter-predicate
> +	  (get-text-property 0 'isearch-filter-predicate string)))
>    (when (plist-member (text-properties-at 0 string) 'isearch-regexp-function)
>      (setq isearch-regexp-function
>  	  (get-text-property 0 'isearch-regexp-function string))))
> @@ -1640,6 +1648,38 @@ isearch--momentary-message
>         "match invisible text"
>       "match visible text")))
>  
> +(isearch-define-mode-toggle textual "ft" nil
> +  "This determines whether to search inside or outside textual parts.
> +Toggles the variable `isearch-filter-predicate' between three states:
> +searching inside strings or comments, searching outside of strings or
> +comments, the third state removes the filtering altogether."
> +  (cond
> +   ((advice-function-member-p #'isearch-filter-textual
> +                              isearch-filter-predicate)
> +    (remove-function (local 'isearch-filter-predicate)
> +                     #'isearch-filter-textual)
> +    (add-function :before-while (local 'isearch-filter-predicate)
> +                  #'isearch-filter-nontextual
> +                  '((isearch-message-prefix . "nontextual "))))
> +   ((advice-function-member-p #'isearch-filter-nontextual
> +                              isearch-filter-predicate)
> +    (remove-function (local 'isearch-filter-predicate)
> +                     #'isearch-filter-nontextual))
> +   (t
> +    (add-function :before-while (local 'isearch-filter-predicate)
> +                  #'isearch-filter-textual
> +                  '((isearch-message-prefix . "textual "))))))
> +
> +(defun isearch-filter-textual (_beg _end)
> +  "Test whether the current search hit inside a string or comment."
> +  (save-match-data
> +    (or (nth 3 (syntax-ppss))
> +        (nth 4 (syntax-ppss)))))
> +
> +(defun isearch-filter-nontextual (beg end)
> +  "Test whether the current search hit outside a string or comment."
> +  (not (isearch-filter-textual beg end)))
> +
>  
>  ;; Word search
>  
> @@ -3179,6 +3219,7 @@ isearch-lazy-highlight-window-group
>  (defvar isearch-lazy-highlight-window-start nil)
>  (defvar isearch-lazy-highlight-window-end nil)
>  (defvar isearch-lazy-highlight-case-fold-search nil)
> +(defvar isearch-lazy-highlight-filter-predicate nil)
>  (defvar isearch-lazy-highlight-regexp nil)
>  (defvar isearch-lazy-highlight-lax-whitespace nil)
>  (defvar isearch-lazy-highlight-regexp-lax-whitespace nil)
> @@ -3218,6 +3259,8 @@ isearch-lazy-highlight-new-loop
>                              isearch-lazy-highlight-window-group))
>  		 (not (eq isearch-lazy-highlight-case-fold-search
>  			  isearch-case-fold-search))
> +		 (not (equal isearch-lazy-highlight-filter-predicate
> +			     isearch-filter-predicate))
>  		 (not (eq isearch-lazy-highlight-regexp
>  			  isearch-regexp))
>  		 (not (eq isearch-lazy-highlight-regexp-function
> @@ -3259,6 +3302,7 @@ isearch-lazy-highlight-new-loop
>  	  isearch-lazy-highlight-wrapped      nil
>  	  isearch-lazy-highlight-last-string  isearch-string
>  	  isearch-lazy-highlight-case-fold-search isearch-case-fold-search
> +	  isearch-lazy-highlight-filter-predicate isearch-filter-predicate
>  	  isearch-lazy-highlight-regexp       isearch-regexp
>  	  isearch-lazy-highlight-lax-whitespace   isearch-lax-whitespace
>  	  isearch-lazy-highlight-regexp-lax-whitespace isearch-regexp-lax-whitespace
> diff --git a/lisp/replace.el b/lisp/replace.el
> index 3503b65..2f297b1 100644
> --- a/lisp/replace.el
> +++ b/lisp/replace.el
> @@ -2383,7 +2383,10 @@ perform-replace
>           ;; If non-nil, it is marker saying where in the buffer to stop.
>           (limit nil)
>           ;; Use local binding in add-function below.
> -         (isearch-filter-predicate isearch-filter-predicate)
> +         (isearch-filter-predicate
> +          (or (and (plist-member (text-properties-at 0 from-string) 'isearch-filter-predicate)
> +                   (get-text-property 0 'isearch-filter-predicate from-string))
> +              isearch-filter-predicate))
>           (region-bounds nil)
>  
>           ;; Data for the next match.  If a cons, it has the same format as
> @@ -2395,6 +2398,15 @@ perform-replace
>                (apply 'propertize
>                       (concat "Query replacing "
>                               (if backward "backward " "")
> +                             (if (not case-fold-search)
> +                                 "case-sensitive ")
> +                             (let ((prefix ""))
> +                               (advice-function-mapc
> +                                (lambda (_ props)
> +                                  (let ((np (cdr (assq 'isearch-message-prefix props))))
> +                                    (if np (setq prefix (concat np prefix)))))
> +                                isearch-filter-predicate)
> +                               prefix)
>                               (if delimited-flag
>                                   (or (and (symbolp delimited-flag)
>                                            (get delimited-flag