1
0
Fork 0
blog/syntaxes/Sublime-console/console.sublime-syntax
2020-05-19 18:20:18 +02:00

1409 lines
45 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

%YAML 1.2
# [Sublime]: https://www.sublimetext.com/docs/3/syntax.html
# [Bash]: https://www.gnu.org/software/bash/manual/bash.html
--- #---------------------------------------------------------------------------
name: console
scope: source.shell.console
file_extensions:
- sh
- bash
- zsh
- ash
- .bash_aliases
- .bash_completions
- .bash_functions
- .bash_login
- .bash_logout
- .bash_profile
- .bash_variables
- .bashrc
- .profile
- .textmate_init
- .zlogin
- .zlogout
- .zprofile
- .zshenv
- .zshrc
- PKGBUILD # https://jlk.fjfi.cvut.cz/arch/manpages/man/PKGBUILD.5
- .ebuild
- .eclass
#first_line_match: |
# (?x)
# ^\#! .* \b(bash|zsh|sh|tcsh|ash)\b
# | ^\# \s* -\*- [^*]* mode: \s* shell-script [^*]* -\*-
#-------------------------------------------------------------------------------
variables:
call_token: \./
cmd_boundary: (?=\s|;|$|>|<)
extension: \.sh
identifier: '[[:alpha:]_][[:alnum:]_]*'
identifier_non_posix: '[^{{metachar}}\d][^{{metachar}}=]*'
is_command: (?=\S)
is_end_of_interpolation: \)
is_end_of_option: '[^\w$-]|$'
is_function: \s*\b(function)\s+|(?=\s*{{identifier_non_posix}}\s*\(\s*\))
is_path_component: (?=[^\s/]*/)
is_start_of_arguments: '[`=|&;()<>\s]'
is_variable: (?=\s*{{nbc}}(?:[({]{{nbc}}[)}])?{{nbc}}=)
keyword_break: (?![-=\w])
# A character that, when unquoted, separates words. A metacharacter is a
# space, tab, newline, or one of the following characters: |, &, ;,
# (, ), <, or >.
metachar: '[\s\t\n|&;()<>]'
nbc: '[^{}()=\s]*' # non bracket characters (and also non-whitespace, parens)
start_of_option: (?:\s+|^)--?(?=[\w$])
varassign: '[+\-?]?='
#-------------------------------------------------------------------------------
contexts:
comment:
- match: (?:^\s*|\s+)(\#)
captures:
1:
comment.line.number-sign.shell
punctuation.definition.comment.begin.shell
push:
- meta_content_scope: comment.line.number-sign.shell
# NOTE: The reason for consuming the newline character is as follows.
# When triggering a snippet, its scope is tested to the *right* of the
# cursor. So, if you don't want your snippet to trigger in a comment,
# you have to use something like <scope>source.shell - comment</scope>.
# If the newline character is not scoped as a comment too, then that
# scope will never work, because the scope to the right of the cursor
# will never be a comment scope. That is, unless we consume the newline
# character (or we are editing something in the middle of an existing
# comment).
- match: '(?=\n)'
scope: comment.line.number-sign.shell
pop: true
line-continuation-or-pop-at-end:
- include: pop-at-end
- include: line-continuation
pop-at-end:
- match: $
pop: true
any-escape:
- match: \\.
scope: constant.character.escape.shell
line-continuation:
- match: \\\n
scope: punctuation.separator.continuation.line.shell
push:
- match: ^
pop: true
- match: \\(\s+)\n
captures:
1: invalid.illegal.extraneous-spaces-after-line-continuation.shell
prototype:
- include: comment
- include: line-continuation
- include: any-escape
main:
- meta_include_prototype: false
- match: ^(\$|\#|)(?:\s)
captures:
1: keyword.operator.assignment.redirection.process.shell
push: console-line
console-line:
- include: prototype
- include: funcdef
- include: vardef
- include: redirection
- include: operator-exclamation
- match: '{{is_command}}'
push: cmd
- match: $
pop: true
# NOTE: Contexts with a "-bt" suffix are the "backtick" contexts. They mirror
# the ordinary contexts, except that when a backtick is encountered while in
# a "-bt" context, we pop.
# Normally, we are in a non-bt context. When we encounter a backtick character
# (the ` character), we enter the main-bt context. Popping when encountering
# another ` character then ensures that we don't enter yet another backtick
# context.
# The "expansion" context is the **only** place where this main-bt context is
# used. If you, the reader, knows of a more elegant way to handle backticks,
# don't hesitate to change it.
main-bt:
- include: funcdef-bt
- include: vardef
- include: redirection
- match: '{{is_command}}'
push: cmd-bt
control:
- match: \bif{{keyword_break}}
scope: keyword.control.conditional.if.shell
pop: true
- match: \bthen{{keyword_break}}
scope: keyword.control.conditional.then.shell
pop: true
- match: \belif{{keyword_break}}
scope: keyword.control.conditional.elseif.shell
pop: true
- match: \bfi{{keyword_break}}
scope: keyword.control.conditional.end.shell
set: [cmd-post, cmd-args]
- match: \belse{{keyword_break}}
scope: keyword.control.conditional.else.shell
pop: true
- match: \bfor{{keyword_break}}
scope: keyword.control.loop.for.shell
set: [cmd-post, for-args]
- match: \bdo{{keyword_break}}
scope: keyword.control.loop.do.shell
pop: true
- match: \bdone{{keyword_break}}
scope: keyword.control.loop.end.shell
set: [cmd-post, cmd-args]
- match: \bwhile{{keyword_break}}
scope: keyword.control.loop.while.shell
- match: \buntil{{keyword_break}}
scope: keyword.control.loop.until.shell
- match: \bcase{{keyword_break}}
scope: keyword.control.conditional.case.shell
set: [case-body, case-word]
- match: \bcontinue{{keyword_break}}
scope: keyword.control.flow.continue.shell
- match: \bbreak{{keyword_break}}
scope: keyword.control.flow.break.shell
set: [cmd-post, cmd-args]
- match: \besac{{keyword_break}}
scope: keyword.control.conditional.end.shell
pop: true
case-word:
- match: \bin{{keyword_break}}
scope: keyword.control.in.shell
pop: true
- include: case-end-ahead
- include: expansion-and-string
case-body:
- meta_scope: meta.conditional.case.shell
- match: \besac{{keyword_break}}
scope: keyword.control.conditional.end.shell
pop: true
- match: (?=\()
push:
- clear_scopes: 1 # remove meta.conditional.case.shell
- match: \(
scope: keyword.control.conditional.patterns.begin.shell
set: case-clause-patterns
- match: (?=\S)
push: case-clause-patterns
case-clause-patterns:
- clear_scopes: 1 # remove meta.conditional.case.shell
- meta_scope: meta.conditional.case.clause.patterns.shell
- match: \)
scope: keyword.control.conditional.patterns.end.shell
set: case-clause-commands
# emergency bail outs if ')' is missing
- match: (?=;;&?|;&)
set: case-clause-commands
- include: case-end-ahead
- include: case-clause-patterns-body
case-clause-patterns-body:
# [Bash] 3.2.4.2: Each pattern undergoes tilde expansion, parameter
# expansion, command substitution, and arithmetic expansion.
- include: expansion-pattern
- include: expansion-tilde
- include: expansion-parameter
- include: expansion-command
- include: expansion-arithmetic
- include: string
- match: \|
scope: keyword.operator.logical.shell
- match: \(
scope: punctuation.section.parens.begin.shell
push:
- match: \)
scope: punctuation.section.parens.end.shell
pop: true
- include: case-clause-patterns-body
case-clause-commands:
- clear_scopes: 1 # remove meta.conditional.case.shell
- meta_content_scope: meta.conditional.case.clause.commands.shell
- match: ;;&?|;&
scope:
meta.conditional.case.clause.commands.shell
punctuation.terminator.case.clause.shell
pop: true
- include: case-end-ahead
- include: main
case-end-ahead:
- match: (?=\besac{{keyword_break}})
pop: true
# I don't think anybody will write a for-loop inside backticks. Hence no
# for-args-bt context.
for-args:
- match: ""
set:
- meta_scope: meta.group.for.shell
- include: cmd-args-boilerplate
- include: arithmetic
- match: \bin{{keyword_break}}
scope: keyword.control.in.shell
expansion-and-string:
- include: string
- include: expansion
funcdef:
- match: '{{is_function}}'
captures:
1: storage.type.function.shell
push: [funcdef-body, funcdef-parens, funcdef-name]
- match: \bcoproc{{keyword_break}}
scope: keyword.other.coproc.shell
push: [cmd-post, cmd-args, coproc-body]
funcdef-bt:
- match: '{{is_function}}'
captures:
1: storage.type.function.shell
push: [funcdef-body-bt, funcdef-parens, funcdef-name]
- match: \bcoproc{{keyword_break}}
scope: keyword.other.coproc.shell
push: [cmd-post, cmd-args-bt, coproc-body]
coproc-body:
- match: \s*(?=\S+\s*\{)
set:
- meta_content_scope: entity.name.function.coproc.shell
- match: (?=\s*\{)
set:
- match: \{
scope: punctuation.section.braces.begin.shell
set:
- meta_scope: meta.function.coproc.shell
- match: \}
scope: punctuation.section.braces.end.shell
pop: true
- include: main
- match: ""
set: main-with-pop-at-end
funcdef-name:
- match: \s*
set:
- meta_content_scope: entity.name.function.shell
- match: (?=\s*[({]|$)
pop: true
funcdef-parens:
- match: (\()\s*(\))
captures:
1: punctuation.section.parens.begin.shell
2: punctuation.section.parens.end.shell
- match: \{
scope: punctuation.section.braces.begin.shell
pop: true
- match: \(
scope: punctuation.definition.compound.begin.shell
pop: true
funcdef-body:
- meta_scope: meta.function.shell
- match: \}
scope: punctuation.section.braces.end.shell
pop: true
- match: \)
scope: punctuation.definition.compound.end.shell
pop: true
- include: main
funcdef-body-bt:
- meta_scope: meta.function.shell
- match: \}
scope: punctuation.section.braces.end.shell
pop: true
- match: \)
scope: punctuation.definition.compound.end.shell
pop: true
- include: main-bt
vardef:
- match: \s*\b(alias){{keyword_break}}
captures:
1: support.function.alias.shell
push:
- vardef-ensure-function-call-scope
- vardef-maybe-more
- vardef-value
- vardef-assign
- vardef-alias-name
- vardef-alias-options
- match: \s*\b(typeset|declare|local){{keyword_break}}
captures:
1: storage.modifier.shell
push:
- vardef-ensure-function-call-scope
- vardef-maybe-more
- vardef-value
- vardef-assign
- vardef-name
- vardef-declare-options
- match: \s*\b(export){{keyword_break}}
captures:
1: storage.modifier.shell
push:
- vardef-ensure-function-call-scope
- vardef-maybe-more
- vardef-value
- vardef-assign
- vardef-name
- vardef-export-options
- match: \s*\b(readonly){{keyword_break}}
captures:
1: storage.modifier.shell
push:
- vardef-ensure-function-call-scope
- vardef-maybe-more
- vardef-value
- vardef-assign
- vardef-name
- vardef-readonly-options
- match: '{{is_variable}}'
push:
- vardef-value
- vardef-assign
- vardef-name
vardef-readonly-options:
- match: \s*((-)(?:[aAf]+|p))
captures:
2: punctuation.definition.parameter.shell
1: variable.parameter.option.shell
- match: \s*
pop: true
vardef-export-options:
- match: \s*((-)(?:[fn]+|p))
captures:
2: punctuation.definition.parameter.shell
1: variable.parameter.option.shell
- match: \s*
pop: true
vardef-ensure-function-call-scope:
- meta_include_prototype: false
- meta_scope: meta.function-call.shell
- match: ""
pop: true
vardef-maybe-more:
- meta_include_prototype: false
- match: (?=`)
pop: true
- match: (?=\s*#)
pop: true
- include: cmd-args-boilerplate
- match: (?=\S)
push: [vardef-value, vardef-assign, vardef-name]
vardef-alias-options:
- match: \s*((-)p)
captures:
2: punctuation.definition.parameter.shell
1: variable.parameter.option.shell
- match: \s*
pop: true
vardef-alias-name:
- match: \s*
set:
- meta_include_prototype: false
- meta_content_scope: entity.name.function.alias.shell
- include: line-continuation-or-pop-at-end
- include: any-escape
- match: (?={{varassign}}|\s)|$
pop: true
- include: array
- match: \s*$
pop: true
- include: string
vardef-declare-options:
- match: \s*((-)(?:[aAfFgilnrtux]+|p))
captures:
2: punctuation.definition.parameter.shell
1: variable.parameter.option.shell
- match: \s*
pop: true
vardef-name:
- match: \s*
set:
- meta_include_prototype: false
- meta_content_scope: variable.other.readwrite.assignment.shell
- include: line-continuation-or-pop-at-end
- include: any-escape
- match: (?={{varassign}}|\s)|$|(?=[;&`]|{{metachar}})
pop: true
- include: array
- match: \s*$
pop: true
- include: string
vardef-assign:
- meta_include_prototype: false
- include: line-continuation-or-pop-at-end
- include: any-escape
- match: '{{varassign}}'
scope: keyword.operator.assignment.shell
pop: true
- match: ""
pop: true
vardef-value:
- meta_include_prototype: false
- match: \(
scope: punctuation.section.parens.begin.shell
set:
- match: \)
scope: punctuation.section.parens.end.shell
pop: true
- match: \[
scope: punctuation.section.brackets.begin.shell
push:
- match: \]
scope: punctuation.section.brackets.end.shell
set:
- match: =
scope: keyword.operator.assignment.shell
pop: true
- match: ""
pop: true
- include: expansion-and-string
- include: expansion-and-string
- match: (?=[&`])
pop: true
- match: ""
set:
- meta_include_prototype: false
- meta_scope: string.unquoted.shell
- match: (?=`)
pop: true
- include: expansion-and-string
- include: line-continuation-or-pop-at-end
- include: any-escape
- match: (?={{metachar}})
pop: true
redirection:
- include: redirection-here-string
- include: redirection-here-document
- include: redirection-process
- include: redirection-input
- include: redirection-output
- include: redirection-inout
redirection-process:
- match: (\d*)([<>])(\()
captures:
1: constant.numeric.integer.decimal.file-descriptor.shell
2: keyword.operator.assignment.redirection.process.shell
3: punctuation.section.parens.begin.shell
push:
- match: \)
scope: punctuation.section.parens.end.shell
pop: true
- include: main
redirection-output:
- match: (\d*)(>>!?|>&?|&>|&?>(?:\||>))
captures:
1: constant.numeric.integer.decimal.file-descriptor.shell
2: keyword.operator.assignment.redirection.shell
push: redirection-post
redirection-input:
- match: (\d*)(<&?)
captures:
1: constant.numeric.integer.decimal.file-descriptor.shell
2: keyword.operator.assignment.redirection.shell
push: redirection-post
redirection-post:
- match: \s*(?:(\d+)|(-))
captures:
1: constant.numeric.integer.decimal.file-descriptor.shell
2: punctuation.terminator.file-descriptor.shell
pop: true
- match: \s*(?=\S)
set:
- match: (?={{metachar}}|`)
pop: true
- include: expansion-and-string
- match: \s*
pop: true
redirection-inout:
- match: (\d*)(<>)
captures:
1: constant.numeric.integer.decimal.file-descriptor.shell
2: keyword.operator.assignment.redirection.shell
redirection-here-string:
- match: (\d*)(<<<)\s
captures:
1: constant.numeric.integer.decimal.file-descriptor.shell
2: keyword.operator.herestring.shell
redirection-here-document:
# These are the variants that allow tabs before the end token
- match: (\d*)(<<-)\s*(')({{identifier}})(')
captures:
1: constant.numeric.integer.decimal.file-descriptor.shell
2: keyword.operator.assignment.redirection.shell
3: punctuation.definition.string.begin.shell
4: keyword.control.heredoc-token.shell
5: punctuation.definition.string.end.shell
push: [heredocs-body-allow-tabs-no-expansion, heredocs-preamble]
- match: (\d*)(<<-)\s*(")({{identifier}})(")
captures:
1: constant.numeric.integer.decimal.file-descriptor.shell
2: keyword.operator.assignment.redirection.shell
3: punctuation.definition.string.begin.shell
4: keyword.control.heredoc-token.shell
5: punctuation.definition.string.end.shell
push: [heredocs-body-allow-tabs-no-expansion, heredocs-preamble]
- match: (\d*)(<<-)\s*(\\)({{identifier}})
captures:
1: constant.numeric.integer.decimal.file-descriptor.shell
2: keyword.operator.assignment.redirection.shell
3: punctuation.definition.string.shell
4: keyword.control.heredoc-token.shell
push: [heredocs-body-allow-tabs-no-expansion, heredocs-preamble]
- match: (\d*)(<<-)\s*({{identifier}})
captures:
1: constant.numeric.integer.decimal.file-descriptor.shell
2: keyword.operator.assignment.redirection.shell
3: keyword.control.heredoc-token.shell
push: [heredocs-body-allow-tabs, heredocs-preamble]
# These are the variants that DON'T allow tabs before the end token
- match: (\d*)(<<)\s*(')({{identifier}})(')
captures:
1: constant.numeric.integer.decimal.file-descriptor.shell
2: keyword.operator.assignment.redirection.shell
3: punctuation.definition.string.begin.shell
4: keyword.control.heredoc-token.shell
5: punctuation.definition.string.end.shell
push: [heredocs-body-no-expansion, heredocs-preamble]
- match: (\d*)(<<)\s*(")({{identifier}})(")
captures:
1: constant.numeric.integer.decimal.file-descriptor.shell
2: keyword.operator.assignment.redirection.shell
3: punctuation.definition.string.begin.shell
4: keyword.control.heredoc-token.shell
5: punctuation.definition.string.end.shell
push: [heredocs-body-no-expansion, heredocs-preamble]
- match: (\d*)(<<)\s*(\\)({{identifier}})
captures:
1: constant.numeric.integer.decimal.file-descriptor.shell
2: keyword.operator.assignment.redirection.shell
3: punctuation.definition.string.shell
4: keyword.control.heredoc-token.shell
push: [heredocs-body-no-expansion, heredocs-preamble]
- match: (\d*)(<<)\s*({{identifier}})
captures:
1: constant.numeric.integer.decimal.file-descriptor.shell
2: keyword.operator.assignment.redirection.shell
3: keyword.control.heredoc-token.shell
push: [heredocs-body, heredocs-preamble]
heredocs-body:
- meta_include_prototype: false
- meta_scope: string.unquoted.heredoc.shell
- include: heredocs-body-common-with-expansion
- match: ^\3(\s+)\n # the third capture from redirection-here-document
captures:
1: invalid.illegal.no-spaces-allowed-after-heredoc-token.shell
# rather not pop, but sublime throws an error otherwise.
pop: true
- match: ^\3$ # the third capture from redirection-here-document
scope: keyword.control.heredoc-token.shell
pop: true
heredocs-body-allow-tabs:
- meta_include_prototype: false
- meta_scope: string.unquoted.heredoc.shell
- include: heredocs-body-common-with-expansion
- match: ^\s*\3(\s+)\n # the third capture from redirection-here-document
captures:
1: invalid.illegal.no-spaces-allowed-after-heredoc-token.shell
# rather not pop, but sublime throws an error otherwise.
pop: true
- match: ^\s*(\3)$ # the third capture from redirection-here-document
captures:
1: keyword.control.heredoc-token.shell
pop: true
heredocs-body-common-with-expansion:
# [Bash] 3.6.6: all lines of the here-document are subjected to parameter
# expansion, command substitution, and arithmetic expansion, the character
# sequence \newline is ignored, and \ must be used to quote the
# characters \, $, and `.
- match: \\[`$"\\]
scope: constant.character.escape.backtick.shell
- include: expansion-parameter
- include: expansion-arithmetic
- include: expansion-command
heredocs-body-no-expansion:
- meta_include_prototype: false
- meta_scope: string.unquoted.heredoc.shell
- match: ^\4(\s+)\n # the fourth capture from redirection-here-document
captures:
1: invalid.illegal.no-spaces-allowed-after-heredoc-token.shell
# rather not pop, but sublime throws an error otherwise.
pop: true
- match: ^\4$ # the fourth capture from redirection-here-document
scope: keyword.control.heredoc-token.shell
pop: true
heredocs-body-allow-tabs-no-expansion:
- meta_include_prototype: false
- meta_scope: string.unquoted.heredoc.shell
- match: ^\s*\4(\s+)\n # the fourth capture from redirection-here-document
captures:
1: invalid.illegal.no-spaces-allowed-after-heredoc-token.shell
# rather not pop, but sublime throws an error otherwise.
pop: true
- match: ^\s*(\4)$ # the fourth capture from redirection-here-document
captures:
1: keyword.control.heredoc-token.shell
pop: true
heredocs-preamble:
- match: ""
set:
# This enables us to keep parsing on the line where the start token of
# the heredoc is. Once the first line has ended, we enter the body of
# the heredoc, where everything is just an unquoted string.
# One clear_scope for the string.unquoted.
# The problem with this is that when we also end a function definition
# on the same line (with the "}" token), we cannot do that.
- clear_scopes: 1
- match: $
pop: true
- match: \s*(?=\S)
push: [main-with-pop-at-end, cmd-post, cmd-args]
main-with-pop-at-end:
- include: line-continuation-or-pop-at-end
- include: main
cmd-name-common:
- match: (?=}|\s+#|\s*(?:[|;]|&(?!>)))
pop: true
- include: string
- include: expansion-parameter
- include: expansion-arithmetic
- include: expansion-command
- include: expansion-tilde
- include: expansion-job
- include: line-continuation-or-pop-at-end
cmd-args-common:
- match: (?=}|\s+#)
pop: true
- include: redirection
- match: (?=\s*([|;]|&(?!>)))
pop: true
- include: expansion-and-string
- include: line-continuation-or-pop-at-end
cmd-post: # looks like [main, cmd-post] at this point
- match: ;(?![;&])
scope: keyword.operator.logical.continue.shell
pop: true
- match: \|\|
scope: keyword.operator.logical.or.shell
pop: true
- match: \|
scope: keyword.operator.logical.pipe.shell
pop: true
- match: \&\&
scope: keyword.operator.logical.and.shell
pop: true
- match: \&
scope: keyword.operator.logical.job.shell
pop: true
- match: $|(?=\S)
pop: true
cmd-args-boilerplate:
- match: (?={{is_end_of_interpolation}})
pop: true
- include: cmd-args-common
- match: (?:\s+|^)--(?=\s|$)
scope: keyword.operator.end-of-options.shell
set:
- meta_content_scope: meta.function-call.arguments.shell
- include: end-of-options-common
cmd-args-boilerplate-bt:
- match: (?={{is_end_of_interpolation}}|`) # <-------------- extra backtick
pop: true
- include: cmd-args-common
- match: (?:\s+|^)--(?=\s|$)
scope: keyword.operator.end-of-options.shell
set:
- meta_content_scope: meta.function-call.arguments.shell
- match: (?=`) # <-------------------------------------- extra backtick
pop: true
- include: end-of-options-common
end-of-options-common:
- include: redirection
- match: (?=[)};&|])
pop: true
- include: expansion-and-string
- include: line-continuation-or-pop-at-end
cmd-args:
- match: ""
set:
- meta_scope: meta.function-call.arguments.shell
- include: cmd-args-boilerplate
- match: '{{start_of_option}}'
scope: punctuation.definition.parameter.shell
push:
- meta_scope: variable.parameter.option.shell
- match: (?==)
set:
- match: =
scope: keyword.operator.assignment.option.shell
pop: true
- match: (?={{is_end_of_option}})
pop: true
- include: expansion-and-string
cmd-args-bt:
- match: ""
set:
- meta_scope: meta.function-call.arguments.shell
- include: cmd-args-boilerplate-bt
- match: '{{start_of_option}}'
scope: punctuation.definition.parameter.shell
push:
- meta_scope: variable.parameter.option.shell
- match: (?==)
set:
- match: =
scope: keyword.operator.assignment.option.shell
pop: true
- match: (?={{is_end_of_option}}|`) # <------------- extra backtick
pop: true
- include: expansion-and-string
cmd:
- include: cmd-common
- match: \(
scope: punctuation.definition.compound.begin.shell
push:
- match: \)
scope: punctuation.definition.compound.end.shell
set: [cmd-post, cmd-args]
- include: main
- include: scope:commands.builtin.shell.bash#main
- match: \blet\b
scope: support.function.let.bash
push:
- meta_scope: meta.function-call.shell
- match: $
pop: true
- include: expression
- match: (\[\[)(?=\s)
captures:
1: support.function.double-brace.begin.shell
set: [cmd-post, cmd-test-double-brace-args]
- match: (\[)(?=\s)
captures:
1: support.function.test.begin.shell
set: [cmd-post, cmd-test-brace-args]
- match: (\{)(?=\s)
captures:
1: punctuation.definition.compound.braces.begin.shell
push:
- match: \}
scope: punctuation.definition.compound.braces.end.shell
set: [cmd-post, cmd-args]
- include: main
- match: (?=\S)
set: [cmd-post, cmd-args, cmd-name]
cmd-bt:
- include: cmd-common
- match: \(
scope: punctuation.definition.compound.begin.shell
push:
- match: \)
scope: punctuation.definition.compound.end.shell
set: [cmd-post, cmd-args-bt]
- include: main
- include: scope:commands.builtin.shell.bash#main-bt
- match: \blet\b
scope: support.function.let.bash
push:
- meta_scope: meta.function-call.shell
- match: $|(?=\`)
pop: true
- include: expression
- match: (\[\[)(?=\s)
captures:
1: support.function.double-brace.begin.shell
set: [cmd-post, cmd-test-double-brace-args-bt]
- match: (\[)(?=\s)
captures:
1: support.function.test.begin.shell
set: [cmd-post, cmd-test-brace-args-bt]
- match: (\{)(?=\s)
captures:
1: punctuation.definition.compound.braces.begin.shell
push:
- match: \}
scope: punctuation.definition.compound.braces.end.shell
set: [cmd-post, cmd-args-bt]
- include: main-bt
- match: (?=\S)
set: [cmd-post, cmd-args-bt, cmd-name-bt]
cmd-test-brace-args:
- match: ""
set:
- meta_scope: meta.function-call.arguments.shell
- include: cmd-args-boilerplate
- match: \s+(\])
captures:
1: support.function.test.end.shell
pop: true
- include: expression-test
cmd-test-brace-args-bt:
- match: ""
set:
- meta_scope: meta.function-call.arguments.shell
- include: cmd-args-boilerplate-bt
- match: \s+(\])
captures:
1: support.function.test.end.shell
pop: true
- include: expression-test
cmd-test-double-brace-args:
- meta_scope: meta.function-call.arguments.shell
- match: \s+(\]\])
captures:
1: support.function.double-brace.end.shell
pop: true
- include: expression-test
# - include: cmd-args-boilerplate
cmd-test-double-brace-args-bt:
- meta_scope: meta.function-call.arguments.shell
- match: \s+(\]\])
captures:
1: support.function.double-brace.end.shell
pop: true
- include: expression-test
# - include: cmd-args-boilerplate-bt
cmd-name:
- match: ""
set:
- meta_scope: meta.function-call.shell variable.function.shell
- match: (?={{is_start_of_arguments}}|{{is_end_of_interpolation}})
pop: true
- include: cmd-name-common
cmd-name-bt:
- match: ""
set:
- meta_scope: meta.function-call.shell variable.function.shell
# extra backtick
- match: (?={{is_start_of_arguments}}|{{is_end_of_interpolation}}|`)
pop: true
- include: cmd-name-common
cmd-common:
- include: control
- include: arithmetic
- match: (?=\)|})
pop: true
- include: line-continuation-or-pop-at-end
arithmetic:
- match: \(\((?=.+\)\))
scope: punctuation.section.arithmetic.begin.shell
push:
- meta_scope: meta.group.arithmetic.shell
- match: \)\)
scope: punctuation.section.arithmetic.end.shell
pop: true
- include: expression
expansion-tilde:
- match: '~'
scope: meta.group.expansion.tilde variable.language.tilde.shell
expansion-brace:
- match: \{
scope: punctuation.section.expansion.brace.begin.shell
push:
- meta_scope: meta.group.expansion.brace.shell
- match: \}
scope: punctuation.section.expansion.brace.end.shell
pop: true
- match: \,
scope: punctuation.separator.shell
- include: expansion-and-string
expansion-parameter:
- match: (\$)(\{)
captures:
0: meta.group.expansion.parameter.shell
1: punctuation.definition.variable.shell
2: punctuation.section.expansion.parameter.begin.shell
push:
- meta_content_scope: meta.group.expansion.parameter.shell
- meta_include_prototype: false
- match: \!
scope: keyword.operator.indirection.shell
set: expansion-parameter-post-first-character
- match: \#
scope: keyword.operator.arithmetic.shell
set: expansion-parameter-post-first-character
- match: ""
set: expansion-parameter-post-first-character
- match: (\$)(\d)
captures:
0: meta.group.expansion.parameter.shell
1: punctuation.definition.variable.shell
2: variable.other.readwrite.shell
- match: (\$)([$#@!~*?_-])(?!\w)
captures:
0: meta.group.expansion.parameter.shell
1: punctuation.definition.variable.shell
2: variable.language.shell
- match: (\$)({{identifier}})
captures:
0: meta.group.expansion.parameter.shell
1: punctuation.definition.variable.shell
2: variable.other.readwrite.shell
expansion-pattern:
- match: ([?*+@!])(\()
captures:
1: keyword.operator.regexp.quantifier.shell
2: punctuation.section.parens.begin.shell
push:
- match: \)
scope: punctuation.section.parens.end.shell
pop: true
- match: \|
scope: keyword.operator.logical.or.shell
- include: expansion-and-string
- match: '[*?]'
scope: keyword.operator.regexp.quantifier.shell
- match: \[(?=.*])
scope: keyword.control.regexp.set.begin.shell
push:
- match: (?=])
set: expansion-pattern-post-first-char
- match: '[!^]'
scope: keyword.operator.logical.not.shell
set: expansion-pattern-post-first-char
- match: \-
set: expansion-pattern-post-first-char
- match: ""
set: expansion-pattern-post-first-char
expansion-pattern-post-first-char:
- match: (?:-)?(\])
captures:
1: keyword.control.regexp.set.end.shell
pop: true
- match: \-
scope: keyword.operator.word.shell
- match: (\.)[[:word:]](\.)
captures:
1: punctuation.separator.collate.begin.shell
2: punctuation.separator.collate.end.shell
- match: (=)[[:word:]](=)
captures:
1: punctuation.separator.equivalence-class.begin.shell
2: punctuation.separator.equivalence-class.end.shell
- match: (:)[[:lower:]]+(:)
captures:
1: punctuation.separator.character-class.begin.shell
2: punctuation.separator.character-class.end.shell
# You cannot have a regex set inside a regex set, so just consume this
# character in order to not push into another regex set.
# Except when writing a character class like [:lower:], so negative look
# ahead for that possibility.
- match: \[(?![\.=:])
- include: expansion-and-string
expansion-arithmetic:
- match: (\$)(\(\()(?=.+\)\))
captures:
1: punctuation.definition.variable.shell
2: punctuation.section.parens.begin.shell
push:
- meta_scope: meta.group.expansion.arithmetic.shell
- match: \)\)
scope: punctuation.section.parens.end.shell
pop: true
- include: expression
expansion-command:
- match: (\$)(\()
captures:
1: punctuation.definition.variable.shell
2: punctuation.section.parens.begin.shell
push:
- meta_scope: meta.group.expansion.command.parens.shell
- match: \s*(\))
captures:
1: punctuation.section.parens.end.shell
pop: true
- include: main
- match: \`
scope: punctuation.section.group.begin.shell
push:
- meta_scope: meta.group.expansion.command.backticks.shell
- match: \`
scope: punctuation.section.group.end.shell
pop: true
- include: main-bt # all those *-bt contexts just for this!!!!
expansion:
- include: expansion-pattern
- include: expansion-parameter
- include: expansion-brace
- include: expansion-arithmetic
- include: expansion-command
- include: expansion-tilde
- include: expansion-job
expansion-parameter-common:
- meta_content_scope: meta.group.expansion.parameter.shell
- match: \}
scope:
meta.group.expansion.parameter.shell
punctuation.section.expansion.parameter.end.shell
pop: true
- include: string
- include: expansion-parameter
# no brace expansion
- include: expansion-arithmetic
- include: expansion-command
- include: expansion-tilde
# no pattern expansion
- include: any-escape
array:
- match: \[
scope: punctuation.section.braces.begin.shell
push:
- match: \]
scope: punctuation.section.braces.end.shell
pop: true
- match: '[*@]'
scope: variable.language.array.shell
- include: expression
expansion-parameter-post-first-character:
- meta_content_scope:
meta.group.expansion.parameter.shell
variable.other.readwrite.shell
- include: expansion-parameter-common
- match: (?=[@*]?/)
set:
- meta_content_scope: meta.group.expansion.parameter.shell
- match: ([@*])?(/)
captures:
1: variable.language.shell
2: keyword.operator.substitution.shell
set:
- meta_include_prototype: false
- meta_content_scope: meta.group.expansion.parameter.shell
- match: '[/#%]'
scope: variable.parameter.switch.shell
set: expansion-parameter-pattern
- match: ""
set: expansion-parameter-pattern
- match: (?=\:?[-+=?])
set:
- meta_content_scope: meta.group.expansion.parameter.shell
- match: \:?[-+=?]
scope: keyword.operator.assignment.shell
set: expansion-parameter-common
- match: (?=@?:)
set:
- meta_content_scope: meta.group.expansion.parameter.shell
- match: '(@)?(:)'
captures:
1: variable.language.shell
2: keyword.operator.substring.begin.shell
set:
- meta_content_scope: meta.group.expansion.parameter.shell
- match: (?=:)
set:
- meta_content_scope: meta.group.expansion.parameter.shell
- match: ":"
scope: keyword.operator.substring.end.shell
set:
- meta_content_scope: meta.group.expansion.parameter.shell
- include: expression
- include: expansion-parameter-common
- include: expression
- include: expansion-parameter-common
- match: \#(?=})
- match: ([@*])?(\#\#?|%%?|\^\^?|,,?)
captures:
1: variable.language.shell
2: keyword.operator.expansion.shell
set:
- meta_include_prototype: false
- meta_content_scope: meta.group.expansion.parameter.shell
- include: expansion-parameter-common
- include: expansion-pattern
- match: ([@*]?)(@)([QEPAa])(?=})
captures:
1: variable.language.shell
2: keyword.operator.expansion.shell
3: variable.parameter.switch.shell
- include: array
- match: '[*@](?=})'
scope: variable.language.shell
expansion-parameter-pattern:
- meta_content_scope: meta.group.expansion.parameter.shell
- match: /
scope: keyword.operator.substitution.shell
set: expansion-parameter-common
- include: expansion-parameter-common
- include: expansion-pattern
expansion-job:
# There are a number of ways to refer to a job in the shell.
# The symbols %% and %+ refer to the shells notion of the current job,
# which is the last job stopped while it was in the foreground or started in
# the background. The previous job may be referenced using %-.
- match: (%)([%+-])
captures:
0: meta.group.expansion.job.shell
1: punctuation.definition.variable.job.shell
2: variable.language.job.shell
# The character % introduces a job specification (jobspec). Job number n
# may be referred to as %n.
- match: (%)(\d+)
captures:
0: meta.group.expansion.job.shell
1: punctuation.definition.variable.job.shell
2: constant.numeric.integer.decimal.job.shell
# A job may also be referred to using a prefix of the name used to start it,
# or using a substring that appears in its command line. For example, %ce
# refers to a stopped ce job. Using %?ce, on the other hand, refers to any
# job containing the string ce in its command line. If the prefix or
# substring matches more than one job, Bash reports an error.
- match: (%)(\??)(\w+)
captures:
0: meta.group.expansion.job.shell
1: punctuation.definition.variable.job.shell
2: keyword.operator.regexp.quantifier.shell
3: variable.other.readwrite.shell
# A single % (with no accompanying job specification) also refers to the
# current job.
- match: '%'
scope:
meta.group.expansion.job.shell
punctuation.definition.variable.job.shell
expression:
# A leading 0x or 0X denotes hexadecimal.
- match: \b0[xX]
scope: punctuation.definition.numeric.base.shell
push:
- meta_scope: constant.numeric.integer.hexadecimal.shell
- match: '[g-zG-Z]'
scope: invalid.illegal.not-a-hex-character.shell
pop: true
- match: (?=\H)
pop: true
# Constants with a leading 0 are interpreted as octal numbers.
- match: \b0(?=[0-7])
scope: punctuation.definition.numeric.base.shell
push:
- meta_scope: constant.numeric.integer.octal.shell
- match: '[89]'
scope: invalid.illegal.not-an-octal-character.shell
pop: true
- match: (?=[^0-7])
pop: true
# Otherwise, numbers take the form [base#]n, where the optional base is a
# decimal number between 2 and 64 representing the arithmetic base, and n is
# a number in that base. When specifying n, the digits greater than 9 are
# represented by the lowercase letters, the uppercase letters, @, and _,
# in that order.
- match: \b(\d+#)[a-zA-Z0-9@_]+
scope: constant.numeric.integer.other.shell
captures:
1: punctuation.definition.numeric.base.shell
# If base# is omitted, then base 10 is used.
- match: \b\d+
scope: constant.numeric.integer.decimal.shell
- match: '[*/%+\-&^|]?=|<<=|>>='
scope: keyword.operator.assignment.shell
- match: \+\+?|\-\-?|\*\*?|%|/
scope: keyword.operator.arithmetic.shell
- match: <[=<]?|>[=>]?|[=!]=|&&|\:|\|\||!
scope: keyword.operator.logical.shell
- match: '[&|^~]'
scope: keyword.operator.bitwise.shell
- match: '[,;]'
scope: punctuation.separator.shell
- match: \?
scope: keyword.operator.ternary.shell
- match: \(
scope: punctuation.section.parens.begin.shell
push:
- meta_scope: meta.group.parens.shell
- match: \)
scope: punctuation.section.parens.end.shell
pop: true
- include: expression
# Shell variables are allowed as operands; parameter expansion is performed
# before the expression is evaluated. Within an expression, shell variables
# may also be referenced by name without using the parameter expansion
# syntax.
- include: string
- include: expansion-parameter
- include: expansion-arithmetic
- include: expansion-command
expression-test:
- include: expansion-and-string
- match: ((-)[aobcdefghknoprstuvwxzGLNORS])(?=\s)
captures:
2: punctuation.definition.parameter.shell
1: variable.parameter.option.shell
- match: ((-)(?:ef|nt|ot|eq|ne|lt|le|gt|ge))(?=\s)
captures:
2: punctuation.definition.parameter.shell
1: variable.parameter.option.shell
- match: (=~)\s*
captures:
1: keyword.operator.logical.shell
push:
- meta_content_scope: meta.regexp.shell
- match: (?=\s)
pop: true
- include: expansion-and-string
- match: ==?|!=?|<|>|\|\||&&
scope: keyword.operator.logical.shell
operator-exclamation:
- match: \!(?!\S)
scope: keyword.operator.logical.shell
- match: (\!)(-?\d+|!)
scope: variable.language.history.shell
captures:
1: punctuation.definition.history.shell
- match: \!
scope: punctuation.definition.history.shell
string:
- include: string-quoted-double
- include: string-quoted-single
- include: string-ansi-c
- include: string-locale
# nothing is escaped in a singly-quoted string!
string-quoted-single:
- match: \'
scope: punctuation.definition.string.begin.shell
push:
- meta_include_prototype: false
- meta_scope: string.quoted.single.shell
- match: \'
scope: punctuation.definition.string.end.shell
pop: true
string-quoted-double:
- match: \"
scope: punctuation.definition.string.begin.shell
push:
- meta_include_prototype: false
- meta_scope: string.quoted.double.shell
- include: string-quoted-double-common
string-quoted-double-escape-character:
- match: \\[$`"\\]
scope: constant.character.escape.shell
- match: \\\n
scope: constant.character.escape.shell
push:
- meta_include_prototype: false
- match: (?=\S)
pop: true
# [Bash] 3.1.2.4
string-ansi-c:
- match: \$'
scope: punctuation.definition.string.begin.shell
push:
- meta_include_prototype: false
- meta_scope: string.quoted.single.ansi-c.shell
- match: "'"
scope: punctuation.definition.string.end.shell
pop: true
- include: string-quoted-double-escape-character
- match: \\([abfnrtv'"?]|[0-8]{1,3}|x\h{1,8}|c[a-z])
scope: constant.character.escape.shell
# [Bash] 3.1.2.5
# If the string is translated and replaced, the replacement is double-quoted.
string-locale:
- match: \$"
scope: punctuation.definition.string.begin.shell
push:
- meta_include_prototype: false
- meta_scope: string.quoted.double.locale.shell
- include: string-quoted-double-common
string-quoted-double-common:
- match: \"
scope: punctuation.definition.string.end.shell
pop: true
- include: string-quoted-double-escape-character
- include: expansion-parameter
- include: expansion-arithmetic
- include: expansion-command