feat(parsers): add zsh support (#8240)

Co-authored-by: Christian Clason <c.clason@uni-graz.at>
Co-authored-by: Riley Bruins <ribru17@hotmail.com>
Co-authored-by: Tayfun Bocek <tayfunbocek@live.ca>
This commit is contained in:
George Harker
2025-11-05 20:13:53 -08:00
committed by GitHub
parent 896e92a7f6
commit 1ddb266477
7 changed files with 762 additions and 0 deletions

View File

@@ -339,6 +339,7 @@ jsx (queries only)[^jsx] | unstable | `HFIJ ` | @steelsojka
[zig](https://github.com/tree-sitter-grammars/tree-sitter-zig) | unstable | `HFIJL` | @amaanq
[ziggy](https://github.com/kristoff-it/ziggy) | unstable | `H I  ` | @rockorager
[ziggy_schema](https://github.com/kristoff-it/ziggy) | unstable | `H I  ` | @rockorager
[zsh](https://github.com/georgeharker/tree-sitter-zsh) | stable | `HF JL` | @georgeharker
[^bp]: Android Blueprint
[^ecma]: queries required by javascript, typescript, tsx, qmljs
[^gap]: GAP system

View File

@@ -2701,4 +2701,12 @@ return {
maintainers = { '@rockorager' },
tier = 2,
},
zsh = {
install_info = {
revision = 'v0.34.0',
url = 'https://github.com/georgeharker/tree-sitter-zsh',
},
maintainers = { '@georgeharker' },
tier = 1,
},
}

View File

@@ -0,0 +1,9 @@
[
(function_definition)
(if_statement)
(case_statement)
(for_statement)
(while_statement)
(c_style_for_statement)
(heredoc_redirect)
] @fold

View File

@@ -0,0 +1,294 @@
[
"("
")"
"{"
"}"
"["
"]"
"[["
"]]"
"(("
"))"
] @punctuation.bracket
[
";"
";;"
";&"
";;&"
"&"
] @punctuation.delimiter
[
">"
">>"
"<"
"<<"
"&&"
"|"
"|&"
"||"
"="
"+="
"=~"
"=="
"!="
"&>"
"&>>"
"<&"
">&"
">|"
"<&-"
">&-"
"<<-"
"<<<"
".."
"!"
] @operator
; Do *not* spell check strings since they typically have some sort of
; interpolation in them, or, are typically used for things like filenames, URLs,
; flags and file content.
[
(string)
(raw_string)
(ansi_c_string)
(heredoc_body)
] @string
[
(heredoc_start)
(heredoc_end)
] @label
(variable_assignment
(word) @string)
; (command
; argument: "variable_ref" @string) ; bare dollar
(concatenation
(word) @string)
[
"if"
"then"
"else"
"elif"
"fi"
"case"
"in"
"esac"
] @keyword.conditional
[
"for"
"do"
"done"
"select"
"until"
"while"
] @keyword.repeat
[
"declare"
"typeset"
"readonly"
"local"
"unset"
"unsetenv"
] @keyword
"export" @keyword.import
"function" @keyword.function
(special_variable_name) @variable.builtin
; trap -l
((word) @constant.builtin
(#any-of? @constant.builtin
"SIGHUP" "SIGINT" "SIGQUIT" "SIGILL" "SIGTRAP" "SIGABRT" "SIGBUS" "SIGFPE" "SIGKILL" "SIGUSR1"
"SIGSEGV" "SIGUSR2" "SIGPIPE" "SIGALRM" "SIGTERM" "SIGSTKFLT" "SIGCHLD" "SIGCONT" "SIGSTOP"
"SIGTSTP" "SIGTTIN" "SIGTTOU" "SIGURG" "SIGXCPU" "SIGXFSZ" "SIGVTALRM" "SIGPROF" "SIGWINCH"
"SIGIO" "SIGPWR" "SIGSYS" "SIGRTMIN" "SIGRTMIN+1" "SIGRTMIN+2" "SIGRTMIN+3" "SIGRTMIN+4"
"SIGRTMIN+5" "SIGRTMIN+6" "SIGRTMIN+7" "SIGRTMIN+8" "SIGRTMIN+9" "SIGRTMIN+10" "SIGRTMIN+11"
"SIGRTMIN+12" "SIGRTMIN+13" "SIGRTMIN+14" "SIGRTMIN+15" "SIGRTMAX-14" "SIGRTMAX-13"
"SIGRTMAX-12" "SIGRTMAX-11" "SIGRTMAX-10" "SIGRTMAX-9" "SIGRTMAX-8" "SIGRTMAX-7" "SIGRTMAX-6"
"SIGRTMAX-5" "SIGRTMAX-4" "SIGRTMAX-3" "SIGRTMAX-2" "SIGRTMAX-1" "SIGRTMAX"))
((word) @boolean
(#any-of? @boolean "true" "false"))
(comment) @comment @spell
(test_operator) @operator
(command_substitution
"$(" @punctuation.special
")" @punctuation.special)
(process_substitution
[
"<("
">("
] @punctuation.special
")" @punctuation.special)
(arithmetic_expansion
[
"$(("
"(("
] @punctuation.special
"))" @punctuation.special)
(arithmetic_expansion
"," @punctuation.delimiter)
(ternary_expression
[
"?"
":"
] @keyword.conditional.ternary)
(binary_expression
operator: _ @operator)
(unary_expression
operator: _ @operator)
(postfix_expression
operator: _ @operator)
(function_definition
name: (word) @function)
(command_name
(word) @function.call)
(command_name
(word) @function.builtin
(#any-of? @function.builtin
"." ":" "alias" "bg" "bind" "break" "builtin" "caller" "cd" "command" "compgen" "complete"
"compopt" "continue" "coproc" "dirs" "disown" "echo" "enable" "eval" "exec" "exit" "false" "fc"
"fg" "getopts" "hash" "help" "history" "jobs" "kill" "let" "logout" "mapfile" "popd" "printf"
"pushd" "pwd" "read" "readarray" "return" "set" "shift" "shopt" "source" "suspend" "test" "time"
"times" "trap" "true" "type" "typeset" "ulimit" "umask" "unalias" "wait"))
(command
argument: [
(word) @variable.parameter
(concatenation
(word) @variable.parameter)
])
(declaration_command
(word) @variable.parameter)
(unset_command
(word) @variable.parameter)
(number) @number
((word) @number
(#lua-match? @number "^[0-9]+$"))
(file_redirect
(word) @string.special.path)
(herestring_redirect
(word) @string)
(file_descriptor) @operator
(variable_ref
"$" @punctuation.special)
(expansion
"${" @punctuation.special
"}" @punctuation.special)
"``" @punctuation.special
(array_star) @variable.builtin
(array_at) @variable.builtin
(expansion_flags) @attribute.builtin
(expansion_style) @attribute.builtin
(expansion_pattern
pattern: "#" @attribute.builtin)
(expansion_modifier) @attribute.builtin
(simple_variable_name) @variable
(glob_pattern) @string.regexp
(variable_name) @variable
((variable_name) @constant
(#lua-match? @constant "^[A-Z][A-Z_0-9]*$"))
((variable_name) @variable.builtin
(#any-of? @variable.builtin
; https://www.gnu.org/software/bash/manual/html_node/Bourne-Shell-Variables.html
"CDPATH" "HOME" "IFS" "MAIL" "MAILPATH" "OPTARG" "OPTIND" "PATH" "PS1" "PS2"
; https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html
"_" "BASH" "BASHOPTS" "BASHPID" "BASH_ALIASES" "BASH_ARGC" "BASH_ARGV" "BASH_ARGV0" "BASH_CMDS"
"BASH_COMMAND" "BASH_COMPAT" "BASH_ENV" "BASH_EXECUTION_STRING" "BASH_LINENO"
"BASH_LOADABLES_PATH" "BASH_REMATCH" "BASH_SOURCE" "BASH_SUBSHELL" "BASH_VERSINFO"
"BASH_VERSION" "BASH_XTRACEFD" "CHILD_MAX" "COLUMNS" "COMP_CWORD" "COMP_LINE" "COMP_POINT"
"COMP_TYPE" "COMP_KEY" "COMP_WORDBREAKS" "COMP_WORDS" "COMPREPLY" "COPROC" "DIRSTACK" "EMACS"
"ENV" "EPOCHREALTIME" "EPOCHSECONDS" "EUID" "EXECIGNORE" "FCEDIT" "FIGNORE" "FUNCNAME"
"FUNCNEST" "GLOBIGNORE" "GROUPS" "histchars" "HISTCMD" "HISTCONTROL" "HISTFILE" "HISTFILESIZE"
"HISTIGNORE" "HISTSIZE" "HISTTIMEFORMAT" "HOSTFILE" "HOSTNAME" "HOSTTYPE" "IGNOREEOF" "INPUTRC"
"INSIDE_EMACS" "LANG" "LC_ALL" "LC_COLLATE" "LC_CTYPE" "LC_MESSAGES" "LC_NUMERIC" "LC_TIME"
"LINENO" "LINES" "MACHTYPE" "MAILCHECK" "MAPFILE" "OLDPWD" "OPTERR" "OSTYPE" "PIPESTATUS"
"POSIXLY_CORRECT" "PPID" "PROMPT_COMMAND" "PROMPT_DIRTRIM" "PS0" "PS3" "PS4" "PWD" "RANDOM"
"READLINE_ARGUMENT" "READLINE_LINE" "READLINE_MARK" "READLINE_POINT" "REPLY" "SECONDS" "SHELL"
"SHELLOPTS" "SHLVL" "SRANDOM" "TIMEFORMAT" "TMOUT" "TMPDIR" "UID"))
((simple_variable_name) @variable.builtin
(#any-of? @variable.builtin
; https://www.gnu.org/software/bash/manual/html_node/Bourne-Shell-Variables.html
"CDPATH" "HOME" "IFS" "MAIL" "MAILPATH" "OPTARG" "OPTIND" "PATH" "PS1" "PS2"
; https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html
"_" "BASH" "BASHOPTS" "BASHPID" "BASH_ALIASES" "BASH_ARGC" "BASH_ARGV" "BASH_ARGV0" "BASH_CMDS"
"BASH_COMMAND" "BASH_COMPAT" "BASH_ENV" "BASH_EXECUTION_STRING" "BASH_LINENO"
"BASH_LOADABLES_PATH" "BASH_REMATCH" "BASH_SOURCE" "BASH_SUBSHELL" "BASH_VERSINFO"
"BASH_VERSION" "BASH_XTRACEFD" "CHILD_MAX" "COLUMNS" "COMP_CWORD" "COMP_LINE" "COMP_POINT"
"COMP_TYPE" "COMP_KEY" "COMP_WORDBREAKS" "COMP_WORDS" "COMPREPLY" "COPROC" "DIRSTACK" "EMACS"
"ENV" "EPOCHREALTIME" "EPOCHSECONDS" "EUID" "EXECIGNORE" "FCEDIT" "FIGNORE" "FUNCNAME"
"FUNCNEST" "GLOBIGNORE" "GROUPS" "histchars" "HISTCMD" "HISTCONTROL" "HISTFILE" "HISTFILESIZE"
"HISTIGNORE" "HISTSIZE" "HISTTIMEFORMAT" "HOSTFILE" "HOSTNAME" "HOSTTYPE" "IGNOREEOF" "INPUTRC"
"INSIDE_EMACS" "LANG" "LC_ALL" "LC_COLLATE" "LC_CTYPE" "LC_MESSAGES" "LC_NUMERIC" "LC_TIME"
"LINENO" "LINES" "MACHTYPE" "MAILCHECK" "MAPFILE" "OLDPWD" "OPTERR" "OSTYPE" "PIPESTATUS"
"POSIXLY_CORRECT" "PPID" "PROMPT_COMMAND" "PROMPT_DIRTRIM" "PS0" "PS3" "PS4" "PWD" "RANDOM"
"READLINE_ARGUMENT" "READLINE_LINE" "READLINE_MARK" "READLINE_POINT" "REPLY" "SECONDS" "SHELL"
"SHELLOPTS" "SHLVL" "SRANDOM" "TIMEFORMAT" "TMOUT" "TMPDIR" "UID"))
((command
name: (command_name
(word) @_printf)
.
argument: (word) @_v
.
argument: (word) @variable)
(#eq? @_printf "printf")
(#eq? @_v "-v")
(#lua-match? @variable "^[a-zA-Z_][a-zA-Z0-9_]*$"))
(case_item
value: (word) @variable.parameter)
[
(regex)
(extglob_pattern)
] @string.regexp
((program
.
(comment) @keyword.directive @nospell)
(#lua-match? @keyword.directive "^#![ \t]*/"))

View File

@@ -0,0 +1,78 @@
((comment) @injection.content
(#set! injection.language "comment"))
((regex) @injection.content
(#set! injection.language "regex"))
(heredoc_redirect
(heredoc_body) @injection.content
(heredoc_end) @injection.language)
; printf 'format'
((command
name: (command_name) @_command
.
argument: [
(string) @injection.content
(concatenation
(string) @injection.content)
(raw_string) @injection.content
(concatenation
(raw_string) @injection.content)
])
(#eq? @_command "printf")
(#offset! @injection.content 0 1 0 -1)
(#set! injection.include-children)
(#set! injection.language "printf"))
; printf -v var 'format'
((command
name: (command_name) @_command
argument: (word) @_arg
.
(_)
.
argument: [
(string) @injection.content
(concatenation
(string) @injection.content)
(raw_string) @injection.content
(concatenation
(raw_string) @injection.content)
])
(#eq? @_command "printf")
(#eq? @_arg "-v")
(#offset! @injection.content 0 1 0 -1)
(#set! injection.include-children)
(#set! injection.language "printf"))
; printf -- 'format'
((command
name: (command_name) @_command
argument: (word) @_arg
.
argument: [
(string) @injection.content
(concatenation
(string) @injection.content)
(raw_string) @injection.content
(concatenation
(raw_string) @injection.content)
])
(#eq? @_command "printf")
(#eq? @_arg "--")
(#offset! @injection.content 0 1 0 -1)
(#set! injection.include-children)
(#set! injection.language "printf"))
((command
name: (command_name) @_command
.
argument: [
(string)
(raw_string)
] @injection.content)
(#eq? @_command "bind")
(#offset! @injection.content 0 1 0 -1)
(#set! injection.include-children)
(#set! injection.language "readline"))

View File

@@ -0,0 +1,14 @@
; Scopes
(function_definition) @local.scope
; Definitions
(variable_assignment
name: (variable_name) @local.definition.var)
(function_definition
name: (word) @local.definition.function)
; References
(variable_name) @local.reference
(word) @local.reference

View File

@@ -0,0 +1,358 @@
#!/bin/zsh
# ^^^^^^^^ @comment @spell
# Basic comment
# <- @comment @spell
# Variables and assignments
name="value"
#^^^ @variable @constant
# ^ @operator
# ^^^^^^^ @string
export PATH="/usr/bin:$PATH"
#^^^^^ @keyword.import
# ^^^^ @variable @constant @variable.builtin
# ^ @operator
# ^^^^^^^^^^^^^^^^ @string
# ^ @punctuation.special
# ^^^^ @variable.builtin
# Function definition
function myfunction() {
#^^^^^^^ @keyword.function
# ^^^^^^^^^^ @function @number
echo "Hello World"
#^^^ @function.call @function.builtin @number
# ^^^^^^^^^^^^^ @string
}
myfunction() {
#^^^^^^^^^ @function @number
local var="test"
#^^^^ @keyword
# ^^^ @variable @constant
# ^ @operator
# ^^^^^^ @string
}
# Built-in commands
echo "Hello"
#^^^ @function.call @function.builtin @number
# ^^^^^^^ @string
cd /home/user
#^ @function.call @function.builtin @number
# ^^^^^^^^^^ @variable.parameter @number
ls -la
#^ @function.call @number
# ^^^ @variable.parameter @number
# Control structures
if [[ -f "$file" ]]; then
#^ @keyword.conditional
# ^^ @punctuation.bracket
# ^^ @operator @operator
# ^^^^^^^ @string
# ^ @punctuation.special @none
# ^^ @punctuation.bracket
# ^ @punctuation.delimiter
# ^^^^ @keyword.conditional
echo "File exists"
#^^^ @function.call @function.builtin @number
# ^^^^^^^^^^^^^ @string
fi
#^ @keyword.conditional
for i in {1..10}; do
#^^ @keyword.repeat
# ^^ @keyword.conditional
# ^ @punctuation.bracket
# ^ @number
# ^^ @operator
# ^^ @number
# ^ @punctuation.bracket
# ^ @punctuation.delimiter
# ^^ @keyword.repeat
echo $i
#^^^ @function.call @function.builtin @number
# ^ @punctuation.special
done
#^^^ @keyword.repeat
while read line; do
#^^^^ @keyword.repeat
# ^^^^ @function.call @function.builtin @number
# ^^^^ @variable.parameter @number
# ^ @punctuation.delimiter
# ^^ @keyword.repeat
echo "$line"
#^^^ @function.call @function.builtin @number
# ^^^^^^^ @string
# ^ @punctuation.special @none
done
#^^^ @keyword.repeat
case $input in
#^^^ @keyword.conditional
# ^ @punctuation.special @none
# ^^ @keyword.conditional
"yes"|"y")
#^^^^ @string @variable.parameter
# ^ @operator
# ^^^ @string @variable.parameter
# ^ @punctuation.bracket
echo "Yes"
#^^^ @function.call @function.builtin @number
# ^^^^^ @string
;;
#^ @punctuation.delimiter
"no"|"n")
#^^^ @string @variable.parameter
# ^ @operator
# ^^^ @string @variable.parameter
# ^ @punctuation.bracket
echo "No"
#^^^ @function.call @function.builtin @number
# ^^^^ @string
;;
#^ @punctuation.delimiter
*)
#^ @string.regexp
# ^ @punctuation.bracket
echo "Unknown"
#^^^ @function.call @function.builtin @number
# ^^^^^^^^^ @string
;;
#^ @punctuation.delimiter
esac
#^^^ @keyword.conditional
# Arrays
arr=(one two three)
#^^ @variable @constant
# ^ @operator
# ^ @punctuation.bracket
# ^ @punctuation.bracket
echo ${arr[1]}
#^^^ @function.call @function.builtin @number
# ^ @punctuation.special
# ^ @punctuation.special
# ^ @punctuation.bracket
# ^ @number
# ^ @punctuation.bracket
# ^ @punctuation.special
# Parameter expansion
echo ${name:-default}
#^^^ @function.call @function.builtin @number
# ^^ @punctuation.special
# ^ @punctuation.special
echo ${#name}
#^^^ @function.call @function.builtin @number
# ^^ @punctuation.special
# ^ @punctuation.special
# Command substitution
result=$(date)
#^^^^^ @variable @constant
# ^ @operator
# ^ @punctuation.special
# ^ @punctuation.special
# ^^^^ @function.call @number
# ^ @punctuation.special
result=`date`
#^^^^^ @variable @constant
# ^ @operator
# ^^^^ @function.call @number
# Pipes and redirection
ls | grep "test" > output.txt
#^ @function.call @number
# ^ @operator
# ^^^^ @function.call @number
# ^^^^^^ @string
# ^ @operator
# ^^^^^^^^^^ @string.special.path @number
cat < input.txt >> output.txt
#^^ @function.call @number
# ^ @operator
# ^^^^^^^^^ @string.special.path @number
# ^^ @operator
# ^^^^^^^^^^ @string.special.path @number
# Process substitution
diff <(ls dir1) <(ls dir2)
#^^^ @function.call @number
# ^ @punctuation.special
# ^ @punctuation.special
# ^^ @function.call @number
# ^^^^ @variable.parameter @number
# ^^ @punctuation.special
# ^^ @function.call @number
# ^^^^ @variable.parameter @number
# ^ @punctuation.special @punctuation.bracket
# Test commands
[[ -f file.txt ]]
#^ @punctuation.bracket
# ^^ @operator @operator
# ^^ @punctuation.bracket
[ -n "$var" ]
#^ @punctuation.bracket
# ^^ @operator @operator
# ^^^^^ @string
# ^ @punctuation.special
# ^ @punctuation.bracket
# Arithmetic expansion
echo $((2 + 3))
#^^^ @function.call @function.builtin @number
# ^ @punctuation.special
# ^^ @punctuation.special
# ^ @number
# ^ @operator
# ^ @number
# ^^ @punctuation.special @punctuation.bracket
# Globbing patterns
ls *.txt
#^ @function.call @number
# Brace expansion
echo {a,b,c}
#^^^ @function.call @function.builtin @number
# Here documents
cat << EOF
#^^ @function.call @number
# ^^ @operator
# ^^^ @label
This is a heredoc
#^^^^^^^^^^^^^^^^^ @string
EOF
#^^ @label
cat <<< "string"
#^^ @function.call @number
# ^^ @operator
# ^ @operator
# ^^^^^^^^ @string
# Special variables
echo $0 $1 $@ $* $# $?
#^^^ @function.call @function.builtin @number
# ^^ @punctuation.special @none @variable.builtin
# ^ @variable.builtin
# ^^ @punctuation.special @none @variable.builtin
# ^ @variable.builtin
# ^^ @punctuation.special @none @variable.builtin
# ^ @variable.builtin
# ^^ @punctuation.special @none @variable.builtin
# ^ @variable.builtin
# ^^ @punctuation.special @none @variable.builtin
# ^ @variable.builtin
# ^^ @punctuation.special @none @variable.builtin
# ^ @variable.builtin
# NOTE: $$ is not captured by current query/grammar
# Conditional operators
[[ $a == $b ]] && echo "equal"
#^ @punctuation.bracket
# ^ @punctuation.special @none
# ^^ @operator @operator
# ^ @punctuation.special @none
# ^^ @punctuation.bracket
# ^^ @operator
# ^^^^ @function.builtin @function.call @number
# ^^^^^^^ @string
[[ $a != $b ]] || echo "not equal"
#^ @punctuation.bracket
# ^^ @punctuation.special @none
# ^^ @operator
# ^^ @punctuation.special @none
# ^^ @operator
# ^^^^ @function.call @function.builtin @number
# ^^^^^^^^^^^ @string
# Background jobs
sleep 10 &
#^^^^ @function.call @number
# ^^ @number
# ^ @punctuation.delimiter
# Negation
! command
#^ @operator
#^^^^^^ @function.call @number
# File descriptors
exec 3> file.txt
#^^^ @function.call @function.builtin @number
# ^ @operator
# ^ @operator
# ^^^^^^^^ @string.special.path @number
# ZSH-specific features
setopt AUTO_CD
#^^^^^ @function.call @number
# ^^^^^^^ @variable.parameter @number
autoload -U compinit
#^^^^^^^ @function.call @number
# ^^ @variable.parameter @number
# ^^^^^^^^ @variable.parameter @number
# Associative arrays
typeset -A hash
#^^^^^^ @keyword
# ^^ @variable.parameter @number
# ^^^^ @variable.parameter @constant
hash[key]="value"
#^^^ @variable @constant
# ^ punctuation.bracket
# ^ punctuation.bracket
# ^ @operator
# ^^^^^^^ @string
# Regular expressions
[[ "text" =~ ^[a-z]+$ ]]
#^ @punctuation.bracket
# ^^^^^^ @string
# ^^ @operator
# ^^^^^^^^^ @string.regexp
# ^^ @punctuation.bracket
# Quotes and escaping
echo "double quotes with $var"
#^^^ @function.call @function.builtin @number
# ^^^^^^^^^^^^^^^^^^^^^^^^^ @string
# ^ @punctuation.special @none
echo 'single quotes $var'
#^^^ @function.call @function.builtin @number
# ^^^^^^^^^^^^^^^^^^^^ @string
echo $'ansi-c quotes\n'
#^^^ @function.call @function.builtin @number
# ^^^^^^^^^^^^^^^^^ @string
echo "escaped \" quote"
#^^^ @function.call @function.builtin @number
# ^^^^^^^^^^^^^^^^^^ @string
# Command chaining
cmd1 && cmd2 || cmd3
#^^^ @function.call @number
# ^^ @operator
# ^^^^ @function.call @number
# ^^ @operator
# ^^^^ @function.call @number