neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

sh.vim (66221B)


      1 " Vim syntax file
      2 " Language:		shell (sh) Korn shell (ksh) bash (sh)
      3 " Maintainer:		This runtime file is looking for a new maintainer.
      4 " Previous Maintainers:	Charles E. Campbell
      5 " 		Lennart Schultz <Lennart.Schultz@ecmwf.int>
      6 " Last Change:		2024 Mar 04 by Vim Project {{{1
      7 "		2024 Nov 03 by Aliaksei Budavei <0x000c70 AT gmail DOT com> improved bracket expressions, #15941
      8 "		2025 Jan 06 add $PS0 to bashSpecialVariables #16394
      9 "		2025 Jan 18 add bash coproc, remove duplicate syn keywords #16467
     10 "		2025 Mar 21 update shell capability detection #16939
     11 "		2025 Apr 03 command substitution opening paren at EOL #17026
     12 "		2025 Apr 10 improve shell detection #17084
     13 "		2025 Apr 29 match escaped chars in test operands #17221
     14 "		2025 May 06 improve single-quote string matching in parameter expansions
     15 "		2025 May 06 match KornShell compound arrays
     16 "		2025 May 10 improve wildcard character class lists
     17 "		2025 May 21 improve supported KornShell features
     18 "		2025 Jun 16 change how sh_fold_enabled is reset #17557
     19 "		2025 Jul 18 properly delete :commands #17785
     20 "		2025 Aug 23 bash: add support for ${ cmd;} and ${|cmd;} #18084
     21 "		2025 Sep 23 simplify ksh logic, update sh statements #18355
     22 "		2026 Jan 15 highlight command switches that contain a digit
     23 "		2026 Feb 11 improve support for KornShell function names and variables
     24 "		2026 Feb 15 improve comment handling #19414
     25 " }}}
     26 " Version:		208
     27 " Former URL:		http://www.drchip.org/astronaut/vim/index.html#SYNTAX_SH
     28 " For options and settings, please use:      :help ft-sh-syntax
     29 " This file includes many ideas from Eric Brunet (eric.brunet@ens.fr) and heredoc fixes from Felipe Contreras
     30 
     31 " quit when a syntax file was already loaded {{{1
     32 if exists("b:current_syntax")
     33    finish
     34 endif
     35 
     36 " Ensure this is set unless we find another shell
     37 let b:is_sh = 1
     38 
     39 " If the shell script itself specifies which shell to use, use it
     40 let s:shebang = getline(1)
     41 
     42 if s:shebang =~ '^#!.\{-2,}\<ksh\>'
     43    " The binary is too ambiguous (i.e. '/bin/ksh' or some such).
     44    let b:is_kornshell = 1
     45    let b:generic_korn = 1
     46 elseif s:shebang =~ '^#!.\{-2,}\<ksh93u\+\>'
     47    " ksh93u+ (or 93u-) release (still much too common to encounter)
     48    let b:is_kornshell = 1
     49    let b:is_ksh93u = 1
     50 elseif s:shebang =~ '^#!.\{-2,}\<ksh93v\>'
     51    " ksh93v- alpha or beta
     52    let b:is_kornshell = 1
     53    let b:is_ksh93v = 1
     54 elseif s:shebang =~ '^#!.\{-2,}\<ksh93\>'
     55    " Could be any ksh93 release
     56    let b:is_kornshell = 1
     57    let b:is_ksh93 = 1
     58 elseif s:shebang =~ '^#!.\{-2,}\<ksh2020\>'
     59    let b:is_kornshell = 1
     60    let b:is_ksh2020 = 1
     61 elseif s:shebang =~ '^#!.\{-2,}\<ksh88\>'
     62    " The actual AT&T ksh88 and its feature set is assumed.
     63    let b:is_kornshell = 1
     64    let b:is_ksh88 = 1
     65 elseif s:shebang =~ '^#!.\{-2,}\<mksh\>'
     66    " MirBSD Korn Shell
     67    let b:is_kornshell = 1
     68    let b:is_mksh = 1
     69 elseif s:shebang =~ '^#!.\{-2,}\<bash\>'
     70    let b:is_bash      = 1
     71 elseif s:shebang =~ '^#!.\{-2,}\<dash\>'
     72    let b:is_dash      = 1
     73 " handling /bin/sh with is_kornshell/is_sh {{{1
     74 " b:is_sh will be set when "#! /bin/sh" is found;
     75 " However, it often is just a masquerade by bash (typically Linux)
     76 " or kornshell (typically workstations with Posix "sh").
     77 " So, when the user sets "g:is_kornshell", "g:is_bash",
     78 " "g:is_posix" or "g:is_dash", a b:is_sh is converted into
     79 " b:is_kornshell/b:is_bash/b:is_posix/b:is_dash, respectively.
     80 elseif !exists("b:is_kornshell") && !exists("b:is_bash") && !exists("b:is_posix") && !exists("b:is_dash")
     81    if exists("g:is_kornshell")
     82        let b:is_kornshell= 1
     83        let b:generic_korn = 1
     84    elseif exists("g:is_bash")
     85        let b:is_bash= 1
     86    elseif exists("g:is_dash")
     87        let b:is_dash= 1
     88    elseif exists("g:is_posix")
     89        let b:is_posix= 1
     90    elseif exists("g:is_sh")
     91        let b:is_sh= 1
     92    else
     93        " user did not specify which shell to use, and
     94        " the script itself does not specify which shell to use. FYI: /bin/sh is ambiguous.
     95        " Assuming /bin/sh is executable, and if its a link, find out what it links to.
     96        let s:shell = ""
     97        if executable("/bin/sh")
     98            let s:shell = resolve("/bin/sh")
     99        elseif executable("/usr/bin/sh")
    100            let s:shell = resolve("/usr/bin/sh")
    101        endif
    102        if s:shell =~ '\<ksh\>'
    103            " The binary is too ambiguous (i.e. '/bin/ksh' or some such).
    104            let b:is_kornshell = 1
    105            let b:generic_korn = 1
    106        elseif s:shell =~ '\<ksh93u\+\>'
    107            " ksh93u+ (or 93u-) release (still much too common to encounter)
    108            let b:is_kornshell = 1
    109            let b:is_ksh93u = 1
    110        elseif s:shell =~ '\<ksh93v\>'
    111            " ksh93v- alpha or beta
    112            let b:is_kornshell = 1
    113            let b:is_ksh93v = 1
    114        elseif s:shell =~ '\<ksh93\>'
    115            " Could be any ksh93 release
    116            let b:is_kornshell = 1
    117            let b:is_ksh93 = 1
    118        elseif s:shebang =~ '\<ksh2020\>'
    119            let b:is_kornshell = 1
    120            let b:is_ksh2020 = 1
    121        elseif s:shell =~ '\<ksh88\>'
    122            " The actual AT&T ksh88 and its feature set is assumed.
    123            let b:is_kornshell = 1
    124            let b:is_ksh88 = 1
    125        elseif s:shell =~ '\<mksh\>'
    126            " MirBSD Korn Shell
    127            let b:is_kornshell = 1
    128            let b:is_mksh = 1
    129        elseif s:shell =~ '\<bash\>'
    130            let b:is_bash = 1
    131        elseif s:shell =~ '\<dash\>'
    132            let b:is_dash = 1
    133        else
    134            let b:is_posix = 1
    135        endif
    136        unlet s:shell
    137    endif
    138 endif
    139 
    140 unlet s:shebang
    141 
    142 " if b:is_dash, set b:is_posix too
    143 if exists("b:is_dash")
    144    let b:is_posix= 1
    145 endif
    146 
    147 if exists("b:is_kornshell") || exists("b:is_bash")
    148    if exists("b:is_sh")
    149        unlet b:is_sh
    150    endif
    151 endif
    152 
    153 " set up default g:sh_fold_enabled {{{1
    154 " ================================
    155 if !exists("g:sh_fold_enabled")
    156    let g:sh_fold_enabled= 0
    157 elseif g:sh_fold_enabled != 0 && !has("folding")
    158    echomsg "Ignoring g:sh_fold_enabled=".g:sh_fold_enabled."; need to re-compile vim for +fold support"
    159    let g:sh_fold_enabled= 0
    160 endif
    161 let s:sh_fold_functions= and(g:sh_fold_enabled,1)
    162 let s:sh_fold_heredoc  = and(g:sh_fold_enabled,2)
    163 let s:sh_fold_ifdofor  = and(g:sh_fold_enabled,4)
    164 if g:sh_fold_enabled && &fdm == "manual"
    165    " Given that	the	user provided g:sh_fold_enabled
    166    " 	AND	g:sh_fold_enabled is manual (usual default)
    167    " 	implies	a desire for syntax-based folding
    168    setl fdm=syntax
    169 endif
    170 
    171 " set up the syntax-highlighting for iskeyword
    172 if (v:version == 704 && has("patch-7.4.1142")) || v:version > 704
    173    if !exists("g:sh_syntax_isk") || (exists("g:sh_syntax_isk") && g:sh_syntax_isk)
    174        if exists("b:is_bash")
    175            exe "syn iskeyword ".&iskeyword.",-,:"
    176        elseif exists("b:is_kornshell") && !exists("b:is_ksh88") && !exists("b:is_mksh")
    177            exe "syn iskeyword ".&iskeyword.",-,."
    178        else
    179            exe "syn iskeyword ".&iskeyword.",-"
    180        endif
    181    endif
    182 endif
    183 
    184 " Set up folding commands for shell {{{1
    185 " =================================
    186 if exists(":ShFoldFunctions") == 2
    187    delc ShFoldFunctions
    188 endif
    189 if exists(":ShFoldIfHereDoc") == 2
    190    delc ShFoldHereDoc
    191 endif
    192 if exists(":ShFoldIfDoFor") == 2
    193    delc ShFoldIfDoFor
    194 endif
    195 if s:sh_fold_functions
    196    com! -nargs=* ShFoldFunctions <args> fold
    197 else
    198    com! -nargs=* ShFoldFunctions <args>
    199 endif
    200 if s:sh_fold_heredoc
    201    com! -nargs=* ShFoldHereDoc <args> fold
    202 else
    203    com! -nargs=* ShFoldHereDoc <args>
    204 endif
    205 if s:sh_fold_ifdofor
    206    com! -nargs=* ShFoldIfDoFor <args> fold
    207 else
    208    com! -nargs=* ShFoldIfDoFor <args>
    209 endif
    210 
    211 " Generate bracket expression items {{{1
    212 " =================================
    213 " Note that the following function can be invoked as many times as necessary
    214 " provided that these constraints hold for the passed dictionary argument:
    215 " - every time a unique group-name value is assigned to the "itemGroup" key;
    216 " - only ONCE either the "extraArgs" key is not entered or it is entered and
    217 "   its value does not have "contained" among other optional arguments (":help
    218 "   :syn-arguments").
    219 fun! s:GenerateBracketExpressionItems(dict) abort
    220    let itemGroup = a:dict.itemGroup
    221    let bracketGroup = a:dict.bracketGroup
    222    let invGroup = itemGroup . 'Inv'
    223    let skipLeftBracketGroup = itemGroup . 'SkipLeftBracket'
    224    let skipRightBracketGroup = itemGroup . 'SkipRightBracket'
    225    let extraArgs = has_key(a:dict, 'extraArgs') ? a:dict.extraArgs : ''
    226 
    227    " Make the leading "[!^]" stand out in a NON-matching expression.
    228    exec 'syn match ' . invGroup . ' contained "\[\@<=[!^]"'
    229 
    230    " Set up indirections for unbalanced-bracket highlighting.
    231    exec 'syn region ' . skipRightBracketGroup . ' contained matchgroup=' . bracketGroup . ' start="\[\%([!^]\=\\\=\]\)\@=" matchgroup=shCollSymb end="\[\.[^]]\{-}\][^]]\{-}\.\]" matchgroup=' . itemGroup . ' end="\]" contains=@shBracketExprList,shDoubleQuote,' . invGroup
    232    exec 'syn region ' . skipLeftBracketGroup . ' contained matchgroup=' . bracketGroup . ' start="\[\%([!^]\=\\\=\]\)\@=" skip="[!^]\=\\\=\]\%(\[[^]]\+\]\|[^]]\)\{-}\%(\[[:.=]\@!\)\@=" matchgroup=' . itemGroup . ' end="\[[:.=]\@!" contains=@shBracketExprList,shDoubleQuote,' . invGroup
    233 
    234    " Look for a general matching expression.
    235    exec 'syn region ' . itemGroup . ' matchgroup=' . bracketGroup . ' start="\[\S\@=" end="\]" contains=@shBracketExprList,shDoubleQuote ' . extraArgs
    236    " Look for a general NON-matching expression.
    237    exec 'syn region ' . itemGroup . ' matchgroup=' . bracketGroup . ' start="\[[!^]\@=" end="\]" contains=@shBracketExprList,shDoubleQuote,' . invGroup . ' ' . extraArgs
    238 
    239    " Accommodate unbalanced brackets in bracket expressions.  The supported
    240    " syntax for a plain "]" can be: "[]ws]" and "[^]ws]"; or, "[ws[.xs]ys.]zs]"
    241    " and "[^ws[.xs]ys.]zs]"; see §9.3.5 RE Bracket Expression (in XBD).
    242    exec 'syn region ' . itemGroup . ' matchgroup=NONE start="\[[!^]\=\\\=\]" matchgroup=' . bracketGroup . ' end="\]" contains=@shBracketExprList,shDoubleQuote,' . skipRightBracketGroup . ' ' . extraArgs
    243    " Strive to handle "[]...[]" etc.
    244    exec 'syn region ' . itemGroup . ' matchgroup=NONE start="\[[!^]\=\\\=\]\%(\[[^]]\+\]\|[^]]\)\{-}\[[:.=]\@!" matchgroup=' . bracketGroup . ' end="\]" contains=@shBracketExprList,shDoubleQuote,' . skipLeftBracketGroup . ' ' . extraArgs
    245 
    246    if !exists("g:skip_sh_syntax_inits")
    247        exec 'hi def link ' . skipLeftBracketGroup . ' ' . itemGroup
    248        exec 'hi def link ' . skipRightBracketGroup . ' ' . itemGroup
    249        exec 'hi def link ' . invGroup . ' Underlined'
    250    endif
    251 endfun
    252 
    253 call s:GenerateBracketExpressionItems({'itemGroup': 'shBracketExpr', 'bracketGroup': 'shBracketExprDelim'})
    254 
    255 " sh syntax is case sensitive {{{1
    256 syn case match
    257 
    258 " Clusters: contains=@... clusters {{{1
    259 "==================================
    260 syn cluster shErrorList	contains=shDoError,shIfError,shInError,shCaseError,shEsacError,shCurlyError,shParenError,shTestError,shOK
    261 if exists("b:is_kornshell") || exists("b:is_bash")
    262    syn cluster ErrorList add=shDTestError
    263 endif
    264 syn cluster shArithParenList	contains=shArithmetic,shArithParen,shCaseEsac,shComment,shDeref,shDerefVarArray,shDo,shDerefSimple,shEcho,shEscape,shExpr,shNumber,shOperator,shPosnParm,shExSingleQuote,shExDoubleQuote,shHereString,shRedir,shSingleQuote,shDoubleQuote,shStatement,shVariable,shAlias,shTest,shCtrlSeq,shSpecial,shParen,bashSpecialVariables,bashStatement,shIf,shFor,shFunctionKey,shFunctionOne,shFunctionTwo,shNamespaceOne
    265 syn cluster shArithList	contains=@shArithParenList,shParenError
    266 syn cluster shBracketExprList	contains=shCharClassOther,shCharClass,shCollSymb,shEqClass
    267 syn cluster shCaseEsacList	contains=shCaseStart,shCaseLabel,shCase,shCaseBar,shCaseIn,shComment,shDeref,shDerefSimple,shCaseCommandSub,shCaseExSingleQuote,shCaseSingleQuote,shCaseDoubleQuote,shCtrlSeq,@shErrorList,shStringSpecial,shCaseRange
    268 syn cluster shCaseList	contains=@shCommandSubList,shCaseEsac,shColon,shCommandSub,shCommandSubBQ,shSubshare,shValsub,shComment,shDblBrace,shDo,shEcho,shExpr,shFor,shHereDoc,shIf,shHereString,shRedir,shSetList,shSource,shStatement,shVariable,shCtrlSeq
    269 if exists("b:is_kornshell") || exists("b:is_bash")
    270    syn cluster shCaseList	add=shForPP,shDblParen
    271 endif
    272 syn cluster shCommandSubList	contains=shAlias,shArithmetic,shBracketExpr,shCmdParenRegion,shCommandSub,shComment,shCtrlSeq,shDeref,shDerefSimple,shDoubleQuote,shEcho,shEscape,shExDoubleQuote,shExpr,shExSingleQuote,shHereDoc,shNumber,shOperator,shOption,shPosnParm,shHereString,shRedir,shSingleQuote,shSpecial,shStatement,shSubSh,shTest,shVariable
    273 syn cluster shCurlyList	contains=shNumber,shComma,shDeref,shDerefSimple,shDerefSpecial
    274 " COMBAK: removing shEscape from shDblQuoteList fails ksh04:43 -- Jun 09, 2022: I don't see the problem with ksh04, so am reinstating shEscape
    275 syn cluster shDblQuoteList	contains=shArithmetic,shCommandSub,shCommandSubBQ,shSubshare,shValsub,shDeref,shDerefSimple,shEscape,shPosnParm,shCtrlSeq,shSpecial,shSpecialDQ
    276 syn cluster shDerefList	contains=shDeref,shDerefSimple,shDerefVar,shDerefSpecial,shDerefWordError,shDerefPSR,shDerefPPS
    277 syn cluster shDerefVarList	contains=shDerefOffset,shDerefOp,shDerefVarArray,shDerefOpError
    278 syn cluster shEchoList	contains=shArithmetic,shBracketExpr,shCommandSub,shCommandSubBQ,shDerefVarArray,shSubshare,shValsub,shDeref,shDerefSimple,shEscape,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shCtrlSeq,shEchoQuote
    279 syn cluster shExprList1	contains=shBracketExpr,shNumber,shOperator,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shExpr,shDblBrace,shDeref,shDerefSimple,shCtrlSeq
    280 syn cluster shExprList2	contains=@shExprList1,@shCaseList,shTest
    281 syn cluster shFunctionList	contains=shBracketExpr,@shCommandSubList,shCaseEsac,shColon,shComment,shDo,shEcho,shExpr,shFor,shHereDoc,shIf,shOption,shHereString,shRedir,shSetList,shSource,shStatement,shVariable,shOperator,shCtrlSeq
    282 if exists("b:is_kornshell") || exists("b:is_bash")
    283    syn cluster shFunctionList	add=shRepeat,shDblBrace,shDblParen,shForPP
    284    syn cluster shDerefList	add=shCommandSubList,shEchoDeref
    285 endif
    286 syn cluster shHereBeginList	contains=@shCommandSubList
    287 syn cluster shHereList	contains=shBeginHere,shHerePayload
    288 syn cluster shHereListDQ	contains=shBeginHere,@shDblQuoteList,shHerePayload
    289 syn cluster shIdList	contains=shArithmetic,shCommandSub,shCommandSubBQ,shDerefVarArray,shSubshare,shValsub,shWrapLineOperator,shSetOption,shComment,shDeref,shDerefSimple,shHereString,shNumber,shOperator,shRedir,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shExpr,shCtrlSeq,shStringSpecial,shAtExpr
    290 syn cluster shIfList	contains=@shLoopList,shDblBrace,shDblParen,shFunctionKey,shFunctionOne,shFunctionTwo,shNamespaceOne
    291 syn cluster shLoopList	contains=@shCaseList,@shErrorList,shCaseEsac,shConditional,shDblBrace,shExpr,shFor,shIf,shOption,shSet,shTest,shTestOpr,shTouch
    292 if exists("b:is_kornshell") || exists("b:is_bash")
    293    syn cluster shLoopList	add=shForPP,shDblParen
    294 endif
    295 syn cluster shPPSLeftList	contains=shAlias,shArithmetic,shBracketExpr,shCmdParenRegion,shCommandSub,shSubshare,shValsub,shCtrlSeq,shDeref,shDerefSimple,shDoubleQuote,shEcho,shEscape,shExDoubleQuote,shExpr,shExSingleQuote,shHereDoc,shNumber,shOperator,shOption,shPosnParm,shHereString,shRedir,shSingleQuote,shSpecial,shStatement,shSubSh,shTest,shVariable
    296 syn cluster shPPSRightList	contains=shDeref,shDerefSimple,shEscape,shPosnParm
    297 syn cluster shSubShList	contains=shBracketExpr,@shCommandSubList,shCommandSubBQ,shSubshare,shValsub,shCaseEsac,shColon,shCommandSub,shComment,shDo,shEcho,shExpr,shFor,shIf,shHereString,shRedir,shSetList,shSource,shStatement,shVariable,shCtrlSeq,shOperator
    298 syn cluster shTestList	contains=shArithmetic,shBracketExpr,shCommandSub,shCommandSubBQ,shSubshare,shValsub,shCtrlSeq,shDeref,shDerefSimple,shDoubleQuote,shSpecialDQ,shExDoubleQuote,shExpr,shExSingleQuote,shNumber,shOperator,shSingleQuote,shTest,shTestOpr
    299 syn cluster shNoZSList	contains=shSpecialNoZS
    300 syn cluster shForList	contains=shTestOpr,shNumber,shDerefSimple,shDeref,shCommandSub,shCommandSubBQ,shSubshare,shValsub,shArithmetic
    301 
    302 " Echo: {{{1
    303 " ====
    304 " This one is needed INSIDE a CommandSub, so that `echo bla` be correct
    305 if (exists("b:is_kornshell") && !exists("b:is_ksh88"))
    306    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
    307    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
    308 else
    309    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 contains=@shEchoList skipwhite nextgroup=shQuickComment
    310    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 contains=@shEchoList skipwhite nextgroup=shQuickComment
    311 endif
    312 if exists("b:is_kornshell") || exists("b:is_bash") || exists("b:is_posix")
    313    syn region shEchoDeref contained matchgroup=shStatement start="\<echo\>"  skip="\\$" matchgroup=shEchoDelim end="$" end="[<>;&|()`}]"me=e-1 end="\d[<>]"me=e-2 end="#"me=e-1 contains=@shEchoList skipwhite nextgroup=shQuickComment
    314    syn region shEchoDeref contained matchgroup=shStatement start="\<print\>" skip="\\$" matchgroup=shEchoDelim end="$" end="[<>;&|()`}]"me=e-1 end="\d[<>]"me=e-2 end="#"me=e-1 contains=@shEchoList skipwhite nextgroup=shQuickComment
    315 endif
    316 syn match  shEchoQuote contained	'\%(\\\\\)*\\["`'()]'
    317 
    318 " This must be after the strings, so that ... \" will be correct
    319 syn region shEmbeddedEcho contained matchgroup=shStatement start="\<print\>" skip="\\$" matchgroup=shEchoDelim end="$" matchgroup=NONE end="[<>;&|`)]"me=e-1 end="\d[<>]"me=e-2 end="\s#"me=e-2 contains=shBracketExpr,shNumber,shExSingleQuote,shSingleQuote,shDeref,shDerefSimple,shSpecialVar,shOperator,shExDoubleQuote,shDoubleQuote,shCtrlSeq
    320 
    321 " Alias: {{{1
    322 " =====
    323 if exists("b:is_kornshell") || exists("b:is_bash") || exists("b:is_posix")
    324    syn match shStatement "\<alias\>"
    325    syn region shAlias matchgroup=shStatement start="\<alias\>\s\+\(\h[-._[:alnum:]]*\)\@="  skip="\\$" end="\>\|`"
    326    syn region shAlias matchgroup=shStatement start="\<alias\>\s\+\(\h[-._[:alnum:]]*=\)\@=" skip="\\$" end="="
    327 
    328    " Touch: {{{1
    329    " =====
    330    syn match shTouch	'\<touch\>[^;#]*'	skipwhite nextgroup=shComment contains=shTouchCmd,shDoubleQuote,shSingleQuote,shDeref,shDerefSimple
    331    syn match shTouchCmd	'\<touch\>'		contained
    332 endif
    333 
    334 " Error Codes: {{{1
    335 " ============
    336 if !exists("g:sh_no_error")
    337    syn match   shDoError "\<done\>"
    338    syn match   shIfError "\<fi\>"
    339    syn match   shInError "\<in\>"
    340    syn match   shCaseError ";;"
    341    syn match   shEsacError "\<esac\>"
    342    syn match   shCurlyError "}"
    343    syn match   shParenError ")"
    344    syn match   shOK	'\.\(done\|fi\|in\|esac\)'
    345    if exists("b:is_kornshell") || exists("b:is_bash")
    346        syn match     shDTestError "]]"
    347    endif
    348    syn match     shTestError "]"
    349 endif
    350 
    351 " Options: {{{1
    352 " ====================
    353 syn match   shOption	"\s\zs[-+][-_a-zA-Z#@0-9]\+"
    354 syn match   shOption	"\s\zs--[^ \t$=`'"|);]\+"
    355 
    356 " File Redirection Highlighted As Operators: {{{1
    357 "===========================================
    358 syn match      shRedir	"\d\=>\(&[-0-9]\)\="
    359 syn match      shRedir	"\d\=>>-\="
    360 syn match      shRedir	"\d\=<\(&[-0-9]\)\="
    361 syn match      shRedir	"\d<<-\="
    362 
    363 " Operators: {{{1
    364 " ==========
    365 syn match   shOperator	"<<\|>>"		contained
    366 syn match   shOperator	"[!&;|]"		contained
    367 syn match   shOperator	"[-=/*+%]\=="		skipwhite nextgroup=shPattern
    368 syn match   shPattern	"\<\S\+\())\)\@="	contained contains=shExSingleQuote,shSingleQuote,shExDoubleQuote,shDoubleQuote,shDeref
    369 
    370 " Subshells: {{{1
    371 " ==========
    372 syn region shExpr  transparent matchgroup=shExprRegion  start="{" end="}"		contains=@shExprList2 nextgroup=shSpecialNxt
    373 syn region shSubSh transparent matchgroup=shSubShRegion start="[^(]\zs(" end=")"	contains=@shSubShList nextgroup=shSpecialNxt
    374 
    375 " Tests: {{{1
    376 "=======
    377 syn region shExpr	matchgroup=shRange start="\[\s\@=" skip=+\\\\\|\\$\|\[+ end="\]" contains=@shTestList,shSpecial
    378 syn region shTest	transparent matchgroup=shStatement start="\<test\s" skip=+\\\\\|\\$+ matchgroup=NONE end="[;&|]"me=e-1 end="$" contains=@shExprList1
    379 syn region shNoQuote	start='\S'	skip='\%(\\\\\)*\\.'	end='\ze\s' end="\ze['"]"	contained contains=shBracketExpr,shDerefSimple,shDeref,shEscape
    380 syn match  shAstQuote	contained	'\*\ze"'	nextgroup=shString
    381 syn match  shTestOpr	contained	'[^-+/%]\zs=' skipwhite nextgroup=shTestDoubleQuote,shTestSingleQuote,shTestPattern
    382 syn match  shTestOpr	contained	"<=\|>=\|!=\|==\|=\~\|-.\>\|-\(nt\|ot\|ef\|eq\|ne\|lt\|le\|gt\|ge\)\>\|[!<>]"
    383 syn match  shTestPattern	contained	'\w\+'
    384 syn region shTestDoubleQuote	contained	start='\%(\%(\\\\\)*\\\)\@<!"' skip=+\\\\\|\\"+ end='"'	contains=shDeref,shDerefSimple,shDerefSpecial
    385 syn match  shTestSingleQuote	contained	'\\.'	nextgroup=shTestSingleQuote
    386 syn match  shTestSingleQuote	contained	"'[^']*'"
    387 if exists("b:is_kornshell") || exists("b:is_bash")
    388    syn region  shDblBrace matchgroup=Delimiter start="\[\[\s\@="	skip=+\%(\\\\\)*\\$+ end="\]\]"	contains=@shTestList,shAstQuote,shNoQuote,shComment
    389    syn region  shDblParen matchgroup=Delimiter start="(("	skip=+\%(\\\\\)*\\$+ end="))"	contains=@shTestList,shComment
    390 endif
    391 
    392 " Character Class In Range: {{{1
    393 " =========================
    394 syn match   shCharClassOther	contained	"\[:\w\{-}:\]"
    395 syn match   shCharClass	contained	"\[:\%(alnum\|alpha\|blank\|cntrl\|digit\|graph\|lower\|print\|punct\|space\|upper\|xdigit\):\]"
    396 syn match   shCollSymb	contained	"\[\..\{-}\.\]"
    397 syn match   shEqClass	contained	"\[=.\{-}=\]"
    398 
    399 " Loops: do, if, while, until {{{1
    400 " ======
    401 ShFoldIfDoFor syn region shDo	transparent	matchgroup=shConditional start="\<do\>" matchgroup=shConditional end="\<done\>"			contains=@shLoopList
    402 ShFoldIfDoFor syn region shIf	transparent	matchgroup=shConditional start="\<if\_s" matchgroup=shConditional skip=+-fi\>+ end="\<;\_s*then\>" end="\<fi\>"	contains=@shIfList
    403 ShFoldIfDoFor syn region shFor		matchgroup=shLoop start="\<for\ze\_s\s*\%(((\)\@!" end="\<in\>" end="\<do\>"me=e-2			contains=@shLoopList,shDblParen skipwhite nextgroup=shCurlyIn
    404 if exists("b:is_kornshell") || exists("b:is_bash")
    405    ShFoldIfDoFor syn region shForPP	matchgroup=shLoop start='\<for\>\_s*((' end='))' contains=@shForList
    406 endif
    407 
    408 if exists("b:is_kornshell") || exists("b:is_bash") || exists("b:is_posix")
    409    syn cluster shCaseList	add=shRepeat
    410    syn cluster shFunctionList	add=shRepeat
    411    syn region shRepeat   matchgroup=shLoop   start="\<while\_s" end="\<do\>"me=e-2	contains=@shLoopList,shDblParen,shDblBrace
    412    syn region shRepeat   matchgroup=shLoop   start="\<until\_s" end="\<do\>"me=e-2	contains=@shLoopList,shDblParen,shDblBrace
    413    if !exists("b:is_posix")
    414        syn region shCaseEsac matchgroup=shConditional start="\<select\s" matchgroup=shConditional end="\<in\>" end="\<do\>" contains=@shLoopList
    415    endif
    416 else
    417    syn region shRepeat   matchgroup=shLoop   start="\<while\_s" end="\<do\>"me=e-2		contains=@shLoopList
    418    syn region shRepeat   matchgroup=shLoop   start="\<until\_s" end="\<do\>"me=e-2		contains=@shLoopList
    419 endif
    420 syn region shCurlyIn   contained	matchgroup=Delimiter start="{" end="}" contains=@shCurlyList
    421 syn match  shComma     contained	","
    422 
    423 " Case: case...esac {{{1
    424 " ====
    425 syn match shCaseBar	contained skipwhite "\(^\|[^\\]\)\(\\\\\)*\zs|"		nextgroup=shCase,shCaseStart,shCaseBar,shComment,shCaseExSingleQuote,shCaseSingleQuote,shCaseDoubleQuote
    426 syn match shCaseStart	contained skipwhite skipnl "("			nextgroup=shCase,shCaseBar
    427 syn match shCaseLabel	contained skipwhite	"\%(\\.\|[-a-zA-Z0-9_*.]\)\+"	contains=shBracketExpr
    428 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"))
    429    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
    430 elseif exists("b:is_kornshell")
    431    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
    432 else
    433    ShFoldIfDoFor syn region	shCase	contained skipwhite skipnl matchgroup=shSnglCase start="\%(\\.\|[^#$()'" \t]\)\{-}\zs)"  end=";;" end="esac"me=s-1		contains=@shCaseList	nextgroup=shCaseStart,shCase,shComment
    434 endif
    435 ShFoldIfDoFor syn region	shCaseEsac	matchgroup=shConditional start="\<case\>" end="\<esac\>"	contains=@shCaseEsacList
    436 
    437 syn keyword shCaseIn	contained skipwhite skipnl in			nextgroup=shCase,shCaseStart,shCaseBar,shComment,shCaseExSingleQuote,shCaseSingleQuote,shCaseDoubleQuote
    438 if exists("b:is_bash") || (exists("b:is_kornshell") && !exists("b:is_ksh88"))
    439    syn region  shCaseExSingleQuote	matchgroup=shQuote start=+\$'+ skip=+\\\\\|\\.+ end=+'+	contains=shStringSpecial,shSpecial	skipwhite skipnl nextgroup=shCaseBar	contained
    440 elseif !exists("g:sh_no_error")
    441    syn region  shCaseExSingleQuote	matchgroup=Error start=+\$'+ skip=+\\\\\|\\.+ end=+'+	contains=shStringSpecial	skipwhite skipnl nextgroup=shCaseBar	contained
    442 endif
    443 syn region  shCaseSingleQuote	matchgroup=shQuote start=+'+ end=+'+		contains=shStringSpecial		skipwhite skipnl nextgroup=shCaseBar	contained
    444 syn region  shCaseDoubleQuote	matchgroup=shQuote start=+"+ skip=+\\\\\|\\.+ end=+"+	contains=@shDblQuoteList,shStringSpecial	skipwhite skipnl nextgroup=shCaseBar	contained
    445 syn region  shCaseCommandSub	start=+`+ skip=+\\\\\|\\.+ end=+`+		contains=@shCommandSubList		skipwhite skipnl nextgroup=shCaseBar	contained
    446 call s:GenerateBracketExpressionItems({'itemGroup': 'shCaseRange', 'bracketGroup': 'shBracketExprDelim', 'extraArgs': 'skip=+\\\\+ contained'})
    447 if exists("b:is_bash")
    448    syn match   shCharClass	"\[:\%(alnum\|alpha\|ascii\|blank\|cntrl\|digit\|graph\|lower\|print\|punct\|space\|upper\|word\|xdigit\):\]"	contained
    449 elseif exists("b:is_kornshell")
    450    syn match   shCharClass	"\[:\%(alnum\|alpha\|blank\|cntrl\|digit\|graph\|lower\|print\|punct\|space\|upper\|word\|xdigit\):\]"	contained
    451 endif
    452 " Misc: {{{1
    453 "======
    454 syn match   shWrapLineOperator "\\$"
    455 syn region  shCommandSubBQ   	start="`" skip="\\\\\|\\." end="`"	contains=shBQComment,@shCommandSubList
    456 syn match   shEscape	contained	'\%(^\)\@!\%(\\\\\)*\\.'
    457 
    458 " $() and $(()): {{{1
    459 " $(..) is not supported by sh (Bourne shell).  However, apparently
    460 " some systems (HP?) have as their /bin/sh a (link to) Korn shell
    461 " (ie. Posix compliant shell).  /bin/ksh should work for those
    462 " systems too, however, so the following syntax will flag $(..) as
    463 " an Error under /bin/sh.  By consensus of vimdev'ers!
    464 if exists("b:is_kornshell") || exists("b:is_bash") || exists("b:is_posix")
    465    syn region shCommandSub matchgroup=shCmdSubRegion start="\$((\@!"  skip='\\\\\|\\.' end=")"  contains=@shCommandSubList
    466    if exists("b:is_kornshell")
    467        if !exists("b:is_ksh88")
    468            if exists("b:is_mksh")
    469 syn region shSubshare matchgroup=shCmdSubRegion start="\${\ze[ \t\n]"  skip='\\\\\|\\.' end="\zs[ \t\n;]}"  contains=@shCommandSubList
    470            else
    471 syn region shSubshare matchgroup=shCmdSubRegion start="\${\ze[ \t\n<]"  skip='\\\\\|\\.' end="\zs[ \t\n;]}"  contains=@shCommandSubList
    472            endif
    473        endif
    474        if exists("b:is_mksh") || exists("b:generic_korn")
    475            syn region shValsub matchgroup=shCmdSubRegion start="\${|"  skip='\\\\\|\\.' end="}"  contains=@shCommandSubList
    476        endif
    477    elseif exists("b:is_bash")
    478        syn region shSubshare matchgroup=shCmdSubRegion start="\${\ze[ \t\n]"  skip='\\\\\|\\.' end="\zs[;\n][ \t\n]*}"  contains=@shCommandSubList
    479        syn region shValsub matchgroup=shCmdSubRegion start="\${|"  skip='\\\\\|\\.' end="[;\n][ \t\n]*}"  contains=@shCommandSubList
    480    endif
    481    syn region shArithmetic matchgroup=shArithRegion  start="\$((" skip='\\\\\|\\.' end="))" contains=@shArithList
    482    syn region shArithmetic matchgroup=shArithRegion  start="\$\[" skip='\\\\\|\\.' end="\]" contains=@shArithList
    483    syn match  shSkipInitWS contained	"^\s\+"
    484    syn region shArithParen matchgroup=shArithRegion  contained start="(" end=")" contains=@shArithParenList
    485 elseif !exists("g:sh_no_error")
    486    syn region shCommandSub matchgroup=Error start="\$(" end=")" contains=@shCommandSubList
    487 endif
    488 syn region shCmdParenRegion matchgroup=shCmdSubRegion start="((\@!" skip='\\\\\|\\.' end=")" contains=@shCommandSubList
    489 
    490 if exists("b:is_bash")
    491    syn cluster shCommandSubList add=bashSpecialVariables,bashStatement
    492    syn cluster shCaseList add=bashAdminStatement,bashStatement
    493    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
    494    syn keyword bashStatement bind builtin caller compgen complete compopt declare dirs disown enable fg help history let logout mapfile popd pushd readarray shopt source suspend time
    495    syn keyword bashStatement typeset nextgroup=shSetOption
    496    syn keyword bashAdminStatement reload restart start status stop
    497 endif
    498 
    499 " for all kornshells
    500 if exists("b:is_kornshell")
    501    syn cluster shCaseList add=kshStatement
    502    syn cluster shCommandSubList add=kshSpecialVariables,kshStatement
    503    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
    504    syn keyword kshStatement autoload builtin compound disown enum fg float functions hist history integer let nameref r redirect source stop suspend time whence
    505    syn keyword shStatement typeset skipwhite nextgroup=shSetOption
    506 endif
    507 
    508 " kornshell flavors
    509 if exists("b:generic_korn")
    510    syn keyword kshSpecialVariables contained BASHPID EPOCHREALTIME ERRNO 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 SYSTYPE CSWIDTH .sh .sh.edchar .sh.edcol .sh.edtext .sh.edmode .sh.name .sh.subscript .sh.value .sh.version .sh.match .sh.command .sh.file .sh.fun .sh.subshell .sh.level .sh.lineno .sh.stats .sh.math .sh.pid .sh.ppid .sh.tilde .sh.dollar .sh.pool .sh.pgrp .sh.pwdfd .sh.op_astbin .sh.sig .sh.sig.addr .sh.sig.band .sh.sig.code .sh.sig.errno .sh.sig.name .sh.sig.pid .sh.sig.signo .sh.sig.status .sh.sig.uid .sh.sig.value .sh.sig.value.q .sh.sig.value.Q .sh.stats .sh.stats.arg_cachehits .sh.stats.arg_expands .sh.stats.comsubs .sh.stats.forks .sh.stats.funcalls .sh.stats.globs .sh.stats.linesread .sh.stats.nv_cachehit .sh.stats.nv_opens .sh.stats.pathsearch .sh.stats.posixfuncall .sh.stats.simplecmds .sh.stats.spawns .sh.stats.subshell .sh.install_prefix
    511    syn keyword kshStatement alarm bind compgen complete eloop fds mkservice pids poll sha2sum vmstate xgrep
    512 elseif exists("b:is_ksh88")
    513    syn keyword kshSpecialVariables contained ERRNO
    514 elseif exists("b:is_ksh93")
    515    syn keyword kshSpecialVariables contained COMP_CWORD COMP_KEY COMP_LINE COMP_POINT COMPREPLY COMP_TYPE COMP_WORDBREAKS COMP_WORDS CSWIDTH SH_OPTIONS SRANDOM SYSTYPE VPATH .sh .sh.edchar .sh.edcol .sh.edtext .sh.edmode .sh.name .sh.subscript .sh.value .sh.version .sh.match .sh.command .sh.file .sh.fun .sh.subshell .sh.level .sh.lineno .sh.stats .sh.math .sh.pid .sh.ppid .sh.tilde .sh.dollar .sh.pool .sh.pgrp .sh.pwdfd .sh.op_astbin .sh.sig .sh.sig.addr .sh.sig.band .sh.sig.code .sh.sig.errno .sh.sig.name .sh.sig.pid .sh.sig.signo .sh.sig.status .sh.sig.uid .sh.sig.value .sh.sig.value.q .sh.sig.value.Q .sh.stats .sh.stats.arg_cachehits .sh.stats.arg_expands .sh.stats.comsubs .sh.stats.forks .sh.stats.funcalls .sh.stats.globs .sh.stats.linesread .sh.stats.nv_cachehit .sh.stats.nv_opens .sh.stats.pathsearch .sh.stats.posixfuncall .sh.stats.simplecmds .sh.stats.spawns .sh.stats.subshell
    516    syn keyword kshStatement alarm compgen complete eloop fds mkservice pids poll sha2sum vmstate xgrep
    517 elseif exists("b:is_ksh93v")
    518    syn keyword kshSpecialVariables contained COMP_CWORD COMP_KEY COMP_LINE COMP_POINT COMPREPLY COMP_TYPE COMP_WORDBREAKS COMP_WORDS CSWIDTH SH_OPTIONS VPATH .sh .sh.edchar .sh.edcol .sh.edtext .sh.edmode .sh.name .sh.subscript .sh.value .sh.version .sh.match .sh.command .sh.file .sh.fun .sh.subshell .sh.level .sh.lineno .sh.stats .sh.math .sh.dollar .sh.pool .sh.pgrp .sh.pwdfd .sh.op_astbin .sh.sig .sh.sig.addr .sh.sig.band .sh.sig.code .sh.sig.errno .sh.sig.name .sh.sig.pid .sh.sig.signo .sh.sig.status .sh.sig.uid .sh.sig.value .sh.sig.value.q .sh.sig.value.Q .sh.stats .sh.stats.arg_cachehits .sh.stats.arg_expands .sh.stats.comsubs .sh.stats.forks .sh.stats.funcalls .sh.stats.globs .sh.stats.linesread .sh.stats.nv_cachehit .sh.stats.nv_opens .sh.stats.pathsearch .sh.stats.posixfuncall .sh.stats.simplecmds .sh.stats.spawns .sh.stats.subshell
    519    syn keyword kshStatement alarm compgen complete fds pids poll sha2sum vmstate xgrep
    520 elseif exists("b:is_ksh93u")
    521    syn keyword kshSpecialVariables contained CSWIDTH SYSTYPE VPATH .sh .sh.edchar .sh.edcol .sh.edtext .sh.edmode .sh.name .sh.subscript .sh.value .sh.version .sh.match .sh.command .sh.file .sh.fun .sh.subshell .sh.level .sh.lineno .sh.stats .sh.math .sh.dollar .sh.pool .sh.stats .sh.stats.arg_cachehits .sh.stats.arg_expands .sh.stats.comsubs .sh.stats.forks .sh.stats.funcalls .sh.stats.globs .sh.stats.linesread .sh.stats.nv_cachehit .sh.stats.nv_opens .sh.stats.pathsearch .sh.stats.posixfuncall .sh.stats.simplecmds .sh.stats.spawns .sh.stats.subshell
    522    syn keyword kshStatement alarm fds pids vmstate
    523 elseif exists("b:is_ksh2020")
    524    syn keyword kshSpecialVariables contained COMP_CWORD COMP_KEY COMP_LINE COMP_POINT COMPREPLY COMP_TYPE COMP_WORDBREAKS COMP_WORDS SH_OPTIONS .sh .sh.edchar .sh.edcol .sh.edtext .sh.edmode .sh.name .sh.subscript .sh.value .sh.version .sh.match .sh.command .sh.file .sh.fun .sh.subshell .sh.level .sh.lineno .sh.stats .sh.math .sh.dollar .sh.pool .sh.pgrp .sh.pwdfd .sh.op_astbin .sh.sig .sh.sig.addr .sh.sig.band .sh.sig.code .sh.sig.errno .sh.sig.name .sh.sig.pid .sh.sig.signo .sh.sig.status .sh.sig.uid .sh.sig.value .sh.stats .sh.stats.arg_cachehits .sh.stats.arg_expands .sh.stats.comsubs .sh.stats.forks .sh.stats.funcalls .sh.stats.globs .sh.stats.linesread .sh.stats.nv_cachehit .sh.stats.nv_opens .sh.stats.pathsearch .sh.stats.posixfuncall .sh.stats.simplecmds .sh.stats.spawns .sh.stats.subshell .sh.install_prefix
    525    syn keyword kshStatement compgen complete
    526 elseif exists("b:is_mksh")
    527    syn keyword kshSpecialVariables contained BASHPID EPOCHREALTIME EXECSHELL KSHEGID KSHGID KSHUID KSH_MATCH PATHSEP PGRP PIPESTATUS TMPDIR USER_ID
    528    syn keyword kshStatement bind
    529 endif
    530 
    531 syn match   shSource	"^\.\s"
    532 syn match   shSource	"\s\.\s"
    533 if exists("b:is_kornshell") || exists("b:is_posix")
    534    syn match   shColon	'^\s*\zs:'
    535 endif
    536 
    537 " String And Character Constants: {{{1
    538 "================================
    539 syn match   shNumber	"\<\d\+\>#\="
    540 syn match   shNumber	"\<-\=\.\=\d\+\>#\="
    541 syn match   shCtrlSeq	"\\\d\d\d\|\\[abcfnrtv0]"			contained
    542 if exists("b:is_bash") || exists("b:is_kornshell")
    543    syn match   shSpecial	"[^\\]\(\\\\\)*\zs\\\o\o\o\|\\x\x\x\|\\c[^"]\|\\[abefnrtv]"	contained
    544    syn match   shSpecial	"^\(\\\\\)*\zs\\\o\o\o\|\\x\x\x\|\\c[^"]\|\\[abefnrtv]"	contained
    545    syn region  shExSingleQuote	matchgroup=shQuote start=+\$'+ skip=+\\\\\|\\.+ end=+'+	contains=shStringSpecial,shSpecial		nextgroup=shSpecialNxt
    546    syn region  shExDoubleQuote	matchgroup=shQuote start=+\$"+ skip=+\\\\\|\\.\|\\"+ end=+"+	contains=@shDblQuoteList,shStringSpecial,shSpecial	nextgroup=shSpecialNxt
    547 elseif !exists("g:sh_no_error")
    548    syn region  shExSingleQuote	matchGroup=Error start=+\$'+ skip=+\\\\\|\\.+ end=+'+	contains=shStringSpecial
    549    syn region  shExDoubleQuote	matchGroup=Error start=+\$"+ skip=+\\\\\|\\.+ end=+"+	contains=shStringSpecial
    550 endif
    551 syn region  shSingleQuote	matchgroup=shQuote start=+'+ end=+'+		contains=@Spell	nextgroup=shSpecialStart,shSpecialSQ
    552 syn region  shDoubleQuote	matchgroup=shQuote start=+\%(\%(\\\\\)*\\\)\@<!"+ skip=+\\.+ end=+"+			contains=@shDblQuoteList,shStringSpecial,@Spell	nextgroup=shSpecialStart
    553 syn match   shStringSpecial	"[^[:print:] \t]"			contained
    554 syn match   shStringSpecial	"[^\\]\zs\%(\\\\\)*\(\\[\\"'`$()#]\)\+"			nextgroup=shComment
    555 syn match   shSpecialSQ	"[^\\]\zs\%(\\\\\)*\(\\[\\"'`$()#]\)\+"		contained	nextgroup=shBkslshSnglQuote,@shNoZSList
    556 syn match   shSpecialDQ	"[^\\]\zs\%(\\\\\)*\(\\[\\"'`$()#]\)\+"		contained	nextgroup=shBkslshDblQuote,@shNoZSList
    557 syn match   shSpecialStart	"\%(\\\\\)*\\[\\"'`$()#]"			contained	nextgroup=shBkslshSnglQuote,shBkslshDblQuote,@shNoZSList
    558 syn match   shSpecial	"^\%(\\\\\)*\\[\\"'`$()#]"
    559 syn match   shSpecialNoZS	contained	"\%(\\\\\)*\\[\\"'`$()#]"
    560 syn match   shSpecialNxt	contained	"\\[\\"'`$()#]"
    561 
    562 " Comments: {{{1
    563 "==========
    564 syn cluster	shCommentGroup	contains=shTodo,@Spell
    565 if exists("b:is_bash")
    566    syn match	shTodo	contained		"\<\%(COMBAK\|FIXME\|TODO\|XXX\)\ze:\=\>"
    567 else
    568    syn keyword	shTodo	contained		COMBAK FIXME TODO XXX
    569 endif
    570 syn match	shComment		"^\s*\zs#.*$"	contains=@shCommentGroup
    571 syn match	shComment		"\s\zs#.*$"	contains=@shCommentGroup
    572 syn match	shComment	contained	"#.*$"	contains=@shCommentGroup
    573 syn match	shQuickComment	contained	"#.*$"          contains=@shCommentGroup
    574 syn match	shBQComment	contained	"#.\{-}\ze`"	contains=@shCommentGroup
    575 
    576 " Here Documents: {{{1
    577 "  (modified by Felipe Contreras)
    578 " =========================================
    579 ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc01 start="<<\s*\z([^ \t|>]\+\)"		matchgroup=shHereDoc01 end="^\z1$"	contains=@shDblQuoteList
    580 ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc02 start="<<-\s*\z([^ \t|>]\+\)"		matchgroup=shHereDoc02 end="^\t*\z1$"	contains=@shDblQuoteList
    581 ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc03 start="<<\s*\\\z([^ \t|>]\+\)"		matchgroup=shHereDoc03 end="^\z1$"
    582 ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc04 start="<<-\s*\\\z([^ \t|>]\+\)"		matchgroup=shHereDoc04 end="^\t*\z1$"
    583 ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc05 start="<<\s*'\z([^']\+\)'"		matchgroup=shHereDoc05 end="^\z1$"
    584 ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc06 start="<<-\s*'\z([^']\+\)'"		matchgroup=shHereDoc06 end="^\t*\z1$"
    585 ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc07 start="<<\s*\"\z([^"]\+\)\""		matchgroup=shHereDoc07 end="^\z1$"
    586 ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc08 start="<<-\s*\"\z([^"]\+\)\""		matchgroup=shHereDoc08 end="^\t*\z1$"
    587 ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc09 start="<<\s*\\\_$\_s*\z([^ \t|>]\+\)"		matchgroup=shHereDoc09 end="^\z1$"	contains=@shDblQuoteList
    588 ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc10 start="<<-\s*\\\_$\_s*\z([^ \t|>]\+\)"	matchgroup=shHereDoc10 end="^\t*\z1$"	contains=@shDblQuoteList
    589 ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc11 start="<<\s*\\\_$\_s*\\\z([^ \t|>]\+\)"	matchgroup=shHereDoc11 end="^\z1$"
    590 ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc12 start="<<-\s*\\\_$\_s*\\\z([^ \t|>]\+\)"	matchgroup=shHereDoc12 end="^\t*\z1$"
    591 ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc13 start="<<\s*\\\_$\_s*'\z([^']\+\)'"		matchgroup=shHereDoc13 end="^\z1$"
    592 ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc14 start="<<-\s*\\\_$\_s*'\z([^']\+\)'"		matchgroup=shHereDoc14 end="^\t*\z1$"
    593 ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc15 start="<<\s*\\\_$\_s*\"\z([^"]\+\)\""		matchgroup=shHereDoc15 end="^\z1$"
    594 ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc16 start="<<-\s*\\\_$\_s*\"\z([^"]\+\)\""	matchgroup=shHereDoc16 end="^\t*\z1$"
    595 
    596 
    597 " Here Strings: {{{1
    598 " =============
    599 " available for: bash and ksh (except ksh88) but not if its a posix
    600 if exists("b:is_bash") || ((exists("b:is_kornshell") && !exists("b:is_ksh88")) && !exists("b:is_posix"))
    601    syn match shHereString "<<<"	skipwhite	nextgroup=shCmdParenRegion
    602 endif
    603 
    604 " Identifiers: {{{1
    605 "=============
    606 syn match  shSetOption	"\s\zs[-+][a-zA-Z0-9]\+\>"	contained
    607 syn match  shVariable	"\<\h\w*\ze="			nextgroup=shVarAssign
    608 if exists("b:is_bash")
    609    " The subscript form for array values, e.g. "foo=([2]=10 [4]=100)".
    610    syn region  shArrayValue	contained	start="\[\%(..\{-}\]=\)\@=" end="\]=\@="	contains=@shArrayValueList nextgroup=shVarAssign
    611    syn cluster shArrayValueList	contains=shArithmetic,shArithParen,shCommandSub,shDeref,shDerefSimple,shExpr,shNumber,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shSpecial,shParen,bashSpecialVariables,shParenError
    612    syn region  shArrayRegion	contained matchgroup=shShellVariables start="(" skip='\\\\\|\\.' end=")" contains=@shArrayValueList,shArrayValue,shComment
    613 elseif (exists("b:is_kornshell") && !exists("b:is_ksh88"))
    614    " The subscript form for array values, e.g. "foo=([2]=10 [4]=100)".
    615    syn region  shArrayValue	contained	start="\[\%(..\{-}\]=\)\@=" end="\]=\@="	contains=@shArrayValueList nextgroup=shVarAssign
    616    syn cluster shArrayValueList	contains=shArithmetic,shArithParen,shCommandSub,shDeref,shDerefSimple,shExpr,shNumber,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shSpecial,shParen,kshSpecialVariables,shParenError
    617    syn region  shArrayRegion	contained matchgroup=shShellVariables start="(" skip='\\\\\|\\.' end=")" contains=@shArrayValueList,shArrayValue,shComment,shArrayRegion
    618 endif
    619 if exists("b:is_bash") || exists("b:is_kornshell")
    620    syn match shVariable	"\<\h\w*\%(\[..\{-}\]\)\=\ze\%([|^&*/%+-]\|[<^]<\|[>^]>\)\=="	contains=shDerefVarArray nextgroup=shVarAssign
    621    syn match shVarAssign	contained	"\%([|^&*/%+-]\|[<^]<\|[>^]>\)\=="	nextgroup=shArrayRegion,shPattern,shDeref,shDerefSimple,shDoubleQuote,shExDoubleQuote,shSingleQuote,shExSingleQuote,shVar
    622 else
    623    syn match  shVarAssign	contained	"="	nextgroup=shPattern,shDeref,shDerefSimple,shDoubleQuote,shExDoubleQuote,shSingleQuote,shExSingleQuote,shVar
    624 endif
    625 syn match  shVar	contained	"\h\w*"
    626 syn region shAtExpr	contained	start="@(" end=")" contains=@shIdList
    627 if exists("b:is_bash")
    628    syn match  shSet "^\s*set\ze\s\+$"
    629    syn region shSetList oneline matchgroup=shSet start="\<\%(declare\|local\|export\)\>\ze[/a-zA-Z_]\@!" end="$"		matchgroup=shSetListDelim end="\ze[}|);&]" matchgroup=NONE end="\ze\s\+#\|="	contains=@shIdList
    630    syn region shSetList oneline matchgroup=shSet start="\<\%(set\|unset\)\>[/a-zA-Z_]\@!" end="\ze[;|#)]\|$"		matchgroup=shSetListDelim end="\ze[}|);&]" matchgroup=NONE end="\ze\s\+="	contains=@shIdList nextgroup=shComment
    631 elseif exists("b:is_kornshell") || exists("b:is_posix")
    632    syn match  shSet "^\s*set\ze\s\+$"
    633    if exists("b:is_dash")
    634        syn region shSetList oneline matchgroup=shSet start="\<\%(local\)\>\ze[/]\@!" end="$"			matchgroup=shSetListDelim end="\ze[}|);&]" matchgroup=NONE end="\ze\s\+[#=]"	contains=@shIdList
    635        syn keyword shStatement chdir
    636    endif
    637    syn region shSetList oneline matchgroup=shSet start="\<\(export\)\>\ze[/]\@!" end="$"			matchgroup=shSetListDelim end="\ze[}|);&]" matchgroup=NONE end="\ze\s\+[#=]"	contains=@shIdList
    638    syn region shSetList oneline matchgroup=shSet start="\<\%(set\|unset\>\)\ze[/a-zA-Z_]\@!" end="\ze[;|#)]\|$"		matchgroup=shSetListDelim end="\ze[}|);&]" matchgroup=NONE end="\ze\s\+[#=]"	contains=@shIdList nextgroup=shComment
    639 else
    640    syn region shSetList oneline matchgroup=shSet start="\<\(set\|export\|unset\)\>\ze[/a-zA-Z_]\@!" end="\ze[;|#)]\|$"	matchgroup=shSetListDelim end="\ze[}|);&]" matchgroup=NONE end="\ze\s\+[#=]"	contains=@shIdList
    641 endif
    642 
    643 " KornShell namespace: {{{1
    644 if exists("b:is_kornshell") && !exists("b:is_ksh88") && !exists("b:is_mksh")
    645    syn keyword shFunctionKey namespace	skipwhite skipnl nextgroup=shNamespaceOne
    646 endif
    647 
    648 " Functions: {{{1
    649 if !exists("b:is_posix")
    650    syn keyword shFunctionKey function	skipwhite skipnl nextgroup=shFunctionTwo
    651 endif
    652 
    653 if exists("b:is_bash")
    654    syn keyword shFunctionKey coproc
    655    ShFoldFunctions syn region shFunctionOne	matchgroup=shFunction start="^\s*[A-Za-z_0-9:][-a-zA-Z_0-9:]*\s*()\_s*{"		end="}"	contains=@shFunctionList		 skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
    656    ShFoldFunctions syn region shFunctionTwo	matchgroup=shFunction start="\%(do\)\@!\&\<[A-Za-z_0-9:][-a-zA-Z_0-9:]*\>\s*\%(()\)\=\_s*{"	end="}"	contains=shFunctionKey,@shFunctionList contained skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
    657    ShFoldFunctions syn region shFunctionThree	matchgroup=shFunction start="^\s*[A-Za-z_0-9:][-a-zA-Z_0-9:]*\s*()\_s*("		end=")"	contains=@shFunctionList		 skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
    658    ShFoldFunctions syn region shFunctionFour	matchgroup=shFunction start="\%(do\)\@!\&\<[A-Za-z_0-9:][-a-zA-Z_0-9:]*\>\s*\%(()\)\=\_s*)"	end=")"	contains=shFunctionKey,@shFunctionList contained skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
    659 elseif exists("b:is_ksh88")
    660    " AT&T ksh88
    661    ShFoldFunctions syn region shFunctionOne	matchgroup=shFunction start="^\s*[A-Za-z_][A-Za-z_0-9]*\s*()\_s*{"		end="}"	contains=@shFunctionList		 skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
    662    ShFoldFunctions syn region shFunctionTwo	matchgroup=shFunction start="\%(do\)\@!\&\<[A-Za-z_][A-Za-z_0-9]*\>\_s*{"		end="}"	contains=shFunctionKey,@shFunctionList contained skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
    663    ShFoldFunctions syn region shFunctionThree	matchgroup=shFunction start="^\s*[A-Za-z_][A-Za-z_0-9]*\s*()\_s*("		end=")"	contains=@shFunctionList		 skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
    664    ShFoldFunctions syn region shFunctionFour	matchgroup=shFunction start="\%(do\)\@!\&\<[A-Za-z_][A-Za-z_0-9]*\>\_s*("		end=")"	contains=shFunctionKey,@shFunctionList contained skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
    665 elseif exists("b:is_mksh")
    666    " MirBSD ksh is the wild west of absurd and abstruse function names...
    667    ShFoldFunctions syn region shFunctionOne	matchgroup=shFunction start="^\s*[-A-Za-z_@!+.%,0-9:]*[-A-Za-z_.%,0-9:]\s*()\_s*{"		end="}"	contains=@shFunctionList		 skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
    668    ShFoldFunctions syn region shFunctionTwo	matchgroup=shFunction start="\%(do\)\@!\&\<[-A-Za-z_@!+.%,0-9:]*[-A-Za-z_.%,0-9:]\>\s*\%(()\)\=\_s*{"	end="}"	contains=shFunctionKey,@shFunctionList contained skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
    669    ShFoldFunctions syn region shFunctionThree	matchgroup=shFunction start="^\s*[-A-Za-z_@!+.%,0-9:]*[-A-Za-z_.%,0-9:]\s*()\_s*("		end=")"	contains=@shFunctionList		 skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
    670    ShFoldFunctions syn region shFunctionFour	matchgroup=shFunction start="\%(do\)\@!\&\<[-A-Za-z_@!+.%,0-9:]*[-A-Za-z_.%,0-9:]\>\s*\%(()\)\=\_s*("	end=")"	contains=shFunctionKey,@shFunctionList contained skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
    671 elseif exists("b:is_kornshell")
    672    " ksh93
    673    ShFoldFunctions syn region shFunctionOne	matchgroup=shFunction start="^\s*[A-Za-z_.][A-Za-z_.0-9]*\s*()\_s*{"		end="}"	contains=@shFunctionList		 skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
    674    ShFoldFunctions syn region shFunctionTwo	matchgroup=shFunction start="\%(do\)\@!\&\<[A-Za-z_.][A-Za-z_.0-9]*\>\_s*{"		end="}"	contains=shFunctionKey,@shFunctionList contained skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
    675    ShFoldFunctions syn region shFunctionThree	matchgroup=shFunction start="^\s*[A-Za-z_.][A-Za-z_.0-9]*\s*()\_s*("		end=")"	contains=@shFunctionList		 skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
    676    ShFoldFunctions syn region shFunctionFour	matchgroup=shFunction start="\%(do\)\@!\&\<[A-Za-z_.][A-Za-z_.0-9]*\>\_s*("		end=")"	contains=shFunctionKey,@shFunctionList contained skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
    677    ShFoldFunctions syn region shNamespaceOne	matchgroup=shFunction start="\%(do\)\@!\&\<\h\w*\>\_s*{"			end="}"	contains=shFunctionKey,@shFunctionList contained skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
    678 else
    679    ShFoldFunctions syn region shFunctionOne	matchgroup=shFunction start="^\s*\h\w*\s*()\_s*{"			end="}"	contains=@shFunctionList		 skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
    680    ShFoldFunctions syn region shFunctionTwo	matchgroup=shFunction start="\%(do\)\@!\&\<\h\w*\>\s*\%(()\)\=\_s*{"		end="}"	contains=shFunctionKey,@shFunctionList contained skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
    681    ShFoldFunctions syn region shFunctionThree	matchgroup=shFunction start="^\s*\h\w*\s*()\_s*("			end=")"	contains=@shFunctionList		 skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
    682    ShFoldFunctions syn region shFunctionFour	matchgroup=shFunction start="\%(do\)\@!\&\<\h\w*\>\s*\%(()\)\=\_s*("		end=")"	contains=shFunctionKey,@shFunctionList contained skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
    683 endif
    684 
    685 " Parameter Dereferencing: {{{1
    686 " ========================
    687 if !exists("g:sh_no_error")
    688    syn match  shDerefWordError	"[^}$[~]"	contained
    689 endif
    690 syn match  shDerefSimple	"\$\%(\h\w*\|\d\)"	nextgroup=@shNoZSList
    691 if exists("b:is_kornshell") && !exists("b:is_ksh88")
    692    if exists("b:is_mksh")
    693        syn region shDeref	matchgroup=PreProc start="\${\ze[^ \t\n|]" end="}"	contains=@shDerefList,shDerefVarArray nextgroup=shSpecialStart
    694    elseif exists("b:generic_korn")
    695        syn region shDeref	matchgroup=PreProc start="\${\ze[^ \t\n<|]" end="}"	contains=@shDerefList,shDerefVarArray nextgroup=shSpecialStart
    696    else
    697        syn region shDeref	matchgroup=PreProc start="\${\ze[^ \t\n<]" end="}"	contains=@shDerefList,shDerefVarArray nextgroup=shSpecialStart
    698    endif
    699 elseif exists("b:is_bash")
    700    syn region shDeref	matchgroup=PreProc start="\${\ze[^ \t\n|]" end="}"	contains=@shDerefList,shDerefVarArray nextgroup=shSpecialStart
    701 else
    702    syn region shDeref	matchgroup=PreProc start="\${" end="}"			contains=@shDerefList,shDerefVarArray nextgroup=shSpecialStart
    703 endif
    704 syn match  shDerefSimple	"\$[-#*@!?]"	nextgroup=@shNoZSList
    705 syn match  shDerefSimple	"\$\$"	nextgroup=@shNoZSList
    706 syn match  shDerefSimple	"\${\d}"	nextgroup=@shNoZSList	nextgroup=shSpecialStart
    707 if exists("b:is_bash") || exists("b:is_kornshell") || exists("b:is_posix")
    708    syn region shDeref	matchgroup=PreProc start="\${##\=" end="}"	contains=@shDerefList	nextgroup=@shSpecialNoZS,shSpecialStart
    709    syn region shDeref	matchgroup=PreProc start="\${\$\$" end="}"	contains=@shDerefList	nextgroup=@shSpecialNoZS,shSpecialStart
    710 endif
    711 
    712 " ksh: ${.sh.*} variables: {{{1
    713 " ========================================
    714 if exists("b:is_kornshell") && !exists("b:is_ksh88") && !exists("b:is_mksh")
    715    syn match  shDerefVar	contained	"\.\+"	nextgroup=@shDerefVarList
    716    syn region shDeref	matchgroup=PreProc start="\${\ze[\.]" end="}"	contains=@shDerefVarList,shDerefPSR,shDerefPPS
    717 endif
    718 
    719 " ksh: ${!var[*]} array index list syntax: {{{1
    720 " ========================================
    721 if (exists("b:is_kornshell") && !exists("b:is_ksh88")) || exists("b:is_posix")
    722    syn region shDeref	matchgroup=PreProc start="\${!" end="}"	contains=@shDerefVarArray
    723 endif
    724 
    725 " bash: ${!prefix*} and ${#parameter}: {{{1
    726 " ====================================
    727 if exists("b:is_bash")
    728    syn region shDeref	matchgroup=PreProc start="\${!" end="\*\=}"	contains=@shDerefList,shDerefOffset
    729    syn match  shDerefVar	contained	"{\@<=!\h\w*"		nextgroup=@shDerefVarList
    730 endif
    731 if (exists("b:is_kornshell") && !exists("b:is_ksh88"))
    732    syn match  shDerefVar	contained	"{\@<=!\h\w*[[:alnum:]_.]*"	nextgroup=@shDerefVarList
    733 endif
    734 
    735 syn match  shDerefSpecial	contained	"{\@<=[-*@?0]"		nextgroup=shDerefOp,shDerefOffset,shDerefOpError
    736 syn match  shDerefSpecial	contained	"\({[#!]\)\@<=[[:alnum:]*@_]\+"	nextgroup=@shDerefVarList,shDerefOp
    737 syn match  shDerefVar	contained	"{\@<=\h\w*"		nextgroup=@shDerefVarList
    738 syn match  shDerefVar	contained	'\d'                            nextgroup=@shDerefVarList
    739 if exists("b:is_kornshell") || exists("b:is_posix")
    740    syn match  shDerefVar	contained	"{\@<=\h\w*[[:alnum:]_.]*"	nextgroup=@shDerefVarList
    741 endif
    742 
    743 " sh ksh bash : ${var[... ]...}  array reference: {{{1
    744 syn region  shDerefVarArray   contained	matchgroup=shDeref start="\[" end="]"	contains=@shCommandSubList nextgroup=shDerefOp,shDerefOpError,shDerefOffset
    745 
    746 " Special ${parameter OPERATOR word} handling: {{{1
    747 " sh ksh bash : ${parameter:-word}    word is default value
    748 " sh ksh bash : ${parameter:=word}    assign word as default value
    749 " sh ksh bash : ${parameter:?word}    display word if parameter is null
    750 " sh ksh bash : ${parameter:+word}    use word if parameter is not null, otherwise nothing
    751 "    ksh bash : ${parameter#pattern}  remove small left  pattern
    752 "    ksh bash : ${parameter##pattern} remove large left  pattern
    753 "    ksh bash : ${parameter%pattern}  remove small right pattern
    754 "    ksh bash : ${parameter%%pattern} remove large right pattern
    755 "    ksh bash : ${parameter^pattern}  Case modification
    756 "    ksh bash : ${parameter^^pattern} Case modification
    757 "    ksh bash : ${parameter,pattern}  Case modification
    758 "    ksh bash : ${parameter,,pattern} Case modification
    759 "        bash : ${@:start:qty}        display command line arguments from start to start+qty-1 (inferred)
    760 "        bash : ${parameter@operator} transforms parameter (operator∈[uULqEPARa])
    761 syn cluster shDerefPatternList	contains=shDerefPattern,shDerefString
    762 if !exists("g:sh_no_error")
    763    syn match shDerefOpError	contained	":[[:punct:]]"
    764 endif
    765 syn match  shDerefOp	contained	":\=[-=?]"	nextgroup=@shDerefPatternList
    766 syn match  shDerefOp	contained	":\=+"	nextgroup=@shDerefPatternList
    767 if exists("b:is_bash") || exists("b:is_kornshell") || exists("b:is_posix")
    768    syn match  shDerefOp	contained	"#\{1,2}"		nextgroup=@shDerefPatternList
    769    syn match  shDerefOp	contained	"%\{1,2}"		nextgroup=@shDerefPatternList
    770    syn match  shDerefPattern	contained	"[^{}]\+"		contains=shDeref,shDerefSimple,shDerefPattern,shDerefString,shCommandSub,shDerefEscape nextgroup=shDerefPattern skipnl
    771    syn region shDerefPattern	contained	start="{" end="}"	contains=shDeref,shDerefSimple,shDerefString,shCommandSub nextgroup=shDerefPattern
    772    " Match parametric bracket expressions with a leading whitespace character.
    773    syn region shDerefPattern	contained	matchgroup=shBracketExprDelim start="\[" end="\]"	contains=@shBracketExprList,shDoubleQuote nextgroup=shDerefPattern
    774    call s:GenerateBracketExpressionItems({'itemGroup': 'shDerefPattern', 'bracketGroup': 'shBracketExprDelim', 'extraArgs': 'contained nextgroup=shDerefPattern'})
    775    syn match  shDerefEscape	contained	'\%(\\\\\)*\\.'
    776 endif
    777 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"))
    778    syn match  shDerefOp	contained	"[,^]\{1,2}"	nextgroup=@shDerefPatternList
    779 endif
    780 if exists("b:is_bash")
    781    syn match  shDerefOp	contained	"@[uULQEPAKa]"
    782 endif
    783 syn region shDerefString	contained	matchgroup=shDerefDelim start=+\%(\\\)\@<!'+ end=+'+
    784 syn region shDerefString	contained	matchgroup=shDerefDelim start=+\%(\\\)\@<!"+ skip=+\\"+ end=+"+	contains=@shDblQuoteList,shStringSpecial
    785 syn match  shDerefString	contained	"\\["']"	nextgroup=shDerefPattern
    786 
    787 if exists("b:is_bash") || (exists("b:is_kornshell") && !exists("b:is_ksh88")) || exists("b:is_posix")
    788    " bash ksh posix : ${parameter:offset}
    789    " bash ksh posix : ${parameter:offset:length}
    790    syn region shDerefOffset	contained	start=':[^-=?+]' end='\ze:'	end='\ze}'	contains=shDeref,shDerefSimple,shDerefEscape	nextgroup=shDerefLen,shDeref,shDerefSimple
    791    syn region shDerefOffset	contained	start=':\s-'	end='\ze:'	end='\ze}'	contains=shDeref,shDerefSimple,shDerefEscape	nextgroup=shDerefLen,shDeref,shDerefSimple
    792    syn match  shDerefLen	contained	":[^}]\+"	contains=shDeref,shDerefSimple,shArithmetic
    793 endif
    794 
    795 if exists("b:is_bash") || (exists("b:is_kornshell") && !exists("b:is_ksh88"))
    796    " bash ksh : ${parameter/pattern/string}
    797    " bash ksh : ${parameter//pattern/string}
    798    syn match  shDerefPPS	contained	'/\{1,2}'	nextgroup=shDerefPPSleft
    799    syn region shDerefPPSleft	contained	start='.'	skip=@\%(\\\\\)*\\/@ matchgroup=shDerefOp	end='/' end='\ze}' end='"'	nextgroup=shDerefPPSright	contains=@shPPSLeftList
    800    syn region shDerefPPSright	contained	start='.'	skip=@\%(\\\\\)\+@		end='\ze}'				contains=@shPPSRightList
    801 
    802    " bash ksh : ${parameter/#pattern/string}
    803    " bash ksh : ${parameter/%pattern/string}
    804    syn match  shDerefPSR	contained	'/[#%]'	nextgroup=shDerefPSRleft,shDoubleQuote,shSingleQuote
    805    syn region shDerefPSRleft	contained	start='[^"']'	skip=@\%(\\\\\)*\\/@ matchgroup=shDerefOp	end='/' end='\ze}'	nextgroup=shDerefPSRright	contains=shBracketExpr
    806    syn region shDerefPSRright	contained	start='.'	skip=@\%(\\\\\)\+@		end='\ze}'
    807 endif
    808 
    809 " Arithmetic Parenthesized Expressions: {{{1
    810 "syn region shParen matchgroup=shArithRegion start='[^$]\zs(\%(\ze[^(]\|$\)' end=')' contains=@shArithParenList
    811 syn region shParen matchgroup=shArithRegion start='\$\@!(\%(\ze[^(]\|$\)' end=')' contains=@shArithParenList
    812 
    813 " Additional sh Keywords: {{{1
    814 " ===================
    815 " builtins:
    816 syn keyword shStatement bg break cd continue command eval exec exit export fc getopts hash jobs local printf read readonly return shift times trap type ulimit umask unalias wait
    817 " external programs:
    818 syn keyword shStatement basename cat chgrp chmod chown cksum clear cmp comm cp cut date dirname du egrep expr false fgrep find fmt fold getconf grep head iconv id join kill killall less ln login logname ls md5sum mkdir mkfifo mknod mktemp mv newgrp nice nohup od paste pathchk printenv pwd readlink realpath rename rev rm rmdir sed sha1sum sha224sum sha256sum sha384sum sha512sum sleep sort strip stty sum sync tail tee test tput tr true tty uname uniq wc which xargs
    819 syn keyword shConditional contained elif else then
    820 if !exists("g:sh_no_error")
    821    syn keyword shCondError elif else then
    822 endif
    823 
    824 " Synchronization: {{{1
    825 " ================
    826 if !exists("g:sh_minlines")
    827    let s:sh_minlines = 200
    828 else
    829    let s:sh_minlines= g:sh_minlines
    830 endif
    831 if !exists("g:sh_maxlines")
    832    let s:sh_maxlines = 2*s:sh_minlines
    833    if s:sh_maxlines < 25
    834        let s:sh_maxlines= 25
    835    endif
    836 else
    837    let s:sh_maxlines= g:sh_maxlines
    838 endif
    839 exec "syn sync minlines=" . s:sh_minlines . " maxlines=" . s:sh_maxlines
    840 syn sync match shCaseEsacSync	grouphere	shCaseEsac	"\<case\>"
    841 syn sync match shCaseEsacSync	groupthere	shCaseEsac	"\<esac\>"
    842 syn sync match shDoSync	grouphere	shDo	"\<do\>"
    843 syn sync match shDoSync	groupthere	shDo	"\<done\>"
    844 syn sync match shForSync	grouphere	shFor	"\<for\>"
    845 syn sync match shForSync	groupthere	shFor	"\<in\>"
    846 syn sync match shIfSync	grouphere	shIf	"\<if\>"
    847 syn sync match shIfSync	groupthere	shIf	"\<fi\>"
    848 syn sync match shUntilSync	grouphere	shRepeat	"\<until\>"
    849 syn sync match shWhileSync	grouphere	shRepeat	"\<while\>"
    850 
    851 " Default Highlighting: {{{1
    852 " =====================
    853 if !exists("skip_sh_syntax_inits")
    854    hi def link shArithRegion	shShellVariables
    855    hi def link shArrayValue	shDeref
    856    hi def link shAstQuote	shDoubleQuote
    857    hi def link shAtExpr	shSetList
    858    hi def link shBkslshSnglQuote	shSingleQuote
    859    hi def link shBkslshDblQuote	shDOubleQuote
    860    hi def link shBeginHere	shRedir
    861    hi def link shCaseBar	shConditional
    862    hi def link shCaseCommandSub	shCommandSub
    863    hi def link shCaseDoubleQuote	shDoubleQuote
    864    hi def link shCaseIn	shConditional
    865    hi def link shQuote	shOperator
    866    hi def link shCaseSingleQuote	shSingleQuote
    867    hi def link shCaseStart	shConditional
    868    hi def link shCmdSubRegion	shShellVariables
    869    hi def link shColon	shComment
    870    hi def link shDerefOp	shOperator
    871    hi def link shDerefPOL	shDerefOp
    872    hi def link shDerefPPS	shDerefOp
    873    hi def link shDerefPSR	shDerefOp
    874    hi def link shDeref	shShellVariables
    875    hi def link shDerefDelim	shOperator
    876    hi def link shDerefSimple	shDeref
    877    hi def link shDerefSpecial	shDeref
    878    hi def link shDerefString	shDoubleQuote
    879    hi def link shDerefVar	shDeref
    880    hi def link shDoubleQuote	shString
    881    hi def link shEcho	shString
    882    hi def link shEchoDelim	shOperator
    883    hi def link shEchoQuote	shString
    884    hi def link shForPP	shLoop
    885    hi def link shFunction	Function
    886    hi def link shEmbeddedEcho	shString
    887    hi def link shEscape	shCommandSub
    888    hi def link shExDoubleQuote	shDoubleQuote
    889    hi def link shExSingleQuote	shSingleQuote
    890    hi def link shHereDoc	shString
    891    hi def link shHereString	shRedir
    892    hi def link shHerePayload	shHereDoc
    893    hi def link shLoop	shStatement
    894    hi def link shSpecialNxt	shSpecial
    895    hi def link shNoQuote	shDoubleQuote
    896    hi def link shOption	shCommandSub
    897    hi def link shPattern	shString
    898    hi def link shParen	shArithmetic
    899    hi def link shPosnParm	shShellVariables
    900    hi def link shQuickComment	shComment
    901    hi def link shBQComment	shComment
    902    hi def link shRange	shOperator
    903    hi def link shRedir	shOperator
    904    hi def link shSetListDelim	shOperator
    905    hi def link shSetOption	shOption
    906    hi def link shSingleQuote	shString
    907    hi def link shSource	shOperator
    908    hi def link shStringSpecial	shSpecial
    909    hi def link shSpecialStart	shSpecial
    910    hi def link shSubShRegion	shOperator
    911    hi def link shTestOpr	shConditional
    912    hi def link shTestPattern	shString
    913    hi def link shTestDoubleQuote	shString
    914    hi def link shTestSingleQuote	shString
    915    hi def link shTouchCmd	shStatement
    916    hi def link shVariable	shSetList
    917    hi def link shWrapLineOperator	shOperator
    918 
    919    if exists("b:is_bash")
    920        hi def link bashAdminStatement	shStatement
    921        hi def link bashSpecialVariables	shShellVariables
    922        hi def link bashStatement		shStatement
    923        hi def link shCharClass		shSpecial
    924        hi def link shDerefOffset		shDerefOp
    925        hi def link shDerefLen		shDerefOffset
    926    endif
    927    if exists("b:is_kornshell") || exists("b:is_posix")
    928        hi def link kshSpecialVariables	shShellVariables
    929        hi def link kshStatement		shStatement
    930    endif
    931 
    932    if !exists("g:sh_no_error")
    933        hi def link shCaseError		Error
    934        hi def link shCondError		Error
    935        hi def link shCurlyError		Error
    936        hi def link shDerefOpError		Error
    937        hi def link shDerefWordError		Error
    938        hi def link shDoError		Error
    939        hi def link shEsacError		Error
    940        hi def link shIfError		Error
    941        hi def link shInError		Error
    942        hi def link shParenError		Error
    943        hi def link shTestError		Error
    944        if exists("b:is_kornshell") || exists("b:is_posix")
    945            hi def link shDTestError		Error
    946        endif
    947    endif
    948 
    949    hi def link shArithmetic		Special
    950    hi def link shBracketExprDelim		Delimiter
    951    hi def link shCharClass		Identifier
    952    hi def link shCollSymb		shCharClass
    953    hi def link shEqClass		shCharClass
    954    hi def link shSnglCase		Statement
    955    hi def link shCommandSub		Special
    956    hi def link shCommandSubBQ		shCommandSub
    957    hi def link shSubshare		shCommandSub
    958    hi def link shValsub		shCommandSub
    959    hi def link shComment		Comment
    960    hi def link shConditional		Conditional
    961    hi def link shCtrlSeq		Special
    962    hi def link shExprRegion		Delimiter
    963    hi def link shFunctionKey		Function
    964    hi def link shFunctionName		Function
    965    hi def link shNumber		Number
    966    hi def link shOperator		Operator
    967    hi def link shRepeat		Repeat
    968    hi def link shSet		Statement
    969    hi def link shSetList		Identifier
    970    hi def link shShellVariables		PreProc
    971    hi def link shSpecial		Special
    972    hi def link shSpecialDQ		Special
    973    hi def link shSpecialSQ		Special
    974    hi def link shSpecialNoZS		shSpecial
    975    hi def link shStatement		Statement
    976    hi def link shString		String
    977    hi def link shTodo		Todo
    978    hi def link shAlias		Identifier
    979    hi def link shHereDoc01		shRedir
    980    hi def link shHereDoc02		shRedir
    981    hi def link shHereDoc03		shRedir
    982    hi def link shHereDoc04		shRedir
    983    hi def link shHereDoc05		shRedir
    984    hi def link shHereDoc06		shRedir
    985    hi def link shHereDoc07		shRedir
    986    hi def link shHereDoc08		shRedir
    987    hi def link shHereDoc09		shRedir
    988    hi def link shHereDoc10		shRedir
    989    hi def link shHereDoc11		shRedir
    990    hi def link shHereDoc12		shRedir
    991    hi def link shHereDoc13		shRedir
    992    hi def link shHereDoc14		shRedir
    993    hi def link shHereDoc15		shRedir
    994    hi def link shHereDoc16		shRedir
    995 endif
    996 
    997 " Delete shell folding commands {{{1
    998 " =============================
    999 delc ShFoldFunctions
   1000 delc ShFoldHereDoc
   1001 delc ShFoldIfDoFor
   1002 
   1003 " Delete the bracket expression function {{{1
   1004 " ======================================
   1005 delfun s:GenerateBracketExpressionItems
   1006 
   1007 " Set Current Syntax: {{{1
   1008 " ===================
   1009 if exists("b:is_bash")
   1010    let b:current_syntax = "bash"
   1011 elseif exists("b:is_kornshell")
   1012    let b:current_syntax = "ksh"
   1013 elseif exists("b:is_posix")
   1014    let b:current_syntax = "posix"
   1015 else
   1016    let b:current_syntax = "sh"
   1017 endif
   1018 
   1019 " vim: ts=16 fdm=marker