aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeorge Harker <george@george-graphics.co.uk>2025-11-05 20:13:53 -0800
committerGitHub <noreply@github.com>2025-11-05 20:13:53 -0800
commit1ddb266477eccf432983d723f94b86361b020010 (patch)
tree08f07f4ddc900ecc11bc6929b1dfec4008325a6f
parentfix(typescript): support type param for styled components (#8066) (diff)
downloadnvim-treesitter-1ddb266477eccf432983d723f94b86361b020010.tar
nvim-treesitter-1ddb266477eccf432983d723f94b86361b020010.tar.gz
nvim-treesitter-1ddb266477eccf432983d723f94b86361b020010.tar.bz2
nvim-treesitter-1ddb266477eccf432983d723f94b86361b020010.tar.lz
nvim-treesitter-1ddb266477eccf432983d723f94b86361b020010.tar.xz
nvim-treesitter-1ddb266477eccf432983d723f94b86361b020010.tar.zst
nvim-treesitter-1ddb266477eccf432983d723f94b86361b020010.zip
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>
-rw-r--r--SUPPORTED_LANGUAGES.md1
-rw-r--r--lua/nvim-treesitter/parsers.lua8
-rw-r--r--runtime/queries/zsh/folds.scm9
-rw-r--r--runtime/queries/zsh/highlights.scm294
-rw-r--r--runtime/queries/zsh/injections.scm78
-rw-r--r--runtime/queries/zsh/locals.scm14
-rw-r--r--tests/query/highlights/zsh/test.zsh358
7 files changed, 762 insertions, 0 deletions
diff --git a/SUPPORTED_LANGUAGES.md b/SUPPORTED_LANGUAGES.md
index 177b3951a..95510e5af 100644
--- a/SUPPORTED_LANGUAGES.md
+++ b/SUPPORTED_LANGUAGES.md
@@ -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
diff --git a/lua/nvim-treesitter/parsers.lua b/lua/nvim-treesitter/parsers.lua
index e980ae3c3..883403fd6 100644
--- a/lua/nvim-treesitter/parsers.lua
+++ b/lua/nvim-treesitter/parsers.lua
@@ -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,
+ },
}
diff --git a/runtime/queries/zsh/folds.scm b/runtime/queries/zsh/folds.scm
new file mode 100644
index 000000000..766dbe598
--- /dev/null
+++ b/runtime/queries/zsh/folds.scm
@@ -0,0 +1,9 @@
+[
+ (function_definition)
+ (if_statement)
+ (case_statement)
+ (for_statement)
+ (while_statement)
+ (c_style_for_statement)
+ (heredoc_redirect)
+] @fold
diff --git a/runtime/queries/zsh/highlights.scm b/runtime/queries/zsh/highlights.scm
new file mode 100644
index 000000000..3bf1eed00
--- /dev/null
+++ b/runtime/queries/zsh/highlights.scm
@@ -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]*/"))
diff --git a/runtime/queries/zsh/injections.scm b/runtime/queries/zsh/injections.scm
new file mode 100644
index 000000000..e1095773b
--- /dev/null
+++ b/runtime/queries/zsh/injections.scm
@@ -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"))
diff --git a/runtime/queries/zsh/locals.scm b/runtime/queries/zsh/locals.scm
new file mode 100644
index 000000000..347f51fa2
--- /dev/null
+++ b/runtime/queries/zsh/locals.scm
@@ -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
diff --git a/tests/query/highlights/zsh/test.zsh b/tests/query/highlights/zsh/test.zsh
new file mode 100644
index 000000000..b1c4e398f
--- /dev/null
+++ b/tests/query/highlights/zsh/test.zsh
@@ -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