RRethy/nvim-treesitter-endwise

Support for Julia language

phgz opened this issue · 11 comments

phgz commented

Hi,

This seems a great plugin. Could it be implemented for Julia as well? The syntax is very similar to the Ruby one.

Thank you!

I'm open to having Julia support added, but since it's not a language I've ever used I wouldn't implement it myself and instead would be willing to help guide someone with Julia experience to implement it. I'll give some details below on how to add support.

Adding support is as simple as added a single tree-sitter query file, see https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries for query syntax. Make a file queries/julia/endwise.scm, this is where the queries will go. There will be generally two queries for each thing which can have an end node added to it: when the parsing fails, and when the parsing succeeds. To get a better understanding of this, let's take a look at Lua which has a decently simple grammar.

In Lua, we have the following queries, https://github.com/RRethy/nvim-treesitter-endwise/blob/master/queries/lua/endwise.scm. Let's focus on the function definition queries, of which there are two:

((ERROR ("function" . (_)? . (parameters) @cursor) @indent) (#endwise! "end"))

The first query with ERROR is simple and will match the following:

function foo()<cursor>

Since we are working with syntax trees that have an error in them, we will expect the parser to be in a weird state and not be able to parse this code into a function_definition node, instead it gets parsed as a sequence of tokens and nodes and placed into the generic ERROR node to signify that the parser couldn't make sense of this sequence of tokens. In this example, we have a "function" followed by an (identifier) followed by (parameters). We also have two captures, @cursor to mark where we expect the users cursor to be in the query match, and @indent to signify what kind of indentation the end node should have when we place it. Lastly, we also have the #endwise! directive which is how we can configure the end node, if we made it foo then the text that gets inserted would be that.

Let's now take a look at the second query:

((function_definition parameters: (_) @cursor) @endable @indent (#endwise! "end"))

This one is a bit more complicated but it's a side effect of tree-sitter generating parsers which follow maximal-munch. Let's take a look at a piece of code:

function foo()
  function bar()<cursor>
end

If we look at this, we know that the end token is for the foo function, but the parser will parse it as part of the bar function which means the bar function will have a valid parse tree even though it shouldn't. To get around this, we can use the @indent in conjunction with the @endable capture group, if the last child of the node captured by @endable has a different indentation than the node captured by @indent, and if an ancestor node in the syntax tree has an error, then we can assume that the end node is actually meant to be a part of a different node. As such, we insert an end node with matching indentation.

To help you with writing queries, try writing some Julia code which you would expect to have an end node added, then run :TSPlayground and inspect the syntax tree (pressing a in the playground shows anonymous nodes). You can also add tests in tests/endwise/julia.rb that follow tests/endwise/lua.rb to verify your queries work. The consist of a few lines prefixed by a - with a to mark the initial cursor position, followed by lines prefixed by a + to show the expected end state of the text. You can run these tests with ruby ./tests/runner.rb <keyword> where <keyword> is something to match the provided description (or empty to match all tests).

phgz commented

Thank you much for the detailed guidelines. I am myself still learning the language, so it will be a good challenge. I am however quite occupied these days, so i will not be able to give it a grasp in the coming days, but as soon as I do, I will start working on it.

As you say, I think it will be straightforward since the syntax is so similar to ruby and lua.

I will try to make a PR when it is ready 😄.

I think @jasonrhansen has started work on adding Julia support FWIW (https://github.com/jasonrhansen/nvim-treesitter-endwise/tree/julia).

phgz commented

Oh, I just saw that! Well awesome then.

Can you review the Julia PR linked to go through the use-cases.

phgz commented

I will be able to check it after next week (exams).

Sorry for the delay!

Have you had a chance to take a look through the PR?

phgz commented

Yes, it seems OK!

The only thing I don't understand is why, when I'm here

for i in 1:5
    |
end

and press ENTER, then it triggers another end.

Can you comment that on the PR as part of a PR review.

Bumping this, can you provide a PR review on that PR raising the issues you found.