xref: /vim-8.2.3635/runtime/syntax/sh.vim (revision d4755bb0)
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:		Jul 26, 2004
6" Version:		69
7" URL:		http://www.erols.com/astronaut/vim/index.html#vimlinks_syntax
8"
9" Using the following VIM variables: {{{1
10" b:is_kornshell	if defined, enhance with kornshell syntax
11" b:is_bash		if defined, enhance with bash syntax
12"   is_kornshell	if neither b:is_kornshell or b:is_bash is
13"		defined, then if is_kornshell is set
14"		b:is_kornshell is default
15"   is_bash		if none of the previous three variables are
16"		defined, then if is_bash is set b:is_bash is default
17" g:sh_fold_enabled	if non-zero, syntax folding is enabled
18"   sh_minlines		sets up syn sync minlines  (default: 200)
19"   sh_maxlines		sets up syn sync maxlines  (default: twice sh_minlines)
20"
21" This file includes many ideas from �ric Brunet ([email protected])
22
23" For version 5.x: Clear all syntax items {{{1
24" For version 6.x: Quit when a syntax file was already loaded
25if version < 600
26  syntax clear
27elseif exists("b:current_syntax")
28  finish
29endif
30
31" handling /bin/sh with is_kornshell/is_sh {{{1
32" b:is_sh is set when "#! /bin/sh" is found;
33" However, it often is just a masquerade by bash (typically Linux)
34" or kornshell (typically workstations with Posix "sh").
35" So, when the user sets "is_bash" or "is_kornshell",
36" a b:is_sh is converted into b:is_bash/b:is_kornshell,
37" respectively.
38if !exists("b:is_kornshell") && !exists("b:is_bash")
39  if exists("is_kornshell")
40    let b:is_kornshell= 1
41    if exists("b:is_sh")
42      unlet b:is_sh
43    endif
44  elseif exists("is_bash")
45    let b:is_bash= 1
46    if exists("b:is_sh")
47      unlet b:is_sh
48    endif
49  else
50    let b:is_sh= 1
51  endif
52endif
53
54if !exists("g:sh_fold_enabled")
55 let g:sh_fold_enabled= 0
56endif
57
58" sh syntax is case sensitive
59syn case match
60
61" Clusters: contains=@... clusters {{{1
62"==================================
63syn cluster shCaseEsacList	contains=shCaseStart,shCase,shCaseBar,shCaseIn,shComment,shDeref,shDerefSimple,shCaseCommandSub,shCaseSingleQuote,shCaseDoubleQuote,shSpecial
64syn cluster shCaseList	contains=@shCommandSubList,shCaseEsac,shColon,shCommandSub,shCommandSub,shComment,shDo,shEcho,shExpr,shFor,shHereDoc,shIf,shRedir,shSetList,shSource,shStatement,shVariable,bkshFunction,shSpecial
65syn cluster shColonList	contains=@shCaseList
66syn cluster shCommandSubList	contains=shArithmetic,shDeref,shDerefSimple,shNumber,shOperator,shPosnParm,shSpecial,shSingleQuote,shDoubleQuote,shStatement,shVariable,shSubSh,shAlias
67syn cluster shDblQuoteList	contains=shCommandSub,shDeref,shDerefSimple,shSpecial,shPosnParm
68syn cluster shDerefList	contains=shDeref,shDerefSimple,shDerefVar,shDerefSpecial,shDerefWordError
69syn cluster shDerefVarList	contains=shDerefOp,shDerefVarArray,shDerefOpError
70syn cluster shEchoList	contains=shArithmetic,shCommandSub,shDeref,shDerefSimple,shExpr,shSingleQuote,shDoubleQuote,shSpecial
71syn cluster shExprList1	contains=shCharClass,shNumber,shOperator,shSingleQuote,shDoubleQuote,shSpecial,shExpr,shDblBrace,shDeref,shDerefSimple
72syn cluster shExprList2	contains=@shExprList1,@shCaseList
73syn cluster shFunctionList	contains=@shCaseList,shOperator
74syn cluster shHereBeginList	contains=@shCommandSubList
75syn cluster shHereList	contains=shBeginHere,shHerePayload
76syn cluster shHereListDQ	contains=shBeginHere,@shDblQuoteList,shHerePayload
77syn cluster shIdList	contains=shCommandSub,shWrapLineOperator,shIdWhiteSpace,shDeref,shDerefSimple,shSpecial,shRedir,shSingleQuote,shDoubleQuote,shExpr
78syn cluster shLoopList	contains=@shCaseList,shTestOpr,shExpr,shDblBrace,shConditional,shCaseEsac
79syn cluster shSubShList	contains=@shCaseList
80syn cluster shTestList	contains=shCharClass,shComment,shCommandSub,shDeref,shDerefSimple,shDoubleQuote,shExpr,shExpr,shNumber,shOperator,shSingleQuote,shSpecial,shTestOpr
81
82
83" Echo: {{{1
84" ====
85" This one is needed INSIDE a CommandSub, so that `echo bla` be correct
86syn region shEcho matchgroup=shStatement start="\<echo\>"  skip="\\$" matchgroup=shOperator end="$" matchgroup=NONE end="[<>;&|()]"me=e-1 end="\d[<>]"me=e-2 end="#"me=e-1 contains=@shEchoList
87syn region shEcho matchgroup=shStatement start="\<print\>" skip="\\$" matchgroup=shOperator end="$" matchgroup=NONE end="[<>;&|()]"me=e-1 end="\d[<>]"me=e-2 end="#"me=e-1 contains=@shEchoList
88
89" This must be after the strings, so that bla \" be correct
90syn region shEmbeddedEcho contained matchgroup=shStatement start="\<print\>" skip="\\$" matchgroup=shOperator end="$" matchgroup=NONE end="[<>;&|`)]"me=e-1 end="\d[<>]"me=e-2 end="#"me=e-1 contains=shNumber,shSingleQuote,shDeref,shDerefSimple,shSpecialVar,shSpecial,shOperator,shDoubleQuote,shCharClass
91
92" Alias: {{{1
93" =====
94if exists("b:is_kornshell") || exists("b:is_bash")
95 syn match shStatement "\<alias\>"
96 syn region shAlias matchgroup=shStatement start="\<alias\>\s\+\(\w\+\)\@=" skip="\\$" end="\>\|`"
97 syn region shAlias matchgroup=shStatement start="\<alias\>\s\+\(\w\+=\)\@=" skip="\\$" end="="
98endif
99
100" Error Codes: {{{1
101" ============
102syn match   shDoError "\<done\>"
103syn match   shIfError "\<fi\>"
104syn match   shInError "\<in\>"
105syn match   shCaseError ";;"
106syn match   shEsacError "\<esac\>"
107syn match   shCurlyError "}"
108syn match   shParenError ")"
109if exists("b:is_kornshell")
110 syn match     shDTestError "]]"
111endif
112syn match     shTestError "]"
113
114" Options Interceptor: {{{1
115" ====================
116syn match   shOption  "\s[\-+][a-zA-Z0-9]\+\>"ms=s+1
117syn match   shOption  "\s--\S\+"ms=s+1
118
119" Operators: {{{1
120" ==========
121syn match   shOperator	"[!&;|]"
122syn match   shOperator	"\[[[^:]\|\]]"
123syn match   shOperator	"!\=="		skipwhite nextgroup=shPattern
124syn match   shPattern	"\<\S\+\())\)\@="	contained contains=shSingleQuote,shDoubleQuote,shDeref
125
126" Subshells: {{{1
127" ==========
128syn region shExpr  transparent matchgroup=shExprRegion  start="{" end="}"	contains=@shExprList2
129syn region shSubSh transparent matchgroup=shSubShRegion start="(" end=")"	contains=@shSubShList
130
131" Tests: {{{1
132"=======
133"syn region  shExpr transparent matchgroup=shRange start="\[" skip=+\\\\\|\\$+ end="\]" contains=@shTestList
134syn region  shExpr matchgroup=shRange start="\[" skip=+\\\\\|\\$+ end="\]" contains=@shTestList
135syn region  shExpr transparent matchgroup=shStatement start="\<test\>" skip=+\\\\\|\\$+ matchgroup=NONE end="[;&|]"me=e-1 end="$" contains=@shExprList1
136syn match   shTestOpr contained "<=\|>=\|!=\|==\|-.\>\|-\(nt\|ot\|ef\|eq\|ne\|lt\|le\|gt\|ge\)\>\|[!=<>]"
137if exists("b:is_kornshell") || exists("b:is_bash")
138 syn region  shDblBrace matchgroup=Delimiter start="\[\[" skip=+\\\\\|\\$+ end="\]\]"	contains=@shTestList
139 syn region  shDblParen matchgroup=Delimiter start="((" skip=+\\\\\|\\$+ end="))"	contains=@shTestList
140endif
141
142" Character Class In Range: {{{1
143" =========================
144syn match   shCharClass	contained	"\[:\(backspace\|escape\|return\|xdigit\|alnum\|alpha\|blank\|cntrl\|digit\|graph\|lower\|print\|punct\|space\|upper\|tab\):\]"
145
146" Loops: do, if, while, until {{{1
147" ======
148syn region shDo		transparent matchgroup=shConditional start="\<do\>" matchgroup=shConditional end="\<done\>" contains=@shLoopList
149syn region shIf		transparent matchgroup=shConditional start="\<if\>" matchgroup=shConditional end="\<;\_s*then\>" end="\<fi\>"   contains=@shLoopList,shDblBrace,shDblParen
150syn region shFor	matchgroup=shLoop start="\<for\>" end="\<in\>" end="\<do\>"me=e-2	contains=@shLoopList,shDblParen
151if exists("b:is_kornshell") || exists("b:is_bash")
152 syn cluster shCaseList add=shRepeat
153 syn region shRepeat   matchgroup=shLoop   start="\<while\>" end="\<in\>" end="\<do\>"me=e-2	contains=@shLoopList,shDblParen,shDblBrace
154 syn region shRepeat   matchgroup=shLoop   start="\<until\>" end="\<in\>" end="\<do\>"me=e-2	contains=@shLoopList,shDblParen,shDblBrace
155 syn region shCaseEsac matchgroup=shConditional start="\<select\>" matchgroup=shConditional end="\<in\>" end="\<do\>" contains=@shLoopList
156else
157 syn region shRepeat   matchgroup=shLoop   start="\<while\>" end="\<do\>"me=e-2		contains=@shLoopList
158 syn region shRepeat   matchgroup=shLoop   start="\<until\>" end="\<do\>"me=e-2		contains=@shLoopList
159endif
160
161" Case: case...esac {{{1
162" ====
163syn match   shCaseBar	contained skipwhite "[^|"`'()]\{-}|"hs=e		nextgroup=shCase,shCaseStart,shCaseBar,shComment,shCaseSingleQuote,shCaseDoubleQuote
164syn match   shCaseStart	contained skipwhite skipnl "("			nextgroup=shCase,shCaseBar
165syn region  shCase	contained skipwhite skipnl matchgroup=shSnglCase start="[^$()]\{-})"ms=s,hs=e  end=";;" end="esac"me=s-1 contains=@shCaseList nextgroup=shCaseStart,shCase,,shComment
166syn region  shCaseEsac	matchgroup=shConditional start="\<case\>" end="\<esac\>"	contains=@shCaseEsacList
167syn keyword shCaseIn	contained skipwhite skipnl in			nextgroup=shCase,shCaseStart,shCaseBar,shComment,shCaseSingleQuote,shCaseDoubleQuote
168syn region  shCaseSingleQuote	matchgroup=shOperator start=+'+ skip=+\\\\\|\\.+ end=+'+	contains=shStringSpecial		skipwhite skipnl nextgroup=shCaseBar	contained
169syn region  shCaseDoubleQuote	matchgroup=shOperator start=+"+ skip=+\\\\\|\\.+ end=+"+	contains=@shDblQuoteList,shStringSpecial	skipwhite skipnl nextgroup=shCaseBar	contained
170syn region  shCaseCommandSub	start=+`+ skip=+\\\\\|\\.+ end=+`+		contains=@shCommandSubList		skipwhite skipnl nextgroup=shCaseBar	contained
171
172" Misc: {{{1
173"======
174syn match   shWrapLineOperator "\\$"
175syn region  shCommandSub   start="`" skip="\\\\\|\\." end="`" contains=@shCommandSubList
176
177" $() and $(()): {{{1
178" $(..) is not supported by sh (Bourne shell).  However, apparently
179" some systems (HP?) have as their /bin/sh a (link to) Korn shell
180" (ie. Posix compliant shell).  /bin/ksh should work for those
181" systems too, however, so the following syntax will flag $(..) as
182" an Error under /bin/sh.  By consensus of vimdev'ers!
183if exists("b:is_kornshell") || exists("b:is_bash")
184 syn region shCommandSub matchgroup=shCmdSubRegion start="\$("  skip='\\\\\|\\.' end=")"  contains=@shCommandSubList
185 syn region shArithmetic matchgroup=shArithRegion  start="\$((" skip='\\\\\|\\.' end="))" contains=@shCommandSubList
186 syn match  shSkipInitWS contained	"^\s\+"
187else
188 syn region shCommandSub matchgroup=Error start="$(" end=")" contains=@shCommandSubList
189endif
190
191if exists("b:is_bash")
192 syn cluster shCommandSubList add=bashSpecialVariables,bashStatement
193 syn cluster shCaseList add=bashAdminStatement,bashStatement
194 syn keyword bashSpecialVariables contained BASH BASH_ENV BASH_VERSINFO BASH_VERSION CDPATH DIRSTACK EUID FCEDIT FIGNORE GLOBIGNORE GROUPS HISTCMD HISTCONTROL HISTFILE HISTFILESIZE HISTIGNORE HISTSIZE HOME HOSTFILE HOSTNAME HOSTTYPE IFS IGNOREEOF INPUTRC LANG LC_ALL LC_COLLATE LC_MESSAGES LINENO MACHTYPE MAIL MAILCHECK MAILPATH OLDPWD OPTARG OPTERR OPTIND OSTYPE PATH PIPESTATUS PPID PROMPT_COMMAND PS1 PS2 PS3 PS4 PWD RANDOM REPLY SECONDS SHELLOPTS SHLVL TIMEFORMAT TIMEOUT UID auto_resume histchars
195 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
196 syn keyword bashAdminStatement daemon killall killproc nice reload restart start status stop
197endif
198
199if exists("b:is_kornshell")
200 syn cluster shCommandSubList add=kshSpecialVariables,kshStatement
201 syn cluster shCaseList add=kshStatement
202 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
203 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
204endif
205
206syn match   shSource	"^\.\s"
207syn match   shSource	"\s\.\s"
208syn region  shColon	start="^\s*:" end="$\|" end="#"me=e-1 contains=@shColonList
209
210" String And Character Constants: {{{1
211"================================
212syn match   shNumber	"-\=\<\d\+\>"
213syn match   shSpecial	"\\\d\d\d\|\\[abcfnrtv0]"	contained
214syn region  shSingleQuote	matchgroup=shOperator start=+'+ end=+'+		contains=shStringSpecial
215syn region  shDoubleQuote	matchgroup=shOperator start=+"+ skip=+\\"+ end=+"+	contains=@shDblQuoteList,shStringSpecial
216syn match   shStringSpecial	"[^[:print:]]"	contained
217syn match   shSpecial	"\\[\\\"\'`$()#]"
218
219" Comments: {{{1
220"==========
221syn cluster    shCommentGroup	contains=shTodo,@Spell
222syn keyword    shTodo	contained	TODO
223syn match      shComment	"#.*$" contains=@shCommentGroup
224
225" File Redirection Highlighted As Operators: {{{1
226"===========================================
227syn match      shRedir	"\d\=>\(&[-0-9]\)\="
228syn match      shRedir	"\d\=>>-\="
229syn match      shRedir	"\d\=<\(&[-0-9]\)\="
230syn match      shRedir	"\d<<-\="
231
232" Here Documents: {{{1
233" =========================================
234if version < 600
235 syn region shHereDoc matchgroup=shRedir start="<<\s*\**END[a-zA-Z_0-9]*\**"  matchgroup=shRedir end="^END[a-zA-Z_0-9]*$" contains=@shDblQuoteList
236 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
237 syn region shHereDoc matchgroup=shRedir start="<<\s*\**EOF\**"	matchgroup=shRedir	end="^EOF$"	contains=@shDblQuoteList
238 syn region shHereDoc matchgroup=shRedir start="<<-\s*\**EOF\**" matchgroup=shRedir	end="^\s*EOF$"	contains=@shDblQuoteList
239 syn region shHereDoc matchgroup=shRedir start="<<\s*\**\.\**"	matchgroup=shRedir	end="^\.$"	contains=@shDblQuoteList
240 syn region shHereDoc matchgroup=shRedir start="<<-\s*\**\.\**"	matchgroup=shRedir	end="^\s*\.$"	contains=@shDblQuoteList
241
242elseif g:sh_fold_enabled
243
244 if v:version > 602 || (v:version == 602 && has("patch219"))
245  syn region shHereDoc fold start="\(<<\s*\\\=\z(\S*\)\)\@="		matchgroup=shRedir end="^\z1$"		contains=@shHereListDQ	keepend
246  syn region shHereDoc fold start="\(<<\s*\"\z(\S*\)\"\)\@="		matchgroup=shRedir end="^\z1$""		contains=@shHereList	keepend
247  syn region shHereDoc fold start="\(<<\s*'\z(\S*\)'\)\@="		matchgroup=shRedir end="^\z1$""		contains=@shHereList	keepend
248  syn region shHereDoc fold start="\(<<\s*\\\_$\_s*\z(\S*\)\)\@="	matchgroup=shRedir end="^\z1$""		contains=@shHereList	keepend
249  syn region shHereDoc fold start="\(<<\s*\\\_$\_s*\"\z(\S*\)\"\)\@="	matchgroup=shRedir end="^\z1$""		contains=@shHereList	keepend
250  syn region shHereDoc fold start="\(<<\s*\\\_$\_s*'\z(\S*\)'\)\@="	matchgroup=shRedir end="^\z1$"		contains=@shHereList	keepend
251  syn region shHereDoc fold start="\(<<-\s*\z(\S*\)\)\@="		matchgroup=shRedir end="^\s*\z1$"	contains=@shHereList	keepend
252  syn region shHereDoc fold start="\(<<-\s*\"\z(\S*\)\"\)\@="		matchgroup=shRedir end="^\s*\z1$""	contains=@shHereListDQ	keepend
253  syn region shHereDoc fold start="\(<<-\s*'\z(\S*\)'\)\@="		matchgroup=shRedir end="^\s*\z1$""	contains=@shHereList	keepend
254  syn region shHereDoc fold start="\(<<-\s*\\\_$\_s*'\z(\S*\)'\)\@="	matchgroup=shRedir end="^\s*\z1$"	contains=@shHereList	keepend
255  syn region shHereDoc fold start="\(<<-\s*\\\_$\_s*\z(\S*\)\)\@="	matchgroup=shRedir end="^\s*\z1$"	contains=@shHereList	keepend
256  syn region shHereDoc fold start="\(<<-\s*\\\_$\_s*\"\z(\S*\)\"\)\@="	matchgroup=shRedir end="^\s*\z1$"	contains=@shHereList	keepend
257 else
258  syn region shHereDoc matchgroup=shRedir fold start="<<\s*\z(\S*\)"		matchgroup=shRedir end="^\z1$"		contains=@shDblQuoteList
259  syn region shHereDoc matchgroup=shRedir fold start="<<\s*\"\z(\S*\)\""		matchgroup=shRedir end="^\z1$"
260  syn region shHereDoc matchgroup=shRedir fold start="<<\s*'\z(\S*\)'"		matchgroup=shRedir end="^\z1$"
261  syn region shHereDoc matchgroup=shRedir fold start="<<-\s*\z(\S*\)"		matchgroup=shRedir end="^\s*\z1$"	contains=@shDblQuoteList
262  syn region shHereDoc matchgroup=shRedir fold start="<<-\s*\"\z(\S*\)\""		matchgroup=shRedir end="^\s*\z1$"
263  syn region shHereDoc matchgroup=shRedir fold start="<<-\s*'\z(\S*\)'"		matchgroup=shRedir end="^\s*\z1$"
264  syn region shHereDoc matchgroup=shRedir fold start="<<\s*\\\_$\_s*\z(\S*\)"		matchgroup=shRedir end="^\z1$"
265  syn region shHereDoc matchgroup=shRedir fold start="<<\s*\\\_$\_s*\"\z(\S*\)\""	matchgroup=shRedir end="^\z1$"
266  syn region shHereDoc matchgroup=shRedir fold start="<<-\s*\\\_$\_s*'\z(\S*\)'"	matchgroup=shRedir end="^\s*\z1$"
267  syn region shHereDoc matchgroup=shRedir fold start="<<-\s*\\\_$\_s*\z(\S*\)"		matchgroup=shRedir end="^\s*\z1$"
268  syn region shHereDoc matchgroup=shRedir fold start="<<-\s*\\\_$\_s*\"\z(\S*\)\""	matchgroup=shRedir end="^\s*\z1$"
269  syn region shHereDoc matchgroup=shRedir fold start="<<\s*\\\_$\_s*'\z(\S*\)'"		matchgroup=shRedir end="^\z1$"
270 endif
271else
272 if v:version > 602 || (v:version == 602 && has("patch219"))
273  syn region shHereDoc start="\(<<\s*\\\=\z(\S*\)\)\@="		matchgroup=shRedir end="^\z1$"		contains=@shHereList	keepend
274  syn region shHereDoc start="\(<<\s*\"\z(\S*\)\"\)\@="		matchgroup=shRedir end="^\z1$""		contains=@shHereListDQ	keepend
275  syn region shHereDoc start="\(<<\s*'\z(\S*\)'\)\@="		matchgroup=shRedir end="^\z1$""		contains=@shHereList	keepend
276  syn region shHereDoc start="\(<<\s*\\\_$\_s*\z(\S*\)\)\@="		matchgroup=shRedir end="^\z1$""		contains=@shHereList	keepend
277  syn region shHereDoc start="\(<<\s*\\\_$\_s*\"\z(\S*\)\"\)\@="	matchgroup=shRedir end="^\z1$""		contains=@shHereList	keepend
278  syn region shHereDoc start="\(<<\s*\\\_$\_s*'\z(\S*\)'\)\@="		matchgroup=shRedir end="^\z1$"		contains=@shHereList	keepend
279  syn region shHereDoc start="\(<<-\s*\z(\S*\)\)\@="		matchgroup=shRedir end="^\s*\z1$"	contains=@shHereList	keepend
280  syn region shHereDoc start="\(<<-\s*\"\z(\S*\)\"\)\@="		matchgroup=shRedir end="^\s*\z1$""	contains=@shHereListDQ	keepend
281  syn region shHereDoc start="\(<<-\s*'\z(\S*\)'\)\@="		matchgroup=shRedir end="^\s*\z1$""	contains=@shHereList	keepend
282  syn region shHereDoc start="\(<<-\s*\\\_$\_s*'\z(\S*\)'\)\@="		matchgroup=shRedir end="^\s*\z1$"	contains=@shHereList	keepend
283  syn region shHereDoc start="\(<<-\s*\\\_$\_s*\z(\S*\)\)\@="		matchgroup=shRedir end="^\s*\z1$"	contains=@shHereList	keepend
284  syn region shHereDoc start="\(<<-\s*\\\_$\_s*\"\z(\S*\)\"\)\@="	matchgroup=shRedir end="^\s*\z1$"	contains=@shHereList	keepend
285 else
286  syn region shHereDoc matchgroup=shRedir start="<<\s*\\\=\z(\S*\)"	matchgroup=shRedir end="^\z1$"    contains=@shDblQuoteList
287  syn region shHereDoc matchgroup=shRedir start="<<\s*\"\z(\S*\)\""	matchgroup=shRedir end="^\z1$"
288  syn region shHereDoc matchgroup=shRedir start="<<-\s*\z(\S*\)"	matchgroup=shRedir end="^\s*\z1$" contains=@shDblQuoteList
289  syn region shHereDoc matchgroup=shRedir start="<<-\s*'\z(\S*\)'"	matchgroup=shRedir end="^\s*\z1$"
290  syn region shHereDoc matchgroup=shRedir start="<<\s*'\z(\S*\)'"	matchgroup=shRedir end="^\z1$"
291  syn region shHereDoc matchgroup=shRedir start="<<-\s*\"\z(\S*\)\""	matchgroup=shRedir end="^\s*\z1$"
292  syn region shHereDoc matchgroup=shRedir start="<<\s*\\\_$\_s*\z(\S*\)"	matchgroup=shRedir end="^\z1$"
293  syn region shHereDoc matchgroup=shRedir start="<<-\s*\\\_$\_s*\z(\S*\)"	matchgroup=shRedir end="^\s*\z1$"
294  syn region shHereDoc matchgroup=shRedir start="<<-\s*\\\_$\_s*'\z(\S*\)'"	matchgroup=shRedir end="^\s*\z1$"
295  syn region shHereDoc matchgroup=shRedir start="<<\s*\\\_$\_s*'\z(\S*\)'"	matchgroup=shRedir end="^\z1$"
296  syn region shHereDoc matchgroup=shRedir start="<<\s*\\\_$\_s*\"\z(\S*\)\""	matchgroup=shRedir end="^\z1$"
297  syn region shHereDoc matchgroup=shRedir start="<<-\s*\\\_$\_s*\"\z(\S*\)\""	matchgroup=shRedir end="^\s*\z1$"
298 endif
299 if v:version > 602 || (v:version == 602 && has("patch219"))
300  syn match  shHerePayload "^.*$"	contained	skipnl	nextgroup=shHerePayload	contains=@shDblQuoteList
301  syn match  shBeginLine ".*$"		contained	skipnl	nextgroup=shHerePayload	contains=@shCommandSubList
302  syn match  shBeginHere "<<-\=\s*\S\+"	contained		nextgroup=shBeginLine
303 endif
304endif
305
306" Here Strings: {{{1
307" =============
308if exists("b:is_bash")
309 syn match shRedir "<<<"
310endif
311
312" Identifiers: {{{1
313"=============
314syn match  shVariable "\<\([bwglsav]:\)\=[a-zA-Z0-9.!@_%+,]*\ze="	nextgroup=shSetIdentifier
315syn match  shIdWhiteSpace  contained	"\s"
316syn match  shSetIdentifier contained	"="	nextgroup=shPattern,shDeref,shDerefSimple,shDoubleQuote,shSingleQuote
317if exists("b:is_bash")
318  syn region shSetList matchgroup=shSet start="\<\(declare\|typeset\|local\|export\|unset\)\>[^/]"me=e-1 end="$" matchgroup=shOperator end="[;&]"me=e-1 matchgroup=NONE end="#\|="me=e-1 contains=@shIdList
319  syn region shSetList matchgroup=shSet start="\<set\>[^/]"me=e-1 end="$" end="[|)]"me=e-1		 matchgroup=shOperator end="[;&]"me=e-1 matchgroup=NONE end="[#=]"me=e-1 contains=@shIdList
320  syn match  shSet "\<\(declare\|typeset\|local\|export\|set\|unset\)$"
321elseif exists("b:is_kornshell")
322  syn region shSetList matchgroup=shSet start="\<\(typeset\|export\|unset\)\>[^/]"me=e-1 end="$"	matchgroup=shOperator end="[;&]"me=e-1 matchgroup=NONE end="[#=]"me=e-1 contains=@shIdList
323  syn region shSetList matchgroup=shSet start="\<set\>[^/]"me=e-1 end="$"			matchgroup=shOperator end="[;&]"me=e-1 matchgroup=NONE end="[#=]"me=e-1 contains=@shIdList
324  syn match  shSet "\<\(typeset\|set\|export\|unset\)$"
325else
326  syn region shSetList matchgroup=shSet start="\<\(set\|export\|unset\)\>[^/]"me=e-1 end="$" end="[)|]"me=e-1	matchgroup=shOperator end="[;&]" matchgroup=NONE end="[#=]"me=e-1 contains=@shIdList
327  syn match  shStatement "\<\(set\|export\|unset\)$"
328endif
329
330" Functions: {{{1
331" handles functions which start:  Function () {
332"   Apparently Bourne shell accepts functions too,
333"   even though it isn't documented by my man pages
334"   for it.
335syn cluster shCommandSubList  add=bkshFunction
336if g:sh_fold_enabled
337 syn region bkshFunctionBody	transparent fold	matchgroup=Delimiter start="^\s*\<\h\w*\>\s*()\s*{" end="}" contains=bkshFunction,bkshFunctionDelim,@shFunctionList
338else
339 syn region bkshFunctionBody	transparent matchgroup=Delimiter start="^\s*\<\h\w*\>\s*()\s*{" end="}" contains=bkshFunction,bkshFunctionDelim,@shFunctionList
340endif
341syn match bkshFunction	"^\s*\<\h\w*\>\s*()"	skipwhite skipnl contains=bkshFunctionParen
342syn match bkshFunctionParen	"[()]"	contained
343syn match bkshFunctionDelim	"[{}]"	contained
344
345" Parameter Dereferencing: {{{1
346" ========================
347syn match  shDerefSimple	"\$\w\+"
348syn region shDeref	matchgroup=PreProc start="\${" end="}"	contains=@shDerefList,shDerefVarArray
349syn match  shDerefWordError	"[^}$[]"	contained
350syn match  shDerefSimple	"\$[-#*@!?]"
351syn match  shDerefSimple	"\$\$"
352if exists("b:is_bash") || exists("b:is_kornshell")
353 syn region shDeref	matchgroup=PreProc start="\${##\=" end="}"	contains=@shDerefList
354endif
355
356" bash: ${!prefix*} and ${#parameter}: {{{1
357" ====================================
358if exists("b:is_bash")
359 syn region shDeref	matchgroup=PreProc start="\${!" end="\*\=}"	contains=@shDerefList,shDerefOp
360 syn match  shDerefVar	contained	"{\@<=!\w\+"		nextgroup=@shDerefVarList
361endif
362
363syn match  shDerefSpecial	contained	"{\@<=[-*@?0]"		nextgroup=shDerefOp,shDerefOpError
364syn match  shDerefSpecial	contained	"\({[#!]\)\@<=[[:alnum:]*@_]\+"	nextgroup=@shDerefVarList,shDerefOp
365syn match  shDerefVar	contained	"{\@<=\w\+"		nextgroup=@shDerefVarList
366
367" sh ksh bash : ${var[... ]...}  array reference: {{{1
368syn region  shDerefVarArray   contained	matchgroup=shDeref start="\[" end="]"	contains=@shCommandSubList nextgroup=shDerefOp,shDerefOpError
369
370" Special ${parameter OPERATOR word} handling: {{{1
371" sh ksh bash : ${parameter:-word}    word is default value
372" sh ksh bash : ${parameter:=word}    assign word as default value
373" sh ksh bash : ${parameter:?word}    display word if parameter is null
374" sh ksh bash : ${parameter:+word}    use word if parameter is not null, otherwise nothing
375"    ksh bash : ${parameter#pattern}  remove small left  pattern
376"    ksh bash : ${parameter##pattern} remove large left  pattern
377"    ksh bash : ${parameter%pattern}  remove small right pattern
378"    ksh bash : ${parameter%%pattern} remove large right pattern
379syn cluster shDerefPatternList	contains=shDerefPattern,shDerefString
380syn match shDerefOpError	contained	":[[:punct:]]"
381syn match  shDerefOp	contained	":\=[-=?]"	nextgroup=@shDerefPatternList
382syn match  shDerefOp	contained	":\=+"	nextgroup=@shDerefPatternList
383if exists("b:is_bash") || exists("b:is_kornshell")
384 syn match  shDerefOp	contained	"#\{1,2}"	nextgroup=@shDerefPatternList
385 syn match  shDerefOp	contained	"%\{1,2}"	nextgroup=@shDerefPatternList
386 syn match  shDerefPattern	contained	"[^{}]\+"	contains=shDeref,shDerefSimple,shDerefPattern,shDerefString,shCommandSub nextgroup=shDerefPattern
387 syn region shDerefPattern	contained	start="{" end="}"	contains=shDeref,shDerefSimple,shDerefString,shCommandSub nextgroup=shDerefPattern
388endif
389syn region shDerefString	contained	matchgroup=shOperator start=+'+ end=+'+		contains=shStringSpecial
390syn region shDerefString	contained	matchgroup=shOperator start=+"+ skip=+\\"+ end=+"+	contains=@shDblQuoteList,shStringSpecial
391syn match  shDerefString	contained	"\\["']"
392
393"	bash : ${parameter:offset}
394"	bash : ${parameter:offset:length}
395"	bash : ${parameter//pattern/string}
396"	bash : ${parameter//pattern}
397if exists("b:is_bash")
398 syn region shDerefOp	contained	start=":[$[:alnum:]_]"me=e-1 end=":"me=e-1 end="}"me=e-1 contains=@shCommandSubList nextgroup=shDerefPOL
399 syn match  shDerefPOL	contained	":[^}]\{1,}"	contains=@shCommandSubList
400 syn match  shDerefOp	contained	"/\{1,2}"	nextgroup=shDerefPat
401 syn match  shDerefPat	contained	"[^/}]\{1,}"	nextgroup=shDerefPatStringOp
402 syn match  shDerefPatStringOp	contained	"/"	nextgroup=shDerefPatString
403 syn match  shDerefPatString	contained	"[^}]\{1,}"
404endif
405
406" Useful sh Keywords: {{{1
407" ===================
408syn keyword shStatement break cd chdir continue eval exec exit kill newgrp pwd read readonly return shift test trap ulimit umask wait
409syn keyword shConditional contained elif else then
410syn keyword shCondError elif else then
411
412" Useful ksh Keywords: {{{1
413" ====================
414if exists("b:is_kornshell") || exists("b:is_bash")
415 syn keyword shFunction	function
416 syn keyword shStatement autoload bg false fc fg functions getopts hash history integer jobs let nohup print printf r stop suspend time times true type unalias whence
417
418" Useful bash Keywords: {{{1
419" =====================
420 if exists("b:is_bash")
421  syn keyword shStatement bind builtin dirs disown enable help local logout popd pushd shopt source
422 else
423  syn keyword shStatement login newgrp
424 endif
425endif
426
427" Synchronization: {{{1
428" ================
429if !exists("sh_minlines")
430  let sh_minlines = 200
431endif
432if !exists("sh_maxlines")
433  let sh_maxlines = 2 * sh_minlines
434endif
435exec "syn sync minlines=" . sh_minlines . " maxlines=" . sh_maxlines
436syn sync match shCaseEsacSync	grouphere	shCaseEsac	"\<case\>"
437syn sync match shCaseEsacSync	groupthere	shCaseEsac	"\<esac\>"
438syn sync match shDoSync	grouphere	shDo	"\<do\>"
439syn sync match shDoSync	groupthere	shDo	"\<done\>"
440syn sync match shForSync	grouphere	shFor	"\<for\>"
441syn sync match shForSync	groupthere	shFor	"\<in\>"
442syn sync match shIfSync	grouphere	shIf	"\<if\>"
443syn sync match shIfSync	groupthere	shIf	"\<fi\>"
444syn sync match shUntilSync	grouphere	shRepeat	"\<until\>"
445syn sync match shWhileSync	grouphere	shRepeat	"\<while\>"
446
447" Default Highlighting: {{{1
448" =====================
449hi def link shArithRegion	shShellVariables
450hi def link shCaseBar	shConditional
451hi def link shCaseIn	shConditional
452hi def link shCaseCommandSub	shCommandSub
453hi def link shCaseDoubleQuote	shDoubleQuote
454hi def link shCaseSingleQuote	shSingleQuote
455hi def link shCaseStart	shConditional
456hi def link shCmdSubRegion	shShellVariables
457hi def link shColon	shStatement
458
459hi def link shDeref	shShellVariables
460hi def link shDerefOp	shOperator
461
462hi def link shDerefVar	shDeref
463hi def link shDerefPOL	shDerefOp
464hi def link shDerefPatString	shDerefPattern
465hi def link shDerefPatStringOp	shDerefOp
466hi def link shDerefSimple	shDeref
467hi def link shDerefSpecial	shDeref
468hi def link shDerefString	shDoubleQuote
469hi def link shHerePayload	shHereDoc
470hi def link shBeginHere	shRedir
471
472hi def link shDoubleQuote	shString
473hi def link shEcho	shString
474hi def link shEmbeddedEcho	shString
475hi def link shHereDoc	shString
476hi def link shLoop	shStatement
477hi def link shOption	shCommandSub
478hi def link shPattern	shString
479hi def link shPosnParm	shShellVariables
480hi def link shRange	shOperator
481hi def link shRedir	shOperator
482hi def link shSingleQuote	shString
483hi def link shSource	shOperator
484hi def link shStringSpecial	shSpecial
485hi def link shSubShRegion	shOperator
486hi def link shTestOpr	shConditional
487hi def link shVariable	shSetList
488hi def link shWrapLineOperator	shOperator
489
490if exists("b:is_bash")
491  hi def link bashAdminStatement	shStatement
492  hi def link bashSpecialVariables	shShellVariables
493  hi def link bashStatement		shStatement
494  hi def link bkshFunction		Function
495  hi def link bkshFunctionParen		Delimiter
496  hi def link bkshFunctionDelim		Delimiter
497endif
498if exists("b:is_kornshell")
499  hi def link kshSpecialVariables	shShellVariables
500  hi def link kshStatement		shStatement
501  hi def link bkshFunction		Function
502  hi def link bkshFunctionParen		Delimiter
503endif
504
505hi def link shCaseError		Error
506hi def link shCondError		Error
507hi def link shCurlyError		Error
508hi def link shDerefError		Error
509hi def link shDerefOpError		Error
510hi def link shDerefWordError		Error
511hi def link shDoError		Error
512hi def link shEsacError		Error
513hi def link shIfError		Error
514hi def link shInError		Error
515hi def link shParenError		Error
516hi def link shTestError		Error
517if exists("b:is_kornshell")
518  hi def link shDTestError		Error
519endif
520
521hi def link shArithmetic		Special
522hi def link shCharClass		Identifier
523hi def link shSnglCase		Statement
524hi def link shCommandSub		Special
525hi def link shComment		Comment
526hi def link shConditional		Conditional
527hi def link shExprRegion		Delimiter
528hi def link shFunction		Function
529hi def link shFunctionName		Function
530hi def link shNumber		Number
531hi def link shOperator		Operator
532hi def link shRepeat		Repeat
533hi def link shSet		Statement
534hi def link shSetList		Identifier
535hi def link shShellVariables		PreProc
536hi def link shSpecial		Special
537hi def link shStatement		Statement
538hi def link shString		String
539hi def link shTodo		Todo
540hi def link shAlias		Identifier
541
542" Set Current Syntax: {{{1
543" ===================
544if exists("b:is_bash")
545 let b:current_syntax = "bash"
546elseif exists("b:is_kornshell")
547 let b:current_syntax = "ksh"
548else
549 let b:current_syntax = "sh"
550endif
551
552" vim: ts=16 fdm=marker
553