From 44d205b0f20cc7e0d47f710605b0929ae0ffc626 Mon Sep 17 00:00:00 2001 From: Arthur Deierlein <110528300+c0rydoras@users.noreply.github.com> Date: Tue, 19 Mar 2024 17:26:50 +0100 Subject: [PATCH] Add support for ember.js templates (#9902) * feat: add support for ember .hbs (glimmer) templates * adjust highlights to helix * highlight this correctly in block statements * correctly highlight attributes * correctly highlight hash_pair * add newline to highlights.scm * refactor: use #any-of and #eq instead of #match * chore: add newline to languages.toml --- book/src/generated/lang-support.md | 1 + languages.toml | 25 ++++++- runtime/queries/glimmer/highlights.scm | 94 ++++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 runtime/queries/glimmer/highlights.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index 47f8057e006f..b74a8cdb7cd5 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -60,6 +60,7 @@ | git-ignore | ✓ | | | | | git-rebase | ✓ | | | | | gleam | ✓ | ✓ | | `gleam` | +| glimmer | ✓ | | | `ember-language-server` | | glsl | ✓ | ✓ | ✓ | | | gn | ✓ | | | | | go | ✓ | ✓ | ✓ | `gopls`, `golangci-lint-langserver` | diff --git a/languages.toml b/languages.toml index ccd546fa7de7..5ce10bf0ee9e 100644 --- a/languages.toml +++ b/languages.toml @@ -100,6 +100,7 @@ blueprint-compiler = { command = "blueprint-compiler", args = ["lsp"] } typst-lsp = { command = "typst-lsp" } pkgbuild-language-server = { command = "pkgbuild-language-server" } helm_ls = { command = "helm_ls", args = ["serve"] } +ember-language-server = { command = "ember-language-server", args = ["--stdio"] } [language-server.ansible-language-server] command = "ansible-language-server" @@ -3390,4 +3391,26 @@ scope = "source.helm" roots = ["Chart.yaml"] comment-token = "#" language-servers = ["helm_ls"] -file-types = [ { glob = "templates/*.yaml" }, { glob = "templates/_helpers.tpl"}, { glob = "templates/NOTES.txt" } ] \ No newline at end of file +file-types = [ { glob = "templates/*.yaml" }, { glob = "templates/_helpers.tpl"}, { glob = "templates/NOTES.txt" } ] + +[[language]] +name = "glimmer" +scope = "source.glimmer" +injection-regex = "hbs" +file-types = [{ glob = "{app,addon}/{components,templates}/*.hbs" }] +block-comment-tokens = { start = "{{!", end = "}}" } +roots = ["package.json", "ember-cli-build.js"] +grammar = "glimmer" +language-servers = ["ember-language-server"] +formatter = { command = "prettier", args = ['--parser', 'glimmer'] } + +[language.auto-pairs] +'"' = '"' +'{' = '}' +'(' = ')' +'<' = '>' +"'" = "'" + +[[grammar]] +name = "glimmer" +source = { git = "https://github.com/ember-tooling/tree-sitter-glimmer", rev = "5dc6d1040e8ff8978ff3680e818d85447bbc10aa" } diff --git a/runtime/queries/glimmer/highlights.scm b/runtime/queries/glimmer/highlights.scm new file mode 100644 index 000000000000..789722ad0736 --- /dev/null +++ b/runtime/queries/glimmer/highlights.scm @@ -0,0 +1,94 @@ +; === Tag Names === + +; Tags that start with a lower case letter are HTML tags +; We'll also use this highlighting for named blocks (which start with `:`) +((tag_name) @tag + (#match? @tag "^(:)?[a-z]")) +; Tags that start with a capital letter are Glimmer components +((tag_name) @constructor + (#match? @constructor "^[A-Z]")) + +(attribute_name) @attribute + +(string_literal) @string +(number_literal) @constant.numeric.integer +(boolean_literal) @constant.builtin.boolean + +(concat_statement) @string + +; === Block Statements === + +; Highlight the brackets +(block_statement_start) @punctuation.delimiter +(block_statement_end) @punctuation.delimiter + +; Highlight `if`/`each`/`let` +(block_statement_start path: (identifier) @keyword.control.conditional) +(block_statement_end path: (identifier) @keyword.control.conditional) +((mustache_statement (identifier) @keyword.control.conditional) + (#eq? @keyword.control.conditional "else")) + +; == Mustache Statements === + +; Hightlight the whole statement, to color brackets and separators +(mustache_statement) @punctuation.delimiter + +; An identifier in a mustache expression is a variable +((mustache_statement [ + (path_expression (identifier) @variable) + (identifier) @variable + ]) + (#not-any-of? @variable "yield" "outlet" "this" "else")) +; As are arguments in a block statement +((block_statement_start argument: [ + (path_expression (identifier) @variable) + (identifier) @variable + ]) + (#not-eq? @variable "this")) +; As is an identifier in a block param +(block_params (identifier) @variable) +; As are helper arguments +((helper_invocation argument: [ + (path_expression (identifier) @variable) + (identifier) @variable + ]) + (#not-eq? @variable "this")) +; `this` should be highlighted as a built-in variable +((identifier) @variable.builtin + (#eq? @variable.builtin "this")) + +; If the identifier is just "yield" or "outlet", it's a keyword +((mustache_statement (identifier) @keyword.control.return) + (#any-of? @keyword.control.return "yield" "outlet")) + +; Helpers are functions +((helper_invocation helper: [ + (path_expression (identifier) @function) + (identifier) @function + ]) + (#not-any-of? @function "if" "yield")) + +((helper_invocation helper: (identifier) @keyword.control.conditional) + (#any-of? @keyword.control.conditional "if" "yield")) + +(hash_pair key: (identifier) @variable) +(hash_pair value: (identifier) @variable) +(hash_pair [ + (path_expression (identifier) @variable) + (identifier) @variable + ]) + +(comment_statement) @comment + +(attribute_node "=" @operator) + +(block_params "as" @keyword.control) +(block_params "|" @operator) + +[ + "<" + ">" + "" +] @punctuation.delimiter +