unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#62204: 30.0.50; Feature Request: treesit-major-mode-hook
@ 2023-03-15 11:50 Aleksandar Dimitrov
  2023-03-15 14:24 ` Eli Zaretskii
  2023-03-18  7:49 ` Yuan Fu
  0 siblings, 2 replies; 7+ messages in thread
From: Aleksandar Dimitrov @ 2023-03-15 11:50 UTC (permalink / raw)
  To: 62204


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/





^ permalink raw reply	[flat|nested] 7+ messages in thread

* bug#62204: 30.0.50; Feature Request: treesit-major-mode-hook
  2023-03-15 11:50 bug#62204: 30.0.50; Feature Request: treesit-major-mode-hook Aleksandar Dimitrov
@ 2023-03-15 14:24 ` Eli Zaretskii
  2023-03-16  0:35   ` Aleksandar Dimitrov
  2023-03-18  7:49 ` Yuan Fu
  1 sibling, 1 reply; 7+ messages in thread
From: Eli Zaretskii @ 2023-03-15 14:24 UTC (permalink / raw)
  To: Aleksandar Dimitrov; +Cc: 62204

> From: Aleksandar Dimitrov <code@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.





^ permalink raw reply	[flat|nested] 7+ messages in thread

* bug#62204: 30.0.50; Feature Request: treesit-major-mode-hook
  2023-03-15 14:24 ` Eli Zaretskii
@ 2023-03-16  0:35   ` Aleksandar Dimitrov
  2023-03-16  6:38     ` Eli Zaretskii
  0 siblings, 1 reply; 7+ messages in thread
From: Aleksandar Dimitrov @ 2023-03-16  0:35 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 62204


>> 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.







^ permalink raw reply	[flat|nested] 7+ messages in thread

* bug#62204: 30.0.50; Feature Request: treesit-major-mode-hook
  2023-03-16  0:35   ` Aleksandar Dimitrov
@ 2023-03-16  6:38     ` Eli Zaretskii
  2023-03-19 22:35       ` Aleksandar Dimitrov
  0 siblings, 1 reply; 7+ messages in thread
From: Eli Zaretskii @ 2023-03-16  6:38 UTC (permalink / raw)
  To: Aleksandar Dimitrov; +Cc: 62204

> From: Aleksandar Dimitrov <code@aleks.bg>
> Cc: 62204@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.





^ permalink raw reply	[flat|nested] 7+ messages in thread

* bug#62204: 30.0.50; Feature Request: treesit-major-mode-hook
  2023-03-15 11:50 bug#62204: 30.0.50; Feature Request: treesit-major-mode-hook Aleksandar Dimitrov
  2023-03-15 14:24 ` Eli Zaretskii
@ 2023-03-18  7:49 ` Yuan Fu
  2023-03-19 22:26   ` Aleksandar Dimitrov
  1 sibling, 1 reply; 7+ messages in thread
From: Yuan Fu @ 2023-03-18  7:49 UTC (permalink / raw)
  To: code; +Cc: 62204, Eli Zaretskii


Aleksandar Dimitrov <code@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





^ permalink raw reply	[flat|nested] 7+ messages in thread

* bug#62204: 30.0.50; Feature Request: treesit-major-mode-hook
  2023-03-18  7:49 ` Yuan Fu
@ 2023-03-19 22:26   ` Aleksandar Dimitrov
  0 siblings, 0 replies; 7+ messages in thread
From: Aleksandar Dimitrov @ 2023-03-19 22:26 UTC (permalink / raw)
  To: Yuan Fu; +Cc: 62204, Eli Zaretskii, code


Yuan Fu <casouri@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





^ permalink raw reply	[flat|nested] 7+ messages in thread

* bug#62204: 30.0.50; Feature Request: treesit-major-mode-hook
  2023-03-16  6:38     ` Eli Zaretskii
@ 2023-03-19 22:35       ` Aleksandar Dimitrov
  0 siblings, 0 replies; 7+ messages in thread
From: Aleksandar Dimitrov @ 2023-03-19 22:35 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 62204, Aleksandar Dimitrov

Hi Eli,

Eli Zaretskii <eliz@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





^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2023-03-19 22:35 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-03-15 11:50 bug#62204: 30.0.50; Feature Request: treesit-major-mode-hook Aleksandar Dimitrov
2023-03-15 14:24 ` Eli Zaretskii
2023-03-16  0:35   ` Aleksandar Dimitrov
2023-03-16  6:38     ` Eli Zaretskii
2023-03-19 22:35       ` Aleksandar Dimitrov
2023-03-18  7:49 ` Yuan Fu
2023-03-19 22:26   ` Aleksandar Dimitrov

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).