Files
dotfiles/dot_config/bat/syntaxes/just.sublime-syntax
2025-06-18 11:53:02 +01:00

801 lines
20 KiB
YAML

%YAML 1.2
---
# Syntax for the just command runner
#
# Based on the just.sublime-syntax file by @TonioGela
# Largely rewritten in 2022 by @nk9 using examples by @deathaxe
# https://www.sublimetext.com/docs/syntax.html
name: Just
scope: source.just
version: 2
hidden: false
file_extensions:
- .justfile
- just
- justfile
variables:
valid_name: '[a-zA-Z_][a-zA-Z0-9_-]*'
built_in_functions: |-
(?x:
absolute_path
| append
| arch
| blake3
| blake3_file
| cache_dir
| cache_directory
| canonicalize
| capitalize
| choose
| clean
| config_dir
| config_directory
| config_local_dir
| config_local_directory
| data_dir
| data_directory
| data_local_dir
| data_local_directory
| datetime
| datetime_utc
| encode_uri_component
| env
| env_var
| env_var_or_default
| error
| executable_dir
| executable_directory
| extension
| file_name
| file_stem
| home_dir
| home_directory
| invocation_dir
| invocation_dir_native
| invocation_directory
| invocation_directory_native
| is_dependency
| join
| just_executable
| just_pid
| justfile
| justfile_dir
| justfile_directory
| kebabcase
| lowercamelcase
| lowercase
| module_dir
| module_directory
| module_file
| num_cpus
| os
| os_family
| parent_dir
| parent_directory
| path_exists
| prepend
| quote
| read
| replace
| replace_regex
| require
| semver_matches
| sha256
| sha256_file
| shell
| shoutykebabcase
| shoutysnakecase
| snakecase
| source_dir
| source_directory
| source_file
| style
| titlecase
| trim
| trim_end
| trim_end_match
| trim_end_matches
| trim_start
| trim_start_match
| trim_start_matches
| uppercamelcase
| uppercase
| uuid
| which
| without_extension
)
boolean_settings: |-
(?x:
allow-duplicate-recipes
| allow-duplicate-variables
| dotenv-load
| export
| fallback
| ignore-comments
| positional-arguments
| quiet
| unstable
)
string_settings: |-
(?x:
dotenv-filename
| dotenv-path
| tempdir
| working-directory
)
shell_settings: |-
(?x:
script-interpreter
| shell
| windows-shell
)
deprecated_settings: |-
(?x: windows-powershell )
recipe_attributes_bare: |-
(?x:
confirm
| linux
| macos
| no-cd
| no-exit-message
| no-quiet
| openbsd
| positional-arguments
| private
| script
| unix
| windows
)
recipe_attributes_arguments: |-
(?x:
confirm
| doc
| extension
| group
| script
| working-directory
)
###############################################################################
# MAIN CONTEXT
###############################################################################
contexts:
main:
- include: includes
- include: modules
- include: settings
- include: aliases
- include: comments
- include: assignment
- include: recipe-attributes
- include: recipe-definition
- include: recipe-generic-line
prototype:
- include: comments
###[ COMMENTS ]################################################################
comments:
- match: '#[^!]'
scope: punctuation.definition.comment.begin.just
push: comment-line
comment-line:
- meta_include_prototype: false
- meta_scope: comment.line.number-sign.just
- include: line-end
###[ IMPORTS ]#################################################################
includes:
- match: ^(!include) (.*)$
scope: meta.statement.import.just
captures:
1: keyword.control.import.just
2: meta.generic-name.just
modules:
- match: ^(mod)(\??)(\s+\w+)
scope: meta.statement.mod.just
captures:
1: keyword.control.import.just
2: keyword.operator.assignment.just
3: meta.generic-name.just
- match: '`'
scope: invalid.illegal.just
pop: 2
- include: quoted-strings
###[ ALIASES ]#################################################################
aliases:
- match: ^(alias)\s+({{valid_name}})\s*(:=)\s+({{valid_name}})(?=.*$)
captures:
1: support.function.export.just
2: variable.other.just
3: keyword.operator.assignment.just
4: variable.function.just
###[ STATEMENTS ]##############################################################
just-expressions:
- include: groups
- include: operators
- include: function-calls
- include: if-statements
- include: all-strings
- include: operands-variables
###[ FUNCTION CALL ]###########################################################
function-calls:
- match: '{{built_in_functions}}\s*(?=\()'
scope:
meta.function-call.identifier.just
support.function.builtin.just
push: function-call-arguments
function-call-arguments:
- meta_include_prototype: false
- match: \(
scope: punctuation.section.group.begin.just
set: function-call-arguments-body
function-call-arguments-body:
- meta_scope: meta.function-call.arguments.just
- include: group-end
- include: just-expressions
- match: ({{valid_name}})\s*
captures:
1: variable.parameter.just
###[ GROUPS ]##################################################################
groups:
- match: \(
scope: punctuation.section.group.begin.just
push: group-body
group-end:
- match: \)
scope: punctuation.section.group.end.just
pop: 1
group-body:
- meta_scope: meta.group.just
- include: group-end
- include: just-expressions
###[ IF STATEMENT ]############################################################
if-statements:
- match: if\b
scope: keyword.control.conditional.if.just
push: if-statement-condition-body
- match: else\b
scope: keyword.control.conditional.else.just
push: else-statement-block
if-statement-condition-body:
- meta_scope: meta.statement.conditional.if.just
- match: \{
scope: punctuation.section.block.begin.just
push: if-else-block-body
- match: '!=|==|=~'
scope: keyword.operator.comparison.just
- include: just-expressions
- include: else-pop
else-statement-block:
- meta_scope: meta.statement.conditional.else.just
- match: \{
scope: punctuation.section.block.begin.just
push: if-else-block-body
- include: else-pop
if-else-block-body:
- meta_scope: meta.block.just
- match: \}
scope: punctuation.section.block.end.just
pop: 2
- include: just-expressions
###[ OPERATORS ]###############################################################
operators:
- include: punctuation-separators
- match: (\+|\/)
scope: keyword.operator.arithmetic.just
punctuation-separators:
- match: ','
scope: punctuation.separator.sequence.just
###[ CHARACTERS ]##############################################################
all-strings:
- include: quoted-strings
- include: backtick-strings
quoted-strings:
- include: single-quote-block-strings
- include: single-quote-strings
- include: double-quote-block-strings
- include: double-quote-strings
backtick-strings:
- include: backtick-quote-block-strings
- include: backtick-quote-strings
backtick-quote-block-strings:
- match: '```'
scope: punctuation.section.interpolation.begin.just
push:
- meta_scope: meta.string.shell meta.interpolation.command.shell
- meta_include_prototype: false
- match: '```'
scope: punctuation.section.interpolation.end.just
pop: 1
backtick-quote-strings:
- match: '`'
scope: punctuation.section.interpolation.begin.just
push:
- meta_scope: meta.string.shell meta.interpolation.command.shell
- meta_include_prototype: false
- match: '`'
scope: punctuation.section.interpolation.end.just
pop: 1
double-quote-block-strings:
- match: '"""'
scope: punctuation.definition.string.begin.just
push:
- meta_scope: string.quoted.double.block.just
- meta_include_prototype: false
- match: \\.
scope: constant.character.escape.just
- match: '"""'
scope: punctuation.definition.string.end.just
pop: 1
double-quote-strings:
- match: '"'
scope: punctuation.definition.string.begin.just
push:
- meta_scope: string.quoted.double.just
- meta_include_prototype: false
- match: \\.
scope: constant.character.escape.just
- match: '"'
scope: punctuation.definition.string.end.just
pop: 1
single-quote-block-strings:
- match: "'''"
scope: punctuation.definition.string.begin.just
push:
- meta_scope: string.quoted.single.block.just
- meta_include_prototype: false
- match: "'''"
scope: punctuation.definition.string.end.just
pop: 1
single-quote-strings:
- match: "'"
scope: punctuation.definition.string.begin.just
push:
- meta_scope: string.quoted.single.just
- meta_include_prototype: false
- match: "'"
scope: punctuation.definition.string.end.just
pop: 1
###[ VARIABLES ]###############################################################
operands-variables:
# First check for an invalid function
- match: \b({{valid_name}})\b\s*(\()
captures:
1: source.just
2: invalid.illegal.just
- match: \b(?:{{valid_name}})\b
scope: variable.other.just
###[ VARIABLE ASSIGNMENT ]#####################################################
assignment:
- match: (export)?\s*({{valid_name}})\s*(?=:=)
captures:
1: keyword.declaration.variable.just
2: variable.other.just
push: assignment-value
assignment-value:
- meta_include_prototype: false
- match: :=
scope: keyword.operator.assignment.just
set: assignment-value-body
assignment-value-body:
- include: eol-pop
- include: just-expressions
###[ RECIPE DEFINITION ]#######################################################
# Recipe definition lines, including attributes, arguments and dependencies
recipe-attributes:
- match: ^\[
scope: meta.annotation.just variable.annotation.just
push:
- recipe-attributes-body
- expect-recipe-attribute-name
- include: eol-pop
recipe-attributes-body:
- meta_scope: meta.sequence.list.just meta.annotation.just
- match: \]
scope: variable.annotation.just
pop: 1
- match: ','
scope: punctuation.separator.parameters.just
push: expect-recipe-attribute-name
- include: eol-pop
expect-recipe-attribute-name:
- match: ({{recipe_attributes_arguments}})
scope: variable.annotation.just
set: recipe-attribute-argument
- match: ({{recipe_attributes_bare}})
scope: variable.annotation.just
pop: 1
- match: \]
scope: invalid.illegal.just
comment: If properly handled, this frame will be popped before the \] is encountered
pop: 2
- include: else-pop
recipe-attribute-argument:
- match: \(
scope: punctuation.definition.annotation.begin.just
set: recipe-attribute-argument-paren-body
- match: ':'
scope: keyword.operator.assignment.just
set: recipe-attribute-argument-colon-body
- include: else-pop
recipe-attribute-argument-paren-body:
- match: \)
scope: punctuation.definition.annotation.end.just
pop: 1
- match: ','
scope: punctuation.separator.parameters.just
- match: '`'
scope: invalid.illegal.just
pop: 1
- include: quoted-strings
recipe-attribute-argument-colon-body:
- match: '`'
scope: invalid.illegal.just
pop: 1
- include: quoted-strings
- include: else-pop
recipe-definition:
- match: (?=^@?{{valid_name}}(?![^:]*:=)) # Matches '^recipeName' but not '^varName :='
push:
- recipe-body
- recipe-name
- recipe-modifier
recipe-modifier:
- match: ^@
scope: meta.function.just storage.modifier.quiet.just
- include: else-pop
recipe-name:
- match: \b{{valid_name}}
scope: meta.function.just entity.name.function.just
pop: 1 # Only match the first instance
push:
- recipe-dependencies
- recipe-assignment
- recipe-parameter
- include: else-pop
recipe-assignment:
- match: ':'
scope: keyword.operator.assignment.just
- include: eol-pop
- include: else-pop
recipe-parameter:
- meta_content_scope: meta.function.parameters.just
- match: (?=[\+\*$a-zA-Z_])
push:
- recipe-parameter-assignment
- recipe-parameter-name
- recipe-export-operator
- recipe-variadic-operator
- include: else-pop
recipe-variadic-operator:
- match: '[\+\*](?!\s*:)'
scope: keyword.operator.variadic.just
pop: 1 # Only one allowed
- include: else-pop
recipe-export-operator:
- match: \$(?=\s*{{valid_name}})
scope: keyword.operator.exported.just
- match: '[\+\*\$]'
scope: invalid.illegal.just
- include: else-pop
recipe-parameter-assignment:
- match: =
scope: keyword.operator.assignment.just
push:
- include: just-expressions
- match: (\s+|(?=:))
pop: 1
- include: else-pop
recipe-parameter-name:
- match: \b{{valid_name}}\b
scope: variable.parameter.just
- include: else-pop
recipe-dependencies:
- match: (?=\()
push: recipe-dependency-with-args
- match: \b{{valid_name}}\b
scope: variable.function.just
- match: '&&'
scope: keyword.operator.logical.just
- include: eol-pop
- include: else-pop
recipe-dependency-with-args:
- match: \(
scope: punctuation.section.group.begin.just
push: recipe-dependency-with-args-body
recipe-dependency-with-args-body:
- meta_scope: meta.group.just
- match: \b{{valid_name}}
scope: variable.function.just
push:
- include: just-expressions
- include: else-pop
- include: recipe-dependency-group-end
recipe-dependency-group-end:
- match: \)
scope: punctuation.section.group.end.just
pop: 2 # End dependency group
recipe-body:
# Python script shebang
- match: ^\s+(?=#!.*\bpython(?:\d(?:\.\d+)?)?\b)
pop: 1
embed: scope:source.python.embedded.just
escape: ^(?=\S)
# Shell script shebang
- match: ^\s+(?=#!.*\b(bash|zsh|sh)\b)
pop: 1
embed: scope:source.shell.embedded.just
escape: ^(?=\S)
- match: ^
comment: Content using the default shell
pop: 1
###[ RECIPE CONTENTS ]#########################################################
recipe-generic-line:
- match: ^(?=\s+\S)
push:
- recipe-generic-content
- recipe-content-modifiers
recipe-generic-content:
- include: recipe-content-interpolations
- include: recipe-content-strings
- match: (\\)$\n?
captures:
1: punctuation.separator.continuation.line.just
- match: $\n
comment: |
No trailing '?', so this will NOT match lines matched by the previous rule.
There is no pop there, so this context will remain on the stack WITHOUT the
recipe-content-modifiers. This ensures those modifiers are matched
IFF they occur at the beginning of non-continuation lines.
pop: 1
recipe-content-modifiers:
- match: ^\s+((@)|(-)(@)|(-)|(@)(-))(?!-)
captures:
2: storage.modifier.quiet.just
3: storage.modifier.ignore-error.just
4: storage.modifier.quiet.just
5: storage.modifier.ignore-error.just
6: storage.modifier.quiet.just
7: storage.modifier.ignore-error.just
- include: else-pop
recipe-content-interpolations:
- match: \{\{\{\{
comment: Escaped double brace. Do nothing
- match: \{\{(?!\{)
scope: punctuation.section.interpolation.begin.just
push: recipe-content-interpolation-body
recipe-content-interpolation-body:
- meta_scope: meta.interpolation.just
- match: \}\}
scope: punctuation.section.interpolation.end.just
pop: 1
- include: just-expressions
# Sadly, almost an exact duplicate of the 'strings' context, but
# needed to include interpolations, which would have to be nested
# inside a push: in the other context.
recipe-content-strings:
- match: '"'
scope: punctuation.definition.string.begin.just
push:
- meta_scope: meta.string.just string.quoted.double.just
- meta_include_prototype: false
- match: \\.
scope: constant.character.escape.just
- match: '"'
scope: punctuation.definition.string.end.just
pop: 1
- include: recipe-content-string-interpolations
- match: "'"
scope: punctuation.definition.string.begin.just
push:
- meta_scope: meta.string.just string.quoted.single.just
- meta_include_prototype: false
- match: "'"
scope: punctuation.definition.string.end.just
pop: 1
- include: recipe-content-string-interpolations
recipe-content-string-interpolations:
- match: \{\{\{\{
comment: Escaped double brace. Do nothing
- match: \{\{(?!\{)
scope: punctuation.section.interpolation.begin.just
push: recipe-content-string-interpolation-body
recipe-content-string-interpolation-body:
- clear_scopes: 1
- meta_scope: meta.interpolation.just
- include: recipe-content-interpolation-body
###[ Set Expressions ]#########################################################
# Ex: "set shell := ['zsh', '-cu']", "set dotenv-load", "set export := false"
settings:
- match: ^set(?=\s)
scope: storage.modifier.definition.just
push: settings-name
settings-name:
- meta_scope: meta.setting-name.just
- include: settings-boolean-name
- include: settings-shell-name
- include: settings-string-name
- include: settings-deprecated-name
- include: settings-invalid-name
- include: else-pop
settings-boolean-name:
- match: '{{boolean_settings}}\b'
scope: entity.name.definition.just
set:
- settings-boolean-value
- assignment-operator
settings-boolean-value:
- match: (?:true|false)\b
scope: constant.language.boolean.just
pop: 1
- include: else-pop
- include: eol-pop
settings-deprecated-name:
- match: ({{deprecated_settings}})\b
captures:
1: invalid.deprecated.just
push: eol-pop
settings-invalid-name:
- match: ({{valid_name}})\b\s*:=
captures:
1: invalid.illegal.just
push: eol-pop
settings-shell-name:
- match: '{{shell_settings}}\b'
scope: entity.name.definition.just
set:
- settings-shell-value
- assignment-operator
settings-shell-value:
- match: \[
scope: punctuation.section.brackets.start.just
set: string-array-body
- include: else-pop
- include: eol-pop
string-array-body:
- meta_scope: meta.sequence.list.just
- match: \]
scope: punctuation.section.brackets.end.just
pop: 1
- match: ','
scope: punctuation.separator.parameters.just
- include: all-strings
- include: eol-pop #??
settings-string-name:
- match: '{{string_settings}}\b'
scope: entity.name.definition.just
set:
- settings-string-value
- assignment-operator
settings-string-value:
- include: all-strings
- include: eol-pop
# ###[ General Types ]##########################################################
variable-name:
- match: \b{{valid_name}}\b
scope: variable.other.just
- include: else-pop
assignment-operator:
- match: :=
scope: keyword.operator.assignment.just
- include: else-pop
###[ Common Prototypes ]#######################################################
line-end:
- match: $
pop: 1
# Remove the current stack item when we're about to reach a new character
# Learn more: https://github.com/sublimehq/Packages/issues/757#issuecomment-287193733
else-pop:
- match: (?=\S)
pop: 1
eol-pop:
- match: $\n?
pop: 1