diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index 28f25ba3f..0b217ebfa 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -206,6 +206,7 @@ | tact | ✓ | ✓ | ✓ | | | task | ✓ | | | | | tcl | ✓ | | ✓ | | +| teal | ✓ | | | | | templ | ✓ | | | `templ` | | textproto | ✓ | ✓ | ✓ | | | tfvars | ✓ | | ✓ | `terraform-ls` | diff --git a/languages.toml b/languages.toml index ea6e6893f..7f6812404 100644 --- a/languages.toml +++ b/languages.toml @@ -123,6 +123,7 @@ tinymist = { command = "tinymist" } pkgbuild-language-server = { command = "pkgbuild-language-server" } helm_ls = { command = "helm_ls", args = ["serve"] } ember-language-server = { command = "ember-language-server", args = ["--stdio"] } +teal-language-server = { command = "teal-language-server" } [language-server.ansible-language-server] command = "ansible-language-server" @@ -1297,6 +1298,22 @@ language-servers = [ "lua-language-server" ] name = "lua" source = { git = "https://github.com/tree-sitter-grammars/tree-sitter-lua", rev = "88e446476a1e97a8724dff7a23e2d709855077f2" } +[[grammar]] +name = "teal" +source = { git = "https://github.com/euclidianAce/tree-sitter-teal", rev = "485fbdc00d811b01b2090dff4d0469fd1d0350f5" } + +[[language]] +name = "teal" +auto-format = false +scope = "source.tl" +injection-regex = "teal" +file-types = ["tl"] +comment-tokens = "--" +block-comment-tokens = { start = "--[[", end = "--]]" } +roots = [ "tlconfig.lua" ] +language-servers = [ "teal-lsp" ] +grammar = "teal" + [[language]] name = "svelte" scope = "source.svelte" diff --git a/runtime/queries/teal/folds.scm b/runtime/queries/teal/folds.scm new file mode 100644 index 000000000..e756719a4 --- /dev/null +++ b/runtime/queries/teal/folds.scm @@ -0,0 +1,15 @@ +[ +(do_statement) +(numeric_for_statement) +(generic_for_statement) +(while_statement) +(repeat_statement) +(if_statement) +(function_statement) +(record_declaration) +(interface_declaration) +(enum_declaration) +(anon_function) +(table_constructor) +] @fold + diff --git a/runtime/queries/teal/highlights.scm b/runtime/queries/teal/highlights.scm new file mode 100644 index 000000000..0a28fa7cd --- /dev/null +++ b/runtime/queries/teal/highlights.scm @@ -0,0 +1,167 @@ + +;; Primitives +(boolean) @boolean +(comment) @comment +(shebang_comment) @comment +(identifier) @variable +((identifier) @variable.builtin + (#eq? @variable.builtin "self")) +(nil) @constant.builtin +(number) @number +(string) @string +(table_constructor ["{" "}"] @constructor) +(varargs "..." @constant.builtin) +[ "," "." ":" ";" ] @punctuation.delimiter + +(escape_sequence) @string.escape +(format_specifier) @string.escape + +;; Basic statements/Keywords +[ "if" "then" "elseif" "else" ] @conditional +[ "for" "while" "repeat" "until" ] @repeat +[ "in" "local" "return" (break) (goto) "do" "end" ] @keyword +(label) @label + +;; Global isn't a real keyword, but it gets special treatment in these places +(var_declaration "global" @keyword) +(type_declaration "global" @keyword) +(function_statement "global" @keyword) +(record_declaration "global" @keyword) +(interface_declaration "global" @keyword) +(enum_declaration "global" @keyword) + +(macroexp_statement "macroexp" @keyword) + +;; Ops +(bin_op (op) @operator) +(unary_op (op) @operator) +[ "=" "as" ] @operator + +;; Functions +(function_statement + "function" @keyword.function + . name: (_) @function) +(anon_function + "function" @keyword.function) +(function_body "end" @keyword.function) + +(arg name: (identifier) @parameter) + +(function_signature + (arguments + . (arg name: (identifier) @variable.builtin)) + (#eq? @variable.builtin "self")) + +(typeargs + "<" @punctuation.bracket + . (_) @parameter + . ("," . (_) @parameter)* + . ">" @punctuation.bracket) + +(function_call + (identifier) @function . (arguments)) +(function_call + (index (_) key: (identifier) @function) . (arguments)) +(function_call + (method_index (_) key: (identifier) @function) . (arguments)) + +;; Types + +; Contextual keywords in record bodies +(record_declaration + . [ "record" ] @keyword + name: (identifier) @type) +(anon_record . "record" @keyword) +(record_body + (record_declaration + . [ "record" ] @keyword + . name: (identifier) @type)) +(record_body + (enum_declaration + . [ "enum" ] @keyword + . name: (identifier) @type)) +(record_body + (interface_declaration + . [ "interface" ] @keyword + . name: (identifier) @type)) +(record_body + (typedef + . "type" @keyword + . name: (identifier) @type . "=")) +(record_body + (macroexp_declaration + . [ "macroexp" ] @keyword)) +(record_body (metamethod "metamethod" @keyword)) +(record_body (userdata) @keyword) + +; Contextual keywords in interface bodies +(interface_declaration + . [ "interface" ] @keyword + name: (identifier) @type) +(anon_interface . "interface" @keyword) +(interface_body + (record_declaration + . [ "record" ] @keyword + . name: (identifier) @type)) +(interface_body + (enum_declaration + . [ "enum" ] @keyword + . name: (identifier) @type)) +(interface_body + (interface_declaration + . [ "interface" ] @keyword + . name: (identifier) @type)) +(interface_body + (typedef + . "type" @keyword + . name: (identifier) @type . "=")) +(interface_body + (macroexp_declaration + . [ "macroexp" ] @keyword)) +(interface_body (metamethod "metamethod" @keyword)) +(interface_body (userdata) @keyword) + +(enum_declaration + "enum" @keyword + name: (identifier) @type) + +(type_declaration "type" @keyword) +(type_declaration (identifier) @type) +(simple_type) @type +(type_index) @type +(type_union "|" @operator) +(function_type "function" @type) + +;; The rest of it +(var_declaration + declarators: (var_declarators + (var name: (identifier) @variable))) +(var_declaration + declarators: (var_declarators + (var + "<" @punctuation.bracket + . attribute: (attribute) @attribute + . ">" @punctuation.bracket))) +[ "(" ")" "[" "]" "{" "}" ] @punctuation.bracket + +;; Only highlight format specifiers in calls to string.format +;; string.format('...') +;(function_call +; called_object: (index +; (identifier) @base +; key: (identifier) @entry) +; arguments: (arguments . +; (string (format_specifier) @string.escape)) +; +; (#eq? @base "string") +; (#eq? @entry "format")) + +;; ('...'):format() +;(function_call +; called_object: (method_index +; (string (format_specifier) @string.escape) +; key: (identifier) @func-name) +; (#eq? @func-name "format")) + + +(ERROR) @error diff --git a/runtime/queries/teal/locals.scm b/runtime/queries/teal/locals.scm new file mode 100644 index 000000000..da67ae52a --- /dev/null +++ b/runtime/queries/teal/locals.scm @@ -0,0 +1,26 @@ + +(var_declaration + declarators: (var_declarators + (var (identifier)) @definition.var)) + +(var_assignment + variables: (assignment_variables + (var (identifier) @definition.var) @definition.associated)) + +(arg name: (identifier) @definition.parameter) + +(anon_function) @scope +((function_statement + (function_name) @definition.function) @scope + (#set! definition.function.scope "parent")) + +(program) @scope +(if_statement) @scope +(generic_for_statement (for_body) @scope) +(numeric_for_statement (for_body) @scope) +(repeat_statement) @scope +(while_statement (while_body) @scope) +(do_statement) @scope + +(identifier) @reference +