tree-sitter-heex icon indicating copy to clipboard operation
tree-sitter-heex copied to clipboard

Support for `case` statements

Open arcanemachine opened this issue 2 years ago • 4 comments

HEEx templates support the valid (if not somewhat esoteric) usage of case statements (as referenced in this Elixir Forum post). For example:

<%= case some_value do %>
  <% something -> %>
    Some stuff
  <% something_else -> %>
    Some other stuff
  <% _ -> %>
    Default stuff
<% end %>

The syntax currently breaks the highlighting. I have not worked with Tree-sitter grammars before, although I'm looking into it to see if I can implement this myself. If not, I would appreciate some help getting this working if possible.

As an alternative, I could definitely use a series of if blocks, but the syntax above is perfectly valid (and much more elegant and Elixir-esque IMO) so it would be nice to be able to get that working.

Thanks

arcanemachine avatar Aug 17 '23 03:08 arcanemachine

I debugged this, it seems to go wrong with multiple clauses:

~H"""
<%= case @some_value do %>
  <% 123 -> %>
    Some stuff
  <% 456 -> %>
    Some stuff
<% end %>
"""

gives

sigil [0, 0] - [7, 3]
  sigil_name [0, 1] - [0, 2]
  quoted_content [0, 5] - [7, 0]
    call [1, 4] - [1, 20]
      target: identifier [1, 4] - [1, 8]
      arguments [1, 9] - [1, 20]
        unary_operator [1, 9] - [1, 20]
          operand: identifier [1, 10] - [1, 20]
    error [1, 21] - [4, 11]
      arguments [2, 5] - [2, 8]
        integer [2, 5] - [2, 8]
      integer [4, 5] - [4, 8]
      operator_identifier [4, 10] - [4, 11]
    directive [1, 0] - [1, 26]
      partial_expression_value [1, 3] - [1, 23]
    directive [2, 2] - [2, 14]
      partial_expression_value [2, 4] - [2, 11]
    text [3, 4] - [3, 14]
    directive [4, 2] - [4, 14]
      partial_expression_value [4, 4] - [4, 11]
    text [5, 4] - [5, 14]
    directive [6, 0] - [6, 9]
      ending_expression_value [6, 2] - [6, 6]

while

~H"""
<%= case @some_value do %>
  <% 123 -> %>
    Some stuff
<% end %>
"""

gives

sigil [0, 0] - [5, 3]
  sigil_name [0, 1] - [0, 2]
  quoted_content [0, 5] - [5, 0]
    call [1, 4] - [2, 11]
      target: identifier [1, 4] - [1, 8]
      arguments [1, 9] - [1, 20]
        unary_operator [1, 9] - [1, 20]
          operand: identifier [1, 10] - [1, 20]
      do_block [1, 21] - [2, 11]
        stab_clause [2, 5] - [2, 11]
          left: arguments [2, 5] - [2, 8]
            integer [2, 5] - [2, 8]
    directive [1, 0] - [1, 26]
      partial_expression_value [1, 3] - [1, 23]
    directive [2, 2] - [2, 14]
      partial_expression_value [2, 4] - [2, 11]
    text [3, 4] - [3, 14]
    directive [4, 0] - [4, 9]
      ending_expression_value [4, 2] - [4, 6]

When I check the first example in the original repo using playground:

fragment [0, 0] - [8, 0]
  text [0, 0] - [0, 5]
  directive [1, 0] - [1, 26]
    partial_expression_value [1, 3] - [1, 23]
  directive [2, 2] - [2, 14]
    partial_expression_value [2, 4] - [2, 11]
  text [3, 4] - [3, 14]
  directive [4, 2] - [4, 14]
    partial_expression_value [4, 4] - [4, 11]
  text [5, 4] - [5, 14]
  directive [6, 0] - [6, 9]
    ending_expression_value [6, 2] - [6, 6]
  text [7, 0] - [7, 3]

And here is where my knowledge ends.. 😅

dvic avatar Aug 17 '23 05:08 dvic

This happens because of the way we delegate to tree-sitter-elixir. The parts within the <%/%> markers are combined and injected with tree-sitter-elixir so tree-sitter-elixir sees this:

case some_value do
something ->  something_else  ->  _ ->
end

which causes the errors in the parse tree (:point_up:). To properly fix this, I believe the grammar would need to be rewritten to take a dependency on tree-sitter-elixir and parse HEEx and Elixir together rather than only parsing HEEx and injecting Elixir. It would be a very large change though so I'm not sure if it's a good tradeoff.

the-mikedavis avatar Aug 19 '23 15:08 the-mikedavis

Could we inject something like:

case some_value do
something ->  ; something_else  -> ; _ -> ;
end

?

josevalim avatar Aug 19 '23 15:08 josevalim

Tree-sitter doesn't have a way to add new text when parsing or injecting AFAIK so we're stuck with what we have in the source

the-mikedavis avatar Aug 19 '23 16:08 the-mikedavis