GNU bug report logs - #62204
30.0.50; Feature Request: treesit-major-mode-hook

Previous Next

Package: emacs;

Reported by: Aleksandar Dimitrov <code <at> aleks.bg>

Date: Wed, 15 Mar 2023 13:11:02 UTC

Severity: wishlist

Found in version 30.0.50

To reply to this bug, email your comments to 62204 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#62204; Package emacs. (Wed, 15 Mar 2023 13:11:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Aleksandar Dimitrov <code <at> aleks.bg>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org. (Wed, 15 Mar 2023 13:11:02 GMT) Full text and rfc822 format available.

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

From: Aleksandar Dimitrov <code <at> aleks.bg>
To: bug-gnu-emacs <at> gnu.org
Subject: 30.0.50; Feature Request: treesit-major-mode-hook
Date: Wed, 15 Mar 2023 12:50:38 +0100
I'd like to propose a major mode hook that is called every time any treesit-based major mode is enabled.

My use case is this: I'd like to extend the expand-region.el package so that it can make use of treesit to set the region to any treesit node. This should work in all treesit-based major modes.

Currently, I've found two ways to accomplish loading my functionality for all ts-modes:

- enumerate them all and use their respective hooks
- advise something like `treesit-major-mode-setup` to execute my code

The first idea might miss a mode if Emacs decides to add one down the
line, or if the user defines her own ts-mode.

The second idea does not feel idiomatic, as this sort of functionality
is usually covered by hooks.

I'd imagine there are other use cases for minor modes and other
functionality that we'd like to provide to any ts-based mode, not just
particular ones.

The concrete use-case is exemplified here:
https://github.com/magnars/expand-region.el/pull/279/




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#62204; Package emacs. (Wed, 15 Mar 2023 14:25:02 GMT) Full text and rfc822 format available.

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

From: Eli Zaretskii <eliz <at> gnu.org>
To: Aleksandar Dimitrov <code <at> aleks.bg>
Cc: 62204 <at> debbugs.gnu.org
Subject: Re: bug#62204: 30.0.50; Feature Request: treesit-major-mode-hook
Date: Wed, 15 Mar 2023 16:24:45 +0200
> From: Aleksandar Dimitrov <code <at> aleks.bg>
> Date: Wed, 15 Mar 2023 12:50:38 +0100
> 
> 
> I'd like to propose a major mode hook that is called every time any treesit-based major mode is enabled.
> 
> My use case is this: I'd like to extend the expand-region.el package so that it can make use of treesit to set the region to any treesit node. This should work in all treesit-based major modes.
> 
> Currently, I've found two ways to accomplish loading my functionality for all ts-modes:
> 
> - enumerate them all and use their respective hooks
> - advise something like `treesit-major-mode-setup` to execute my code

Isn't it enough to check that the buffer has a treesit parser?

A hooks sounds too blunt and ad-hoc for your purposes, AFAIU.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#62204; Package emacs. (Thu, 16 Mar 2023 04:27:03 GMT) Full text and rfc822 format available.

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

From: Aleksandar Dimitrov <code <at> aleks.bg>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 62204 <at> debbugs.gnu.org
Subject: Re: bug#62204: 30.0.50; Feature Request: treesit-major-mode-hook
Date: Thu, 16 Mar 2023 01:35:17 +0100
>> Currently, I've found two ways to accomplish loading my functionality for all ts-modes:
>> 
>> - enumerate them all and use their respective hooks
>> - advise something like `treesit-major-mode-setup` to execute my code
>
> Isn't it enough to check that the buffer has a treesit parser?

I'm not sure I understand you, so I'll try to provide some code.

I'd like to be able to do something like this:

(defun my-setup ()
  "Code that depends on the presence of TS")
(add-hook 'treesit-major-mode-hook 'my-setup)

If I understand you correctly,  I could probably do something like this:

