runtime(sh): Fix various syntax highlighting problems in ksh93 scripts
- Fixed syntax highlighting for ksh93 namespace variables starting
with '${.'
- Added support for the alarm, eloop, fds, mkservice, pids, poll and
sha2sum builtins (which are indeed ksh93 builtins, albeit whether or
not they are available depends on the ksh release and the compiled
SHOPT options).
- Added support for the many Unix commands provided by ksh93's libcmd
as builtin commands (since these are general commands, scripts for
other shells like bash will also highlight these).
- The dumps for the sh_0{2,5,6,8,9}.sh were recreated due to this
change affecting commands those scripts call (e.g. 'wc').
- Enabled ${parameter/pattern/string} and friends for ksh syntax.
- Enabled case modification for ksh. See also:
https://github.com/ksh93/ksh/commit/c1762e03
- Enabled ;;& support for ksh. See also:
https://github.com/ksh93/ksh/commit/fc89d20a
- Added many special ksh variables using 93u+m's data/variables.c
as a reference.
If vim can't figure out which ksh release is in play using e.g.
the hashbang path, in such a case a generic default that enables
everything and the kitchen sink will be used. Otherwise, features will
be disabled if it's absolutely known a certain feature will not be
present. Examples:
- ERRNO is ksh88 specific, so that is locked to ksh88.
- Only 93u+m (assumed for generic) has SRANDOM, and only 93u+m
and 93v- have case modification support.
- 93u+ and 93v- have VPATH and CSWIDTH variables (the latter
is vestigal, but still present in the hardcoded variable table).
- 93v- and ksh2020 have (buggy and near unusable) implementations
of compgen and complete.
- Only mksh provides function substitutions, i.e. ${|command;}.
This took the better part of my day to implement. It seems to work well
enough though. (Also had to regenerate the dumps again while testing
it, as now there are dup scripts with mere hashbang differences, used
solely for testing syntax highlighting differences.)
closes: #17348
Signed-off-by: Johnothan King <johnothanking@protonmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/runtime/syntax/sh.vim b/runtime/syntax/sh.vim
index b0e02b9..68d43a3 100644
--- a/runtime/syntax/sh.vim
+++ b/runtime/syntax/sh.vim
@@ -14,6 +14,7 @@
" 2025 May 06 improve single-quote string matching in parameter expansions
" 2025 May 06 match KornShell compound arrays
" 2025 May 10 improve wildcard character class lists
+" 2025 May 21 improve supported KornShell features
" Version: 208
" Former URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_SH
" For options and settings, please use: :help ft-sh-syntax
@@ -31,7 +32,32 @@
let s:shebang = getline(1)
if s:shebang =~ '^#!.\{-2,}\<ksh\>'
+ " The binary is too ambiguous (i.e. '/bin/ksh' or some such).
let b:is_kornshell = 1
+ let b:generic_korn = 1
+elseif s:shebang =~ '^#!.\{-2,}\<ksh93u\+\>'
+ " ksh93u+ (or 93u-) release (still much too common to encounter)
+ let b:is_kornshell = 1
+ let b:is_ksh93u = 1
+elseif s:shebang =~ '^#!.\{-2,}\<ksh93v\>'
+ " ksh93v- alpha or beta
+ let b:is_kornshell = 1
+ let b:is_ksh93v = 1
+elseif s:shebang =~ '^#!.\{-2,}\<ksh93\>'
+ " Could be any ksh93 release
+ let b:is_kornshell = 1
+ let b:is_ksh93 = 1
+elseif s:shebang =~ '^#!.\{-2,}\<ksh2020\>'
+ let b:is_kornshell = 1
+ let b:is_ksh2020 = 1
+elseif s:shebang =~ '^#!.\{-2,}\<ksh88\>'
+ " The actual AT&T ksh88 and its feature set is assumed.
+ let b:is_kornshell = 1
+ let b:is_ksh88 = 1
+elseif s:shebang =~ '^#!.\{-2,}\<mksh\>'
+ " MirBSD Korn Shell
+ let b:is_kornshell = 1
+ let b:is_mksh = 1
elseif s:shebang =~ '^#!.\{-2,}\<bash\>'
let b:is_bash = 1
elseif s:shebang =~ '^#!.\{-2,}\<dash\>'
@@ -46,6 +72,7 @@
elseif !exists("b:is_kornshell") && !exists("b:is_bash") && !exists("b:is_posix") && !exists("b:is_dash")
if exists("g:is_kornshell")
let b:is_kornshell= 1
+ let b:generic_korn = 1
elseif exists("g:is_bash")
let b:is_bash= 1
elseif exists("g:is_dash")
@@ -64,8 +91,33 @@
elseif executable("/usr/bin/sh")
let s:shell = resolve("/usr/bin/sh")
endif
- if s:shell =~ '\<ksh\>'
- let b:is_kornshell= 1
+ if s:shell =~ '\<ksh\>'
+ " The binary is too ambiguous (i.e. '/bin/ksh' or some such).
+ let b:is_kornshell = 1
+ let b:generic_korn = 1
+ elseif s:shell =~ '\<ksh93u\+\>'
+ " ksh93u+ (or 93u-) release (still much too common to encounter)
+ let b:is_kornshell = 1
+ let b:is_ksh93u = 1
+ elseif s:shell =~ '\<ksh93v\>'
+ " ksh93v- alpha or beta
+ let b:is_kornshell = 1
+ let b:is_ksh93v = 1
+ elseif s:shell =~ '\<ksh93\>'
+ " Could be any ksh93 release
+ let b:is_kornshell = 1
+ let b:is_ksh93 = 1
+ elseif s:shebang =~ '\<ksh2020\>'
+ let b:is_kornshell = 1
+ let b:is_ksh2020 = 1
+ elseif s:shell =~ '\<ksh88\>'
+ " The actual AT&T ksh88 and its feature set is assumed.
+ let b:is_kornshell = 1
+ let b:is_ksh88 = 1
+ elseif s:shell =~ '\<mksh\>'
+ " MirBSD Korn Shell
+ let b:is_kornshell = 1
+ let b:is_mksh = 1
elseif s:shell =~ '\<bash\>'
let b:is_bash = 1
elseif s:shell =~ '\<dash\>'
@@ -234,7 +286,7 @@
" Echo: {{{1
" ====
" This one is needed INSIDE a CommandSub, so that `echo bla` be correct
-if exists("b:is_kornshell")
+if (exists("b:is_kornshell") && !exists("b:is_ksh88"))
syn region shEcho matchgroup=shStatement start="\<echo\>" skip="\\$" matchgroup=shEchoDelim end="$" matchgroup=NONE end="[<>;&|()`}]"me=e-1 end="\d[<>]"me=e-2 end="#"me=e-1 end="\ze[ \t\n;]}" contains=@shEchoList skipwhite nextgroup=shQuickComment
syn region shEcho matchgroup=shStatement start="\<print\>" skip="\\$" matchgroup=shEchoDelim end="$" matchgroup=NONE end="[<>;&|()`}]"me=e-1 end="\d[<>]"me=e-2 end="#"me=e-1 end="\ze[ \t\n;]}" contains=@shEchoList skipwhite nextgroup=shQuickComment
else
@@ -359,7 +411,7 @@
syn match shCaseBar contained skipwhite "\(^\|[^\\]\)\(\\\\\)*\zs|" nextgroup=shCase,shCaseStart,shCaseBar,shComment,shCaseExSingleQuote,shCaseSingleQuote,shCaseDoubleQuote
syn match shCaseStart contained skipwhite skipnl "(" nextgroup=shCase,shCaseBar
syn match shCaseLabel contained skipwhite "\%(\\.\|[-a-zA-Z0-9_*.]\)\+" contains=shBracketExpr
-if exists("b:is_bash")
+if exists("b:is_bash") || (exists("b:is_kornshell") && !exists("b:is_ksh88") && !exists("b:is_ksh93u") && !exists("b:is_ksh93v") && !exists("b:is_ksh2020"))
ShFoldIfDoFor syn region shCase contained skipwhite skipnl matchgroup=shSnglCase start="\%(\\.\|[^#$()'" \t]\)\{-}\zs)" end=";;" end=";&" end=";;&" end="esac"me=s-1 contains=@shCaseList nextgroup=shCaseStart,shCase,shComment
elseif exists("b:is_kornshell")
ShFoldIfDoFor syn region shCase contained skipwhite skipnl matchgroup=shSnglCase start="\%(\\.\|[^#$()'" \t]\)\{-}\zs)" end=";;" end=";&" end="esac"me=s-1 contains=@shCaseList nextgroup=shCaseStart,shCase,shComment
@@ -369,7 +421,7 @@
ShFoldIfDoFor syn region shCaseEsac matchgroup=shConditional start="\<case\>" end="\<esac\>" contains=@shCaseEsacList
syn keyword shCaseIn contained skipwhite skipnl in nextgroup=shCase,shCaseStart,shCaseBar,shComment,shCaseExSingleQuote,shCaseSingleQuote,shCaseDoubleQuote
-if exists("b:is_bash") || exists("b:is_kornshell")
+if exists("b:is_bash") || (exists("b:is_kornshell") && !exists("b:is_ksh88"))
syn region shCaseExSingleQuote matchgroup=shQuote start=+\$'+ skip=+\\\\\|\\.+ end=+'+ contains=shStringSpecial,shSpecial skipwhite skipnl nextgroup=shCaseBar contained
elseif !exists("g:sh_no_error")
syn region shCaseExSingleQuote matchgroup=Error start=+\$'+ skip=+\\\\\|\\.+ end=+'+ contains=shStringSpecial skipwhite skipnl nextgroup=shCaseBar contained
@@ -403,8 +455,16 @@
if exists("b:is_kornshell") || exists("b:is_bash") || exists("b:is_posix")
syn region shCommandSub matchgroup=shCmdSubRegion start="\$((\@!" skip='\\\\\|\\.' end=")" contains=@shCommandSubList
if exists("b:is_kornshell")
- syn region shSubshare matchgroup=shCmdSubRegion start="\${\ze[ \t\n<]" skip='\\\\\|\\.' end="\zs[ \t\n;]}" contains=@shCommandSubList
- syn region shValsub matchgroup=shCmdSubRegion start="\${|" skip='\\\\\|\\.' end="}" contains=@shCommandSubList
+ if !exists("b:is_ksh88")
+ if exists("b:is_mksh")
+ syn region shSubshare matchgroup=shCmdSubRegion start="\${\ze[ \t\n]" skip='\\\\\|\\.' end="\zs[ \t\n;]}" contains=@shCommandSubList
+ else
+ syn region shSubshare matchgroup=shCmdSubRegion start="\${\ze[ \t\n<]" skip='\\\\\|\\.' end="\zs[ \t\n;]}" contains=@shCommandSubList
+ endif
+ endif
+ if exists("b:is_mksh") || exists("b:generic_korn")
+ syn region shValsub matchgroup=shCmdSubRegion start="\${|" skip='\\\\\|\\.' end="}" contains=@shCommandSubList
+ endif
endif
syn region shArithmetic matchgroup=shArithRegion start="\$((" skip='\\\\\|\\.' end="))" contains=@shArithList
syn region shArithmetic matchgroup=shArithRegion start="\$\[" skip='\\\\\|\\.' end="\]" contains=@shArithList
@@ -419,17 +479,39 @@
syn cluster shCommandSubList add=bashSpecialVariables,bashStatement
syn cluster shCaseList add=bashAdminStatement,bashStatement
syn keyword bashSpecialVariables contained auto_resume BASH BASH_ALIASES BASH_ARGC BASH_ARGV BASH_CMDS BASH_COMMAND BASH_ENV BASH_EXECUTION_STRING BASH_LINENO BASHOPTS BASHPID BASH_REMATCH BASH_SOURCE BASH_SUBSHELL BASH_VERSINFO BASH_VERSION BASH_XTRACEFD CDPATH COLUMNS COMP_CWORD COMP_KEY COMP_LINE COMP_POINT COMPREPLY COMP_TYPE COMP_WORDBREAKS COMP_WORDS COPROC COPROC_PID DIRSTACK EMACS ENV EUID FCEDIT FIGNORE FUNCNAME FUNCNEST GLOBIGNORE GROUPS histchars HISTCMD HISTCONTROL HISTFILE HISTFILESIZE HISTIGNORE HISTSIZE HISTTIMEFORMAT HOME HOSTFILE HOSTNAME HOSTTYPE IFS IGNOREEOF INPUTRC LANG LC_ALL LC_COLLATE LC_CTYPE LC_MESSAGES LC_NUMERIC LINENO LINES MACHTYPE MAIL MAILCHECK MAILPATH MAPFILE OLDPWD OPTARG OPTERR OPTIND OSTYPE PATH PIPESTATUS POSIXLY_CORRECT PPID PROMPT_COMMAND PS0 PS1 PS2 PS3 PS4 PWD RANDOM READLINE_LINE READLINE_POINT REPLY SECONDS SHELL SHELLOPTS SHLVL TIMEFORMAT TIMEOUT TMPDIR UID
- syn keyword bashStatement chmod clear complete du egrep expr fgrep find gnufind gnugrep grep head less ls mkdir mv rm rmdir rpm sed sleep sort strip tail
+ syn keyword bashStatement basename cat chgrp chmod chown cksum clear cmp comm command compgen complete cp cut date dirname du egrep expr fgrep find fmt fold getconf gnufind gnugrep grep head iconv id join less ln logname ls md5sum mkdir mkfifo mknod mktemp mv od paste pathchk readlink realpath rev rm rmdir rpm sed sha1sum sha224sum sha256sum sha384sum sha512sum sleep sort strip stty sum sync tail tee tr tty uname uniq wc which xargs xgrep
syn keyword bashAdminStatement daemon killall killproc nice reload restart start status stop
- syn keyword bashStatement command compgen
endif
if exists("b:is_kornshell") || exists("b:is_posix")
syn cluster shCommandSubList add=kshSpecialVariables,kshStatement
syn cluster shCaseList add=kshStatement
- syn keyword kshSpecialVariables contained CDPATH COLUMNS EDITOR ENV ERRNO FCEDIT FPATH HISTFILE HISTSIZE HOME IFS LINENO LINES MAIL MAILCHECK MAILPATH OLDPWD OPTARG OPTIND PATH PPID PS1 PS2 PS3 PS4 PWD RANDOM REPLY SECONDS SHELL TMOUT VISUAL
- syn keyword kshStatement cat chmod clear cp du egrep expr fgrep find grep head killall less ls mkdir mv nice printenv rm rmdir sed sort strip stty tail tput
- syn keyword kshStatement command setgroups setsenv
+ syn keyword kshSpecialVariables contained CDPATH COLUMNS EDITOR ENV FCEDIT FIGNORE FPATH HISTCMD HISTEDIT HISTFILE HISTSIZE HOME IFS JOBMAX KSH_VERSION LANG LC_ALL LC_COLLATE LC_CTYPE LC_MESSAGES LC_NUMERIC LC_TIME LINENO LINES MAIL MAILCHECK MAILPATH OLDPWD OPTARG OPTIND PATH PPID PS1 PS2 PS3 PS4 PWD RANDOM REPLY SECONDS SHELL SHLVL TMOUT VISUAL
+ syn keyword kshStatement basename cat chgrp chmod chown cksum clear cmp comm command cp cut date dirname du egrep expr fgrep find fmt fold grep head iconv id join killall less ln logname ls md5sum mkdir mknod mkfifo mktemp mv nice od paste pathchk printenv readlink realpath rev rm rmdir sed setgroups setsenv sha1sum sha224sum sha256sum sha384sum sha512sum sort strip stty sum sync tail tee tput tr tty uname uniq wc which xargs xgrep
+ if exists("b:is_ksh88")
+ syn keyword kshSpecialVariables contained ERRNO
+ elseif exists("b:is_mksh")
+ syn keyword kshSpecialVariables contained BASHPID EPOCHREALTIME EXECSHELL KSHEGID KSHGID KSHUID KSH_MATCH PATHSEP PGRP PIPESTATUS TMPDIR USER_ID
+ syn keyword kshStatement bind rename
+ elseif exists("b:is_ksh93v") || exists("b:is_ksh2020")
+ syn keyword kshSpecialVariables contained SH_OPTIONS COMP_CWORD COMP_LINE COMP_POINT COMP_WORDS COMP_KEY COMPREPLY COMP_WORDBREAKS COMP_TYPE
+ syn keyword kshStatement compgen complete
+ if exists("b:is_ksh93v")
+ syn keyword kshSpecialVariables contained VPATH CSWIDTH
+ syn keyword kshStatement vmstate alarm fds pids poll sha2sum
+ endif
+ elseif exists("b:is_ksh93u")
+ " ksh93u+
+ syn keyword kshSpecialVariables contained VPATH CSWIDTH
+ syn keyword kshStatement alarm fds pids vmstate
+ else
+ " 'ksh' is ambiguous; include everything
+ syn keyword kshSpecialVariables contained BASHPID EPOCHREALTIME EXECSHELL KSHEGID KSHGID KSHUID KSH_MATCH PATHSEP PGRP PIPESTATUS TMPDIR USER_ID SH_OPTIONS COMP_CWORD COMP_LINE COMP_POINT COMP_WORDS COMP_KEY COMPREPLY COMP_WORDBREAKS COMP_TYPE VPATH SRANDOM CSWIDTH
+ if !exists("b:is_ksh93")
+ syn keyword kshSpecialVariables contained ERRNO
+ endif
+ syn keyword kshStatement vmstate alarm fds pids poll sha2sum alarm eloop fds mkservice pids compgen complete bind rename
+ endif
endif
syn match shSource "^\.\s"
@@ -504,8 +586,8 @@
" Here Strings: {{{1
" =============
-" available for: bash; ksh (really should be ksh93 only) but not if its a posix
-if exists("b:is_bash") || (exists("b:is_kornshell") && !exists("b:is_posix"))
+" available for: bash and ksh (except ksh88) but not if its a posix
+if exists("b:is_bash") || ((exists("b:is_kornshell") && !exists("b:is_ksh88")) && !exists("b:is_posix"))
syn match shHereString "<<<" skipwhite nextgroup=shCmdParenRegion
endif
@@ -518,7 +600,7 @@
syn region shArrayValue contained start="\[\%(..\{-}\]=\)\@=" end="\]=\@=" contains=@shArrayValueList nextgroup=shVarAssign
syn cluster shArrayValueList contains=shArithmetic,shArithParen,shCommandSub,shDeref,shDerefSimple,shExpr,shNumber,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shSpecial,shParen,bashSpecialVariables,shParenError
syn region shArrayRegion contained matchgroup=shShellVariables start="(" skip='\\\\\|\\.' end=")" contains=@shArrayValueList,shArrayValue,shComment
-elseif exists("b:is_kornshell")
+elseif (exists("b:is_kornshell") && !exists("b:is_ksh88"))
" The subscript form for array values, e.g. "foo=([2]=10 [4]=100)".
syn region shArrayValue contained start="\[\%(..\{-}\]=\)\@=" end="\]=\@=" contains=@shArrayValueList nextgroup=shVarAssign
syn cluster shArrayValueList contains=shArithmetic,shArithParen,shCommandSub,shDeref,shDerefSimple,shExpr,shNumber,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shSpecial,shParen,kshSpecialVariables,shParenError
@@ -548,7 +630,7 @@
endif
" KornShell namespace: {{{1
-if exists("b:is_kornshell")
+if exists("b:is_kornshell") && !exists("b:is_ksh88") && !exists("b:is_mksh")
syn keyword shFunctionKey namespace skipwhite skipnl nextgroup=shFunctionTwo
endif
@@ -578,8 +660,14 @@
syn match shDerefWordError "[^}$[~]" contained
endif
syn match shDerefSimple "\$\%(\h\w*\|\d\)" nextgroup=@shNoZSList
-if exists("b:is_kornshell")
- syn region shDeref matchgroup=PreProc start="\${\ze[^ \t\n<|]" end="}" contains=@shDerefList,shDerefVarArray nextgroup=shSpecialStart
+if exists("b:is_kornshell") && !exists("b:is_ksh88")
+ if exists("b:is_mksh")
+ syn region shDeref matchgroup=PreProc start="\${\ze[^ \t\n|]" end="}" contains=@shDerefList,shDerefVarArray nextgroup=shSpecialStart
+ elseif exists("b:generic_korn")
+ syn region shDeref matchgroup=PreProc start="\${\ze[^ \t\n<|]" end="}" contains=@shDerefList,shDerefVarArray nextgroup=shSpecialStart
+ else
+ syn region shDeref matchgroup=PreProc start="\${\ze[^ \t\n<]" end="}" contains=@shDerefList,shDerefVarArray nextgroup=shSpecialStart
+ endif
else
syn region shDeref matchgroup=PreProc start="\${" end="}" contains=@shDerefList,shDerefVarArray nextgroup=shSpecialStart
endif
@@ -593,13 +681,14 @@
" ksh: ${.sh.*} variables: {{{1
" ========================================
-if exists("b:is_kornshell")
+if exists("b:is_kornshell") && !exists("b:is_ksh88") && !exists("b:is_mksh")
syn match shDerefVar contained "\.\+" nextgroup=@shDerefVarList
+ syn region shDeref matchgroup=PreProc start="\${\ze[\.]" end="}" contains=@shDerefVarList,shDerefPSR,shDerefPPS
endif
" ksh: ${!var[*]} array index list syntax: {{{1
" ========================================
-if exists("b:is_kornshell") || exists("b:is_posix")
+if (exists("b:is_kornshell") && !exists("b:is_ksh88")) || exists("b:is_posix")
syn region shDeref matchgroup=PreProc start="\${!" end="}" contains=@shDerefVarArray
endif
@@ -609,7 +698,7 @@
syn region shDeref matchgroup=PreProc start="\${!" end="\*\=}" contains=@shDerefList,shDerefOffset
syn match shDerefVar contained "{\@<=!\h\w*" nextgroup=@shDerefVarList
endif
-if exists("b:is_kornshell")
+if (exists("b:is_kornshell") && !exists("b:is_ksh88"))
syn match shDerefVar contained "{\@<=!\h\w*[[:alnum:]_.]*" nextgroup=@shDerefVarList
endif
@@ -633,10 +722,10 @@
" ksh bash : ${parameter##pattern} remove large left pattern
" ksh bash : ${parameter%pattern} remove small right pattern
" ksh bash : ${parameter%%pattern} remove large right pattern
-" bash : ${parameter^pattern} Case modification
-" bash : ${parameter^^pattern} Case modification
-" bash : ${parameter,pattern} Case modification
-" bash : ${parameter,,pattern} Case modification
+" ksh bash : ${parameter^pattern} Case modification
+" ksh bash : ${parameter^^pattern} Case modification
+" ksh bash : ${parameter,pattern} Case modification
+" ksh bash : ${parameter,,pattern} Case modification
" bash : ${@:start:qty} display command line arguments from start to start+qty-1 (inferred)
" bash : ${parameter@operator} transforms parameter (operator∈[uULqEPARa])
syn cluster shDerefPatternList contains=shDerefPattern,shDerefString
@@ -655,15 +744,17 @@
call s:GenerateBracketExpressionItems({'itemGroup': 'shDerefPattern', 'bracketGroup': 'shBracketExprDelim', 'extraArgs': 'contained nextgroup=shDerefPattern'})
syn match shDerefEscape contained '\%(\\\\\)*\\.'
endif
-if exists("b:is_bash")
+if exists("b:is_bash") || (exists("b:is_kornshell") && !exists("b:is_ksh88") && !exists("b:is_mksh") && !exists("b:is_ksh93u") && !exists("b:is_ksh2020"))
syn match shDerefOp contained "[,^]\{1,2}" nextgroup=@shDerefPatternList
+endif
+if exists("b:is_bash")
syn match shDerefOp contained "@[uULQEPAKa]"
endif
syn region shDerefString contained matchgroup=shDerefDelim start=+\%(\\\)\@<!'+ end=+'+
syn region shDerefString contained matchgroup=shDerefDelim start=+\%(\\\)\@<!"+ skip=+\\"+ end=+"+ contains=@shDblQuoteList,shStringSpecial
syn match shDerefString contained "\\["']" nextgroup=shDerefPattern
-if exists("b:is_bash") || exists("b:is_kornshell") || exists("b:is_posix")
+if exists("b:is_bash") || (exists("b:is_kornshell") && !exists("b:is_ksh88")) || exists("b:is_posix")
" bash ksh posix : ${parameter:offset}
" bash ksh posix : ${parameter:offset:length}
syn region shDerefOffset contained start=':[^-=?+]' end='\ze:' end='\ze}' contains=shDeref,shDerefSimple,shDerefEscape nextgroup=shDerefLen,shDeref,shDerefSimple
@@ -671,15 +762,15 @@
syn match shDerefLen contained ":[^}]\+" contains=shDeref,shDerefSimple,shArithmetic
endif
-if exists("b:is_bash")
- " bash : ${parameter/pattern/string}
- " bash : ${parameter//pattern/string}
+if exists("b:is_bash") || (exists("b:is_kornshell") && !exists("b:is_ksh88"))
+ " bash ksh : ${parameter/pattern/string}
+ " bash ksh : ${parameter//pattern/string}
syn match shDerefPPS contained '/\{1,2}' nextgroup=shDerefPPSleft
syn region shDerefPPSleft contained start='.' skip=@\%(\\\\\)*\\/@ matchgroup=shDerefOp end='/' end='\ze}' end='"' nextgroup=shDerefPPSright contains=@shPPSLeftList
syn region shDerefPPSright contained start='.' skip=@\%(\\\\\)\+@ end='\ze}' contains=@shPPSRightList
- " bash : ${parameter/#pattern/string}
- " bash : ${parameter/%pattern/string}
+ " bash ksh : ${parameter/#pattern/string}
+ " bash ksh : ${parameter/%pattern/string}
syn match shDerefPSR contained '/[#%]' nextgroup=shDerefPSRleft,shDoubleQuote,shSingleQuote
syn region shDerefPSRleft contained start='[^"']' skip=@\%(\\\\\)*\\/@ matchgroup=shDerefOp end='/' end='\ze}' nextgroup=shDerefPSRright contains=shBracketExpr
syn region shDerefPSRright contained start='.' skip=@\%(\\\\\)\+@ end='\ze}'