shell.py
361 lines
| 12.8 KiB
| text/x-python
|
PythonLexer
Jeandet Alexis
|
r0 | # -*- coding: utf-8 -*- | ||
""" | ||||
pygments.lexers.shell | ||||
~~~~~~~~~~~~~~~~~~~~~ | ||||
Lexers for various shells. | ||||
:copyright: Copyright 2006-2012 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'] | ||||
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'<<-?\s*(\'?)\\?(\w+)[\w\W]+?\2', String), | ||||
(r'&&|\|\|', Operator), | ||||
], | ||||
'data': [ | ||||
(r'(?s)\$?"(\\\\|\\[0-7]+|\\.|[^"\\])*"', String.Double), | ||||
(r"(?s)\$?'(\\\\|\\[0-7]+|\\.|[^'\\])*'", String.Single), | ||||
(r';', Text), | ||||
(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): | ||||
return shebang_matches(text, r'(ba|z|)sh') | ||||
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 BatchLexer(RegexLexer): | ||||
""" | ||||
Lexer for the DOS/Windows Batch file format. | ||||
*New in Pygments 0.7.* | ||||
""" | ||||
name = 'Batchfile' | ||||
aliases = ['bat'] | ||||
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'] | ||||
filenames = ['*.ps1'] | ||||
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').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': [ | ||||
(r'\s+', Text), | ||||
(r'^(\s*#[#\s]*)(\.(?:%s))([^\n]*$)' % '|'.join(commenthelp), | ||||
bygroups(Comment, String.Doc, Comment)), | ||||
(r'#[^\n]*?$', Comment), | ||||
(r'(<|<)#', Comment.Multiline, 'multline'), | ||||
(r'@"\n.*?\n"@', String.Heredoc), | ||||
(r"@'\n.*?\n'@", String.Heredoc), | ||||
(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), | ||||
], | ||||
'multline': [ | ||||
(r'[^#&.]+', Comment.Multiline), | ||||
(r'#(>|>)', Comment.Multiline, '#pop'), | ||||
(r'\.(%s)' % '|'.join(commenthelp), String.Doc), | ||||
(r'[#&.]', Comment.Multiline), | ||||
], | ||||
'string': [ | ||||
(r'[^$`"]+', String.Double), | ||||
(r'\$\(', String.Interpol, 'interpol'), | ||||
(r'`"|""', String.Double), | ||||
(r'[`$]', String.Double), | ||||
(r'"', String.Double, '#pop'), | ||||
], | ||||
'interpol': [ | ||||
(r'[^$)]+', String.Interpol), | ||||
(r'\$\(', String.Interpol, '#push'), | ||||
(r'\)', String.Interpol, '#pop'), | ||||
] | ||||
} | ||||