Code like ${!#} flags the "#" as shDerefWordError 1; the "!prefix" syntax region delegates to one of the shDerefSpecial handlers via @shDerefList, but it misses the "#" case as valid for ${##} and ${!#}.
Correct that.
https://github.com/vim/vim/pull/20016
(1 file)
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
thanks, let me add this to a syntax tests as well.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
thanks, let me add this to a syntax tests as well.
Thanks! I wasn't totally sure what tests existed for this or how to deal with them. Happy to learn :)
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
Hm, actually, this seems slightly wrong. It seems ${!...} is only valid for ksh and bash, so I would suggest the following change instead:
diff --git a/runtime/syntax/sh.vim b/runtime/syntax/sh.vim index 05eb488d5..23f8cc19b 100644 --- a/runtime/syntax/sh.vim +++ b/runtime/syntax/sh.vim @@ -24,6 +24,7 @@ " 2026 Feb 15 improve comment handling #19414 " 2026 Mar 23 improve matching of function definitions #19638 " 2026 Apr 02 improve matching of function definitions #19849 +" 2026 Apr 19 improve detection of special variables #20016 " }}} " Version: 208 " Former URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_SH @@ -751,13 +752,15 @@ endif if exists("b:is_bash") syn region shDeref matchgroup=PreProc start="\${!" end="\*\=}" contains=@shDerefList,shDerefOffset syn match shDerefVar contained "{\@<=!\h\w*" nextgroup=@shDerefVarList + syn match shDerefSpecial contained "\({!\)\@<=[[:alnum:]*#@_]\+" nextgroup=@shDerefVarList,shDerefOp endif if (exists("b:is_kornshell") && !exists("b:is_ksh88")) syn match shDerefVar contained "{\@<=!\h\w*[[:alnum:]_.]*" nextgroup=@shDerefVarList + syn match shDerefSpecial contained "\({!\)\@<=[[:alnum:]*#@_]\+" nextgroup=@shDerefVarList,shDerefOp endif syn match shDerefSpecial contained "{\@<=[-*@?0]" nextgroup=shDerefOp,shDerefOffset,shDerefOpError -syn match shDerefSpecial contained "\({[#!]\)\@<=[[:alnum:]*@_]\+" nextgroup=@shDerefVarList,shDerefOp +syn match shDerefSpecial contained "\({#\)\@<=[[:alnum:]*#@_]\+" nextgroup=@shDerefVarList,shDerefOp syn match shDerefVar contained "{\@<=\h\w*" nextgroup=@shDerefVarList syn match shDerefVar contained '\d' nextgroup=@shDerefVarList if exists("b:is_kornshell") || exists("b:is_posix") diff --git a/runtime/syntax/testdir/input/sh_03.sh b/runtime/syntax/testdir/input/sh_03.sh index 8dd6dab9c..28e80e172 100644 --- a/runtime/syntax/testdir/input/sh_03.sh +++ b/runtime/syntax/testdir/input/sh_03.sh @@ -12,6 +12,11 @@ echo ${VariableA##pat1} echo ${VariableB%pat1} echo ${VariableB%%pat1} +# special variables +echo "${!#}" # last positional argument +echo "${!@}" # deref +echo "${#PATH}" # length + # This gets marked as an error Variable=${VariableB:+${VariableC:=eng}} # :+ seems to work for ksh as well as bash Variable=${VariableB:-${VariableC:-eng}} # :- is ksh and bash
plus re-generate the sh_03* dump files
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@benknoble pushed 1 commit.
—
View it on GitHub or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
1: bde43e727 ! 1: 61b1d9833 runtime(sh): allow "#" in special derefs
@@ Commit message
[1]: https://vi.stackexchange.com/q/48617/10604
- Correct that.
+ Correct that. Indirection is only valid in Bash in Ksh, so rearrange the
+ "!" handling to be conditional.
+ Helped-by: Christian Brabandt <c...@256bit.org>
Signed-off-by: D. Ben Knoble <ben.knob...@gmail.com>
## runtime/syntax/sh.vim ##
-@@ runtime/syntax/sh.vim: if (exists("b:is_kornshell") && !exists("b:is_ksh88"))
+@@
+ " 2026 Feb 15 improve comment handling #19414
+ " 2026 Mar 23 improve matching of function definitions #19638
+ " 2026 Apr 02 improve matching of function definitions #19849
++" 2026 Apr 19 improve detection of special variables #20016
+ " }}}
+ " Version: 208
+ " Former URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_SH
+@@ runtime/syntax/sh.vim: endif
+ if exists("b:is_bash")
+ syn region shDeref matchgroup=PreProc start="\${!" end="\*\=}" contains=@shDerefList,shDerefOffset
+ syn match shDerefVar contained "{\@<=!\h\w*" nextgroup=@shDerefVarList
++ syn match shDerefSpecial contained "\({!\)\@<=[[:alnum:]*#@_]\+" nextgroup=@shDerefVarList,shDerefOp
+ endif
+ if (exists("b:is_kornshell") && !exists("b:is_ksh88"))
+ syn match shDerefVar contained "{\@<=!\h\w*[[:alnum:]_.]*" nextgroup=@shDerefVarList
++ syn match shDerefSpecial contained "\({!\)\@<=[[:alnum:]*#@_]\+" nextgroup=@shDerefVarList,shDerefOp
endif
syn match shDerefSpecial contained "{\@<=[-*@?0]" nextgroup=shDerefOp,shDerefOffset,shDerefOpError
-syn match shDerefSpecial contained "\({[#!]\)\@<=[[:alnum:]*@_]\+" nextgroup=@shDerefVarList,shDerefOp
-+syn match shDerefSpecial contained "\({[#!]\)\@<=[[:alnum:]*#@_]\+" nextgroup=@shDerefVarList,shDerefOp
++syn match shDerefSpecial contained "\({[#]\)\@<=[[:alnum:]*@_]\+" nextgroup=@shDerefVarList,shDerefOp
syn match shDerefVar contained "{\@<=\h\w*" nextgroup=@shDerefVarList
syn match shDerefVar contained '\d' nextgroup=@shDerefVarList
if exists("b:is_kornshell") || exists("b:is_posix")
+
+ ## runtime/syntax/testdir/dumps/sh_03_01.dump ##
+@@
+ |:+0#0000e05&| +0#0000000&|'+0#af5f00255&|$+0#e000002&|{|V|a|r|i|a|b|l|e|B|:|-|$|{|V|a|r|i|a|b|l|e|C|:|-|e|n|g|}@1|'+0#af5f00255&| +0#0000000&@39
+ @75
+ |#+0#0000e05&| |A|n|o|t|h|e|r| |t|e|s|t| +0#0000000&@60
+-@57|1|8|,|0|-|1| @7|8|1|%|
++@57|1|8|,|0|-|1| @7|6|5|%|
+
+ ## runtime/syntax/testdir/dumps/sh_03_02.dump ##
+@@
+ |#+0#0000e05#ffffff0| |A|n|o|t|h|e|r| |t|e|s|t| +0#0000000&@60
+ |V+0#00e0e07&|a|r|i|a|b|l|e|=+0#0000000&|$+0#e000e06&|{|V|a|r|i|a|b|l|e|B|:+0#af5f00255&|-|$+0#e000e06&|{|V|a|r|i|a|b|l|e|C|:+0#af5f00255&|-|$+0#e000e06&|{|V|a|r|i|a|b|l|e|D|:+0#af5f00255&|-|$+0#e000e06&|{|V|a|r|i|a|b|l|e|E|:+0#af5f00255&|=|e+0#0000000&|n|g|}+0#e000e06&@3| +0#0000000&@6
+ @7|:+0#0000e05&| +0#0000000&@7|$+0#e000e06&|{|V|a|r|i|a|b|l|e|B|:+0#af5f00255&|=|$+0#e000e06&|{|V|a|r|i|a|b|l|e|C|:+0#af5f00255&|-|$+0#e000e06&|{|V|a|r|i|a|b|l|e|D|:+0#af5f00255&|-|$+0#e000e06&|{|V|a|r|i|a|b|l|e|E|:+0#af5f00255&|=|e+0#0000000&|n|g|}+0#e000e06&@3
+-> +0#0000000&@74
++| +0#0000000&@74
++|#+0#0000e05&| |s|p|e|c|i|a|l| |v|a|r|i|a|b|l|e|s| +0#0000000&@55
++>e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|$+0#e000e06&|{|!|#|}|"+0#af5f00255&| +0#e000002&@11|#+0#0000e05&| |l|a|s|t| |p|o|s|i|t|i|o|n|a|l| |a|r|g|u|m|e|n|t| +0#0000000&@24
++|e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|$+0#e000e06&|{|!|@|}|"+0#af5f00255&| +0#e000002&@11|#+0#0000e05&| |d|e|r|e|f| +0#0000000&@43
++|e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|$+0#e000e06&|{|#|P|A|T|H|}|"+0#af5f00255&| +0#e000002&@8|#+0#0000e05&| |l|e|n|g|t|h| +0#0000000&@42
+ |~+0#4040ff13&| @73
+ |~| @73
+ |~| @73
+@@
+ |~| @73
+ |~| @73
+ |~| @73
+-|~| @73
+-|~| @73
+-|~| @73
+-|~| @73
+-| +0#0000000&@56|3@1|,|0|-|1| @7|B|o|t|
++| +0#0000000&@56|3|5|,|1| @9|B|o|t|
+
+ ## runtime/syntax/testdir/dumps/sh_bash_00.dump ##
+@@
+ |e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|{| |e+0#af5f00255&|c|h|o| +0#e000002&|'+0#af5f00255&|s+0#e000002&|e|v|e|n|'+0#af5f00255&| +0#e000002&@3|;+0#e000e06&|}| +0#0000000&@48
+ |e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|{| |e+0#af5f00255&|c|h|o| +0#e000002&|'+0#af5f00255&|e+0#e000002&|i|g|h|t|'+0#af5f00255&|;+0#e000e06&| @2|}| +0#0000000&@49
+ |t+0#af5f00255&|y|p|e|s|e|t| +0#e000e06&|n+0#00e0e07&|i|n|e|=+0#0000000&|$+0#e000e06&|{| |p+0#af5f00255&|w|d|;| +0#e000e06&|}| +0#0000000&@52
+-|e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|{| |e+0#af5f00255&|c|h|o| +0#e000002&|'+0#af5f00255&|n+0#e000002&|i|n|e|'+0#af5f00255&| +0#e000002&|;+0#e000e06&| | +0#0000000&@52
++|e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|{| |e+0#af5f00255&|c|h|o| +0#e000002&|'+0#af5f00255&|n+0#e000002&|i|n|e|'+0#af5f00255&| +0#e000002&|;+0#e000e06&| +0#0000000&@53
+ | +0#e000e06&|}| +0#0000000&@72
+ @75
+ |i|s|_|b|a|s|h|:| |1|,| @45|1|,|1| @10|T|o|p|
+
+ ## runtime/syntax/testdir/dumps/sh_bash_01.dump ##
+@@
+ |e+0#af5f00255#ffffff0|c|h|o| +0#e000002&|$+0#e000e06&|{| |e+0#af5f00255&|c|h|o| +0#e000002&|'+0#af5f00255&|s+0#e000002&|e|v|e|n|'+0#af5f00255&| +0#e000002&@3|;+0#e000e06&|}| +0#0000000&@48
+ |e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|{| |e+0#af5f00255&|c|h|o| +0#e000002&|'+0#af5f00255&|e+0#e000002&|i|g|h|t|'+0#af5f00255&|;+0#e000e06&| @2|}| +0#0000000&@49
+ |t+0#af5f00255&|y|p|e|s|e|t| +0#e000e06&|n+0#00e0e07&|i|n|e|=+0#0000000&|$+0#e000e06&|{| |p+0#af5f00255&|w|d|;| +0#e000e06&|}| +0#0000000&@52
+-|e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|{| |e+0#af5f00255&|c|h|o| +0#e000002&|'+0#af5f00255&|n+0#e000002&|i|n|e|'+0#af5f00255&| +0#e000002&|;+0#e000e06&| | +0#0000000&@52
++|e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|{| |e+0#af5f00255&|c|h|o| +0#e000002&|'+0#af5f00255&|n+0#e000002&|i|n|e|'+0#af5f00255&| +0#e000002&|;+0#e000e06&| +0#0000000&@53
+ | +0#e000e06&|}| +0#0000000&@72
+ > @74
+ |v+0#e000e06&|a|l|s|u|b|f|u|n|c|(|)| |{| +0#0000000&@60
+@@
+ |p+0#af5f00255&|r|i|n|t|f| +0#e000e06&|'+0#af5f00255&|%+0#e000002&|s|\|n|'+0#af5f00255&| +0#e000e06&|"+0#af5f00255&|$+0#e000e06&|{|||v|a|l|s|u|b|f|u|n|c| |t|w|e|l|v|e| @4|;|}|"+0#af5f00255&| +0#0000000&@31
+ |u+0#00e0e07&|n|l|u|c|k|y|=+0#0000000&|$+0#e000e06&|{||+0#af5f00255&|v+0#e000e06&|a|l|s|u|b|f|u|n|c| |t|h|i|r|t|e@1|n| +0#0000000&@44
+ |}+0#e000e06&| +0#0000000&@73
+-|t+0#af5f00255&|y|p|e|s|e|t| +0#e000e06&|n+0#00e0e07&|o|t|a|f|l|o|a|t|=+0#0000000&|$+0#e000e06&|{||+0#af5f00255&|v+0#e000e06&|a|l|s|u|b|f|u|n|c| |n|o|t|a|n|u|m|b|e|r| @5|;+0#af5f00255&| +0#e000e06&| +0#0000000&@24
++|t+0#af5f00255&|y|p|e|s|e|t| +0#e000e06&|n+0#00e0e07&|o|t|a|f|l|o|a|t|=+0#0000000&|$+0#e000e06&|{||+0#af5f00255&|v+0#e000e06&|a|l|s|u|b|f|u|n|c| |n|o|t|a|n|u|m|b|e|r| @5|;+0#af5f00255&| +0#0000000&@25
+ | +0#e000e06&|}| +0#0000000&@72
+ |e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|u|n|l|u|c|k|y| +0#e000002&|$+0#e000e06&|n|o|t|a|n|u|m|b|e|r| +0#0000000&@49
+ |$+0#e000e06&|{||+0#af5f00255&|e|c|h|o| +0#e000002&|f|o|u|r|t|e@1|n|;+0#af5f00255&|}+0#e000e06&| +0#0000000&@56
+ |$+0#e000e06&|{||+0#af5f00255&|e|c|h|o| +0#e000002&|f|i|f|t|e@1|n| +0#0000000&@59
+-@57|1|9|,|0|-|1| @7|9|2|%|
++@57|1|9|,|0|-|1| @7|7|2|%|
+
+ ## runtime/syntax/testdir/dumps/sh_bash_02.dump ##
+@@
+ |$+0#e000e06#ffffff0|{||+0#af5f00255&|e|c|h|o| +0#e000002&|f|i|f|t|e@1|n| +0#0000000&@59
+->}+0#e000e06&| +0#0000000&@73
++|}+0#e000e06&| +0#0000000&@73
++@75
++|e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|$+0#e000e06&|{|!|#|}|"+0#af5f00255&| +0#e000002&@11|#+0#0000e05&| |l|a|s|t| |p|o|s|i|t|i|o|n|a|l| |a|r|g|u|m|e|n|t| +0#0000000&@24
++|e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|$+0#e000e06&|{|!|@|}|"+0#af5f00255&| +0#e000002&@11|#+0#0000e05&| |d|e|r|e|f| +0#0000000&@43
++>e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|$+0#e000e06&|{|#|P|A|T|H|}|"+0#af5f00255&| +0#e000002&@8|#+0#0000e05&| |l|e|n|g|t|h| +0#0000000&@42
+ |~+0#4040ff13&| @73
+ |~| @73
+ |~| @73
+@@
+ |~| @73
+ |~| @73
+ |~| @73
+-|~| @73
+-|~| @73
+-|~| @73
+-|~| @73
+-| +0#0000000&@56|3@1|,|1| @9|B|o|t|
++| +0#0000000&@56|3|7|,|1| @9|B|o|t|
+
+ ## runtime/syntax/testdir/input/sh_03.sh ##
+@@
+ Variable=${VariableB:-${VariableC:-${VariableD:-${VariableE:=eng}}}}
+ : ${VariableB:=${VariableC:-${VariableD:-${VariableE:=eng}}}}
+
++# special variables
++echo "${!#}" # last positional argument
++echo "${!@}" # deref
++echo "${#PATH}" # length
+
+ ## runtime/syntax/testdir/input/sh_bash.bash ##
+@@ runtime/syntax/testdir/input/sh_bash.bash: echo ${ echo 'six'
+ echo ${ echo 'seven' ;}
+ echo ${ echo 'eight'; }
+ typeset nine=${ pwd; }
+-echo ${ echo 'nine' ;
++echo ${ echo 'nine' ;
+ }
+
+ valsubfunc() {
+@@ runtime/syntax/testdir/input/sh_bash.bash: echo "${|valsubfunc eleven; }"
+ printf '%s\n' "${|valsubfunc twelve ;}"
+ unlucky=${|valsubfunc thirteen
+ }
+-typeset notafloat=${|valsubfunc notanumber ;
++typeset notafloat=${|valsubfunc notanumber ;
+ }
+ echo $unlucky $notanumber
+ ${|echo fourteen;}
+ ${|echo fifteen
+ }
++
++echo "${!#}" # last positional argument
++echo "${!@}" # deref
++echo "${#PATH}" # length
A few notes:
${!#}.foreground changes?—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()