From a92e4e555f19f357c85a2ee4711cb8c524568918 Mon Sep 17 00:00:00 2001 From: Simon Heath Date: Thu, 3 Apr 2025 12:33:49 -0400 Subject: [PATCH] Basic support for Fennel. Might even work. Closes https://github.com/helix-editor/helix/issues/3846 . There are two different tree-sitter parsers for Fennel mentioned in that issue: * https://github.com/travonted/tree-sitter-fennel * https://github.com/alexmozaidze/tree-sitter-fennel This PR has arbitrarily chosen the second one. There's also several different `highlights.scm` options, I've mostly chosen the most recent one but tried to incorporate bits and pieces from the others. --- languages.toml | 16 +++ runtime/queries/fennel/highlights.scm | 196 ++++++++++++++++++++++++++ 2 files changed, 212 insertions(+) create mode 100644 runtime/queries/fennel/highlights.scm diff --git a/languages.toml b/languages.toml index cf6471dea..99614c8ee 100644 --- a/languages.toml +++ b/languages.toml @@ -39,6 +39,7 @@ elm-language-server = { command = "elm-language-server" } elp = { command = "elp", args = ["server"] } elvish = { command = "elvish", args = ["-lsp"] } erlang-ls = { command = "erlang_ls" } +fennel-ls = { command = "fennel-ls" } fish-lsp = { command = "fish-lsp", args = ["start"], environment = { fish_lsp_show_client_popups = "false" } } forc = { command = "forc", args = ["lsp"] } forth-lsp = { command = "forth-lsp" } @@ -396,6 +397,21 @@ indent = { tab-width = 2, unit = " " } name = "elixir" source = { git = "https://github.com/elixir-lang/tree-sitter-elixir", rev = "02a6f7fd4be28dd94ee4dd2ca19cb777053ea74e" } +[[language]] +name = "fennel" +scope = "source.fennel" +file-types = ["fnl"] +shebangs = ["fennel"] +comment-token = ";" +language-servers = ["fennel-ls"] +formatter = { command = "fnlfmt", args = ["-"]} +indent = { tab-width = 2, unit = " " } + +[[grammar]] +name = "fennel" +source.git = "https://github.com/alexmozaidze/tree-sitter-fennel" +source.rev = "ea4a536bca8997e30b22709f210f44f97e75bf7d" + [[language]] name = "fish" scope = "source.fish" diff --git a/runtime/queries/fennel/highlights.scm b/runtime/queries/fennel/highlights.scm new file mode 100644 index 000000000..3d26be314 --- /dev/null +++ b/runtime/queries/fennel/highlights.scm @@ -0,0 +1,196 @@ +; Most primitive nodes +(shebang) @keyword.directive + +(comment) @comment @spell + +(fn_form + name: [ + (symbol) @function + (multi_symbol + member: (symbol_fragment) @function .) + ]) + +(lambda_form + name: [ + (symbol) @function + (multi_symbol + member: (symbol_fragment) @function .) + ]) + +((symbol) @operator + (#any-of? @operator + ; arithmetic + "+" "-" "*" "/" "//" "%" "^" + ; comparison + ">" "<" ">=" "<=" "=" "~=" + ; other + "#" "." "?." "..")) + +((symbol) @keyword.operator + (#any-of? @keyword.operator + ; comparison + "not=" + ; boolean + "and" "or" "not" + ; bitwise + "lshift" "rshift" "band" "bor" "bxor" "bnot" + ; other + "length")) + +(case_guard + call: (_) @keyword.conditional) + +(case_guard_or_special + call: (_) @keyword.conditional) + +(case_catch + call: (symbol) @keyword) + +(import_macros_form + imports: (table_binding + (table_binding_pair + value: (symbol_binding) @function.macro))) + + +((symbol) @keyword.function + (#any-of? @keyword.function "fn" "lambda" "λ" "hashfn")) + +((symbol) @keyword.repeat + (#any-of? @keyword.repeat "for" "each" "while")) + +((symbol) @keyword.conditional + (#any-of? @keyword.conditional "if" "when" "match" "case" "match-try" "case-try")) + +((symbol) @keyword + (#any-of? @keyword + "global" "local" "let" "set" "var" "comment" "do" "doc" "eval-compiler" "lua" "macros" "unquote" + "quote" "tset" "values" "tail!")) + +((symbol) @keyword.import + (#any-of? @keyword.import "require" "require-macros" "import-macros" "include")) + +((symbol) @function.macro + (#any-of? @function.macro + "collect" "icollect" "fcollect" "accumulate" "faccumulate" "->" "->>" "-?>" "-?>>" "?." "doto" + "macro" "macrodebug" "partial" "pick-args" "pick-values" "with-open")) + +((symbol) @variable.builtin + (#eq? @variable.builtin "...")) + +((symbol) @constant.builtin + (#eq? @constant.builtin "_VERSION")) + +((symbol) @function.builtin + (#any-of? @function.builtin + "assert" "collectgarbage" "dofile" "error" "getmetatable" "ipairs" "load" "loadfile" "next" + "pairs" "pcall" "print" "rawequal" "rawget" "rawlen" "rawset" "require" "select" "setmetatable" + "tonumber" "tostring" "type" "warn" "xpcall" "module" "setfenv" "loadstring" "unpack")) + +; TODO: Highlight builtin methods (`table.unpack`, etc) as @function.builtin +([ + (symbol) @module.builtin + (multi_symbol + base: (symbol_fragment) @module.builtin) +] + (#any-of? @module.builtin + "vim" "_G" "_ENV" "debug" "io" "jit" "math" "os" "package" "string" "table" "utf8")) + +([ + (symbol) @variable.builtin + (multi_symbol + . + (symbol_fragment) @variable.builtin) +] + (#eq? @variable.builtin "arg")) +(symbol_option) @keyword.directive + +(escape_sequence) @string.escape + +(multi_symbol + "." @punctuation.delimiter + member: (symbol_fragment) @variable.member) + +(list + call: (symbol) @function.call) + +(list + call: (multi_symbol + member: (symbol_fragment) @function.call .)) + +(multi_symbol_method + ":" @punctuation.delimiter + method: (symbol_fragment) @function.method.call) + +(quasi_quote_reader_macro + macro: _ @punctuation.special) + +(quote_reader_macro + macro: _ @punctuation.special) + +(unquote_reader_macro + macro: _ @punctuation.special) + +[ ":" ":until" "&" "&as" "?" ] @punctuation.special +(vararg) @punctuation.special + +(hashfn_reader_macro + macro: _ @keyword.function) + +(docstring) @string.documentation + +; NOTE: The macro name is highlighted as @variable because it +; gives a nicer contrast instead of everything being the same +; color. Rust queries use this workaround too for `macro_rules!`. +(macro_form + name: [ + (symbol) @variable + (multi_symbol + member: (symbol_fragment) @variable .) + ]) + +[ + "(" + ")" + "{" + "}" + "[" + "]" +] @punctuation.bracket + +(sequence_arguments + (symbol_binding) @variable.parameter) + +(sequence_arguments + (rest_binding + rhs: (symbol_binding) @variable.parameter)) + +((symbol) @variable.parameter + (#any-of? @variable.parameter "$" "$...")) + +((symbol) @variable.parameter + (#any-of? @variable.parameter "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9")) + +[ + (nil) + (nil_binding) +] @constant.builtin + +[ + (boolean) + (boolean_binding) +] @boolean + +[ + (number) + (number_binding) +] @number + +[ + (string) + (string_binding) +] @string + +[ + (symbol) + (symbol_binding) +] @variable