andymass / vim-matchup

vim match-up: even better % :facepunch: navigate and highlight matching words :facepunch: modern matchit and matchparen. Supports both vim and neovim + tree-sitter.
https://www.vim.org/scripts/script.php?script_id=5624
MIT License
1.68k stars 67 forks source link

Is it possible to get matchup to work with treesitter injections? #352

Open pejrich opened 2 months ago

pejrich commented 2 months ago

Is your feature request related to a problem? Please describe. I love this plugin by the way. But I have a slight edge case in Elixir. Elixir can have it's html templates in a separate file with a .html.heex extension. Currently vim-matchup works a charm for these files. HTML markup can also be embedded within an Elixir file(this is a somewhat newer thing, but seems to be moving more in this direction). These files have the regular Elixir extension .ex, and HTML is denoted in the file as follows:

def some_elixir_function(assigns) do
  ~H"""
    <div>some html stuff here</div>
  """
end

Everything from the ~H""" to the next """ is HTML, even though the overall file is of filetype elixir.

This is one place where matchup doesn't work for me, but I would love if it did, and wanted to open this to see what it would take. Of course I'm happy to put the work in, but am a little lost on what the right direction is.

From my limited knowledge of Treesitter, it seems to use something called injections to denote a part of a file that is following different syntax rules than the rest of the file.

I notice in the after/queries/* there isn't currently one for Elixir. Would writing one likely fix this issue? If so I'm happy to look into writing one, but wasn't sure if that was A. What was needed and B. If there is something else needed too.

Describe the solution you'd like Ideally some way to let the plugin be aware of the HTML syntax within the non-HTML file.

Describe alternatives you've considered

Additional context Again, I'm happy to do the grunt work/write the queries, but just wanted to float it by you to see if it's a lot more work than it's worth, or if it's doable.

Thanks!

andymass commented 1 month ago

I believe injections should be supported but it depends on the treesitter language. If I recall correctly it should be simple as adding an elixir query to get matchup to recognize.

On Mon, Jul 1, 2024 at 10:55 PM Peter Richards @.***> wrote:

Is your feature request related to a problem? Please describe. I love this plugin by the way. But I have a slight edge case in Elixir. Elixir can have it's html templates in a separate file with a .html.heex extension. Currently vim-matchup works a charm for these files. HTML markup can also be embedded within an Elixir file(this is a somewhat newer thing, but seems to be moving more in this direction). These files have the regular Elixir extension .ex, and HTML is denoted in the file as follows:

def some_elixir_function(assigns) do ~H"""

some html stuff here

""" end

Everything from the ~H""" to the next """" is HTML, even though the overall file is of filetype elixir.

This is one place where matchup doesn't work for me, but I would love it did, and wanted to open this to see what it would take. Of course I'm happy to put the work in, but am a little lost on what the right direction is.

From my limited knowledge of Treesitter, it seems to use something called injections to denote a part of a file that is following different syntax rules than the rest of the file.

I notice in the after/queries/* there isn't currently one for Elixir. Would writing one likely fix this issue? If so I'm happy to look into writing one, but wasn't sure if that was A. What was needed and B. If there is something else needed too.

Describe the solution you'd like Ideally some way to let the plugin be aware of the HTML syntax within the non-HTML file.

Describe alternatives you've considered

Additional context Again, I'm happy to do the grunt work/write the queries, but just wanted to float it by you to see if it's a lot more work than it's worth, or if it's doable.

Thanks!

— Reply to this email directly, view it on GitHub https://github.com/andymass/vim-matchup/issues/352, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABSY3DOKWYOJS276XD6VVOLZKIJC5AVCNFSM6AAAAABKGUPZFCVHI2DSMVQWIX3LMV43ASLTON2WKOZSGM4DKMBYGYYTGNQ . You are receiving this because you are subscribed to this thread.Message ID: @.***>

pejrich commented 2 weeks ago

Hey @andymass I've been playing around to try to get this to work because I miss matchup dearly every time I'm working with HTML embedded into an elixir document.

This is the query i've cobbled together based on looking at other queries, as well as the syntax tree from :InspectTree

; inherits: quote

(component) @scope.tag
(start_component (identifier) @open.tag)
(end_component
  (identifier) @close.tag
  (#offset! @close.tag 0 -1 0 0))

(self_closing_component
  name: (identifier) @open.selftag
  "/>" @close.selftag) @scope.selftag

(tag) @scope.tag
(start_tag (identifier) @open.tag)
(end_tag
  (identifier) @close.tag
  (#offset! @close.tag 0 -1 0 0))

(self_closing_tag
  name: (identifier) @open.selftag
  "/>" @close.selftag) @scope.selftag

Here's a screenshot of some example code + TS inspect:

Screenshot 2024-08-19 at 13 55 52

However with this I get Invalid node type 'component', and Invalid node type 'tag'

I also tried the following query, as well as copying the current HTML query verbatim:

; inherits: quote

[
    (component)
] @scope.tag

(start_component (component_name) @open.tag)
(end_component
  (component_name) @close.tag
  (#offset! @close.tag 0 -1 0 0))

(self_closing_component
  (component_name) @open.selftag
  "/>" @close.selftag) @scope.selftag

[
    (tag)
] @scope.tag

(start_tag (tag_name) @open.tag)
(end_tag
  (tag_name) @close.tag
  (#offset! @close.tag 0 -1 0 0))

(self_closing_tag
  (tag_name) @open.selftag
  "/>" @close.selftag) @scope.selftag

but even those still gave me the "Invalid node type 'tag'" in the Elixir file, even though the InspectTree does seem to show that those names are correct(or at least the equivalent of what the HTML query seems to be matching again in an HTML file.

I'm wondering if you have any pointers about where I might be getting stuck. I'm not sure if there's some filetype stuff that I need to update.

Currently I have a matchup.scm in after/queries/elixir, as well as an elixir_matchup.vim in after/ftplugin which contains the following(basically just the boilerplate they all seemed to have.

" vim match-up - even better matching
"
" Maintainer: Andy Massimino
" Email:      a@normed.space
"

if !exists('g:loaded_matchup') || !exists('b:did_ftplugin')
  finish
endif

" vim: fdm=marker sw=2

On another note, the HTML tag matching works correctly for filetypes of heex, which is elixir embedded in html .html.heex ext. even though there's no queries for heex. I'm trying to get it to work for the opposite(HTML embedded into an elixir file), but strangely even if I set the filetype of the elixir file to heex, matchup doesn't work for the HTML part of that file. I'm honestly pretty unclear on TS and fairly new to vim/neovim in general. Any advice for what might be stopping this from working?

Thanks in advance. I appreciate your time and effort.

pejrich commented 2 weeks ago

As is so often the case, I manage to get it working just minutes after posting for help. Though the way I got it working was not via TS, but rather match_words. I just queried match_words from the heex file which I think is just picking up HTML match_words, and I set let b:match_words = HEEX_MATCH_WORDS in after/ftplugin/elixir.vim, and now matchup highlights and matches correctly!