424 lines
15 KiB
Python
424 lines
15 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
pygments.lexers.shell
|
|
~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Lexers for various shells.
|
|
|
|
:copyright: Copyright 2006-2013 by the Pygments team, see AUTHORS.
|
|
:license: BSD, see LICENSE for details.
|
|
"""
|
|
|
|
import re
|
|
|
|
from pygments.lexer import Lexer, RegexLexer, do_insertions, bygroups, include
|
|
from pygments.token import Punctuation, \
|
|
Text, Comment, Operator, Keyword, Name, String, Number, Generic
|
|
from pygments.util import shebang_matches
|
|
|
|
|
|
__all__ = ['BashLexer', 'BashSessionLexer', 'TcshLexer', 'BatchLexer',
|
|
'PowerShellLexer', 'ShellSessionLexer']
|
|
|
|
line_re = re.compile('.*?\n')
|
|
|
|
|
|
class BashLexer(RegexLexer):
|
|
"""
|
|
Lexer for (ba|k|)sh shell scripts.
|
|
|
|
*New in Pygments 0.6.*
|
|
"""
|
|
|
|
name = 'Bash'
|
|
aliases = ['bash', 'sh', 'ksh']
|
|
filenames = ['*.sh', '*.ksh', '*.bash', '*.ebuild', '*.eclass',
|
|
'.bashrc', 'bashrc', '.bash_*', 'bash_*']
|
|
mimetypes = ['application/x-sh', 'application/x-shellscript']
|
|
|
|
tokens = {
|
|
'root': [
|
|
include('basic'),
|
|
(r'\$\(\(', Keyword, 'math'),
|
|
(r'\$\(', Keyword, 'paren'),
|
|
(r'\${#?', Keyword, 'curly'),
|
|
(r'`', String.Backtick, 'backticks'),
|
|
include('data'),
|
|
],
|
|
'basic': [
|
|
(r'\b(if|fi|else|while|do|done|for|then|return|function|case|'
|
|
r'select|continue|until|esac|elif)\s*\b',
|
|
Keyword),
|
|
(r'\b(alias|bg|bind|break|builtin|caller|cd|command|compgen|'
|
|
r'complete|declare|dirs|disown|echo|enable|eval|exec|exit|'
|
|
r'export|false|fc|fg|getopts|hash|help|history|jobs|kill|let|'
|
|
r'local|logout|popd|printf|pushd|pwd|read|readonly|set|shift|'
|
|
r'shopt|source|suspend|test|time|times|trap|true|type|typeset|'
|
|
r'ulimit|umask|unalias|unset|wait)\s*\b(?!\.)',
|
|
Name.Builtin),
|
|
(r'#.*\n', Comment),
|
|
(r'\\[\w\W]', String.Escape),
|
|
(r'(\b\w+)(\s*)(=)', bygroups(Name.Variable, Text, Operator)),
|
|
(r'[\[\]{}()=]', Operator),
|
|
(r'<<<', Operator), # here-string
|
|
(r'<<-?\s*(\'?)\\?(\w+)[\w\W]+?\2', String),
|
|
(r'&&|\|\|', Operator),
|
|
],
|
|
'data': [
|
|
(r'(?s)\$?"(\\\\|\\[0-7]+|\\.|[^"\\])*"', String.Double),
|
|
(r"(?s)\$?'(\\\\|\\[0-7]+|\\.|[^'\\])*'", String.Single),
|
|
(r';', Punctuation),
|
|
(r'&', Punctuation),
|
|
(r'\|', Punctuation),
|
|
(r'\s+', Text),
|
|
(r'[^=\s\[\]{}()$"\'`\\<&|;]+', Text),
|
|
(r'\d+(?= |\Z)', Number),
|
|
(r'\$#?(\w+|.)', Name.Variable),
|
|
(r'<', Text),
|
|
],
|
|
'curly': [
|
|
(r'}', Keyword, '#pop'),
|
|
(r':-', Keyword),
|
|
(r'[a-zA-Z0-9_]+', Name.Variable),
|
|
(r'[^}:"\'`$]+', Punctuation),
|
|
(r':', Punctuation),
|
|
include('root'),
|
|
],
|
|
'paren': [
|
|
(r'\)', Keyword, '#pop'),
|
|
include('root'),
|
|
],
|
|
'math': [
|
|
(r'\)\)', Keyword, '#pop'),
|
|
(r'[-+*/%^|&]|\*\*|\|\|', Operator),
|
|
(r'\d+', Number),
|
|
include('root'),
|
|
],
|
|
'backticks': [
|
|
(r'`', String.Backtick, '#pop'),
|
|
include('root'),
|
|
],
|
|
}
|
|
|
|
def analyse_text(text):
|
|
if shebang_matches(text, r'(ba|z|)sh'):
|
|
return 1
|
|
if text.startswith('$ '):
|
|
return 0.2
|
|
|
|
|
|
class BashSessionLexer(Lexer):
|
|
"""
|
|
Lexer for simplistic shell sessions.
|
|
|
|
*New in Pygments 1.1.*
|
|
"""
|
|
|
|
name = 'Bash Session'
|
|
aliases = ['console']
|
|
filenames = ['*.sh-session']
|
|
mimetypes = ['application/x-shell-session']
|
|
|
|
def get_tokens_unprocessed(self, text):
|
|
bashlexer = BashLexer(**self.options)
|
|
|
|
pos = 0
|
|
curcode = ''
|
|
insertions = []
|
|
|
|
for match in line_re.finditer(text):
|
|
line = match.group()
|
|
m = re.match(r'^((?:\(\S+\))?(?:|sh\S*?|\w+\S+[@:]\S+(?:\s+\S+)'
|
|
r'?|\[\S+[@:][^\n]+\].+)[$#%])(.*\n?)' , line)
|
|
if m:
|
|
# To support output lexers (say diff output), the output
|
|
# needs to be broken by prompts whenever the output lexer
|
|
# changes.
|
|
if not insertions:
|
|
pos = match.start()
|
|
|
|
insertions.append((len(curcode),
|
|
[(0, Generic.Prompt, m.group(1))]))
|
|
curcode += m.group(2)
|
|
elif line.startswith('>'):
|
|
insertions.append((len(curcode),
|
|
[(0, Generic.Prompt, line[:1])]))
|
|
curcode += line[1:]
|
|
else:
|
|
if insertions:
|
|
toks = bashlexer.get_tokens_unprocessed(curcode)
|
|
for i, t, v in do_insertions(insertions, toks):
|
|
yield pos+i, t, v
|
|
yield match.start(), Generic.Output, line
|
|
insertions = []
|
|
curcode = ''
|
|
if insertions:
|
|
for i, t, v in do_insertions(insertions,
|
|
bashlexer.get_tokens_unprocessed(curcode)):
|
|
yield pos+i, t, v
|
|
|
|
|
|
class ShellSessionLexer(Lexer):
|
|
"""
|
|
Lexer for shell sessions that works with different command prompts
|
|
|
|
*New in Pygments 1.6.*
|
|
"""
|
|
|
|
name = 'Shell Session'
|
|
aliases = ['shell-session']
|
|
filenames = ['*.shell-session']
|
|
mimetypes = ['application/x-sh-session']
|
|
|
|
def get_tokens_unprocessed(self, text):
|
|
bashlexer = BashLexer(**self.options)
|
|
|
|
pos = 0
|
|
curcode = ''
|
|
insertions = []
|
|
|
|
for match in line_re.finditer(text):
|
|
line = match.group()
|
|
m = re.match(r'^((?:\[?\S+@[^$#%]+)[$#%])(.*\n?)', line)
|
|
if m:
|
|
# To support output lexers (say diff output), the output
|
|
# needs to be broken by prompts whenever the output lexer
|
|
# changes.
|
|
if not insertions:
|
|
pos = match.start()
|
|
|
|
insertions.append((len(curcode),
|
|
[(0, Generic.Prompt, m.group(1))]))
|
|
curcode += m.group(2)
|
|
else:
|
|
if insertions:
|
|
toks = bashlexer.get_tokens_unprocessed(curcode)
|
|
for i, t, v in do_insertions(insertions, toks):
|
|
yield pos+i, t, v
|
|
yield match.start(), Generic.Output, line
|
|
insertions = []
|
|
curcode = ''
|
|
if insertions:
|
|
for i, t, v in do_insertions(insertions,
|
|
bashlexer.get_tokens_unprocessed(curcode)):
|
|
yield pos+i, t, v
|
|
|
|
|
|
class BatchLexer(RegexLexer):
|
|
"""
|
|
Lexer for the DOS/Windows Batch file format.
|
|
|
|
*New in Pygments 0.7.*
|
|
"""
|
|
name = 'Batchfile'
|
|
aliases = ['bat', 'dosbatch', 'winbatch']
|
|
filenames = ['*.bat', '*.cmd']
|
|
mimetypes = ['application/x-dos-batch']
|
|
|
|
flags = re.MULTILINE | re.IGNORECASE
|
|
|
|
tokens = {
|
|
'root': [
|
|
# Lines can start with @ to prevent echo
|
|
(r'^\s*@', Punctuation),
|
|
(r'^(\s*)(rem\s.*)$', bygroups(Text, Comment)),
|
|
(r'".*?"', String.Double),
|
|
(r"'.*?'", String.Single),
|
|
# If made more specific, make sure you still allow expansions
|
|
# like %~$VAR:zlt
|
|
(r'%%?[~$:\w]+%?', Name.Variable),
|
|
(r'::.*', Comment), # Technically :: only works at BOL
|
|
(r'(set)(\s+)(\w+)', bygroups(Keyword, Text, Name.Variable)),
|
|
(r'(call)(\s+)(:\w+)', bygroups(Keyword, Text, Name.Label)),
|
|
(r'(goto)(\s+)(\w+)', bygroups(Keyword, Text, Name.Label)),
|
|
(r'\b(set|call|echo|on|off|endlocal|for|do|goto|if|pause|'
|
|
r'setlocal|shift|errorlevel|exist|defined|cmdextversion|'
|
|
r'errorlevel|else|cd|md|del|deltree|cls|choice)\b', Keyword),
|
|
(r'\b(equ|neq|lss|leq|gtr|geq)\b', Operator),
|
|
include('basic'),
|
|
(r'.', Text),
|
|
],
|
|
'echo': [
|
|
# Escapes only valid within echo args?
|
|
(r'\^\^|\^<|\^>|\^\|', String.Escape),
|
|
(r'\n', Text, '#pop'),
|
|
include('basic'),
|
|
(r'[^\'"^]+', Text),
|
|
],
|
|
'basic': [
|
|
(r'".*?"', String.Double),
|
|
(r"'.*?'", String.Single),
|
|
(r'`.*?`', String.Backtick),
|
|
(r'-?\d+', Number),
|
|
(r',', Punctuation),
|
|
(r'=', Operator),
|
|
(r'/\S+', Name),
|
|
(r':\w+', Name.Label),
|
|
(r'\w:\w+', Text),
|
|
(r'([<>|])(\s*)(\w+)', bygroups(Punctuation, Text, Name)),
|
|
],
|
|
}
|
|
|
|
|
|
class TcshLexer(RegexLexer):
|
|
"""
|
|
Lexer for tcsh scripts.
|
|
|
|
*New in Pygments 0.10.*
|
|
"""
|
|
|
|
name = 'Tcsh'
|
|
aliases = ['tcsh', 'csh']
|
|
filenames = ['*.tcsh', '*.csh']
|
|
mimetypes = ['application/x-csh']
|
|
|
|
tokens = {
|
|
'root': [
|
|
include('basic'),
|
|
(r'\$\(', Keyword, 'paren'),
|
|
(r'\${#?', Keyword, 'curly'),
|
|
(r'`', String.Backtick, 'backticks'),
|
|
include('data'),
|
|
],
|
|
'basic': [
|
|
(r'\b(if|endif|else|while|then|foreach|case|default|'
|
|
r'continue|goto|breaksw|end|switch|endsw)\s*\b',
|
|
Keyword),
|
|
(r'\b(alias|alloc|bg|bindkey|break|builtins|bye|caller|cd|chdir|'
|
|
r'complete|dirs|echo|echotc|eval|exec|exit|fg|filetest|getxvers|'
|
|
r'glob|getspath|hashstat|history|hup|inlib|jobs|kill|'
|
|
r'limit|log|login|logout|ls-F|migrate|newgrp|nice|nohup|notify|'
|
|
r'onintr|popd|printenv|pushd|rehash|repeat|rootnode|popd|pushd|'
|
|
r'set|shift|sched|setenv|setpath|settc|setty|setxvers|shift|'
|
|
r'source|stop|suspend|source|suspend|telltc|time|'
|
|
r'umask|unalias|uncomplete|unhash|universe|unlimit|unset|unsetenv|'
|
|
r'ver|wait|warp|watchlog|where|which)\s*\b',
|
|
Name.Builtin),
|
|
(r'#.*\n', Comment),
|
|
(r'\\[\w\W]', String.Escape),
|
|
(r'(\b\w+)(\s*)(=)', bygroups(Name.Variable, Text, Operator)),
|
|
(r'[\[\]{}()=]+', Operator),
|
|
(r'<<\s*(\'?)\\?(\w+)[\w\W]+?\2', String),
|
|
],
|
|
'data': [
|
|
(r'(?s)"(\\\\|\\[0-7]+|\\.|[^"\\])*"', String.Double),
|
|
(r"(?s)'(\\\\|\\[0-7]+|\\.|[^'\\])*'", String.Single),
|
|
(r'\s+', Text),
|
|
(r'[^=\s\[\]{}()$"\'`\\]+', Text),
|
|
(r'\d+(?= |\Z)', Number),
|
|
(r'\$#?(\w+|.)', Name.Variable),
|
|
],
|
|
'curly': [
|
|
(r'}', Keyword, '#pop'),
|
|
(r':-', Keyword),
|
|
(r'[a-zA-Z0-9_]+', Name.Variable),
|
|
(r'[^}:"\'`$]+', Punctuation),
|
|
(r':', Punctuation),
|
|
include('root'),
|
|
],
|
|
'paren': [
|
|
(r'\)', Keyword, '#pop'),
|
|
include('root'),
|
|
],
|
|
'backticks': [
|
|
(r'`', String.Backtick, '#pop'),
|
|
include('root'),
|
|
],
|
|
}
|
|
|
|
|
|
class PowerShellLexer(RegexLexer):
|
|
"""
|
|
For Windows PowerShell code.
|
|
|
|
*New in Pygments 1.5.*
|
|
"""
|
|
name = 'PowerShell'
|
|
aliases = ['powershell', 'posh', 'ps1', 'psm1']
|
|
filenames = ['*.ps1','*.psm1']
|
|
mimetypes = ['text/x-powershell']
|
|
|
|
flags = re.DOTALL | re.IGNORECASE | re.MULTILINE
|
|
|
|
keywords = (
|
|
'while validateset validaterange validatepattern validatelength '
|
|
'validatecount until trap switch return ref process param parameter in '
|
|
'if global: function foreach for finally filter end elseif else '
|
|
'dynamicparam do default continue cmdletbinding break begin alias \\? '
|
|
'% #script #private #local #global mandatory parametersetname position '
|
|
'valuefrompipeline valuefrompipelinebypropertyname '
|
|
'valuefromremainingarguments helpmessage try catch throw').split()
|
|
|
|
operators = (
|
|
'and as band bnot bor bxor casesensitive ccontains ceq cge cgt cle '
|
|
'clike clt cmatch cne cnotcontains cnotlike cnotmatch contains '
|
|
'creplace eq exact f file ge gt icontains ieq ige igt ile ilike ilt '
|
|
'imatch ine inotcontains inotlike inotmatch ireplace is isnot le like '
|
|
'lt match ne not notcontains notlike notmatch or regex replace '
|
|
'wildcard').split()
|
|
|
|
verbs = (
|
|
'write where wait use update unregister undo trace test tee take '
|
|
'suspend stop start split sort skip show set send select scroll resume '
|
|
'restore restart resolve resize reset rename remove register receive '
|
|
'read push pop ping out new move measure limit join invoke import '
|
|
'group get format foreach export expand exit enter enable disconnect '
|
|
'disable debug cxnew copy convertto convertfrom convert connect '
|
|
'complete compare clear checkpoint aggregate add').split()
|
|
|
|
commenthelp = (
|
|
'component description example externalhelp forwardhelpcategory '
|
|
'forwardhelptargetname functionality inputs link '
|
|
'notes outputs parameter remotehelprunspace role synopsis').split()
|
|
|
|
tokens = {
|
|
'root': [
|
|
# we need to count pairs of parentheses for correct highlight
|
|
# of '$(...)' blocks in strings
|
|
(r'\(', Punctuation, 'child'),
|
|
(r'\s+', Text),
|
|
(r'^(\s*#[#\s]*)(\.(?:%s))([^\n]*$)' % '|'.join(commenthelp),
|
|
bygroups(Comment, String.Doc, Comment)),
|
|
(r'#[^\n]*?$', Comment),
|
|
(r'(<|<)#', Comment.Multiline, 'multline'),
|
|
(r'@"\n', String.Heredoc, 'heredoc-double'),
|
|
(r"@'\n.*?\n'@", String.Heredoc),
|
|
# escaped syntax
|
|
(r'`[\'"$@-]', Punctuation),
|
|
(r'"', String.Double, 'string'),
|
|
(r"'([^']|'')*'", String.Single),
|
|
(r'(\$|@@|@)((global|script|private|env):)?[a-z0-9_]+',
|
|
Name.Variable),
|
|
(r'(%s)\b' % '|'.join(keywords), Keyword),
|
|
(r'-(%s)\b' % '|'.join(operators), Operator),
|
|
(r'(%s)-[a-z_][a-z0-9_]*\b' % '|'.join(verbs), Name.Builtin),
|
|
(r'\[[a-z_\[][a-z0-9_. `,\[\]]*\]', Name.Constant), # .net [type]s
|
|
(r'-[a-z_][a-z0-9_]*', Name),
|
|
(r'\w+', Name),
|
|
(r'[.,;@{}\[\]$()=+*/\\&%!~?^`|<>-]|::', Punctuation),
|
|
],
|
|
'child': [
|
|
(r'\)', Punctuation, '#pop'),
|
|
include('root'),
|
|
],
|
|
'multline': [
|
|
(r'[^#&.]+', Comment.Multiline),
|
|
(r'#(>|>)', Comment.Multiline, '#pop'),
|
|
(r'\.(%s)' % '|'.join(commenthelp), String.Doc),
|
|
(r'[#&.]', Comment.Multiline),
|
|
],
|
|
'string': [
|
|
(r"`[0abfnrtv'\"\$]", String.Escape),
|
|
(r'[^$`"]+', String.Double),
|
|
(r'\$\(', Punctuation, 'child'),
|
|
(r'""', String.Double),
|
|
(r'[`$]', String.Double),
|
|
(r'"', String.Double, '#pop'),
|
|
],
|
|
'heredoc-double': [
|
|
(r'\n"@', String.Heredoc, '#pop'),
|
|
(r'\$\(', Punctuation, 'child'),
|
|
(r'[^@\n]+"]', String.Heredoc),
|
|
(r".", String.Heredoc),
|
|
]
|
|
}
|