Skip to content

Commit

Permalink
add tree-sitter-heex
Browse files Browse the repository at this point in the history
HEEx is a templating engine on top of Elixir's EEx templating
language specific to HTML that is included in Phoenix.LiveView
(though I think the plan is to eventually include it in base
Phoenix). It's a superset of EEx with some additional features
like components and slots.

The injections don't work perfectly because the Elixir grammar is
newline sensitive (the _terminator rule). See
elixir-lang/tree-sitter-elixir#24
for more information.
  • Loading branch information
the-mikedavis authored and archseer committed Apr 13, 2022
1 parent 9d095e0 commit 4836bb3
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 0 deletions.
1 change: 1 addition & 0 deletions book/src/generated/lang-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
| graphql || | | |
| haskell || | | `haskell-language-server-wrapper` |
| hcl || || `terraform-ls` |
| heex || | | |
| html || | | `vscode-html-language-server` |
| iex || | | |
| java || | | |
Expand Down
12 changes: 12 additions & 0 deletions languages.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1125,3 +1125,15 @@ indent = { tab-width = 2, unit = " " }
[[grammar]]
name = "eex"
source = { git = "https://github.com/connorlay/tree-sitter-eex", rev = "f742f2fe327463335e8671a87c0b9b396905d1d1" }

[[language]]
name = "heex"
scope = "source.heex"
injection-regex = "heex"
file-types = ["heex"]
roots = []
indent = { tab-width = 2, unit = " " }

[[grammar]]
name = "heex"
source = { git = "https://github.com/connorlay/tree-sitter-heex", rev = "592e22292a367312c35e13de7fdb888f029981d6" }
7 changes: 7 additions & 0 deletions runtime/queries/elixir/injections.scm
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,10 @@
(#match? @_sigil_name "^(r|R)$")
(#set! injection.language "regex")
(#set! injection.combined))

((sigil
(sigil_name) @_sigil_name
(quoted_content) @injection.content)
(#match? @_sigil_name "^(h|H)$")
(#set! injection.language "heex")
(#set! injection.combined))
58 changes: 58 additions & 0 deletions runtime/queries/heex/highlights.scm
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
; https://github.com/connorlay/tree-sitter-heex/blob/592e22292a367312c35e13de7fdb888f029981d6/queries/highlights.scm
; HEEx delimiters
[
"<!"
"<!--"
"<"
"<%!--"
"<%#"
">"
"</"
"--%>"
"-->"
"/>"
"{"
"}"
; These could be `@keyword`s but the closing `>` wouldn't be highlighted
; as `@keyword`
"<:"
"</:"
] @punctuation.bracket

; Non-comment or tag delimiters
[
"<%"
"<%="
"<%%="
"%>"
] @keyword

; HEEx operators are highlighted as such
"=" @operator

; HEEx inherits the DOCTYPE tag from HTML
(doctype) @constant

; HEEx comments are highlighted as such
(comment) @comment

; HEEx tags are highlighted as HTML
(tag_name) @tag

; HEEx slots are highlighted as atoms (symbols)
(slot_name) @string.special.symbol

; HEEx attributes are highlighted as HTML attributes
(attribute_name) @attribute
[
(attribute_value)
(quoted_attribute_value)
] @string

; HEEx components are highlighted as Elixir modules and functions
(component_name
[
(module) @module
(function) @function
"." @punctuation.delimiter
])
21 changes: 21 additions & 0 deletions runtime/queries/heex/injections.scm
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
; https://github.com/connorlay/tree-sitter-heex/blob/592e22292a367312c35e13de7fdb888f029981d6/queries/injections.scm
; directives are standalone tags like '<%= @x %>'
;
; partial_expression_values are elixir code that is part of an expression that
; spans multiple directive nodes, so they must be combined. For example:
; <%= if true do %>
; <p>hello, tree-sitter!</p>
; <% end %>
((directive (partial_expression_value) @injection.content)
(#set! injection.language "elixir")
(#set! injection.include-children)
(#set! injection.combined))

; Regular expression_values do not need to be combined
((directive (expression_value) @injection.content)
(#set! injection.language "elixir"))

; expressions live within HTML tags, and do not need to be combined
; <link href={ Routes.static_path(..) } />
((expression (expression_value) @injection.content)
(#set! injection.language "elixir"))

0 comments on commit 4836bb3

Please sign in to comment.