GNU bug report logs - #69542
Feature request: making occur obey isearch-filter-predicate

Previous Next

Package: emacs;

Reported by: Gabriele Nicolardi <gabriele <at> medialab.sissa.it>

Date: Mon, 4 Mar 2024 11:46:01 UTC

Severity: wishlist

To reply to this bug, email your comments to 69542 AT debbugs.gnu.org.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-gnu-emacs <at> gnu.org:
bug#69542; Package emacs. (Mon, 04 Mar 2024 11:46:01 GMT) Full text and rfc822 format available.

Acknowledgement sent to Gabriele Nicolardi <gabriele <at> medialab.sissa.it>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org. (Mon, 04 Mar 2024 11:46:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Gabriele Nicolardi <gabriele <at> medialab.sissa.it>
To: bug-gnu-emacs <at> gnu.org
Subject: Feature request: making occur obey isearch-filter-predicate
Date: Mon, 4 Mar 2024 12:44:48 +0100
[Message part 1 (text/plain, inline)]
Hi,

I’d like occur to obey to |isearch-filter-predicate| like query-replace* 
and |isearch*| commands.

This is an example of what I do:

|(defmacro with-ifp-predicates (PREDICATES &rest body) "Allows assigning 
a list of predicates to the variable `isearch-filter-predicate'. |

Below is an example of usage:

|(with-ifp-predicates '(skip-maths skip-comments) (query-replace \"foo\" 
\"bar\" nil (point-min) (point-max)))" (declare (indent 1)) `(let 
((isearch-filter-predicate isearch-filter-predicate)) (mapc (lambda 
(predicate) (add-function :before-while isearch-filter-predicate 
predicate)) ,PREDICATES) ,@body)) |

(I always use let-binding for operations that involve 
|isearch-filter-predicate|.)

In some cases, I would like to have something like this:

|(with-ifp-predicates '(predicate1 predicate2) (save-window-excursion 
(occur "foo") (query-replace \"foo\" \"bar\" nil (point-min) (point-max)))) |

with |occur| showing me only the strings that match the active predicates.

I wrote a mail to emacs-devel <at> gnu.org and I have verified that others 
also believe that this solution would be helpful and consistent with the 
functioning of some other functions defined in |replace.el|.

I’d also like to add this feature to the |how-many| function.

I already wrote this modified version (Emacs 29.2):

|(defun re-search-forward-ifp (REGEXP &optional BOUND NOERROR COUNT) 
"Modified version of `search-forward-regexp' that filters (skips) 
matches according to `isearch-filter-predicate'." (let ((POINT (point))) 
(catch 'filtered (while (search-forward-regexp REGEXP BOUND NOERROR 
COUNT) (let ((B (match-beginning 0)) (E (match-end 0))) (when (funcall 
isearch-filter-predicate B E) (throw 'filtered (point))))) (goto-char 
POINT) nil))) (defalias 'search-forward-regexp-ifp 
're-search-forward-ifp) (defun how-many-ifp (regexp &optional rstart 
rend interactive) "Modified version of `how-many' that filters (skips) 
matches according to `isearch-filter-predicate'." (interactive 
(keep-lines-read-args "How many matches for regexp")) (save-excursion 
(if rstart (if rend (progn (goto-char (min rstart rend)) (setq rend (max 
rstart rend))) (goto-char rstart) (setq rend (point-max))) (if (and 
interactive (use-region-p)) (setq rstart (region-beginning) rend 
(region-end)) (setq rstart (point) rend (point-max))) (goto-char 
rstart)) (let ((count 0) (case-fold-search (if (and case-fold-search 
search-upper-case) (isearch-no-upper-case-p regexp t) 
case-fold-search))) (while (and (< (point) rend) (re-search-forward-ifp 
regexp rend t)) ;; Ensure forward progress on zero-length matches like 
"^$". (when (and (= (match-beginning 0) (match-end 0)) (not (eobp))) 
(forward-char 1)) (setq count (1+ count))) (when interactive (message 
(ngettext "%d occurrence" "%d occurrences" count) count)) count))) 
(defalias 'count-matches-ifp 'how-many-ifp) |

Best regards,

Gabriele Nicolardi

​
[Message part 2 (text/html, inline)]

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69542; Package emacs. (Tue, 05 Mar 2024 16:37:02 GMT) Full text and rfc822 format available.

Message #8 received at 69542 <at> debbugs.gnu.org (full text, mbox):

From: Juri Linkov <juri <at> linkov.net>
To: Gabriele Nicolardi <gabriele <at> medialab.sissa.it>
Cc: 69542 <at> debbugs.gnu.org
Subject: Re: bug#69542: Feature request: making occur obey
 isearch-filter-predicate
Date: Tue, 05 Mar 2024 18:30:20 +0200
> I’d like occur to obey to isearch-filter-predicate like query-replace* and
> isearch* commands.
>
> This is an example of what I do:
>
> (defmacro with-ifp-predicates (PREDICATES &rest body)
>    "Allows assigning a list of predicates to the
>  variable `isearch-filter-predicate'.
>
> Below is an example of usage:
>
> (with-ifp-predicates '(skip-maths skip-comments)
>   (query-replace \"foo\" \"bar\" nil (point-min) (point-max)))"
>   (declare (indent 1))
>   `(let ((isearch-filter-predicate isearch-filter-predicate))
>      (mapc (lambda (predicate)
>                (add-function :before-while isearch-filter-predicate predicate))
>              ,PREDICATES)
>      ,@body))
>
> (I always use let-binding for operations that involve
> isearch-filter-predicate.)
>
> In some cases, I would like to have something like this:
>
> (with-ifp-predicates '(predicate1 predicate2)
>    (save-window-excursion
>      (occur "foo")
>      (query-replace \"foo\" \"bar\" nil (point-min) (point-max)))) 
>
> with occur showing me only the strings that match the active predicates.
>
> I wrote a mail to emacs-devel <at> gnu.org and I have verified that others also
> believe that this solution would be helpful and consistent with the
> functioning of some other functions defined in replace.el.
>
> I’d also like to add this feature to the how-many function.
>
> I already wrote this modified version (Emacs 29.2):
>
> (defun re-search-forward-ifp (REGEXP &optional BOUND NOERROR COUNT)
>   "Modified version of `search-forward-regexp' that
>     filters (skips) matches according to `isearch-filter-predicate'."
>
>   (let ((POINT (point)))
>     (catch 'filtered
>       (while (search-forward-regexp REGEXP BOUND NOERROR COUNT)
>         (let ((B (match-beginning 0))
>               (E (match-end 0)))
>           (when (funcall isearch-filter-predicate B E)
>             (throw 'filtered (point)))))
>       (goto-char POINT)
>       nil)))
> (defalias 'search-forward-regexp-ifp 're-search-forward-ifp)

So you rewrote the loop from isearch-search:

	(while retry
	  (setq isearch-success
		(isearch-search-string isearch-string nil t))
	  (if (or (not isearch-success)
		  (funcall isearch-filter-predicate
			   (match-beginning 0) (match-end 0)))
	      (setq retry nil)

But this doesn't answer the question whether and how this could affect
re-search-forward in a non-hackish way.  Adding an option?  Not sure.

> (defun how-many-ifp (regexp &optional rstart rend interactive)
>   "Modified version of `how-many' that filters (skips) matches
>     according to `isearch-filter-predicate'."
>
>   (interactive
>    (keep-lines-read-args "How many matches for regexp"))
>   (save-excursion
>     (if rstart
>         (if rend
>             (progn
>               (goto-char (min rstart rend))
>               (setq rend (max rstart rend)))
>           (goto-char rstart)
>           (setq rend (point-max)))
>       (if (and interactive (use-region-p))
>           (setq rstart (region-beginning)
>                 rend (region-end))
>         (setq rstart (point)
>               rend (point-max)))
>       (goto-char rstart))
>     (let ((count 0)
>           (case-fold-search
>            (if (and case-fold-search search-upper-case)
>                (isearch-no-upper-case-p regexp t)
>              case-fold-search)))
>       (while (and (< (point) rend)
>                   (re-search-forward-ifp regexp rend t))
>         ;; Ensure forward progress on zero-length matches like "^$".
>         (when (and (= (match-beginning 0) (match-end 0))
>                    (not (eobp)))
>           (forward-char 1))
>         (setq count (1+ count)))
>       (when interactive (message (ngettext "%d occurrence"
>                                            "%d occurrences"
>                                            count)
>                                  count))
>       count)))
> (defalias 'count-matches-ifp 'how-many-ifp)

I think duplicating the whole body of functions is not better
than using advice.  An alternative would be to add a new variable
're-search-forward-function' and then to use it like
'(funcall re-search-forward-function)'.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69542; Package emacs. (Tue, 05 Mar 2024 17:13:02 GMT) Full text and rfc822 format available.

Message #11 received at 69542 <at> debbugs.gnu.org (full text, mbox):

From: Gabriele Nicolardi <gabriele <at> medialab.sissa.it>
To: Juri Linkov <juri <at> linkov.net>
Cc: 69542 <at> debbugs.gnu.org
Subject: Re: bug#69542: Feature request: making occur obey
 isearch-filter-predicate
Date: Tue, 5 Mar 2024 18:11:37 +0100
[Message part 1 (text/plain, inline)]
> I think duplicating the whole body of functions is not better
> than using advice.

Until yesterday, I had no idea how to advise a function.

Now I'm experimenting with this code to obtain modified versions of 
|occur| and |how-many| that are sensitive to |isearch-filter-predicate|.

I'm not a programmer, so my code may not be very rigorous:

;; I need a copy of `re-search-forward' because it will be temporary 
adviced in
;; `how-many-ifp' and `occur-ifp' functions:
(defalias 're-search-forward-copy
  (symbol-function 're-search-forward)
  "Copy of `re-search-forward'")

(defun re-search-forward-ifp (REGEXP &optional BOUND NOERROR COUNT)
  "Modified version of `search-forward-regexp' that filters (skips) matches
according to `isearch-filter-predicate'."
  (let ((POINT (point)))
    (catch 'filtered
      (while (re-search-forward-copy REGEXP BOUND NOERROR COUNT)
        (let ((B (match-beginning 0))
              (E (match-end 0)))
          (when (funcall isearch-filter-predicate B E)
            (throw 'filtered (point)))))
      (goto-char POINT)
      nil)))
(defalias 'search-forward-regexp-ifp 're-search-forward-ifp)

(defun override-re-search-forward (orig-fun &rest args)
  "Temporary advice the `re-search-forward' function, overriding it with
`re-search-forward-ifp' to make it sensitive to `isearch-filter-predicate'"
  (unwind-protect
      (progn
        (advice-add 're-search-forward :override #'re-search-forward-ifp)
        (apply orig-fun args))
    (advice-remove 're-search-forward #'re-search-forward-ifp)))

(defalias 'how-many-ifp
  (symbol-function 'how-many)
  "Copy of the `how-many' function (to be) adviced to obey to
`isearch-filter-predicate'")
(defalias 'count-matches-ifp 'how-many-ifp)

(advice-add 'how-many-ifp :around #'override-re-search-forward)

(defalias 'occur-ifp
  (symbol-function 'occur)
  "Copy of the `occur' function (to be) adviced to obey to
 `isearch-filter-predicate'")

(advice-add 'occur-ifp :around #'override-re-search-forward)

The key is the `re-search-forward` function used in `how-many` and 
`occur` definition. If you make it sensible to 
`isearch-filter-predicate` also `how-many` and `occur` will be.

I don't know if advicing a function to advice a function inside it is a 
good idea but it seem to work (I need more tests).

Now I discovered how to build these functions I could be fine with my 
modified ones, but making this feature avaible (maybe not default) in 
the original functions could be a better idea.

>   An alternative would be to add a new variable
> 're-search-forward-function' and then to use it like
> '(funcall re-search-forward-function)'.

The idea of a new function seems good to me.

[Message part 2 (text/html, inline)]

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69542; Package emacs. (Wed, 06 Mar 2024 17:50:02 GMT) Full text and rfc822 format available.

Message #14 received at 69542 <at> debbugs.gnu.org (full text, mbox):

From: Juri Linkov <juri <at> linkov.net>
To: Gabriele Nicolardi <gabriele <at> medialab.sissa.it>
Cc: 69542 <at> debbugs.gnu.org
Subject: Re: bug#69542: Feature request: making occur obey
 isearch-filter-predicate
Date: Wed, 06 Mar 2024 19:47:50 +0200
>      An alternative would be to add a new variable
> 're-search-forward-function' and then to use it like
> '(funcall re-search-forward-function)'.
>
> The idea of a new function seems good to me.

Indeed, and this will require only a simple change in 'how-many'
and other functions to replace such calls

  (re-search-forward regexp rend t)

with

  (funcall re-search-forward-function regexp rend t)




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69542; Package emacs. (Fri, 08 Mar 2024 23:11:01 GMT) Full text and rfc822 format available.

Message #17 received at 69542 <at> debbugs.gnu.org (full text, mbox):

From: Gabriele Nicolardi <gabriele <at> medialab.sissa.it>
To: Juri Linkov <juri <at> linkov.net>
Cc: 69542 <at> debbugs.gnu.org
Subject: Re: bug#69542: Feature request: making occur obey
 isearch-filter-predicate
Date: Sat, 9 Mar 2024 00:09:58 +0100
[Message part 1 (text/plain, inline)]
Il 05/03/24 18:11, Gabriele Nicolardi ha scritto:

>> I think duplicating the whole body of functions is not better
>> than using advice.
>
> Until yesterday, I had no idea how to advise a function.
>
> I don't know if advicing a function to advice a function inside it is 
> a good idea but it seem to work (I need more tests).
>
> Now I discovered how to build these functions I could be fine with my 
> modified ones, but making this feature avaible (maybe not default) in 
> the original functions could be a better idea
>
I found that advicing the |occur| and |how-many| function, overriding 
the |re-search-forward| function with my modified version of it 
(sensitive to |isearch-filter-predicate|) it’s not good at all.

That because some predicates, like e.g. |texmathp| provided by AUCTeX, 
use |re-search-forward| too.

> .
>
>>   An alternative would be to add a new variable
>> 're-search-forward-function' and then to use it like
>> '(funcall re-search-forward-function)'.
>
> The idea of a new function seems good to me.
>
>
​
[Message part 2 (text/html, inline)]

This bug report was last modified 1 year and 102 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.