(defmacro add-ts-mode-hook (f)
  "Add mode hook that only executes in ts modes"
  `(add-hook 'prog-mode-hook
            (lambda ()
              (when (treesit-language-at (point))
                (,f)))))

I'd say there's bound to be more people who would like to configure a
certain behaviour whenever treesit is available, regardless of major
mode. A macro like the above could be a possible solution, but it
doesn't feel terribly ergonomic.

> A hooks sounds too blunt and ad-hoc for your purposes, AFAIU.

The reason I want to execute my function in a hook is that it sets
buffer local variables, and configures buffer-local behaviour, perhaps
even keybindings. I was under the impression that hooks are the correct
place to do this.






Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#62204; Package emacs. (Thu, 16 Mar 2023 06:39:01 GMT) Full text and rfc822 format available.

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

From: Eli Zaretskii <eliz <at> gnu.org>
To: Aleksandar Dimitrov <code <at> aleks.bg>
Cc: 62204 <at> debbugs.gnu.org
Subject: Re: bug#62204: 30.0.50; Feature Request: treesit-major-mode-hook
Date: Thu, 16 Mar 2023 08:38:34 +0200
> From: Aleksandar Dimitrov <code <at> aleks.bg>
> Cc: 62204 <at> debbugs.gnu.org
> Date: Thu, 16 Mar 2023 01:35:17 +0100
> 
> I'd like to be able to do something like this:
> 
> (defun my-setup ()
>   "Code that depends on the presence of TS")
> (add-hook 'treesit-major-mode-hook 'my-setup)
> 
> If I understand you correctly,  I could probably do something like this:
> 
> (defmacro add-ts-mode-hook (f)
>   "Add mode hook that only executes in ts modes"
>   `(add-hook 'prog-mode-hook
>             (lambda ()
>               (when (treesit-language-at (point))
>                 (,f)))))

Something like that.  Basically, any function that wants to do
something that depends on tree-sitter being available for the major
mode should make such a test to determine whether tree-sitter support
is available.

> I'd say there's bound to be more people who would like to configure a
> certain behaviour whenever treesit is available, regardless of major
> mode. A macro like the above could be a possible solution, but it
> doesn't feel terribly ergonomic.

The above code doesn't have to run from a mode hook, it could be done
directly where the "certain behavior" is implemented, as the condition
for invoking that certain behavior.  And if running the test each time
is too expensive for some reason, the test could be optimized by
performing it just once for each buffer where it runs, and saving the
result in some buffer-local variable.

If the above is for some reason unworkable or otherwise problematic,
please tell why.

> > A hooks sounds too blunt and ad-hoc for your purposes, AFAIU.
> 
> The reason I want to execute my function in a hook is that it sets
> buffer local variables, and configures buffer-local behaviour, perhaps
> even keybindings. I was under the impression that hooks are the correct
> place to do this.

They are, but they are not the only such place.  Many features in
Emacs use buffer-local variables and keybindings without a special
hook.

Please also keep in mind that proliferation of general-purpose hooks
is not without disadvantages.  For starters, a hook disconnects the
cause from the effect, and makes it harder to track the control flow
and thus harder to understand how a given Lisp program works.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#62204; Package emacs. (Sat, 18 Mar 2023 07:50:01 GMT) Full text and rfc822 format available.

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

From: Yuan Fu <casouri <at> gmail.com>
To: code <at> aleks.bg
Cc: 62204 <at> debbugs.gnu.org, Eli Zaretskii <eliz <at> gnu.org>
Subject: Re: bug#62204: 30.0.50; Feature Request: treesit-major-mode-hook
Date: Sat, 18 Mar 2023 00:49:02 -0700
Aleksandar Dimitrov <code <at> aleks.bg> writes:

