25 line_re = re.compile('.*?\n') |
25 line_re = re.compile('.*?\n') |
26 |
26 |
27 |
27 |
28 class BashLexer(RegexLexer): |
28 class BashLexer(RegexLexer): |
29 """ |
29 """ |
30 Lexer for (ba|k|)sh shell scripts. |
30 Lexer for (ba|k|z|)sh shell scripts. |
31 |
31 |
32 .. versionadded:: 0.6 |
32 .. versionadded:: 0.6 |
33 """ |
33 """ |
34 |
34 |
35 name = 'Bash' |
35 name = 'Bash' |
36 aliases = ['bash', 'sh', 'ksh', 'shell'] |
36 aliases = ['bash', 'sh', 'ksh', 'zsh', 'shell'] |
37 filenames = ['*.sh', '*.ksh', '*.bash', '*.ebuild', '*.eclass', |
37 filenames = ['*.sh', '*.ksh', '*.bash', '*.ebuild', '*.eclass', |
38 '*.exheres-0', '*.exlib', |
38 '*.exheres-0', '*.exlib', '*.zsh', |
39 '.bashrc', 'bashrc', '.bash_*', 'bash_*', 'PKGBUILD'] |
39 '.bashrc', 'bashrc', '.bash_*', 'bash_*', 'zshrc', '.zshrc', |
|
40 'PKGBUILD'] |
40 mimetypes = ['application/x-sh', 'application/x-shellscript'] |
41 mimetypes = ['application/x-sh', 'application/x-shellscript'] |
41 |
42 |
42 tokens = { |
43 tokens = { |
43 'root': [ |
44 'root': [ |
44 include('basic'), |
45 include('basic'), |
48 ], |
49 ], |
49 'interp': [ |
50 'interp': [ |
50 (r'\$\(\(', Keyword, 'math'), |
51 (r'\$\(\(', Keyword, 'math'), |
51 (r'\$\(', Keyword, 'paren'), |
52 (r'\$\(', Keyword, 'paren'), |
52 (r'\$\{#?', String.Interpol, 'curly'), |
53 (r'\$\{#?', String.Interpol, 'curly'), |
53 (r'\$[a-zA-Z_][a-zA-Z0-9_]*', Name.Variable), # user variable |
54 (r'\$[a-zA-Z_]\w*', Name.Variable), # user variable |
54 (r'\$(?:\d+|[#$?!_*@-])', Name.Variable), # builtin |
55 (r'\$(?:\d+|[#$?!_*@-])', Name.Variable), # builtin |
55 (r'\$', Text), |
56 (r'\$', Text), |
56 ], |
57 ], |
57 'basic': [ |
58 'basic': [ |
58 (r'\b(if|fi|else|while|do|done|for|then|return|function|case|' |
59 (r'\b(if|fi|else|while|do|done|for|then|return|function|case|' |
66 r'ulimit|umask|unalias|unset|wait)(?=[\s)`])', |
67 r'ulimit|umask|unalias|unset|wait)(?=[\s)`])', |
67 Name.Builtin), |
68 Name.Builtin), |
68 (r'\A#!.+\n', Comment.Hashbang), |
69 (r'\A#!.+\n', Comment.Hashbang), |
69 (r'#.*\n', Comment.Single), |
70 (r'#.*\n', Comment.Single), |
70 (r'\\[\w\W]', String.Escape), |
71 (r'\\[\w\W]', String.Escape), |
71 (r'(\b\w+)(\s*)(=)', bygroups(Name.Variable, Text, Operator)), |
72 (r'(\b\w+)(\s*)(\+?=)', bygroups(Name.Variable, Text, Operator)), |
72 (r'[\[\]{}()=]', Operator), |
73 (r'[\[\]{}()=]', Operator), |
73 (r'<<<', Operator), # here-string |
74 (r'<<<', Operator), # here-string |
74 (r'<<-?\s*(\'?)\\?(\w+)[\w\W]+?\2', String), |
75 (r'<<-?\s*(\'?)\\?(\w+)[\w\W]+?\2', String), |
75 (r'&&|\|\|', Operator), |
76 (r'&&|\|\|', Operator), |
76 ], |
77 ], |
135 innerlexer = self._innerLexerCls(**self.options) |
136 innerlexer = self._innerLexerCls(**self.options) |
136 |
137 |
137 pos = 0 |
138 pos = 0 |
138 curcode = '' |
139 curcode = '' |
139 insertions = [] |
140 insertions = [] |
|
141 backslash_continuation = False |
140 |
142 |
141 for match in line_re.finditer(text): |
143 for match in line_re.finditer(text): |
142 line = match.group() |
144 line = match.group() |
143 m = re.match(self._ps1rgx, line) |
145 m = re.match(self._ps1rgx, line) |
144 if m: |
146 if backslash_continuation: |
|
147 curcode += line |
|
148 backslash_continuation = curcode.endswith('\\\n') |
|
149 elif m: |
145 # To support output lexers (say diff output), the output |
150 # To support output lexers (say diff output), the output |
146 # needs to be broken by prompts whenever the output lexer |
151 # needs to be broken by prompts whenever the output lexer |
147 # changes. |
152 # changes. |
148 if not insertions: |
153 if not insertions: |
149 pos = match.start() |
154 pos = match.start() |
150 |
155 |
151 insertions.append((len(curcode), |
156 insertions.append((len(curcode), |
152 [(0, Generic.Prompt, m.group(1))])) |
157 [(0, Generic.Prompt, m.group(1))])) |
153 curcode += m.group(2) |
158 curcode += m.group(2) |
|
159 backslash_continuation = curcode.endswith('\\\n') |
154 elif line.startswith(self._ps2): |
160 elif line.startswith(self._ps2): |
155 insertions.append((len(curcode), |
161 insertions.append((len(curcode), |
156 [(0, Generic.Prompt, line[:len(self._ps2)])])) |
162 [(0, Generic.Prompt, line[:len(self._ps2)])])) |
157 curcode += line[len(self._ps2):] |
163 curcode += line[len(self._ps2):] |
|
164 backslash_continuation = curcode.endswith('\\\n') |
158 else: |
165 else: |
159 if insertions: |
166 if insertions: |
160 toks = innerlexer.get_tokens_unprocessed(curcode) |
167 toks = innerlexer.get_tokens_unprocessed(curcode) |
161 for i, t, v in do_insertions(insertions, toks): |
168 for i, t, v in do_insertions(insertions, toks): |
162 yield pos+i, t, v |
169 yield pos+i, t, v |
450 'for/f': [ |
457 'for/f': [ |
451 (r'(")((?:%s|[^"])*?")([%s%s]*)(\))' % (_variable, _nl, _ws), |
458 (r'(")((?:%s|[^"])*?")([%s%s]*)(\))' % (_variable, _nl, _ws), |
452 bygroups(String.Double, using(this, state='string'), Text, |
459 bygroups(String.Double, using(this, state='string'), Text, |
453 Punctuation)), |
460 Punctuation)), |
454 (r'"', String.Double, ('#pop', 'for2', 'string')), |
461 (r'"', String.Double, ('#pop', 'for2', 'string')), |
455 (r"('(?:%s|[\w\W])*?')([%s%s]*)(\))" % (_variable, _nl, _ws), |
462 (r"('(?:%%%%|%s|[\w\W])*?')([%s%s]*)(\))" % (_variable, _nl, _ws), |
456 bygroups(using(this, state='sqstring'), Text, Punctuation)), |
463 bygroups(using(this, state='sqstring'), Text, Punctuation)), |
457 (r'(`(?:%s|[\w\W])*?`)([%s%s]*)(\))' % (_variable, _nl, _ws), |
464 (r'(`(?:%%%%|%s|[\w\W])*?`)([%s%s]*)(\))' % (_variable, _nl, _ws), |
458 bygroups(using(this, state='bqstring'), Text, Punctuation)), |
465 bygroups(using(this, state='bqstring'), Text, Punctuation)), |
459 include('for2') |
466 include('for2') |
460 ], |
467 ], |
461 'for/l': [ |
468 'for/l': [ |
462 (r'-?\d+', Number.Integer), |
469 (r'-?\d+', Number.Integer), |
470 (r'(defined%s)(%s)(%s)' % (_token_terminator, _space, _stoken), |
477 (r'(defined%s)(%s)(%s)' % (_token_terminator, _space, _stoken), |
471 bygroups(Keyword, using(this, state='text'), |
478 bygroups(Keyword, using(this, state='text'), |
472 using(this, state='variable')), '#pop'), |
479 using(this, state='variable')), '#pop'), |
473 (r'(exist%s)(%s%s)' % (_token_terminator, _space, _stoken), |
480 (r'(exist%s)(%s%s)' % (_token_terminator, _space, _stoken), |
474 bygroups(Keyword, using(this, state='text')), '#pop'), |
481 bygroups(Keyword, using(this, state='text')), '#pop'), |
475 (r'(%s%s?)(==)(%s?%s)' % (_stoken, _space, _space, _stoken), |
|
476 bygroups(using(this, state='text'), Operator, |
|
477 using(this, state='text')), '#pop'), |
|
478 (r'(%s%s)(%s)(%s%s)' % (_number, _space, _opword, _space, _number), |
482 (r'(%s%s)(%s)(%s%s)' % (_number, _space, _opword, _space, _number), |
479 bygroups(using(this, state='arithmetic'), Operator.Word, |
483 bygroups(using(this, state='arithmetic'), Operator.Word, |
480 using(this, state='arithmetic')), '#pop'), |
484 using(this, state='arithmetic')), '#pop'), |
481 (r'(%s%s)(%s)(%s%s)' % (_stoken, _space, _opword, _space, _stoken), |
485 (_stoken, using(this, state='text'), ('#pop', 'if2')), |
|
486 ], |
|
487 'if2': [ |
|
488 (r'(%s?)(==)(%s?%s)' % (_space, _space, _stoken), |
|
489 bygroups(using(this, state='text'), Operator, |
|
490 using(this, state='text')), '#pop'), |
|
491 (r'(%s)(%s)(%s%s)' % (_space, _opword, _space, _stoken), |
482 bygroups(using(this, state='text'), Operator.Word, |
492 bygroups(using(this, state='text'), Operator.Word, |
483 using(this, state='text')), '#pop') |
493 using(this, state='text')), '#pop') |
484 ], |
494 ], |
485 '(?': [ |
495 '(?': [ |
486 (_space, using(this, state='text')), |
496 (_space, using(this, state='text')), |