helix icon indicating copy to clipboard operation
helix copied to clipboard

Cross injection layers in tree-sitter motions

Open the-mikedavis opened this issue 2 years ago • 9 comments

Tree-sitter motions didn't work well for languages with injections such as Vue or JavaScript inside HTML since only the root layer's syntax-tree was considered. This change uses the range information for each injected language layer to find the appropriate syntax tree so that tree-sitter motions (A-p, A-i, A-o, A-n) work within injected content.

Here the tree-sitter motions work on the JavaScript syntax tree even though the root layer is HTML. Previously, A-o within the script tag would expand the selection to the whole script tag element.

Closes #2311

the-mikedavis avatar Dec 15 '22 21:12 the-mikedavis

I think this could be better if we introduce something that wraps the layers in helix_core::syntax::Syntax but has the same API as tree_sitter::TreeCursor.

  • Currently we use tree_sitter::Node::parent which can't cross injection layers, so A-n/A-p get 'stuck' at the edges of a layer
  • We already use TreeCursor for :tree-sitter-subtree, so if we have the same API, we can trivially support printing the subtree for injected content.

the-mikedavis avatar Dec 17 '22 18:12 the-mikedavis

I think this could be better if we introduce something that wraps the layers in helix_core::syntax::Syntax but has the same API as tree_sitter::TreeCursor.

* Currently we use `tree_sitter::Node::parent` which can't cross injection layers, so `A-n`/`A-p` get 'stuck' at the edges of a layer

* We already use TreeCursor for `:tree-sitter-subtree`, so if we have the same API, we can trivially support printing the subtree for injected content.

Sounds like such an iterator might go in the same direction as what you did in the rainbow brackets PR? I don't have much time this weekend so I didn't get a chance to look at that in more detail so I might just have missunderstood you there.

pascalkuthe avatar Dec 17 '22 18:12 pascalkuthe

They would be similar but the iterator for #2857 covers queries and this would cover walking around the tree without queries. With both together, tree-sitter features are very close to working seemlessly with injections 😀

the-mikedavis avatar Dec 17 '22 18:12 the-mikedavis

They would be similar but the iterator for #2857 covers queries and this would cover walking around the tree without queries. With both together, tree-sitter features are very close to working seemlessly with injections grinning

That makes sense. I guess I was thinking that the query iterator could be a special case of the general tree iterator. It all seems pretty related to me. But it probably makes sense to keep the PRs separate. I am mostly just thinking out loud here so my ideas might not be possible as I dont't have time to look at code too closely right now :D

pascalkuthe avatar Dec 17 '22 19:12 pascalkuthe

I just realized this is also an issue in Rust macros since we use injections for those.

kirawi avatar Dec 22 '22 17:12 kirawi

@the-mikedavis what's the status on this PR? i'm interested in this feature and willing to pick it up if necessary/feasible

divarvel avatar Feb 22 '23 13:02 divarvel

I'm still working on this, I have some changes locally. The current approach works ok but it can have some odd edge cases on the injection boundaries. In order to make it work robustly we need to make a tree out of the injection layers and introduce a cursor type that works on that tree similar to tree_sitter::TreeCursor

the-mikedavis avatar Feb 22 '23 15:02 the-mikedavis

Hey just wanted to check-in on this PR's status. Love the editor, just getting harder to keep using on my projects based on Svelte.

themixednuts avatar Jan 10 '24 05:01 themixednuts

I got stuck for a while trying to use the tree cursor with :tree-sitter-subtree. Combined injections make that impossible since multiple nodes can point to the same child layer. In the future we could still print within injections with :tree-sitter-subtree by selecting the layer the selection range is contained in and printing only that layer (edit: https://github.com/helix-editor/helix/pull/9309). But in this PR I'm only updating A-p/A-o/A-i/A-n.

the-mikedavis avatar Jan 10 '24 22:01 the-mikedavis

lets be ambitious and shhot for the next release with this one, I think this mostly looks good, I just found one perf issue and some smaller stuff

pascalkuthe avatar Mar 17 '24 23:03 pascalkuthe

Out of curiosity: does this also work for indent queries? Using the above example, if we have an html document and write js in a <script> tag, does this block use the injection queries defined for js?

cgahr avatar Mar 22 '24 09:03 cgahr

No this doesn't interact with queries. #9320 has some of the foundations for running queries across injection layers but it's limited to textobjects to keep the PR small. Moving indentation queries to use that functionality would be a follow-up change

the-mikedavis avatar Mar 22 '24 14:03 the-mikedavis