>>> Currently, I've found two ways to accomplish loading my functionality for all ts-modes:
>>> 
>>> - enumerate them all and use their respective hooks
>>> - advise something like `treesit-major-mode-setup` to execute my code
>>
>> Isn't it enough to check that the buffer has a treesit parser?
>
> I'm not sure I understand you, so I'll try to provide some code.
>
> I'd like to be able to do something like this:
>
> (defun my-setup ()
>   "Code that depends on the presence of TS")
> (add-hook 'treesit-major-mode-hook 'my-setup)
>
> If I understand you correctly,  I could probably do something like this:
>
> (defmacro add-ts-mode-hook (f)
>   "Add mode hook that only executes in ts modes"
>   `(add-hook 'prog-mode-hook
>             (lambda ()
>               (when (treesit-language-at (point))
>                 (,f)))))
>
> I'd say there's bound to be more people who would like to configure a
> certain behaviour whenever treesit is available, regardless of major
> mode. A macro like the above could be a possible solution, but it
> doesn't feel terribly ergonomic.
>
>> A hooks sounds too blunt and ad-hoc for your purposes, AFAIU.
>
> The reason I want to execute my function in a hook is that it sets
> buffer local variables, and configures buffer-local behaviour, perhaps
> even keybindings. I was under the impression that hooks are the correct
> place to do this.

IIUC, you are trying to do something like

(er/enable-mode-expansions 'clojure-mode 'er/add-clojure-mode-expansions)

right?

But a tree-sitter-based expander doesn’t really depend on any particular
major mode. In essence, they depend on the existence of a tree-sitter
parser in the current buffer. So I suggest that you define a universal
expander (similar to er/expand-word, etc) that checks the existence of a
tree-sitter parser and uses the parser to expand the region, and simply
do nothing if there isn’t a parser.

But to work with tree-sitter, expand-region might need to disable some
of its expanders when a tree-sitter parser is available, in case there’s
some conflict between the existing language-specific expander and the
tree-sitter expander.

Yuan




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#62204; Package emacs. (Sun, 19 Mar 2023 22:36:02 GMT) Full text and rfc822 format available.

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

From: Aleksandar Dimitrov <code <at> aleks.bg>
To: Yuan Fu <casouri <at> gmail.com>
Cc: 62204 <at> debbugs.gnu.org, Eli Zaretskii <eliz <at> gnu.org>, code <at> aleks.bg
Subject: Re: bug#62204: 30.0.50; Feature Request: treesit-major-mode-hook
Date: Sun, 19 Mar 2023 23:26:03 +0100
Yuan Fu <casouri <at> gmail.com> writes:
> IIUC, you are trying to do something like
>
> (er/enable-mode-expansions 'clojure-mode 'er/add-clojure-mode-expansions)
>
> right?

Yes!

> But a tree-sitter-based expander doesn’t really depend on any particular
> major mode. In essence, they depend on the existence of a tree-sitter
> parser in the current buffer. So I suggest that you define a universal
> expander (similar to er/expand-word, etc) that checks the existence of a
> tree-sitter parser and uses the parser to expand the region, and simply
> do nothing if there isn’t a parser.

That's exactly what I did. However, instead of having an expander check
every time whether there's a tree-sitter parser, a check that's
redundant past the first one, I wanted to only load that expander
whenever it makes sense and not even call it otherwise. I feel that
makes the code easier to follow, and it makes more sense for the domain.

> But to work with tree-sitter, expand-region might need to disable some
> of its expanders when a tree-sitter parser is available, in case there’s
> some conflict between the existing language-specific expander and the
> tree-sitter expander.

Yes, it's not quite straight-forward, but that's a discussion for the PR
I opened against expand-region.

Thanks for your insight!

Aleks




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#62204; Package emacs. (Sun, 19 Mar 2023 22:43:02 GMT) Full text and rfc822 format available.

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

From: Aleksandar Dimitrov <code <at> aleks.bg>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 62204 <at> debbugs.gnu.org, Aleksandar Dimitrov <code <at> aleks.bg>
Subject: Re: bug#62204: 30.0.50; Feature Request: treesit-major-mode-hook
Date: Sun, 19 Mar 2023 23:35:25 +0100
Hi Eli,

Eli Zaretskii <eliz <at> gnu.org> writes:
> Something like that.  Basically, any function that wants to do
> something that depends on tree-sitter being available for the major
> mode should make such a test to determine whether tree-sitter support
> is available.

As mentioned in the other email to Yuan Fu, my goal was to avoid such a check, and
only install those functions that depend on treesitter when treesitter
was available. The reason is less performance-driven and more about my
subjective goal to avoid cluttering functions with conditionals whenever
possible.

> They are, but they are not the only such place.  Many features in
> Emacs use buffer-local variables and keybindings without a special
> hook.
>
> Please also keep in mind that proliferation of general-purpose hooks
> is not without disadvantages.  For starters, a hook disconnects the
> cause from the effect, and makes it harder to track the control flow
> and thus harder to understand how a given Lisp program works.

I agree that hooks add indirection. They were the first tool I pulled
out of my (very limited) Emacs toolbox, because I'm very used to working
with them.

But if such a hook isn't desirable in the grand scheme of things, that's
OK! There are certainly viable alternatives.
Thanks for taking your time to explain this.

Aleks




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

Previous Next


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