shell.py 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920
  1. """
  2. pygments.lexers.shell
  3. ~~~~~~~~~~~~~~~~~~~~~
  4. Lexers for various shells.
  5. :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
  6. :license: BSD, see LICENSE for details.
  7. """
  8. import re
  9. from pygments.lexer import Lexer, RegexLexer, do_insertions, bygroups, \
  10. include, default, this, using, words, line_re
  11. from pygments.token import Punctuation, Whitespace, \
  12. Text, Comment, Operator, Keyword, Name, String, Number, Generic
  13. from pygments.util import shebang_matches
  14. __all__ = ['BashLexer', 'BashSessionLexer', 'TcshLexer', 'BatchLexer',
  15. 'SlurmBashLexer', 'MSDOSSessionLexer', 'PowerShellLexer',
  16. 'PowerShellSessionLexer', 'TcshSessionLexer', 'FishShellLexer',
  17. 'ExeclineLexer']
  18. class BashLexer(RegexLexer):
  19. """
  20. Lexer for (ba|k|z|)sh shell scripts.
  21. .. versionadded:: 0.6
  22. """
  23. name = 'Bash'
  24. aliases = ['bash', 'sh', 'ksh', 'zsh', 'shell']
  25. filenames = ['*.sh', '*.ksh', '*.bash', '*.ebuild', '*.eclass',
  26. '*.exheres-0', '*.exlib', '*.zsh',
  27. '.bashrc', 'bashrc', '.bash_*', 'bash_*', 'zshrc', '.zshrc',
  28. '.kshrc', 'kshrc',
  29. 'PKGBUILD']
  30. mimetypes = ['application/x-sh', 'application/x-shellscript', 'text/x-shellscript']
  31. tokens = {
  32. 'root': [
  33. include('basic'),
  34. (r'`', String.Backtick, 'backticks'),
  35. include('data'),
  36. include('interp'),
  37. ],
  38. 'interp': [
  39. (r'\$\(\(', Keyword, 'math'),
  40. (r'\$\(', Keyword, 'paren'),
  41. (r'\$\{#?', String.Interpol, 'curly'),
  42. (r'\$[a-zA-Z_]\w*', Name.Variable), # user variable
  43. (r'\$(?:\d+|[#$?!_*@-])', Name.Variable), # builtin
  44. (r'\$', Text),
  45. ],
  46. 'basic': [
  47. (r'\b(if|fi|else|while|in|do|done|for|then|return|function|case|'
  48. r'select|break|continue|until|esac|elif)(\s*)\b',
  49. bygroups(Keyword, Whitespace)),
  50. (r'\b(alias|bg|bind|builtin|caller|cd|command|compgen|'
  51. r'complete|declare|dirs|disown|echo|enable|eval|exec|exit|'
  52. r'export|false|fc|fg|getopts|hash|help|history|jobs|kill|let|'
  53. r'local|logout|popd|printf|pushd|pwd|read|readonly|set|shift|'
  54. r'shopt|source|suspend|test|time|times|trap|true|type|typeset|'
  55. r'ulimit|umask|unalias|unset|wait)(?=[\s)`])',
  56. Name.Builtin),
  57. (r'\A#!.+\n', Comment.Hashbang),
  58. (r'#.*\n', Comment.Single),
  59. (r'\\[\w\W]', String.Escape),
  60. (r'(\b\w+)(\s*)(\+?=)', bygroups(Name.Variable, Whitespace, Operator)),
  61. (r'[\[\]{}()=]', Operator),
  62. (r'<<<', Operator), # here-string
  63. (r'<<-?\s*(\'?)\\?(\w+)[\w\W]+?\2', String),
  64. (r'&&|\|\|', Operator),
  65. ],
  66. 'data': [
  67. (r'(?s)\$?"(\\.|[^"\\$])*"', String.Double),
  68. (r'"', String.Double, 'string'),
  69. (r"(?s)\$'(\\\\|\\[0-7]+|\\.|[^'\\])*'", String.Single),
  70. (r"(?s)'.*?'", String.Single),
  71. (r';', Punctuation),
  72. (r'&', Punctuation),
  73. (r'\|', Punctuation),
  74. (r'\s+', Whitespace),
  75. (r'\d+\b', Number),
  76. (r'[^=\s\[\]{}()$"\'`\\<&|;]+', Text),
  77. (r'<', Text),
  78. ],
  79. 'string': [
  80. (r'"', String.Double, '#pop'),
  81. (r'(?s)(\\\\|\\[0-7]+|\\.|[^"\\$])+', String.Double),
  82. include('interp'),
  83. ],
  84. 'curly': [
  85. (r'\}', String.Interpol, '#pop'),
  86. (r':-', Keyword),
  87. (r'\w+', Name.Variable),
  88. (r'[^}:"\'`$\\]+', Punctuation),
  89. (r':', Punctuation),
  90. include('root'),
  91. ],
  92. 'paren': [
  93. (r'\)', Keyword, '#pop'),
  94. include('root'),
  95. ],
  96. 'math': [
  97. (r'\)\)', Keyword, '#pop'),
  98. (r'\*\*|\|\||<<|>>|[-+*/%^|&<>]', Operator),
  99. (r'\d+#[\da-zA-Z]+', Number),
  100. (r'\d+#(?! )', Number),
  101. (r'0[xX][\da-fA-F]+', Number),
  102. (r'\d+', Number),
  103. (r'[a-zA-Z_]\w*', Name.Variable), # user variable
  104. include('root'),
  105. ],
  106. 'backticks': [
  107. (r'`', String.Backtick, '#pop'),
  108. include('root'),
  109. ],
  110. }
  111. def analyse_text(text):
  112. if shebang_matches(text, r'(ba|z|)sh'):
  113. return 1
  114. if text.startswith('$ '):
  115. return 0.2
  116. class SlurmBashLexer(BashLexer):
  117. """
  118. Lexer for (ba|k|z|)sh Slurm scripts.
  119. .. versionadded:: 2.4
  120. """
  121. name = 'Slurm'
  122. aliases = ['slurm', 'sbatch']
  123. filenames = ['*.sl']
  124. mimetypes = []
  125. EXTRA_KEYWORDS = {'srun'}
  126. def get_tokens_unprocessed(self, text):
  127. for index, token, value in BashLexer.get_tokens_unprocessed(self, text):
  128. if token is Text and value in self.EXTRA_KEYWORDS:
  129. yield index, Name.Builtin, value
  130. elif token is Comment.Single and 'SBATCH' in value:
  131. yield index, Keyword.Pseudo, value
  132. else:
  133. yield index, token, value
  134. class ShellSessionBaseLexer(Lexer):
  135. """
  136. Base lexer for shell sessions.
  137. .. versionadded:: 2.1
  138. """
  139. _bare_continuation = False
  140. _venv = re.compile(r'^(\([^)]*\))(\s*)')
  141. def get_tokens_unprocessed(self, text):
  142. innerlexer = self._innerLexerCls(**self.options)
  143. pos = 0
  144. curcode = ''
  145. insertions = []
  146. backslash_continuation = False
  147. for match in line_re.finditer(text):
  148. line = match.group()
  149. venv_match = self._venv.match(line)
  150. if venv_match:
  151. venv = venv_match.group(1)
  152. venv_whitespace = venv_match.group(2)
  153. insertions.append((len(curcode),
  154. [(0, Generic.Prompt.VirtualEnv, venv)]))
  155. if venv_whitespace:
  156. insertions.append((len(curcode),
  157. [(0, Text, venv_whitespace)]))
  158. line = line[venv_match.end():]
  159. m = self._ps1rgx.match(line)
  160. if m:
  161. # To support output lexers (say diff output), the output
  162. # needs to be broken by prompts whenever the output lexer
  163. # changes.
  164. if not insertions:
  165. pos = match.start()
  166. insertions.append((len(curcode),
  167. [(0, Generic.Prompt, m.group(1))]))
  168. curcode += m.group(2)
  169. backslash_continuation = curcode.endswith('\\\n')
  170. elif backslash_continuation:
  171. if line.startswith(self._ps2):
  172. insertions.append((len(curcode),
  173. [(0, Generic.Prompt,
  174. line[:len(self._ps2)])]))
  175. curcode += line[len(self._ps2):]
  176. else:
  177. curcode += line
  178. backslash_continuation = curcode.endswith('\\\n')
  179. elif self._bare_continuation and line.startswith(self._ps2):
  180. insertions.append((len(curcode),
  181. [(0, Generic.Prompt,
  182. line[:len(self._ps2)])]))
  183. curcode += line[len(self._ps2):]
  184. else:
  185. if insertions:
  186. toks = innerlexer.get_tokens_unprocessed(curcode)
  187. for i, t, v in do_insertions(insertions, toks):
  188. yield pos+i, t, v
  189. yield match.start(), Generic.Output, line
  190. insertions = []
  191. curcode = ''
  192. if insertions:
  193. for i, t, v in do_insertions(insertions,
  194. innerlexer.get_tokens_unprocessed(curcode)):
  195. yield pos+i, t, v
  196. class BashSessionLexer(ShellSessionBaseLexer):
  197. """
  198. Lexer for Bash shell sessions, i.e. command lines, including a
  199. prompt, interspersed with output.
  200. .. versionadded:: 1.1
  201. """
  202. name = 'Bash Session'
  203. aliases = ['console', 'shell-session']
  204. filenames = ['*.sh-session', '*.shell-session']
  205. mimetypes = ['application/x-shell-session', 'application/x-sh-session']
  206. _innerLexerCls = BashLexer
  207. _ps1rgx = re.compile(
  208. r'^((?:(?:\[.*?\])|(?:\(\S+\))?(?:| |sh\S*?|\w+\S+[@:]\S+(?:\s+\S+)' \
  209. r'?|\[\S+[@:][^\n]+\].+))\s*[$#%]\s*)(.*\n?)')
  210. _ps2 = '> '
  211. class BatchLexer(RegexLexer):
  212. """
  213. Lexer for the DOS/Windows Batch file format.
  214. .. versionadded:: 0.7
  215. """
  216. name = 'Batchfile'
  217. aliases = ['batch', 'bat', 'dosbatch', 'winbatch']
  218. filenames = ['*.bat', '*.cmd']
  219. mimetypes = ['application/x-dos-batch']
  220. flags = re.MULTILINE | re.IGNORECASE
  221. _nl = r'\n\x1a'
  222. _punct = r'&<>|'
  223. _ws = r'\t\v\f\r ,;=\xa0'
  224. _nlws = r'\s\x1a\xa0,;='
  225. _space = r'(?:(?:(?:\^[%s])?[%s])+)' % (_nl, _ws)
  226. _keyword_terminator = (r'(?=(?:\^[%s]?)?[%s+./:[\\\]]|[%s%s(])' %
  227. (_nl, _ws, _nl, _punct))
  228. _token_terminator = r'(?=\^?[%s]|[%s%s])' % (_ws, _punct, _nl)
  229. _start_label = r'((?:(?<=^[^:])|^[^:]?)[%s]*)(:)' % _ws
  230. _label = r'(?:(?:[^%s%s+:^]|\^[%s]?[\w\W])*)' % (_nlws, _punct, _nl)
  231. _label_compound = r'(?:(?:[^%s%s+:^)]|\^[%s]?[^)])*)' % (_nlws, _punct, _nl)
  232. _number = r'(?:-?(?:0[0-7]+|0x[\da-f]+|\d+)%s)' % _token_terminator
  233. _opword = r'(?:equ|geq|gtr|leq|lss|neq)'
  234. _string = r'(?:"[^%s"]*(?:"|(?=[%s])))' % (_nl, _nl)
  235. _variable = (r'(?:(?:%%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|'
  236. r'[^%%:%s]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%%%s^]|'
  237. r'\^[^%%%s])[^=%s]*=(?:[^%%%s^]|\^[^%%%s])*)?)?%%))|'
  238. r'(?:\^?![^!:%s]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:'
  239. r'[^!%s^]|\^[^!%s])[^=%s]*=(?:[^!%s^]|\^[^!%s])*)?)?\^?!))' %
  240. (_nl, _nl, _nl, _nl, _nl, _nl, _nl, _nl, _nl, _nl, _nl, _nl))
  241. _core_token = r'(?:(?:(?:\^[%s]?)?[^"%s%s])+)' % (_nl, _nlws, _punct)
  242. _core_token_compound = r'(?:(?:(?:\^[%s]?)?[^"%s%s)])+)' % (_nl, _nlws, _punct)
  243. _token = r'(?:[%s]+|%s)' % (_punct, _core_token)
  244. _token_compound = r'(?:[%s]+|%s)' % (_punct, _core_token_compound)
  245. _stoken = (r'(?:[%s]+|(?:%s|%s|%s)+)' %
  246. (_punct, _string, _variable, _core_token))
  247. def _make_begin_state(compound, _core_token=_core_token,
  248. _core_token_compound=_core_token_compound,
  249. _keyword_terminator=_keyword_terminator,
  250. _nl=_nl, _punct=_punct, _string=_string,
  251. _space=_space, _start_label=_start_label,
  252. _stoken=_stoken, _token_terminator=_token_terminator,
  253. _variable=_variable, _ws=_ws):
  254. rest = '(?:%s|%s|[^"%%%s%s%s])*' % (_string, _variable, _nl, _punct,
  255. ')' if compound else '')
  256. rest_of_line = r'(?:(?:[^%s^]|\^[%s]?[\w\W])*)' % (_nl, _nl)
  257. rest_of_line_compound = r'(?:(?:[^%s^)]|\^[%s]?[^)])*)' % (_nl, _nl)
  258. set_space = r'((?:(?:\^[%s]?)?[^\S\n])*)' % _nl
  259. suffix = ''
  260. if compound:
  261. _keyword_terminator = r'(?:(?=\))|%s)' % _keyword_terminator
  262. _token_terminator = r'(?:(?=\))|%s)' % _token_terminator
  263. suffix = '/compound'
  264. return [
  265. ((r'\)', Punctuation, '#pop') if compound else
  266. (r'\)((?=\()|%s)%s' % (_token_terminator, rest_of_line),
  267. Comment.Single)),
  268. (r'(?=%s)' % _start_label, Text, 'follow%s' % suffix),
  269. (_space, using(this, state='text')),
  270. include('redirect%s' % suffix),
  271. (r'[%s]+' % _nl, Text),
  272. (r'\(', Punctuation, 'root/compound'),
  273. (r'@+', Punctuation),
  274. (r'((?:for|if|rem)(?:(?=(?:\^[%s]?)?/)|(?:(?!\^)|'
  275. r'(?<=m))(?:(?=\()|%s)))(%s?%s?(?:\^[%s]?)?/(?:\^[%s]?)?\?)' %
  276. (_nl, _token_terminator, _space,
  277. _core_token_compound if compound else _core_token, _nl, _nl),
  278. bygroups(Keyword, using(this, state='text')),
  279. 'follow%s' % suffix),
  280. (r'(goto%s)(%s(?:\^[%s]?)?/(?:\^[%s]?)?\?%s)' %
  281. (_keyword_terminator, rest, _nl, _nl, rest),
  282. bygroups(Keyword, using(this, state='text')),
  283. 'follow%s' % suffix),
  284. (words(('assoc', 'break', 'cd', 'chdir', 'cls', 'color', 'copy',
  285. 'date', 'del', 'dir', 'dpath', 'echo', 'endlocal', 'erase',
  286. 'exit', 'ftype', 'keys', 'md', 'mkdir', 'mklink', 'move',
  287. 'path', 'pause', 'popd', 'prompt', 'pushd', 'rd', 'ren',
  288. 'rename', 'rmdir', 'setlocal', 'shift', 'start', 'time',
  289. 'title', 'type', 'ver', 'verify', 'vol'),
  290. suffix=_keyword_terminator), Keyword, 'follow%s' % suffix),
  291. (r'(call)(%s?)(:)' % _space,
  292. bygroups(Keyword, using(this, state='text'), Punctuation),
  293. 'call%s' % suffix),
  294. (r'call%s' % _keyword_terminator, Keyword),
  295. (r'(for%s(?!\^))(%s)(/f%s)' %
  296. (_token_terminator, _space, _token_terminator),
  297. bygroups(Keyword, using(this, state='text'), Keyword),
  298. ('for/f', 'for')),
  299. (r'(for%s(?!\^))(%s)(/l%s)' %
  300. (_token_terminator, _space, _token_terminator),
  301. bygroups(Keyword, using(this, state='text'), Keyword),
  302. ('for/l', 'for')),
  303. (r'for%s(?!\^)' % _token_terminator, Keyword, ('for2', 'for')),
  304. (r'(goto%s)(%s?)(:?)' % (_keyword_terminator, _space),
  305. bygroups(Keyword, using(this, state='text'), Punctuation),
  306. 'label%s' % suffix),
  307. (r'(if(?:(?=\()|%s)(?!\^))(%s?)((?:/i%s)?)(%s?)((?:not%s)?)(%s?)' %
  308. (_token_terminator, _space, _token_terminator, _space,
  309. _token_terminator, _space),
  310. bygroups(Keyword, using(this, state='text'), Keyword,
  311. using(this, state='text'), Keyword,
  312. using(this, state='text')), ('(?', 'if')),
  313. (r'rem(((?=\()|%s)%s?%s?.*|%s%s)' %
  314. (_token_terminator, _space, _stoken, _keyword_terminator,
  315. rest_of_line_compound if compound else rest_of_line),
  316. Comment.Single, 'follow%s' % suffix),
  317. (r'(set%s)%s(/a)' % (_keyword_terminator, set_space),
  318. bygroups(Keyword, using(this, state='text'), Keyword),
  319. 'arithmetic%s' % suffix),
  320. (r'(set%s)%s((?:/p)?)%s((?:(?:(?:\^[%s]?)?[^"%s%s^=%s]|'
  321. r'\^[%s]?[^"=])+)?)((?:(?:\^[%s]?)?=)?)' %
  322. (_keyword_terminator, set_space, set_space, _nl, _nl, _punct,
  323. ')' if compound else '', _nl, _nl),
  324. bygroups(Keyword, using(this, state='text'), Keyword,
  325. using(this, state='text'), using(this, state='variable'),
  326. Punctuation),
  327. 'follow%s' % suffix),
  328. default('follow%s' % suffix)
  329. ]
  330. def _make_follow_state(compound, _label=_label,
  331. _label_compound=_label_compound, _nl=_nl,
  332. _space=_space, _start_label=_start_label,
  333. _token=_token, _token_compound=_token_compound,
  334. _ws=_ws):
  335. suffix = '/compound' if compound else ''
  336. state = []
  337. if compound:
  338. state.append((r'(?=\))', Text, '#pop'))
  339. state += [
  340. (r'%s([%s]*)(%s)(.*)' %
  341. (_start_label, _ws, _label_compound if compound else _label),
  342. bygroups(Text, Punctuation, Text, Name.Label, Comment.Single)),
  343. include('redirect%s' % suffix),
  344. (r'(?=[%s])' % _nl, Text, '#pop'),
  345. (r'\|\|?|&&?', Punctuation, '#pop'),
  346. include('text')
  347. ]
  348. return state
  349. def _make_arithmetic_state(compound, _nl=_nl, _punct=_punct,
  350. _string=_string, _variable=_variable,
  351. _ws=_ws, _nlws=_nlws):
  352. op = r'=+\-*/!~'
  353. state = []
  354. if compound:
  355. state.append((r'(?=\))', Text, '#pop'))
  356. state += [
  357. (r'0[0-7]+', Number.Oct),
  358. (r'0x[\da-f]+', Number.Hex),
  359. (r'\d+', Number.Integer),
  360. (r'[(),]+', Punctuation),
  361. (r'([%s]|%%|\^\^)+' % op, Operator),
  362. (r'(%s|%s|(\^[%s]?)?[^()%s%%\^"%s%s]|\^[%s]?%s)+' %
  363. (_string, _variable, _nl, op, _nlws, _punct, _nlws,
  364. r'[^)]' if compound else r'[\w\W]'),
  365. using(this, state='variable')),
  366. (r'(?=[\x00|&])', Text, '#pop'),
  367. include('follow')
  368. ]
  369. return state
  370. def _make_call_state(compound, _label=_label,
  371. _label_compound=_label_compound):
  372. state = []
  373. if compound:
  374. state.append((r'(?=\))', Text, '#pop'))
  375. state.append((r'(:?)(%s)' % (_label_compound if compound else _label),
  376. bygroups(Punctuation, Name.Label), '#pop'))
  377. return state
  378. def _make_label_state(compound, _label=_label,
  379. _label_compound=_label_compound, _nl=_nl,
  380. _punct=_punct, _string=_string, _variable=_variable):
  381. state = []
  382. if compound:
  383. state.append((r'(?=\))', Text, '#pop'))
  384. state.append((r'(%s?)((?:%s|%s|\^[%s]?%s|[^"%%^%s%s%s])*)' %
  385. (_label_compound if compound else _label, _string,
  386. _variable, _nl, r'[^)]' if compound else r'[\w\W]', _nl,
  387. _punct, r')' if compound else ''),
  388. bygroups(Name.Label, Comment.Single), '#pop'))
  389. return state
  390. def _make_redirect_state(compound,
  391. _core_token_compound=_core_token_compound,
  392. _nl=_nl, _punct=_punct, _stoken=_stoken,
  393. _string=_string, _space=_space,
  394. _variable=_variable, _nlws=_nlws):
  395. stoken_compound = (r'(?:[%s]+|(?:%s|%s|%s)+)' %
  396. (_punct, _string, _variable, _core_token_compound))
  397. return [
  398. (r'((?:(?<=[%s])\d)?)(>>?&|<&)([%s]*)(\d)' %
  399. (_nlws, _nlws),
  400. bygroups(Number.Integer, Punctuation, Text, Number.Integer)),
  401. (r'((?:(?<=[%s])(?<!\^[%s])\d)?)(>>?|<)(%s?%s)' %
  402. (_nlws, _nl, _space, stoken_compound if compound else _stoken),
  403. bygroups(Number.Integer, Punctuation, using(this, state='text')))
  404. ]
  405. tokens = {
  406. 'root': _make_begin_state(False),
  407. 'follow': _make_follow_state(False),
  408. 'arithmetic': _make_arithmetic_state(False),
  409. 'call': _make_call_state(False),
  410. 'label': _make_label_state(False),
  411. 'redirect': _make_redirect_state(False),
  412. 'root/compound': _make_begin_state(True),
  413. 'follow/compound': _make_follow_state(True),
  414. 'arithmetic/compound': _make_arithmetic_state(True),
  415. 'call/compound': _make_call_state(True),
  416. 'label/compound': _make_label_state(True),
  417. 'redirect/compound': _make_redirect_state(True),
  418. 'variable-or-escape': [
  419. (_variable, Name.Variable),
  420. (r'%%%%|\^[%s]?(\^!|[\w\W])' % _nl, String.Escape)
  421. ],
  422. 'string': [
  423. (r'"', String.Double, '#pop'),
  424. (_variable, Name.Variable),
  425. (r'\^!|%%', String.Escape),
  426. (r'[^"%%^%s]+|[%%^]' % _nl, String.Double),
  427. default('#pop')
  428. ],
  429. 'sqstring': [
  430. include('variable-or-escape'),
  431. (r'[^%]+|%', String.Single)
  432. ],
  433. 'bqstring': [
  434. include('variable-or-escape'),
  435. (r'[^%]+|%', String.Backtick)
  436. ],
  437. 'text': [
  438. (r'"', String.Double, 'string'),
  439. include('variable-or-escape'),
  440. (r'[^"%%^%s%s\d)]+|.' % (_nlws, _punct), Text)
  441. ],
  442. 'variable': [
  443. (r'"', String.Double, 'string'),
  444. include('variable-or-escape'),
  445. (r'[^"%%^%s]+|.' % _nl, Name.Variable)
  446. ],
  447. 'for': [
  448. (r'(%s)(in)(%s)(\()' % (_space, _space),
  449. bygroups(using(this, state='text'), Keyword,
  450. using(this, state='text'), Punctuation), '#pop'),
  451. include('follow')
  452. ],
  453. 'for2': [
  454. (r'\)', Punctuation),
  455. (r'(%s)(do%s)' % (_space, _token_terminator),
  456. bygroups(using(this, state='text'), Keyword), '#pop'),
  457. (r'[%s]+' % _nl, Text),
  458. include('follow')
  459. ],
  460. 'for/f': [
  461. (r'(")((?:%s|[^"])*?")([%s]*)(\))' % (_variable, _nlws),
  462. bygroups(String.Double, using(this, state='string'), Text,
  463. Punctuation)),
  464. (r'"', String.Double, ('#pop', 'for2', 'string')),
  465. (r"('(?:%%%%|%s|[\w\W])*?')([%s]*)(\))" % (_variable, _nlws),
  466. bygroups(using(this, state='sqstring'), Text, Punctuation)),
  467. (r'(`(?:%%%%|%s|[\w\W])*?`)([%s]*)(\))' % (_variable, _nlws),
  468. bygroups(using(this, state='bqstring'), Text, Punctuation)),
  469. include('for2')
  470. ],
  471. 'for/l': [
  472. (r'-?\d+', Number.Integer),
  473. include('for2')
  474. ],
  475. 'if': [
  476. (r'((?:cmdextversion|errorlevel)%s)(%s)(\d+)' %
  477. (_token_terminator, _space),
  478. bygroups(Keyword, using(this, state='text'),
  479. Number.Integer), '#pop'),
  480. (r'(defined%s)(%s)(%s)' % (_token_terminator, _space, _stoken),
  481. bygroups(Keyword, using(this, state='text'),
  482. using(this, state='variable')), '#pop'),
  483. (r'(exist%s)(%s%s)' % (_token_terminator, _space, _stoken),
  484. bygroups(Keyword, using(this, state='text')), '#pop'),
  485. (r'(%s%s)(%s)(%s%s)' % (_number, _space, _opword, _space, _number),
  486. bygroups(using(this, state='arithmetic'), Operator.Word,
  487. using(this, state='arithmetic')), '#pop'),
  488. (_stoken, using(this, state='text'), ('#pop', 'if2')),
  489. ],
  490. 'if2': [
  491. (r'(%s?)(==)(%s?%s)' % (_space, _space, _stoken),
  492. bygroups(using(this, state='text'), Operator,
  493. using(this, state='text')), '#pop'),
  494. (r'(%s)(%s)(%s%s)' % (_space, _opword, _space, _stoken),
  495. bygroups(using(this, state='text'), Operator.Word,
  496. using(this, state='text')), '#pop')
  497. ],
  498. '(?': [
  499. (_space, using(this, state='text')),
  500. (r'\(', Punctuation, ('#pop', 'else?', 'root/compound')),
  501. default('#pop')
  502. ],
  503. 'else?': [
  504. (_space, using(this, state='text')),
  505. (r'else%s' % _token_terminator, Keyword, '#pop'),
  506. default('#pop')
  507. ]
  508. }
  509. class MSDOSSessionLexer(ShellSessionBaseLexer):
  510. """
  511. Lexer for MS DOS shell sessions, i.e. command lines, including a
  512. prompt, interspersed with output.
  513. .. versionadded:: 2.1
  514. """
  515. name = 'MSDOS Session'
  516. aliases = ['doscon']
  517. filenames = []
  518. mimetypes = []
  519. _innerLexerCls = BatchLexer
  520. _ps1rgx = re.compile(r'^([^>]*>)(.*\n?)')
  521. _ps2 = 'More? '
  522. class TcshLexer(RegexLexer):
  523. """
  524. Lexer for tcsh scripts.
  525. .. versionadded:: 0.10
  526. """
  527. name = 'Tcsh'
  528. aliases = ['tcsh', 'csh']
  529. filenames = ['*.tcsh', '*.csh']
  530. mimetypes = ['application/x-csh']
  531. tokens = {
  532. 'root': [
  533. include('basic'),
  534. (r'\$\(', Keyword, 'paren'),
  535. (r'\$\{#?', Keyword, 'curly'),
  536. (r'`', String.Backtick, 'backticks'),
  537. include('data'),
  538. ],
  539. 'basic': [
  540. (r'\b(if|endif|else|while|then|foreach|case|default|'
  541. r'break|continue|goto|breaksw|end|switch|endsw)\s*\b',
  542. Keyword),
  543. (r'\b(alias|alloc|bg|bindkey|builtins|bye|caller|cd|chdir|'
  544. r'complete|dirs|echo|echotc|eval|exec|exit|fg|filetest|getxvers|'
  545. r'glob|getspath|hashstat|history|hup|inlib|jobs|kill|'
  546. r'limit|log|login|logout|ls-F|migrate|newgrp|nice|nohup|notify|'
  547. r'onintr|popd|printenv|pushd|rehash|repeat|rootnode|popd|pushd|'
  548. r'set|shift|sched|setenv|setpath|settc|setty|setxvers|shift|'
  549. r'source|stop|suspend|source|suspend|telltc|time|'
  550. r'umask|unalias|uncomplete|unhash|universe|unlimit|unset|unsetenv|'
  551. r'ver|wait|warp|watchlog|where|which)\s*\b',
  552. Name.Builtin),
  553. (r'#.*', Comment),
  554. (r'\\[\w\W]', String.Escape),
  555. (r'(\b\w+)(\s*)(=)', bygroups(Name.Variable, Text, Operator)),
  556. (r'[\[\]{}()=]+', Operator),
  557. (r'<<\s*(\'?)\\?(\w+)[\w\W]+?\2', String),
  558. (r';', Punctuation),
  559. ],
  560. 'data': [
  561. (r'(?s)"(\\\\|\\[0-7]+|\\.|[^"\\])*"', String.Double),
  562. (r"(?s)'(\\\\|\\[0-7]+|\\.|[^'\\])*'", String.Single),
  563. (r'\s+', Text),
  564. (r'[^=\s\[\]{}()$"\'`\\;#]+', Text),
  565. (r'\d+(?= |\Z)', Number),
  566. (r'\$#?(\w+|.)', Name.Variable),
  567. ],
  568. 'curly': [
  569. (r'\}', Keyword, '#pop'),
  570. (r':-', Keyword),
  571. (r'\w+', Name.Variable),
  572. (r'[^}:"\'`$]+', Punctuation),
  573. (r':', Punctuation),
  574. include('root'),
  575. ],
  576. 'paren': [
  577. (r'\)', Keyword, '#pop'),
  578. include('root'),
  579. ],
  580. 'backticks': [
  581. (r'`', String.Backtick, '#pop'),
  582. include('root'),
  583. ],
  584. }
  585. class TcshSessionLexer(ShellSessionBaseLexer):
  586. """
  587. Lexer for Tcsh sessions, i.e. command lines, including a
  588. prompt, interspersed with output.
  589. .. versionadded:: 2.1
  590. """
  591. name = 'Tcsh Session'
  592. aliases = ['tcshcon']
  593. filenames = []
  594. mimetypes = []
  595. _innerLexerCls = TcshLexer
  596. _ps1rgx = re.compile(r'^([^>]+>)(.*\n?)')
  597. _ps2 = '? '
  598. class PowerShellLexer(RegexLexer):
  599. """
  600. For Windows PowerShell code.
  601. .. versionadded:: 1.5
  602. """
  603. name = 'PowerShell'
  604. aliases = ['powershell', 'pwsh', 'posh', 'ps1', 'psm1']
  605. filenames = ['*.ps1', '*.psm1']
  606. mimetypes = ['text/x-powershell']
  607. flags = re.DOTALL | re.IGNORECASE | re.MULTILINE
  608. keywords = (
  609. 'while validateset validaterange validatepattern validatelength '
  610. 'validatecount until trap switch return ref process param parameter in '
  611. 'if global: local: function foreach for finally filter end elseif else '
  612. 'dynamicparam do default continue cmdletbinding break begin alias \\? '
  613. '% #script #private #local #global mandatory parametersetname position '
  614. 'valuefrompipeline valuefrompipelinebypropertyname '
  615. 'valuefromremainingarguments helpmessage try catch throw').split()
  616. operators = (
  617. 'and as band bnot bor bxor casesensitive ccontains ceq cge cgt cle '
  618. 'clike clt cmatch cne cnotcontains cnotlike cnotmatch contains '
  619. 'creplace eq exact f file ge gt icontains ieq ige igt ile ilike ilt '
  620. 'imatch ine inotcontains inotlike inotmatch ireplace is isnot le like '
  621. 'lt match ne not notcontains notlike notmatch or regex replace '
  622. 'wildcard').split()
  623. verbs = (
  624. 'write where watch wait use update unregister unpublish unprotect '
  625. 'unlock uninstall undo unblock trace test tee take sync switch '
  626. 'suspend submit stop step start split sort skip show set send select '
  627. 'search scroll save revoke resume restore restart resolve resize '
  628. 'reset request repair rename remove register redo receive read push '
  629. 'publish protect pop ping out optimize open new move mount merge '
  630. 'measure lock limit join invoke install initialize import hide group '
  631. 'grant get format foreach find export expand exit enter enable edit '
  632. 'dismount disconnect disable deny debug cxnew copy convertto '
  633. 'convertfrom convert connect confirm compress complete compare close '
  634. 'clear checkpoint block backup assert approve aggregate add').split()
  635. aliases_ = (
  636. 'ac asnp cat cd cfs chdir clc clear clhy cli clp cls clv cnsn '
  637. 'compare copy cp cpi cpp curl cvpa dbp del diff dir dnsn ebp echo epal '
  638. 'epcsv epsn erase etsn exsn fc fhx fl foreach ft fw gal gbp gc gci gcm '
  639. 'gcs gdr ghy gi gjb gl gm gmo gp gps gpv group gsn gsnp gsv gu gv gwmi '
  640. 'h history icm iex ihy ii ipal ipcsv ipmo ipsn irm ise iwmi iwr kill lp '
  641. 'ls man md measure mi mount move mp mv nal ndr ni nmo npssc nsn nv ogv '
  642. 'oh popd ps pushd pwd r rbp rcjb rcsn rd rdr ren ri rjb rm rmdir rmo '
  643. 'rni rnp rp rsn rsnp rujb rv rvpa rwmi sajb sal saps sasv sbp sc select '
  644. 'set shcm si sl sleep sls sort sp spjb spps spsv start sujb sv swmi tee '
  645. 'trcm type wget where wjb write').split()
  646. commenthelp = (
  647. 'component description example externalhelp forwardhelpcategory '
  648. 'forwardhelptargetname functionality inputs link '
  649. 'notes outputs parameter remotehelprunspace role synopsis').split()
  650. tokens = {
  651. 'root': [
  652. # we need to count pairs of parentheses for correct highlight
  653. # of '$(...)' blocks in strings
  654. (r'\(', Punctuation, 'child'),
  655. (r'\s+', Text),
  656. (r'^(\s*#[#\s]*)(\.(?:%s))([^\n]*$)' % '|'.join(commenthelp),
  657. bygroups(Comment, String.Doc, Comment)),
  658. (r'#[^\n]*?$', Comment),
  659. (r'(&lt;|<)#', Comment.Multiline, 'multline'),
  660. (r'@"\n', String.Heredoc, 'heredoc-double'),
  661. (r"@'\n.*?\n'@", String.Heredoc),
  662. # escaped syntax
  663. (r'`[\'"$@-]', Punctuation),
  664. (r'"', String.Double, 'string'),
  665. (r"'([^']|'')*'", String.Single),
  666. (r'(\$|@@|@)((global|script|private|env):)?\w+',
  667. Name.Variable),
  668. (r'(%s)\b' % '|'.join(keywords), Keyword),
  669. (r'-(%s)\b' % '|'.join(operators), Operator),
  670. (r'(%s)-[a-z_]\w*\b' % '|'.join(verbs), Name.Builtin),
  671. (r'(%s)\s' % '|'.join(aliases_), Name.Builtin),
  672. (r'\[[a-z_\[][\w. `,\[\]]*\]', Name.Constant), # .net [type]s
  673. (r'-[a-z_]\w*', Name),
  674. (r'\w+', Name),
  675. (r'[.,;:@{}\[\]$()=+*/\\&%!~?^`|<>-]', Punctuation),
  676. ],
  677. 'child': [
  678. (r'\)', Punctuation, '#pop'),
  679. include('root'),
  680. ],
  681. 'multline': [
  682. (r'[^#&.]+', Comment.Multiline),
  683. (r'#(>|&gt;)', Comment.Multiline, '#pop'),
  684. (r'\.(%s)' % '|'.join(commenthelp), String.Doc),
  685. (r'[#&.]', Comment.Multiline),
  686. ],
  687. 'string': [
  688. (r"`[0abfnrtv'\"$`]", String.Escape),
  689. (r'[^$`"]+', String.Double),
  690. (r'\$\(', Punctuation, 'child'),
  691. (r'""', String.Double),
  692. (r'[`$]', String.Double),
  693. (r'"', String.Double, '#pop'),
  694. ],
  695. 'heredoc-double': [
  696. (r'\n"@', String.Heredoc, '#pop'),
  697. (r'\$\(', Punctuation, 'child'),
  698. (r'[^@\n]+"]', String.Heredoc),
  699. (r".", String.Heredoc),
  700. ]
  701. }
  702. class PowerShellSessionLexer(ShellSessionBaseLexer):
  703. """
  704. Lexer for PowerShell sessions, i.e. command lines, including a
  705. prompt, interspersed with output.
  706. .. versionadded:: 2.1
  707. """
  708. name = 'PowerShell Session'
  709. aliases = ['pwsh-session', 'ps1con']
  710. filenames = []
  711. mimetypes = []
  712. _innerLexerCls = PowerShellLexer
  713. _bare_continuation = True
  714. _ps1rgx = re.compile(r'^((?:\[[^]]+\]: )?PS[^>]*> ?)(.*\n?)')
  715. _ps2 = '> '
  716. class FishShellLexer(RegexLexer):
  717. """
  718. Lexer for Fish shell scripts.
  719. .. versionadded:: 2.1
  720. """
  721. name = 'Fish'
  722. aliases = ['fish', 'fishshell']
  723. filenames = ['*.fish', '*.load']
  724. mimetypes = ['application/x-fish']
  725. tokens = {
  726. 'root': [
  727. include('basic'),
  728. include('data'),
  729. include('interp'),
  730. ],
  731. 'interp': [
  732. (r'\$\(\(', Keyword, 'math'),
  733. (r'\(', Keyword, 'paren'),
  734. (r'\$#?(\w+|.)', Name.Variable),
  735. ],
  736. 'basic': [
  737. (r'\b(begin|end|if|else|while|break|for|in|return|function|block|'
  738. r'case|continue|switch|not|and|or|set|echo|exit|pwd|true|false|'
  739. r'cd|count|test)(\s*)\b',
  740. bygroups(Keyword, Text)),
  741. (r'\b(alias|bg|bind|breakpoint|builtin|command|commandline|'
  742. r'complete|contains|dirh|dirs|emit|eval|exec|fg|fish|fish_config|'
  743. r'fish_indent|fish_pager|fish_prompt|fish_right_prompt|'
  744. r'fish_update_completions|fishd|funced|funcsave|functions|help|'
  745. r'history|isatty|jobs|math|mimedb|nextd|open|popd|prevd|psub|'
  746. r'pushd|random|read|set_color|source|status|trap|type|ulimit|'
  747. r'umask|vared|fc|getopts|hash|kill|printf|time|wait)\s*\b(?!\.)',
  748. Name.Builtin),
  749. (r'#.*\n', Comment),
  750. (r'\\[\w\W]', String.Escape),
  751. (r'(\b\w+)(\s*)(=)', bygroups(Name.Variable, Whitespace, Operator)),
  752. (r'[\[\]()=]', Operator),
  753. (r'<<-?\s*(\'?)\\?(\w+)[\w\W]+?\2', String),
  754. ],
  755. 'data': [
  756. (r'(?s)\$?"(\\\\|\\[0-7]+|\\.|[^"\\$])*"', String.Double),
  757. (r'"', String.Double, 'string'),
  758. (r"(?s)\$'(\\\\|\\[0-7]+|\\.|[^'\\])*'", String.Single),
  759. (r"(?s)'.*?'", String.Single),
  760. (r';', Punctuation),
  761. (r'&|\||\^|<|>', Operator),
  762. (r'\s+', Text),
  763. (r'\d+(?= |\Z)', Number),
  764. (r'[^=\s\[\]{}()$"\'`\\<&|;]+', Text),
  765. ],
  766. 'string': [
  767. (r'"', String.Double, '#pop'),
  768. (r'(?s)(\\\\|\\[0-7]+|\\.|[^"\\$])+', String.Double),
  769. include('interp'),
  770. ],
  771. 'paren': [
  772. (r'\)', Keyword, '#pop'),
  773. include('root'),
  774. ],
  775. 'math': [
  776. (r'\)\)', Keyword, '#pop'),
  777. (r'[-+*/%^|&]|\*\*|\|\|', Operator),
  778. (r'\d+#\d+', Number),
  779. (r'\d+#(?! )', Number),
  780. (r'\d+', Number),
  781. include('root'),
  782. ],
  783. }
  784. class ExeclineLexer(RegexLexer):
  785. """
  786. Lexer for Laurent Bercot's execline language
  787. (https://skarnet.org/software/execline).
  788. .. versionadded:: 2.7
  789. """
  790. name = 'execline'
  791. aliases = ['execline']
  792. filenames = ['*.exec']
  793. tokens = {
  794. 'root': [
  795. include('basic'),
  796. include('data'),
  797. include('interp')
  798. ],
  799. 'interp': [
  800. (r'\$\{', String.Interpol, 'curly'),
  801. (r'\$[\w@#]+', Name.Variable), # user variable
  802. (r'\$', Text),
  803. ],
  804. 'basic': [
  805. (r'\b(background|backtick|cd|define|dollarat|elgetopt|'
  806. r'elgetpositionals|elglob|emptyenv|envfile|exec|execlineb|'
  807. r'exit|export|fdblock|fdclose|fdmove|fdreserve|fdswap|'
  808. r'forbacktickx|foreground|forstdin|forx|getcwd|getpid|heredoc|'
  809. r'homeof|if|ifelse|ifte|ifthenelse|importas|loopwhilex|'
  810. r'multidefine|multisubstitute|pipeline|piperw|posix-cd|'
  811. r'redirfd|runblock|shift|trap|tryexec|umask|unexport|wait|'
  812. r'withstdinas)\b', Name.Builtin),
  813. (r'\A#!.+\n', Comment.Hashbang),
  814. (r'#.*\n', Comment.Single),
  815. (r'[{}]', Operator)
  816. ],
  817. 'data': [
  818. (r'(?s)"(\\.|[^"\\$])*"', String.Double),
  819. (r'"', String.Double, 'string'),
  820. (r'\s+', Text),
  821. (r'[^\s{}$"\\]+', Text)
  822. ],
  823. 'string': [
  824. (r'"', String.Double, '#pop'),
  825. (r'(?s)(\\\\|\\.|[^"\\$])+', String.Double),
  826. include('interp'),
  827. ],
  828. 'curly': [
  829. (r'\}', String.Interpol, '#pop'),
  830. (r'[\w#@]+', Name.Variable),
  831. include('root')
  832. ]
  833. }
  834. def analyse_text(text):
  835. if shebang_matches(text, r'execlineb'):
  836. return 1