xref: /vim-8.2.3635/runtime/syntax/sh.vim (revision 6c35beaa)
1" Vim syntax file
2" Language:		shell (sh) Korn shell (ksh) bash (sh)
3" Maintainer:		Dr. Charles E. Campbell, Jr.  <[email protected]>
4" Previous Maintainer:	Lennart Schultz <[email protected]>
5" Last Change:		Mar 19, 2012
6" Version:		122
7" URL:		http://mysite.verizon.net/astronaut/vim/index.html#vimlinks_syntax
8" For options and settings, please use:      :help ft-sh-syntax
9" This file includes many ideas from ?ric Brunet ([email protected])
10
11" For version 5.x: Clear all syntax items {{{1
12" For version 6.x: Quit when a syntax file was already loaded
13if version < 600
14  syntax clear
15elseif exists("b:current_syntax")
16  finish
17endif
18
19" AFAICT "." should be considered part of the iskeyword.  Using iskeywords in
20" syntax is dicey, so the following code permits the user to prevent/override
21" its setting.
22if exists("g:sh_isk")          " override support
23 exe "setlocal isk=".g:sh_isk
24elseif !exists("g:sh_noisk")   " prevent modification support
25 setlocal isk+=.
26endif
27
28" trying to answer the question: which shell is /bin/sh, really?
29if !exists("g:is_kornshell") && !exists("g:is_bash") && !exists("g:is_posix") && !exists("g:is_sh")
30 if executable("/bin/sh")
31  if     resolve("/bin/sh") =~ 'bash$'
32   let g:is_bash= 1
33  elseif resolve("/bin/sh") =~ 'ksh$'
34   let g:is_ksh = 1
35  endif
36 elseif executable("/usr/bin/sh")
37  if     resolve("/usr/bin//sh") =~ 'bash$'
38   let g:is_bash= 1
39  elseif resolve("/usr/bin//sh") =~ 'ksh$'
40   let g:is_ksh = 1
41  endif
42 endif
43endif
44
45" handling /bin/sh with is_kornshell/is_sh {{{1
46" b:is_sh is set when "#! /bin/sh" is found;
47" However, it often is just a masquerade by bash (typically Linux)
48" or kornshell (typically workstations with Posix "sh").
49" So, when the user sets "g:is_bash", "g:is_kornshell",
50" or "g:is_posix", a b:is_sh is converted into b:is_bash/b:is_kornshell,
51" respectively.
52if !exists("b:is_kornshell") && !exists("b:is_bash")
53  if exists("g:is_posix") && !exists("g:is_kornshell")
54   let g:is_kornshell= g:is_posix
55  endif
56  if exists("g:is_kornshell")
57    let b:is_kornshell= 1
58    if exists("b:is_sh")
59      unlet b:is_sh
60    endif
61  elseif exists("g:is_bash")
62    let b:is_bash= 1
63    if exists("b:is_sh")
64      unlet b:is_sh
65    endif
66  else
67    let b:is_sh= 1
68  endif
69endif
70
71" set up default g:sh_fold_enabled {{{1
72if !exists("g:sh_fold_enabled")
73 let g:sh_fold_enabled= 0
74elseif g:sh_fold_enabled != 0 && !has("folding")
75 let g:sh_fold_enabled= 0
76 echomsg "Ignoring g:sh_fold_enabled=".g:sh_fold_enabled."; need to re-compile vim for +fold support"
77endif
78if !exists("s:sh_fold_functions")
79 let s:sh_fold_functions = 1
80endif
81if !exists("s:sh_fold_heredoc")
82 let s:sh_fold_heredoc   = 2
83endif
84if !exists("s:sh_fold_ifdofor")
85 let s:sh_fold_ifdofor   = 4
86endif
87if g:sh_fold_enabled && &fdm == "manual"
88 setlocal fdm=syntax
89endif
90
91" sh syntax is case sensitive {{{1
92syn case match
93
94" Clusters: contains=@... clusters {{{1
95"==================================
96syn cluster shErrorList	contains=shDoError,shIfError,shInError,shCaseError,shEsacError,shCurlyError,shParenError,shTestError,shOK
97if exists("b:is_kornshell")
98 syn cluster ErrorList add=shDTestError
99endif
100syn cluster shArithParenList	contains=shArithmetic,shCaseEsac,shDeref,shDerefSimple,shEcho,shEscape,shNumber,shOperator,shPosnParm,shExSingleQuote,shExDoubleQuote,shRedir,shSingleQuote,shDoubleQuote,shStatement,shVariable,shAlias,shTest,shCtrlSeq,shSpecial,shParen,bashSpecialVariables,bashStatement
101syn cluster shArithList	contains=@shArithParenList,shParenError
102syn cluster shCaseEsacList	contains=shCaseStart,shCase,shCaseBar,shCaseIn,shComment,shDeref,shDerefSimple,shCaseCommandSub,shCaseExSingleQuote,shCaseSingleQuote,shCaseDoubleQuote,shCtrlSeq,@shErrorList,shStringSpecial,shCaseRange
103syn cluster shCaseList	contains=@shCommandSubList,shCaseEsac,shColon,shCommandSub,shComment,shDo,shEcho,shExpr,shFor,shHereDoc,shIf,shRedir,shSetList,shSource,shStatement,shVariable,shCtrlSeq
104"syn cluster shColonList	contains=@shCaseList
105syn cluster shCommandSubList	contains=shArithmetic,shDeref,shDerefSimple,shEscape,shNumber,shOperator,shPosnParm,shExSingleQuote,shSingleQuote,shExDoubleQuote,shDoubleQuote,shStatement,shVariable,shSubSh,shAlias,shTest,shCtrlSeq,shSpecial
106syn cluster shCurlyList	contains=shNumber,shComma,shDeref,shDerefSimple,shDerefSpecial
107syn cluster shDblQuoteList	contains=shCommandSub,shDeref,shDerefSimple,shPosnParm,shCtrlSeq,shSpecial
108syn cluster shDerefList	contains=shDeref,shDerefSimple,shDerefVar,shDerefSpecial,shDerefWordError,shDerefPPS
109syn cluster shDerefVarList	contains=shDerefOp,shDerefVarArray,shDerefOpError
110syn cluster shEchoList	contains=shArithmetic,shCommandSub,shDeref,shDerefSimple,shExpr,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shCtrlSeq,shEchoQuote
111syn cluster shExprList1	contains=shCharClass,shNumber,shOperator,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shExpr,shDblBrace,shDeref,shDerefSimple,shCtrlSeq
112syn cluster shExprList2	contains=@shExprList1,@shCaseList,shTest
113syn cluster shFunctionList	contains=@shCommandSubList,shCaseEsac,shColon,shCommandSub,shComment,shDo,shEcho,shExpr,shFor,shHereDoc,shIf,shOption,shRedir,shSetList,shSource,shStatement,shVariable,shOperator,shCtrlSeq
114if exists("b:is_kornshell") || exists("b:is_bash")
115 syn cluster shFunctionList	add=shRepeat
116 syn cluster shFunctionList	add=shDblBrace,shDblParen
117endif
118syn cluster shHereBeginList	contains=@shCommandSubList
119syn cluster shHereList	contains=shBeginHere,shHerePayload
120syn cluster shHereListDQ	contains=shBeginHere,@shDblQuoteList,shHerePayload
121syn cluster shIdList	contains=shCommandSub,shWrapLineOperator,shSetOption,shDeref,shDerefSimple,shRedir,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shExpr,shCtrlSeq,shStringSpecial
122syn cluster shIfList	contains=@shLoopList,shDblBrace,shDblParen,shFunctionKey,shFunctionOne,shFunctionTwo
123syn cluster shLoopList	contains=@shCaseList,shTestOpr,shExpr,shDblBrace,shConditional,shCaseEsac,shTest,@shErrorList,shSet,shOption
124syn cluster shSubShList	contains=@shCommandSubList,shCaseEsac,shColon,shCommandSub,shComment,shDo,shEcho,shExpr,shFor,shIf,shRedir,shSetList,shSource,shStatement,shVariable,shCtrlSeq,shOperator
125syn cluster shTestList	contains=shCharClass,shComment,shCommandSub,shDeref,shDerefSimple,shExDoubleQuote,shDoubleQuote,shExpr,shNumber,shOperator,shExSingleQuote,shSingleQuote,shTestOpr,shTest,shCtrlSeq
126" Echo: {{{1
127" ====
128" This one is needed INSIDE a CommandSub, so that `echo bla` be correct
129syn region shEcho matchgroup=shStatement start="\<echo\>"  skip="\\$" matchgroup=shEchoDelim end="$" matchgroup=NONE end="[<>;&|()]"me=e-1 end="\d[<>]"me=e-2 end="\s#"me=e-2 contains=@shEchoList skipwhite nextgroup=shQuickComment
130syn region shEcho 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=@shEchoList skipwhite nextgroup=shQuickComment
131syn match  shEchoQuote contained	'\%(\\\\\)*\\["`'()]'
132
133" This must be after the strings, so that ... \" will be correct
134syn 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=shNumber,shExSingleQuote,shSingleQuote,shDeref,shDerefSimple,shSpecialVar,shOperator,shExDoubleQuote,shDoubleQuote,shCharClass,shCtrlSeq
135
136" Alias: {{{1
137" =====
138if exists("b:is_kornshell") || exists("b:is_bash")
139 syn match shStatement "\<alias\>"
140 syn region shAlias matchgroup=shStatement start="\<alias\>\s\+\(\h[-._[:alnum:]]\+\)\@="  skip="\\$" end="\>\|`"
141 syn region shAlias matchgroup=shStatement start="\<alias\>\s\+\(\h[-._[:alnum:]]\+=\)\@=" skip="\\$" end="="
142endif
143
144" Error Codes: {{{1
145" ============
146if !exists("g:sh_no_error")
147 syn match   shDoError "\<done\>"
148 syn match   shIfError "\<fi\>"
149 syn match   shInError "\<in\>"
150 syn match   shCaseError ";;"
151 syn match   shEsacError "\<esac\>"
152 syn match   shCurlyError "}"
153 syn match   shParenError ")"
154 syn match   shOK	'\.\(done\|fi\|in\|esac\)'
155 if exists("b:is_kornshell")
156  syn match     shDTestError "]]"
157 endif
158 syn match     shTestError "]"
159endif
160
161" Options: {{{1
162" ====================
163syn match   shOption	"\s\zs[-+][-_a-zA-Z0-9]\+\>"
164syn match   shOption	"\s\zs--[^ \t$`'"|]\+"
165
166" File Redirection Highlighted As Operators: {{{1
167"===========================================
168syn match      shRedir	"\d\=>\(&[-0-9]\)\="
169syn match      shRedir	"\d\=>>-\="
170syn match      shRedir	"\d\=<\(&[-0-9]\)\="
171syn match      shRedir	"\d<<-\="
172
173" Operators: {{{1
174" ==========
175syn match   shOperator	"<<\|>>"		contained
176syn match   shOperator	"[!&;|]"		contained
177syn match   shOperator	"\[[[^:]\|\]]"		contained
178syn match   shOperator	"!\=="		skipwhite nextgroup=shPattern
179syn match   shPattern	"\<\S\+\())\)\@="	contained contains=shExSingleQuote,shSingleQuote,shExDoubleQuote,shDoubleQuote,shDeref
180
181" Subshells: {{{1
182" ==========
183syn region shExpr  transparent matchgroup=shExprRegion  start="{" end="}"		contains=@shExprList2 nextgroup=shMoreSpecial
184syn region shSubSh transparent matchgroup=shSubShRegion start="[^(]\zs(" end=")"	contains=@shSubShList nextgroup=shMoreSpecial
185
186" Tests: {{{1
187"=======
188syn region shExpr	matchgroup=shRange start="\[" skip=+\\\\\|\\$\|\[+ end="\]" contains=@shTestList,shSpecial
189syn region shTest	transparent matchgroup=shStatement start="\<test\s" skip=+\\\\\|\\$+ matchgroup=NONE end="[;&|]"me=e-1 end="$" contains=@shExprList1
190syn match  shTestOpr	contained	"<=\|>=\|!=\|==\|-.\>\|-\(nt\|ot\|ef\|eq\|ne\|lt\|le\|gt\|ge\)\>\|[!<>]"
191syn match  shTestOpr	contained	'=' skipwhite nextgroup=shTestDoubleQuote,shTestSingleQuote,shTestPattern
192syn match  shTestPattern	contained	'\w\+'
193syn match  shTestDoubleQuote	contained	'\%(\%(\\\\\)*\\\)\@<!"[^"]*"'
194syn match  shTestSingleQuote	contained	'\\.'
195syn match  shTestSingleQuote	contained	"'[^']*'"
196if exists("b:is_kornshell") || exists("b:is_bash")
197 syn region  shDblBrace matchgroup=Delimiter start="\[\[" skip=+\\\\\|\\$+ end="\]\]"	contains=@shTestList
198 syn region  shDblParen matchgroup=Delimiter start="((" skip=+\\\\\|\\$+ end="))"	contains=@shTestList
199endif
200
201" Character Class In Range: {{{1
202" =========================
203syn match   shCharClass	contained	"\[:\(backspace\|escape\|return\|xdigit\|alnum\|alpha\|blank\|cntrl\|digit\|graph\|lower\|print\|punct\|space\|upper\|tab\):\]"
204
205" Loops: do, if, while, until {{{1
206" ======
207if (g:sh_fold_enabled % (s:sh_fold_ifdofor * 2))/s:sh_fold_ifdofor
208 syn region shDo	fold transparent matchgroup=shConditional start="\<do\>" matchgroup=shConditional end="\<done\>" contains=@shLoopList
209 syn region shIf	fold transparent matchgroup=shConditional start="\<if\_s" matchgroup=shConditional skip=+-fi\>+ end="\<;\_s*then\>" end="\<fi\>"   contains=@shIfList
210 syn region shFor	fold matchgroup=shLoop start="\<for\_s" end="\<in\_s" end="\<do\>"me=e-2	contains=@shLoopList,shDblParen skipwhite nextgroup=shCurlyIn
211else
212 syn region shDo	transparent matchgroup=shConditional start="\<do\>" matchgroup=shConditional end="\<done\>" contains=@shLoopList
213 syn region shIf	transparent matchgroup=shConditional start="\<if\_s" matchgroup=shConditional skip=+-fi\>+ end="\<;\_s*then\>" end="\<fi\>"   contains=@shIfList
214 syn region shFor	matchgroup=shLoop start="\<for\_s" end="\<in\>" end="\<do\>"me=e-2	contains=@shLoopList,shDblParen skipwhite nextgroup=shCurlyIn
215endif
216if exists("b:is_kornshell") || exists("b:is_bash")
217 syn cluster shCaseList	add=shRepeat
218 syn cluster shFunctionList	add=shRepeat
219 syn region shRepeat   matchgroup=shLoop   start="\<while\_s" end="\<in\_s" end="\<do\>"me=e-2	contains=@shLoopList,shDblParen,shDblBrace
220 syn region shRepeat   matchgroup=shLoop   start="\<until\_s" end="\<in\_s" end="\<do\>"me=e-2	contains=@shLoopList,shDblParen,shDblBrace
221 syn region shCaseEsac matchgroup=shConditional start="\<select\s" matchgroup=shConditional end="\<in\>" end="\<do\>" contains=@shLoopList
222else
223 syn region shRepeat   matchgroup=shLoop   start="\<while\_s" end="\<do\>"me=e-2		contains=@shLoopList
224 syn region shRepeat   matchgroup=shLoop   start="\<until\_s" end="\<do\>"me=e-2		contains=@shLoopList
225endif
226syn region shCurlyIn   contained	matchgroup=Delimiter start="{" end="}" contains=@shCurlyList
227syn match  shComma     contained	","
228
229" Case: case...esac {{{1
230" ====
231syn match   shCaseBar	contained skipwhite "\(^\|[^\\]\)\(\\\\\)*\zs|"		nextgroup=shCase,shCaseStart,shCaseBar,shComment,shCaseExSingleQuote,shCaseSingleQuote,shCaseDoubleQuote
232syn match   shCaseStart	contained skipwhite skipnl "("			nextgroup=shCase,shCaseBar
233if (g:sh_fold_enabled % (s:sh_fold_ifdofor * 2))/s:sh_fold_ifdofor
234 syn region  shCase	fold contained skipwhite skipnl matchgroup=shSnglCase start="\%(\\.\|[^#$()'" \t]\)\{-}\zs)"  end=";;" end="esac"me=s-1 contains=@shCaseList nextgroup=shCaseStart,shCase,shComment
235 syn region  shCaseEsac	fold matchgroup=shConditional start="\<case\>" end="\<esac\>"	contains=@shCaseEsacList
236else
237 syn region  shCase	contained skipwhite skipnl matchgroup=shSnglCase start="\%(\\.\|[^#$()'" \t]\)\{-}\zs)"  end=";;" end="esac"me=s-1 contains=@shCaseList nextgroup=shCaseStart,shCase,shComment
238 syn region  shCaseEsac	matchgroup=shConditional start="\<case\>" end="\<esac\>"	contains=@shCaseEsacList
239endif
240syn keyword shCaseIn	contained skipwhite skipnl in			nextgroup=shCase,shCaseStart,shCaseBar,shComment,shCaseExSingleQuote,shCaseSingleQuote,shCaseDoubleQuote
241if exists("b:is_bash")
242 syn region  shCaseExSingleQuote	matchgroup=shQuote start=+\$'+ skip=+\\\\\|\\.+ end=+'+	contains=shStringSpecial,shSpecial	skipwhite skipnl nextgroup=shCaseBar	contained
243elseif !exists("g:sh_no_error")
244 syn region  shCaseExSingleQuote	matchgroup=Error start=+\$'+ skip=+\\\\\|\\.+ end=+'+	contains=shStringSpecial	skipwhite skipnl nextgroup=shCaseBar	contained
245endif
246syn region  shCaseSingleQuote	matchgroup=shQuote start=+'+ end=+'+		contains=shStringSpecial		skipwhite skipnl nextgroup=shCaseBar	contained
247syn region  shCaseDoubleQuote	matchgroup=shQuote start=+"+ skip=+\\\\\|\\.+ end=+"+	contains=@shDblQuoteList,shStringSpecial	skipwhite skipnl nextgroup=shCaseBar	contained
248syn region  shCaseCommandSub	start=+`+ skip=+\\\\\|\\.+ end=+`+		contains=@shCommandSubList		skipwhite skipnl nextgroup=shCaseBar	contained
249syn region  shCaseRange	matchgroup=Delimiter start=+\[+ skip=+\\\\+ end=+]+	contained
250
251" Misc: {{{1
252"======
253syn match   shWrapLineOperator "\\$"
254syn region  shCommandSub   start="`" skip="\\\\\|\\." end="`" contains=@shCommandSubList
255syn match   shEscape	contained	'\\.'         contains=@shCommandSubList
256
257" $() and $(()): {{{1
258" $(..) is not supported by sh (Bourne shell).  However, apparently
259" some systems (HP?) have as their /bin/sh a (link to) Korn shell
260" (ie. Posix compliant shell).  /bin/ksh should work for those
261" systems too, however, so the following syntax will flag $(..) as
262" an Error under /bin/sh.  By consensus of vimdev'ers!
263if exists("b:is_kornshell") || exists("b:is_bash")
264 syn region shCommandSub matchgroup=shCmdSubRegion start="\$("  skip='\\\\\|\\.' end=")"  contains=@shCommandSubList
265 syn region shArithmetic matchgroup=shArithRegion  start="\$((" skip='\\\\\|\\.' end="))" contains=@shArithList
266 syn region shArithmetic matchgroup=shArithRegion  start="\$\[" skip='\\\\\|\\.' end="\]" contains=@shArithList
267 syn match  shSkipInitWS contained	"^\s\+"
268elseif !exists("g:sh_no_error")
269 syn region shCommandSub matchgroup=Error start="\$(" end=")" contains=@shCommandSubList
270endif
271
272if exists("b:is_bash")
273 syn cluster shCommandSubList add=bashSpecialVariables,bashStatement
274 syn cluster shCaseList add=bashAdminStatement,bashStatement
275 syn keyword bashSpecialVariables contained auto_resume BASH BASH_ALIASES BASH_ALIASES BASH_ARGC BASH_ARGC BASH_ARGV BASH_ARGV BASH_CMDS BASH_CMDS BASH_COMMAND BASH_COMMAND BASH_ENV BASH_EXECUTION_STRING BASH_EXECUTION_STRING BASH_LINENO BASH_LINENO BASHOPTS BASHOPTS BASHPID BASHPID BASH_REMATCH BASH_REMATCH BASH_SOURCE BASH_SOURCE BASH_SUBSHELL BASH_SUBSHELL BASH_VERSINFO BASH_VERSION BASH_XTRACEFD BASH_XTRACEFD CDPATH COLUMNS COLUMNS COMP_CWORD COMP_CWORD COMP_KEY COMP_KEY COMP_LINE COMP_LINE COMP_POINT COMP_POINT COMPREPLY COMPREPLY COMP_TYPE COMP_TYPE COMP_WORDBREAKS COMP_WORDBREAKS COMP_WORDS COMP_WORDS COPROC COPROC DIRSTACK EMACS EMACS ENV ENV EUID FCEDIT FIGNORE FUNCNAME FUNCNAME FUNCNEST FUNCNEST GLOBIGNORE GROUPS histchars HISTCMD HISTCONTROL HISTFILE HISTFILESIZE HISTIGNORE HISTSIZE HISTTIMEFORMAT HISTTIMEFORMAT HOME HOSTFILE HOSTNAME HOSTTYPE IFS IGNOREEOF INPUTRC LANG LC_ALL LC_COLLATE LC_CTYPE LC_CTYPE LC_MESSAGES LC_NUMERIC LC_NUMERIC LINENO LINES LINES MACHTYPE MAIL MAILCHECK MAILPATH MAPFILE MAPFILE OLDPWD OPTARG OPTERR OPTIND OSTYPE PATH PIPESTATUS POSIXLY_CORRECT POSIXLY_CORRECT PPID PROMPT_COMMAND PS1 PS2 PS3 PS4 PWD RANDOM READLINE_LINE READLINE_LINE READLINE_POINT READLINE_POINT REPLY SECONDS SHELL SHELL SHELLOPTS SHLVL TIMEFORMAT TIMEOUT TMPDIR TMPDIR UID
276 syn keyword bashStatement chmod clear complete du egrep expr fgrep find gnufind gnugrep grep install less ls mkdir mv rm rmdir rpm sed sleep sort strip tail touch
277 syn keyword bashAdminStatement daemon killall killproc nice reload restart start status stop
278endif
279
280if exists("b:is_kornshell")
281 syn cluster shCommandSubList add=kshSpecialVariables,kshStatement
282 syn cluster shCaseList add=kshStatement
283 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
284 syn keyword kshStatement cat chmod clear cp du egrep expr fgrep find grep install killall less ls mkdir mv nice printenv rm rmdir sed sort strip stty tail touch tput
285endif
286
287syn match   shSource	"^\.\s"
288syn match   shSource	"\s\.\s"
289"syn region  shColon	start="^\s*:" end="$" end="\s#"me=e-2 contains=@shColonList
290"syn region  shColon	start="^\s*\zs:" end="$" end="\s#"me=e-2
291syn match   shColon	'^\s*\zs:'
292
293" String And Character Constants: {{{1
294"================================
295syn match   shNumber	"-\=\<\d\+\>#\="
296syn match   shCtrlSeq	"\\\d\d\d\|\\[abcfnrtv0]"		contained
297if exists("b:is_bash")
298 syn match   shSpecial	"\\\o\o\o\|\\x\x\x\|\\c[^"]\|\\[abefnrtv]"	contained
299endif
300if exists("b:is_bash")
301 syn region  shExSingleQuote	matchgroup=shQuote start=+\$'+ skip=+\\\\\|\\.+ end=+'+	contains=shStringSpecial,shSpecial
302 syn region  shExDoubleQuote	matchgroup=shQuote start=+\$"+ skip=+\\\\\|\\.\|\\"+ end=+"+	contains=@shDblQuoteList,shStringSpecial,shSpecial
303elseif !exists("g:sh_no_error")
304 syn region  shExSingleQuote	matchGroup=Error start=+\$'+ skip=+\\\\\|\\.+ end=+'+	contains=shStringSpecial
305 syn region  shExDoubleQuote	matchGroup=Error start=+\$"+ skip=+\\\\\|\\.+ end=+"+	contains=shStringSpecial
306endif
307syn region  shSingleQuote	matchgroup=shQuote start=+'+ end=+'+		contains=@Spell
308syn region  shDoubleQuote	matchgroup=shQuote start=+\%(\%(\\\\\)*\\\)\@<!"+ skip=+\\"+ end=+"+	contains=@shDblQuoteList,shStringSpecial,@Spell
309"syn region  shDoubleQuote	matchgroup=shQuote start=+"+ skip=+\\"+ end=+"+	contains=@shDblQuoteList,shStringSpecial,@Spell
310syn match   shStringSpecial	"[^[:print:] \t]"	contained
311syn match   shStringSpecial	"\%(\\\\\)*\\[\\"'`$()#]"
312syn match   shSpecial	"[^\\]\zs\%(\\\\\)*\\[\\"'`$()#]" nextgroup=shMoreSpecial
313syn match   shSpecial	"^\%(\\\\\)*\\[\\"'`$()#]"
314syn match   shMoreSpecial	"\%(\\\\\)*\\[\\"'`$()#]" nextgroup=shMoreSpecial contained
315
316" Comments: {{{1
317"==========
318syn cluster	shCommentGroup	contains=shTodo,@Spell
319syn keyword	shTodo	contained		COMBAK FIXME TODO XXX
320syn match	shComment		"^\s*\zs#.*$"	contains=@shCommentGroup
321syn match	shComment		"\s\zs#.*$"	contains=@shCommentGroup
322syn match	shQuickComment	contained	"#.*$"
323
324" Here Documents: {{{1
325" =========================================
326if version < 600
327 syn region shHereDoc matchgroup=shRedir start="<<\s*\**END[a-zA-Z_0-9]*\**"  matchgroup=shRedir end="^END[a-zA-Z_0-9]*$" contains=@shDblQuoteList
328 syn region shHereDoc matchgroup=shRedir start="<<-\s*\**END[a-zA-Z_0-9]*\**" matchgroup=shRedir end="^\s*END[a-zA-Z_0-9]*$" contains=@shDblQuoteList
329 syn region shHereDoc matchgroup=shRedir start="<<\s*\**EOF\**"	matchgroup=shRedir	end="^EOF$"	contains=@shDblQuoteList
330 syn region shHereDoc matchgroup=shRedir start="<<-\s*\**EOF\**" matchgroup=shRedir	end="^\s*EOF$"	contains=@shDblQuoteList
331 syn region shHereDoc matchgroup=shRedir start="<<\s*\**\.\**"	matchgroup=shRedir	end="^\.$"	contains=@shDblQuoteList
332 syn region shHereDoc matchgroup=shRedir start="<<-\s*\**\.\**"	matchgroup=shRedir	end="^\s*\.$"	contains=@shDblQuoteList
333
334elseif (g:sh_fold_enabled % (s:sh_fold_heredoc * 2))/s:sh_fold_heredoc
335 syn region shHereDoc matchgroup=shRedir fold start="<<\s*\z(\S*\)"		matchgroup=shRedir end="^\z1\s*$"	contains=@shDblQuoteList
336 syn region shHereDoc matchgroup=shRedir fold start="<<\s*\"\z(\S*\)\""		matchgroup=shRedir end="^\z1\s*$"
337 syn region shHereDoc matchgroup=shRedir fold start="<<\s*'\z(\S*\)'"		matchgroup=shRedir end="^\z1\s*$"
338 syn region shHereDoc matchgroup=shRedir fold start="<<-\s*\z(\S*\)"		matchgroup=shRedir end="^\s*\z1\s*$"	contains=@shDblQuoteList
339 syn region shHereDoc matchgroup=shRedir fold start="<<-\s*\"\z(\S*\)\""		matchgroup=shRedir end="^\s*\z1\s*$"
340 syn region shHereDoc matchgroup=shRedir fold start="<<-\s*'\z(\S*\)'"		matchgroup=shRedir end="^\s*\z1\s*$"
341 syn region shHereDoc matchgroup=shRedir fold start="<<\s*\\\_$\_s*\z(\S*\)"		matchgroup=shRedir end="^\z1\s*$"
342 syn region shHereDoc matchgroup=shRedir fold start="<<\s*\\\_$\_s*\"\z(\S*\)\""	matchgroup=shRedir end="^\z1\s*$"
343 syn region shHereDoc matchgroup=shRedir fold start="<<-\s*\\\_$\_s*'\z(\S*\)'"		matchgroup=shRedir end="^\s*\z1\s*$"
344 syn region shHereDoc matchgroup=shRedir fold start="<<-\s*\\\_$\_s*\z(\S*\)"		matchgroup=shRedir end="^\s*\z1\s*$"
345 syn region shHereDoc matchgroup=shRedir fold start="<<-\s*\\\_$\_s*\"\z(\S*\)\""	matchgroup=shRedir end="^\s*\z1\s*$"
346 syn region shHereDoc matchgroup=shRedir fold start="<<\s*\\\_$\_s*'\z(\S*\)'"		matchgroup=shRedir end="^\z1\s*$"
347 syn region shHereDoc matchgroup=shRedir fold start="<<\\\z(\S*\)"		matchgroup=shRedir end="^\z1\s*$"
348
349else
350 syn region shHereDoc matchgroup=shRedir start="<<\s*\\\=\z(\S*\)"	matchgroup=shRedir end="^\z1\s*$"    contains=@shDblQuoteList
351 syn region shHereDoc matchgroup=shRedir start="<<\s*\"\z(\S*\)\""	matchgroup=shRedir end="^\z1\s*$"
352 syn region shHereDoc matchgroup=shRedir start="<<-\s*\z(\S*\)"		matchgroup=shRedir end="^\s*\z1\s*$" contains=@shDblQuoteList
353 syn region shHereDoc matchgroup=shRedir start="<<-\s*'\z(\S*\)'"	matchgroup=shRedir end="^\s*\z1\s*$"
354 syn region shHereDoc matchgroup=shRedir start="<<\s*'\z(\S*\)'"	matchgroup=shRedir end="^\z1\s*$"
355 syn region shHereDoc matchgroup=shRedir start="<<-\s*\"\z(\S*\)\""	matchgroup=shRedir end="^\s*\z1\s*$"
356 syn region shHereDoc matchgroup=shRedir start="<<\s*\\\_$\_s*\z(\S*\)"	matchgroup=shRedir end="^\z1\s*$"
357 syn region shHereDoc matchgroup=shRedir start="<<-\s*\\\_$\_s*\z(\S*\)"	matchgroup=shRedir end="^\s*\z1\s*$"
358 syn region shHereDoc matchgroup=shRedir start="<<-\s*\\\_$\_s*'\z(\S*\)'"	matchgroup=shRedir end="^\s*\z1\s*$"
359 syn region shHereDoc matchgroup=shRedir start="<<\s*\\\_$\_s*'\z(\S*\)'"	matchgroup=shRedir end="^\z1\s*$"
360 syn region shHereDoc matchgroup=shRedir start="<<\s*\\\_$\_s*\"\z(\S*\)\""	matchgroup=shRedir end="^\z1\s*$"
361 syn region shHereDoc matchgroup=shRedir start="<<-\s*\\\_$\_s*\"\z(\S*\)\""	matchgroup=shRedir end="^\s*\z1\s*$"
362 syn region shHereDoc matchgroup=shRedir start="<<\\\z(\S*\)"		matchgroup=shRedir end="^\z1\s*$"
363endif
364
365" Here Strings: {{{1
366" =============
367" available for: bash; ksh (really should be ksh93 only) but not if its a posix
368if exists("b:is_bash") || (exists("b:is_kornshell") && !exists("g:is_posix"))
369 syn match shRedir "<<<"
370endif
371
372" Identifiers: {{{1
373"=============
374syn match  shSetOption	"\s\zs[-+][a-zA-Z0-9]\+\>"	contained
375syn match  shVariable	"\<\([bwglsav]:\)\=[a-zA-Z0-9.!@_%+,]*\ze="	nextgroup=shSetIdentifier
376syn match  shSetIdentifier	"="		contained	nextgroup=shPattern,shDeref,shDerefSimple,shDoubleQuote,shExDoubleQuote,shSingleQuote,shExSingleQuote
377if exists("b:is_bash")
378 syn region shSetList oneline matchgroup=shSet start="\<\(declare\|typeset\|local\|export\|unset\)\>\ze[^/]" end="$"	matchgroup=shSetListDelim end="\ze[}|);&]" matchgroup=NONE end="\ze\s\+#\|=" contains=@shIdList
379 syn region shSetList oneline matchgroup=shSet start="\<set\>\ze[^/]" end="\ze[;|)]\|$"			matchgroup=shSetListDelim end="\ze[}|);&]" matchgroup=NONE end="\ze\s\+[#=]" contains=@shIdList
380elseif exists("b:is_kornshell")
381 syn region shSetList oneline matchgroup=shSet start="\<\(typeset\|export\|unset\)\>\ze[^/]" end="$"		matchgroup=shSetListDelim end="\ze[}|);&]" matchgroup=NONE end="\ze\s\+[#=]" contains=@shIdList
382 syn region shSetList oneline matchgroup=shSet start="\<set\>\ze[^/]" end="$"				matchgroup=shSetListDelim end="\ze[}|);&]" matchgroup=NONE end="\ze\s\+[#=]" contains=@shIdList
383else
384 syn region shSetList oneline matchgroup=shSet start="\<\(set\|export\|unset\)\>\ze[^/]" end="$"		matchgroup=shSetListDelim end="\ze[}|);&]" matchgroup=NONE end="\ze\s\+[#=]" contains=@shIdList
385endif
386
387" Functions: {{{1
388if !exists("g:is_posix")
389 syn keyword shFunctionKey function	skipwhite skipnl nextgroup=shFunctionTwo
390endif
391
392if exists("b:is_bash")
393 if (g:sh_fold_enabled % (s:sh_fold_functions * 2))/s:sh_fold_functions
394  syn region shFunctionOne fold	matchgroup=shFunction start="^\s*\h[-a-zA-Z_0-9]*\s*()\_s*{" end="}"	contains=@shFunctionList			skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
395  syn region shFunctionTwo fold	matchgroup=shFunction start="\h[-a-zA-Z_0-9]*\s*\%(()\)\=\_s*{"	end="}"	contains=shFunctionKey,@shFunctionList contained	skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
396 else
397  syn region shFunctionOne	matchgroup=shFunction start="^\s*\h[-a-zA-Z_0-9]*\s*()\_s*{"	end="}"	contains=@shFunctionList
398  syn region shFunctionTwo	matchgroup=shFunction start="\h[-a-zA-Z_0-9]*\s*\%(()\)\=\_s*{"	end="}"	contains=shFunctionKey,@shFunctionList contained
399 endif
400else
401 if (g:sh_fold_enabled % (s:sh_fold_functions * 2))/s:sh_fold_functions
402  syn region shFunctionOne fold	matchgroup=shFunction start="^\s*\h\w*\s*()\_s*{" end="}"	contains=@shFunctionList			skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
403  syn region shFunctionTwo fold	matchgroup=shFunction start="\h\w*\s*\%(()\)\=\_s*{"	end="}"	contains=shFunctionKey,@shFunctionList contained	skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
404 else
405  syn region shFunctionOne	matchgroup=shFunction start="^\s*\h\w*\s*()\_s*{"	end="}"	contains=@shFunctionList
406  syn region shFunctionTwo	matchgroup=shFunction start="\h\w*\s*\%(()\)\=\_s*{"	end="}"	contains=shFunctionKey,@shFunctionList contained
407 endif
408endif
409
410" Parameter Dereferencing: {{{1
411" ========================
412syn match  shDerefSimple	"\$\%(\h\w*\|\d\)"
413syn region shDeref	matchgroup=PreProc start="\${" end="}"	contains=@shDerefList,shDerefVarArray
414if !exists("g:sh_no_error")
415 syn match  shDerefWordError	"[^}$[]"	contained
416endif
417syn match  shDerefSimple	"\$[-#*@!?]"
418syn match  shDerefSimple	"\$\$"
419if exists("b:is_bash") || exists("b:is_kornshell")
420 syn region shDeref	matchgroup=PreProc start="\${##\=" end="}"	contains=@shDerefList
421 syn region shDeref	matchgroup=PreProc start="\${\$\$" end="}"	contains=@shDerefList
422endif
423
424" bash: ${!prefix*} and ${#parameter}: {{{1
425" ====================================
426if exists("b:is_bash")
427 syn region shDeref	matchgroup=PreProc start="\${!" end="\*\=}"	contains=@shDerefList,shDerefOp
428 syn match  shDerefVar	contained	"{\@<=!\w\+"		nextgroup=@shDerefVarList
429endif
430
431syn match  shDerefSpecial	contained	"{\@<=[-*@?0]"		nextgroup=shDerefOp,shDerefOpError
432syn match  shDerefSpecial	contained	"\({[#!]\)\@<=[[:alnum:]*@_]\+"	nextgroup=@shDerefVarList,shDerefOp
433syn match  shDerefVar	contained	"{\@<=\w\+"		nextgroup=@shDerefVarList
434
435" sh ksh bash : ${var[... ]...}  array reference: {{{1
436syn region  shDerefVarArray   contained	matchgroup=shDeref start="\[" end="]"	contains=@shCommandSubList nextgroup=shDerefOp,shDerefOpError
437
438" Special ${parameter OPERATOR word} handling: {{{1
439" sh ksh bash : ${parameter:-word}    word is default value
440" sh ksh bash : ${parameter:=word}    assign word as default value
441" sh ksh bash : ${parameter:?word}    display word if parameter is null
442" sh ksh bash : ${parameter:+word}    use word if parameter is not null, otherwise nothing
443"    ksh bash : ${parameter#pattern}  remove small left  pattern
444"    ksh bash : ${parameter##pattern} remove large left  pattern
445"    ksh bash : ${parameter%pattern}  remove small right pattern
446"    ksh bash : ${parameter%%pattern} remove large right pattern
447"        bash : ${parameter^pattern}  Case modification
448"        bash : ${parameter^^pattern} Case modification
449"        bash : ${parameter,pattern}  Case modification
450"        bash : ${parameter,,pattern} Case modification
451syn cluster shDerefPatternList	contains=shDerefPattern,shDerefString
452if !exists("g:sh_no_error")
453 syn match shDerefOpError	contained	":[[:punct:]]"
454endif
455syn match  shDerefOp	contained	":\=[-=?]"	nextgroup=@shDerefPatternList
456syn match  shDerefOp	contained	":\=+"	nextgroup=@shDerefPatternList
457if exists("b:is_bash") || exists("b:is_kornshell")
458 syn match  shDerefOp	contained	"#\{1,2}"	nextgroup=@shDerefPatternList
459 syn match  shDerefOp	contained	"%\{1,2}"	nextgroup=@shDerefPatternList
460 syn match  shDerefPattern	contained	"[^{}]\+"	contains=shDeref,shDerefSimple,shDerefPattern,shDerefString,shCommandSub,shDerefEscape nextgroup=shDerefPattern
461 syn region shDerefPattern	contained	start="{" end="}"	contains=shDeref,shDerefSimple,shDerefString,shCommandSub nextgroup=shDerefPattern
462 syn match  shDerefEscape	contained	'\%(\\\\\)*\\.'
463endif
464if exists("b:is_bash")
465 syn match  shDerefOp	contained	"[,^]\{1,2}"	nextgroup=@shDerefPatternList
466endif
467syn region shDerefString	contained	matchgroup=shDerefDelim start=+\%(\\\)\@<!'+ end=+'+		contains=shStringSpecial
468syn region shDerefString	contained	matchgroup=shDerefDelim start=+\%(\\\)\@<!"+ skip=+\\"+ end=+"+	contains=@shDblQuoteList,shStringSpecial
469syn match  shDerefString	contained	"\\["']"	nextgroup=shDerefPattern
470
471if exists("b:is_bash")
472 " bash : ${parameter:offset}
473 " bash : ${parameter:offset:length}
474 syn region shDerefOp	contained	start=":[$[:alnum:]_]"me=e-1 end=":"me=e-1 end="}"me=e-1 contains=@shCommandSubList nextgroup=shDerefPOL
475 syn match  shDerefPOL	contained	":[^}]\+"	contains=@shCommandSubList
476
477 " bash : ${parameter//pattern/string}
478 " bash : ${parameter//pattern}
479 syn match  shDerefPPS	contained	'/\{1,2}'	nextgroup=shDerefPPSleft
480 syn region shDerefPPSleft	contained	start='.'	skip=@\%(\\\)\/@ matchgroup=shDerefOp end='/' end='\ze}' nextgroup=shDerefPPSright contains=@shCommandSubList
481 syn region shDerefPPSright	contained	start='.'	end='\ze}'	contains=@shCommandSubList
482endif
483
484" Arithmetic Parenthesized Expressions: {{{1
485syn region shParen matchgroup=shArithRegion start='(\%(\ze[^(]\|$\)' end=')' contains=@shArithParenList
486
487" Useful sh Keywords: {{{1
488" ===================
489syn keyword shStatement break cd chdir continue eval exec exit kill newgrp pwd read readonly return shift test trap ulimit umask wait
490syn keyword shConditional contained elif else then
491if !exists("g:sh_no_error")
492 syn keyword shCondError elif else then
493endif
494
495" Useful ksh Keywords: {{{1
496" ====================
497if exists("b:is_kornshell") || exists("b:is_bash")
498 syn keyword shStatement autoload bg false fc fg functions getopts hash history integer jobs let nohup printf r stop suspend times true type unalias whence
499 if exists("g:is_posix")
500  syn keyword shStatement command
501 else
502  syn keyword shStatement time
503 endif
504
505" Useful bash Keywords: {{{1
506" =====================
507 if exists("b:is_bash")
508  syn keyword shStatement bind builtin dirs disown enable help local logout popd pushd shopt source
509 else
510  syn keyword shStatement login newgrp
511 endif
512endif
513
514" Synchronization: {{{1
515" ================
516if !exists("sh_minlines")
517  let sh_minlines = 200
518endif
519if !exists("sh_maxlines")
520  let sh_maxlines = 2 * sh_minlines
521endif
522exec "syn sync minlines=" . sh_minlines . " maxlines=" . sh_maxlines
523syn sync match shCaseEsacSync	grouphere	shCaseEsac	"\<case\>"
524syn sync match shCaseEsacSync	groupthere	shCaseEsac	"\<esac\>"
525syn sync match shDoSync	grouphere	shDo	"\<do\>"
526syn sync match shDoSync	groupthere	shDo	"\<done\>"
527syn sync match shForSync	grouphere	shFor	"\<for\>"
528syn sync match shForSync	groupthere	shFor	"\<in\>"
529syn sync match shIfSync	grouphere	shIf	"\<if\>"
530syn sync match shIfSync	groupthere	shIf	"\<fi\>"
531syn sync match shUntilSync	grouphere	shRepeat	"\<until\>"
532syn sync match shWhileSync	grouphere	shRepeat	"\<while\>"
533
534" Default Highlighting: {{{1
535" =====================
536hi def link shArithRegion	shShellVariables
537hi def link shBeginHere	shRedir
538hi def link shCaseBar	shConditional
539hi def link shCaseCommandSub	shCommandSub
540hi def link shCaseDoubleQuote	shDoubleQuote
541hi def link shCaseIn	shConditional
542hi def link shQuote	shOperator
543hi def link shCaseSingleQuote	shSingleQuote
544hi def link shCaseStart	shConditional
545hi def link shCmdSubRegion	shShellVariables
546hi def link shColon	shComment
547hi def link shDerefOp	shOperator
548hi def link shDerefPOL	shDerefOp
549hi def link shDerefPPS	shDerefOp
550hi def link shDeref	shShellVariables
551hi def link shDerefDelim	shOperator
552hi def link shDerefSimple	shDeref
553hi def link shDerefSpecial	shDeref
554hi def link shDerefString	shDoubleQuote
555hi def link shDerefVar	shDeref
556hi def link shDoubleQuote	shString
557hi def link shEcho	shString
558hi def link shEchoDelim	shOperator
559hi def link shEchoQuote	shString
560hi def link shEmbeddedEcho	shString
561hi def link shEscape	shCommandSub
562hi def link shExDoubleQuote	shDoubleQuote
563hi def link shExSingleQuote	shSingleQuote
564hi def link shFunction	Function
565hi def link shHereDoc	shString
566hi def link shHerePayload	shHereDoc
567hi def link shLoop	shStatement
568hi def link shMoreSpecial	shSpecial
569hi def link shOption	shCommandSub
570hi def link shPattern	shString
571hi def link shParen	shArithmetic
572hi def link shPosnParm	shShellVariables
573hi def link shQuickComment	shComment
574hi def link shRange	shOperator
575hi def link shRedir	shOperator
576hi def link shSetListDelim	shOperator
577hi def link shSetOption	shOption
578hi def link shSingleQuote	shString
579hi def link shSource	shOperator
580hi def link shStringSpecial	shSpecial
581hi def link shSubShRegion	shOperator
582hi def link shTestOpr	shConditional
583hi def link shTestPattern	shString
584hi def link shTestDoubleQuote	shString
585hi def link shTestSingleQuote	shString
586hi def link shVariable	shSetList
587hi def link shWrapLineOperator	shOperator
588
589if exists("b:is_bash")
590  hi def link bashAdminStatement	shStatement
591  hi def link bashSpecialVariables	shShellVariables
592  hi def link bashStatement		shStatement
593  hi def link shFunctionParen		Delimiter
594  hi def link shFunctionDelim		Delimiter
595endif
596if exists("b:is_kornshell")
597  hi def link kshSpecialVariables	shShellVariables
598  hi def link kshStatement		shStatement
599  hi def link shFunctionParen		Delimiter
600endif
601
602if !exists("g:sh_no_error")
603 hi def link shCaseError		Error
604 hi def link shCondError		Error
605 hi def link shCurlyError		Error
606 hi def link shDerefError		Error
607 hi def link shDerefOpError		Error
608 hi def link shDerefWordError		Error
609 hi def link shDoError		Error
610 hi def link shEsacError		Error
611 hi def link shIfError		Error
612 hi def link shInError		Error
613 hi def link shParenError		Error
614 hi def link shTestError		Error
615 if exists("b:is_kornshell")
616   hi def link shDTestError		Error
617 endif
618endif
619
620hi def link shArithmetic		Special
621hi def link shCharClass		Identifier
622hi def link shSnglCase		Statement
623hi def link shCommandSub		Special
624hi def link shComment		Comment
625hi def link shConditional		Conditional
626hi def link shCtrlSeq		Special
627hi def link shExprRegion		Delimiter
628hi def link shFunctionKey		Function
629hi def link shFunctionName		Function
630hi def link shNumber		Number
631hi def link shOperator		Operator
632hi def link shRepeat		Repeat
633hi def link shSet		Statement
634hi def link shSetList		Identifier
635hi def link shShellVariables		PreProc
636hi def link shSpecial		Special
637hi def link shStatement		Statement
638hi def link shString		String
639hi def link shTodo		Todo
640hi def link shAlias		Identifier
641
642" Set Current Syntax: {{{1
643" ===================
644if exists("b:is_bash")
645 let b:current_syntax = "bash"
646elseif exists("b:is_kornshell")
647 let b:current_syntax = "ksh"
648else
649 let b:current_syntax = "sh"
650endif
651
652" vim: ts=16 fdm=marker
653