diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index 93f618c0..ab6bcc1f 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -1025,9 +1025,10 @@ impl Loader { match capture { InjectionLanguageMarker::Name(string) => self.language_config_for_name(string), InjectionLanguageMarker::Filename(file) => self.language_config_for_file_name(file), - InjectionLanguageMarker::Shebang(shebang) => { - self.language_config_for_language_id(shebang) - } + InjectionLanguageMarker::Shebang(shebang) => self + .language_config_ids_by_shebang + .get(shebang) + .and_then(|&id| self.language_configs.get(id).cloned()), } } diff --git a/languages.toml b/languages.toml index 4a57a7e5..d51a1821 100644 --- a/languages.toml +++ b/languages.toml @@ -3082,7 +3082,7 @@ indent = { tab-width = 4, unit = " " } [[grammar]] name = "just" -source = { git = "https://github.com/IndianBoy42/tree-sitter-just", rev = "8af0aab79854aaf25b620a52c39485849922f766" } +source = { git = "https://github.com/IndianBoy42/tree-sitter-just", rev = "379fbe36d1e441bc9414ea050ad0c85c9d6935ea" } [[language]] name = "gn" diff --git a/runtime/queries/just/folds.scm b/runtime/queries/just/folds.scm index 6fc68fbd..77079fd4 100644 --- a/runtime/queries/just/folds.scm +++ b/runtime/queries/just/folds.scm @@ -1,4 +1,10 @@ -(body) @fold -(recipe) @fold -(interpolation) @fold -(item (_) @fold) +; From + +; Define collapse points + +([ + (recipe) + (string) + (external_command) +] @fold + (#trim! @fold)) diff --git a/runtime/queries/just/highlights.scm b/runtime/queries/just/highlights.scm index 1026f654..d5e5cc19 100644 --- a/runtime/queries/just/highlights.scm +++ b/runtime/queries/just/highlights.scm @@ -1,33 +1,149 @@ -(assignment (NAME) @variable) -(alias (NAME) @variable) -(value (NAME) @variable) -(parameter (NAME) @variable) -(setting (NAME) @keyword) -(setting "shell" @keyword) +; From -(call (NAME) @function) -(dependency (NAME) @function) -(depcall (NAME) @function) -(recipeheader (NAME) @function) +; This file specifies how matched syntax patterns should be highlighted -(depcall (expression) @variable.parameter) -(parameter) @variable.parameter -(variadic_parameters) @variable.parameter +[ + "export" + "import" +] @keyword.control.import -["if" "else"] @keyword.control.conditional +"mod" @keyword.directive -(string) @string +[ + "alias" + "set" + "shell" +] @keyword -(boolean ["true" "false"]) @constant.builtin.boolean +[ + "if" + "else" +] @keyword.control.conditional -(comment) @comment +; Variables -; (interpolation) @string +(value + (identifier) @variable) -(shebang interpreter:(TEXT) @keyword ) @comment +(alias + left: (identifier) @variable) -["export" "alias" "set"] @keyword +(assignment + left: (identifier) @variable) -["@" "==" "!=" "+" ":="] @operator +; Functions -[ "(" ")" "[" "]" "{{" "}}" "{" "}"] @punctuation.bracket +(recipe_header + name: (identifier) @function) + +(dependency + name: (identifier) @function) + +(dependency_expression + name: (identifier) @function) + +(function_call + name: (identifier) @function) + +; Parameters + +(parameter + name: (identifier) @variable.parameter) + +; Namespaces + +(module + name: (identifier) @namespace) + +; Operators + +[ + ":=" + "?" + "==" + "!=" + "=~" + "@" + "=" + "$" + "*" + "+" + "&&" + "@-" + "-@" + "-" + "/" + ":" +] @operator + +; Punctuation + +"," @punctuation.delimiter + +[ + "{" + "}" + "[" + "]" + "(" + ")" + "{{" + "}}" +] @punctuation.bracket + +[ "`" "```" ] @punctuation.special + +; Literals + +(boolean) @constant.builtin.boolean + +[ + (string) + (external_command) +] @string + +(escape_sequence) @constant.character.escape + +; Comments + +(comment) @comment.line + +(shebang) @keyword.directive + +; highlight known settings (filtering does not always work) +(setting + left: (identifier) @keyword + (#any-of? @keyword + "allow-duplicate-recipes" + "dotenv-filename" + "dotenv-load" + "dotenv-path" + "export" + "fallback" + "ignore-comments" + "positional-arguments" + "shell" + "tempdi" + "windows-powershell" + "windows-shell")) + +; highlight known attributes (filtering does not always work) +(attribute + (identifier) @attribute + (#any-of? @attribute + "private" + "allow-duplicate-recipes" + "dotenv-filename" + "dotenv-load" + "dotenv-path" + "export" + "fallback" + "ignore-comments" + "positional-arguments" + "shell" + "tempdi" + "windows-powershell" + "windows-shell")) + +; Numbers are part of the syntax tree, even if disallowed +(numeric_error) @error diff --git a/runtime/queries/just/indents.scm b/runtime/queries/just/indents.scm index 3db59746..7cfca3d7 100644 --- a/runtime/queries/just/indents.scm +++ b/runtime/queries/just/indents.scm @@ -1,3 +1,11 @@ +; From +; +; This query specifies how to auto-indent logical blocks. +; +; Better documentation with diagrams is in https://docs.helix-editor.com/guides/indent.html + [ - (recipe_body) -] @indent + (recipe) + (string) + (external_command) +] @indent @extend diff --git a/runtime/queries/just/injections.scm b/runtime/queries/just/injections.scm index cae1035a..54393059 100644 --- a/runtime/queries/just/injections.scm +++ b/runtime/queries/just/injections.scm @@ -1,16 +1,84 @@ +; From +; +; Specify nested languages that live within a `justfile` + +; ================ Always applicable ================ + ((comment) @injection.content - (#set! injection.language "comment")) + (#set! injection.language "comment")) -(shebang_recipe - (shebang - interpreter:(TEXT) @injection.language) - (shebang_body) @injection.content -) +; Highlight the RHS of `=~` as regex +((regex_literal + (_) @injection.content) + (#set! injection.language "regex")) -(source_file - (item (setting lang:(NAME) @injection.language)) - (item (recipe (body (recipe_body) @injection.content))) -) +; ================ Global defaults ================ -; ((interpolation (expression) @injection.content) -; (#set! injection.language "just")) +; Default everything to be bash +(recipe_body + !shebang + (#set! injection.language "bash") + (#set! injection.include-children)) @injection.content + +(external_command + (command_body) @injection.content + (#set! injection.language "bash")) + +; ================ Global language specified ================ +; Global language is set with something like one of the following: +; +; set shell := ["bash", "-c", ...] +; set shell := ["pwsh.exe"] +; +; We can extract the first item of the array, but we can't extract the language +; name from the string with something like regex. So instead we special case +; two things: powershell, which is likely to come with a `.exe` attachment that +; we need to strip, and everything else which hopefully has no extension. We +; separate this with a `#match?`. +; +; Unfortunately, there also isn't a way to allow arbitrary nesting or +; alternatively set "global" capture variables. So we can set this for item- +; level external commands, but not for e.g. external commands within an +; expression without getting _really_ annoying. Should at least look fine since +; they default to bash. Limitations... +; See https://github.com/tree-sitter/tree-sitter/issues/880 for more on that. + +(source_file + (setting "shell" ":=" "[" (string) @_langstr + (#match? @_langstr ".*(powershell|pwsh|cmd).*") + (#set! injection.language "powershell")) + [ + (recipe + (recipe_body + !shebang + (#set! injection.include-children)) @injection.content) + + (assignment + (expression + (value + (external_command + (command_body) @injection.content)))) + ]) + +(source_file + (setting "shell" ":=" "[" (string) @injection.language + (#not-match? @injection.language ".*(powershell|pwsh|cmd).*")) + [ + (recipe + (recipe_body + !shebang + (#set! injection.include-children)) @injection.content) + + (assignment + (expression + (value + (external_command + (command_body) @injection.content)))) + ]) + +; ================ Recipe language specified - Helix only ================ + +; Set highlighting for recipes that specify a language using builtin shebang matching +(recipe_body + (shebang) @injection.shebang + (#set! injection.include-children)) @injection.content diff --git a/runtime/queries/just/locals.scm b/runtime/queries/just/locals.scm index 18e162a9..827148a1 100644 --- a/runtime/queries/just/locals.scm +++ b/runtime/queries/just/locals.scm @@ -1,10 +1,42 @@ -(assignment (NAME) @local.definition) -(alias left:(NAME) @local.definition) -(alias right:(NAME) @local.reference) -(value (NAME) @local.reference) -(parameter (NAME) @local.definition) +; From +; +; This file tells us about the scope of variables so e.g. local +; variables override global functions with the same name -(call (NAME) @local.reference) -(dependency (NAME) @local.reference) -(depcall (NAME) @local.reference) -(recipeheader (NAME) @local.definition) +; Scope + +(recipe) @local.scope + +; Definitions + +(alias + left: (identifier) @local.definition) + +(assignment + left: (identifier) @local.definition) + +(module + name: (identifier) @local.definition) + +(parameter + name: (identifier) @local.definition) + +(recipe_header + name: (identifier) @local.definition) + +; References + +(alias + right: (identifier) @local.reference) + +(function_call + name: (identifier) @local.reference) + +(dependency + name: (identifier) @local.reference) + +(dependency_expression + name: (identifier) @local.reference) + +(value + (identifier) @local.reference) diff --git a/runtime/queries/just/textobjects.scm b/runtime/queries/just/textobjects.scm index 4be37958..bb604178 100644 --- a/runtime/queries/just/textobjects.scm +++ b/runtime/queries/just/textobjects.scm @@ -1,48 +1,18 @@ -(body) @function.inside -(recipe) @function.around -(expression - if:(expression) @function.inside -) -(expression - else:(expression) @function.inside -) -(interpolation (expression) @function.inside) @function.around -(settinglist (stringlist) @function.inside) @function.around +; From +; +; Specify how to navigate around logical blocks in code -(call (NAME) @class.inside) @class.around -(dependency (NAME) @class.inside) @class.around -(depcall (NAME) @class.inside) +(recipe + (recipe_body) @function.inside) @function.around -(dependency) @parameter.around -(depcall) @parameter.inside -(depcall (expression) @parameter.inside) +(parameters + ((_) @parameter.inside . ","? @parameter.around)) @parameter.around -(stringlist - (string) @parameter.inside - . ","? @_end - ; Commented out since we don't support `#make-range!` at the moment - ; (#make-range! "parameter.around" @parameter.inside @_end) -) -(parameters - [(parameter) - (variadic_parameters)] @parameter.inside - . " "? @_end - ; Commented out since we don't support `#make-range!` at the moment - ; (#make-range! "parameter.around" @parameter.inside @_end) -) +(dependency_expression + (_) @parameter.inside) @parameter.around -(expression - (condition) @function.inside -) @function.around -(expression - if:(expression) @function.inside -) -(expression - else:(expression) @function.inside -) - -(item [(alias) (assignment) (export) (setting)]) @class.around -(recipeheader) @class.around -(line) @class.around +(function_call + arguments: (sequence + (expression) @parameter.inside) @parameter.around) @function.around (comment) @comment.around