pmacs3/mode/sh.py

287 lines
11 KiB
Python
Raw Permalink Normal View History

import re
import subprocess
from tab import StackTabber
from mode import Fundamental
from lex import Grammar, PatternRule, RegionRule, PatternMatchRule, OverridePatternRule
2007-07-25 16:42:24 -04:00
from method import Method
from method.shell import Interact
2007-07-21 11:40:53 -04:00
char = '[a-zA-Z0-9_]'
word = char + '+'
pname = '[.a-zA-Z0-9_]+'
2007-07-21 11:40:53 -04:00
class StringGrammar1(Grammar): pass
class StringGrammar2(Grammar): pass
class HereGrammar(Grammar): pass
class EvalGrammar(Grammar): pass
class NevalGrammar(Grammar): pass
class StanzaGrammar(Grammar): pass
class CaseGrammar(Grammar): pass
class TestGrammar(Grammar): pass
class ShGrammar(Grammar): pass
StringGrammar1.rules = [
PatternRule(r'data', r'[^\']+'),
]
StringGrammar2.rules = [
PatternRule(r'escaped', r'\\.'),
PatternRule(r'sh.variable', r"\${(?:" + word + "|\?\$)}"),
PatternRule(r"sh.variable", r"\$" + word),
PatternRule(r"sh.variable", r"\$[*@#?\-\$!_\[\]]"),
PatternRule(r'sh.variable', r"\$(?=\()"),
PatternRule(r'data', r'[^\\$"]+'),
]
HereGrammar.rules = [
PatternRule(r'escaped', r'\\.'),
PatternRule(r'sh.variable', r"\${(?:" + word + "|\?\$)}"),
PatternRule(r"sh.variable", r"\$" + word),
PatternRule(r"sh.variable", r"\$[*@#?\-\$!_\[\]]"),
PatternRule(r'sh.variable', r"\$(?=\()"),
PatternRule(r'data', r'[^\\$]+'),
]
2007-09-20 02:10:27 -04:00
EvalGrammar.rules = [
RegionRule(r'string', "'", StringGrammar1, "'"),
RegionRule(r'string', '"', StringGrammar2, '"'),
PatternRule(r'escaped', r'\\.'),
PatternRule(r'sh.variable', r"\${(?:" + word + "|\?\$)}"),
PatternRule(r"sh.variable", r"\$" + word),
PatternRule(r"sh.variable", r"\$[*@#?\-\$!_\[\]]"),
PatternRule(r'sh.variable', r"\$(?=\()"),
PatternRule(r'data', r'[^\\`$\'"]+'),
]
2008-10-02 23:57:32 -04:00
NevalGrammar.rules = [
RegionRule(r'string', "'", Grammar, "'"),
RegionRule(r'string', '"', StringGrammar2, '"'),
PatternRule(r'continuation', r'\\\n$'),
PatternRule(r'escaped', r'\\.'),
PatternRule(r'sh.variable', r"\${(?:" + word + "|\?\$)}"),
PatternRule(r"sh.variable", r"\$" + word),
PatternRule(r"sh.variable", r"\$[*@#?\-\$!_\[\]]"),
PatternRule(r'sh.variable', r"\$(?=\()"),
PatternRule(r'data', r'[^\\)$"\']+'),
]
2007-07-21 11:40:53 -04:00
StanzaGrammar.rules = [
PatternRule(r'spaces', r' +'),
PatternRule(r'start_cont', r'.+\\'),
PatternRule(r'eol', r'\n'),
]
CaseGrammar.rules = [
PatternRule(r'comment', r'#.*$'),
PatternRule(r'spaces', r' +'),
RegionRule(r'stanza', r'.+\\\n$', StanzaGrammar, r'.+\)', ShGrammar, r';;'),
RegionRule(r'stanza', r'.+?\)', ShGrammar, r';;'),
PatternRule(r'eol', r'\n'),
]
TestGrammar.rules = [
PatternRule(r'spaces', r' +'),
PatternRule(r'sh.builtin', r"(?<![-a-zA-Z0-9_=/])(?:source|alias|bg|bind|break|builtin|cd|command|compgen|complete|declare|dirs|disown|echo|enable|eval|exec|exit|export|fc|fg|getops|hash|help|history|jobs|kill|let|local|logout|popd|printf|pushd|pwd|readonly|read|return|set|shift|shopt|suspend|times|trap|type|ulimit|umask|unalias|unset|wait)(?![a-zA-Z0-9_=/])"),
PatternRule(r'sh.reserved', r"(?:done|do|elif|else|esac|fi|for|function|if|in|select|then|until|while|time)(?![a-zA-Z0-9_=/])"),
PatternRule(r'binop', r'==|=|!='),
PatternRule(r'binop', r'-(?:nt|ot|ef|eq|ne|lt|gt|le|ge)(?!' + char + ')'),
PatternRule(r'unop', r'-[a-zA-Z](?!' + char + ')'),
PatternRule(r'continuation', r'\\\n$'),
PatternRule(r'redirect', r'<|>'),
PatternRule(r'delimiter', r";;|[();{}|&><]"),
RegionRule(r'test', r'(?<![^ \t])test(?![a-zA-Z0-9_=/])', None, r';|\n'),
RegionRule(r'test2', r'\[\[', None, r'\]\]'),
RegionRule(r'test3', r'\[', None, r'\]'),
RegionRule(r'sh.eval', r'`', EvalGrammar, r'`'),
RegionRule(r'sh.neval', r'\$\(', NevalGrammar, r'\)'),
PatternRule(r'sh.variable', r"(?:^|(?<= ))" + word + "(?==)"),
PatternRule(r'sh.variable', r"\${(?:" + word + "|\?\$)}"),
PatternRule(r"sh.variable", r"\$" + word),
PatternRule(r"sh.variable", r"\$[*@#?\-\$!_\[\]]"),
PatternRule(r'sh.variable', r"\$(?=\()"),
PatternRule(r'escaped', r'\\.'),
RegionRule(r'string', "'", StringGrammar1, "'"),
RegionRule(r'string', '"', StringGrammar2, '"'),
PatternRule(r'sh.bareword', r'[-a-zA-Z0-9_.]+'),
]
ShGrammar.rules = [
PatternMatchRule('x', '( *)(' + word + ')(=)',
'spaces', 'sh.variable', 'delimiter'),
PatternMatchRule('x', '(alias|export)( +)(' + word + ')(=)',
'sh.builtin', 'spaces', 'sh.variable', 'delimiter'),
PatternMatchRule('x', '(unset)( +)(' + word + ')',
'sh.builtin', 'spaces', 'sh.variable'),
PatternRule(r'spaces', r' +'),
RegionRule(r'heredoc', r"<<-'(?P<heredoc>" + word + ")'", None, "\n", HereGrammar, r'^[ \t]*%(heredoc)s$'),
RegionRule(r'heredoc', r"<<-(?P<heredoc>" + word + ")", None, "\n", HereGrammar, r'^[ \t]*%(heredoc)s$'),
RegionRule(r'heredoc', r"<<[<\\]?(?P<heredoc>" + word + ")", None, "\n", HereGrammar, r'^%(heredoc)s$'),
PatternRule(r'sh.function', word + r'(?= *\(\))'),
PatternRule(r'sh.reserved', r"(?:done|do|elif|else|esac|fi|for|function|if|in|select|then|until|while|time)(?![a-zA-Z0-9_=/])"),
RegionRule(r'sh.case', r'case', None, 'in', CaseGrammar, r'esac'),
PatternRule(r'sh.builtin', r"(?<![-a-zA-Z0-9_])(?:source|alias|bg|bind|break|builtin|cd|command|compgen|complete|declare|dirs|disown|echo|enable|eval|exec|exit|export|fc|fg|getops|hash|help|history|jobs|kill|let|local|logout|popd|printf|pushd|pwd|readonly|read|return|set|shift|shopt|suspend|times|trap|type|ulimit|umask|unalias|unset|wait)(?![a-zA-Z0-9_=/])(?![-a-zA-Z0-9_])"),
RegionRule(r'test', r'(?<![^ \t])test(?![a-zA-Z0-9_=/])', TestGrammar, r';|\n'),
RegionRule(r'test2', r'\[\[', TestGrammar, r'\]\]'),
RegionRule(r'test3', r'\[', TestGrammar, r'\]'),
PatternRule(r'redirect', r'<|>'),
PatternRule(r'delimiter', r";;|[();{}|&><:=/]"),
RegionRule(r'sh.eval', r'`', EvalGrammar, r'`'),
RegionRule(r'sh.neval', r'\$\(', NevalGrammar, r'\)'),
PatternRule(r'sh.variable', r"\${(?:" + word + "|\?\$)}"),
PatternRule(r"sh.variable", r"\$" + word),
PatternRule(r"sh.variable", r"\$[*@#?\-\$!_\[\]]"),
RegionRule(r'sh.param', r"\${", Grammar, "}"),
PatternRule('escaped', r'\\.'),
RegionRule(r'string', "'", StringGrammar1, "'"),
RegionRule(r'string', '"', StringGrammar2, '"'),
OverridePatternRule(r'comment', r'#@@:(?P<token>' + pname + '):(?P<mode>' + pname + ') *$'),
PatternRule(r'comment', r'#.*$'),
PatternRule(r'sh.bareword', r'(?:[-a-zA-Z0-9_.]|\\.)+'),
PatternRule(r'continuation', r'\\\n$'),
PatternRule(r'eol', r'\n$'),
]
2007-10-17 22:44:22 -04:00
class ShTabber(StackTabber):
2007-07-21 11:40:53 -04:00
def is_base(self, y):
if y == 0:
return True
highlighter = self.mode.window.buffer.highlights[self.mode.name]
2007-07-21 11:40:53 -04:00
if not highlighter.tokens[y]:
return False
t = highlighter.tokens[y][0]
return t.name == 'sh.function'
2007-07-21 11:40:53 -04:00
def _handle_close_token(self, currlvl, y, i):
s = self.get_token(y, i).string
if s == ')' and self.markers and self._peek_name() == "sh.case":
2007-07-21 11:40:53 -04:00
# we have to ignore ) when used in "case" statements.
return currlvl
else:
return StackTabber._handle_close_token(self, currlvl, y, i)
2007-07-21 11:40:53 -04:00
def _handle_other_token(self, currlvl, y, i):
2008-04-02 19:06:52 -04:00
w = self.mode.tabwidth
2007-07-21 11:40:53 -04:00
token = self.get_token(y, i)
fqname = token.fqname()
if token.name == 'continuation':
2008-04-02 19:06:52 -04:00
self._opt_append("cont", currlvl + w)
elif token.name == 'sh.reserved' and token.string == 'else':
2008-04-02 19:06:52 -04:00
currlvl -= w
2007-07-21 11:40:53 -04:00
elif token.name == 'eol':
self._opt_pop("cont")
return currlvl
2008-04-18 23:32:08 -04:00
class ShCheckSyntax(Method):
'''Check the syntax of a shell script'''
def _execute(self, w, **vargs):
app = w.application
cmd = "bash -n %r" % w.buffer.path
(status, output) = subprocess.getstatusoutput(cmd)
2008-04-18 23:32:08 -04:00
if status == 0:
app.set_error("Syntax OK")
app.data_buffer("*Sh-Check-Syntax*", output, switch_to=False)
else:
app.data_buffer("*Sh-Check-Syntax*", output)
class BashStart(Interact):
args = []
reuse = True
def _execute(self, w, **vargs):
Interact._execute(self, w, bname='*Bash*', cmd='bash')
class BashLoadFile(Interact):
args = []
reuse = True
def _execute(self, w, **vargs):
path = w.buffer.path
cmd = 'bash %r' % path
Interact._execute(self, w, bname='*Bash*', cmd=cmd)
b = w.application.get_buffer_by_name('*Bash*')
# white is for delimiters, operators, numbers
c_default = ('default', 'default')
# magenta is for keywords/builtins, translation, globs
lo_magenta = ('magenta202', 'default')
hi_magenta = ('magenta505', 'default')
# red is for comments, pods, endblocks
lo_red = ('red300', 'default')
hi_red = ('red511', 'default')
# orange are for arrays and hashes
hi_orange = ('yellow531', 'default')
lo_orange = ('yellow520', 'default')
# yellow is for scalars and prototypes
hi_yellow = ('yellow551', 'default')
lo_yellow = ('yellow330', 'default')
# green is for strings and hash keys
lo_green = ('green030', 'default')
hi_green = ('green050', 'default')
# cyan is for quotes, evals, regexes, subs
lo_cyan = ('cyan033', 'default')
hi_cyan = ('cyan155', 'default')
# blue is unused
lo_blue = ('blue113', 'default')
hi_blue = ('blue225', 'default')
class Sh(Fundamental):
name = 'sh'
paths = ['/etc/profile']
basenames = ['.bashrc', '.bash_profile', '.profile']
extensions = ['.bash', '.sh']
#detection = ['sh', 'bash']
detection = [re.compile('^#!(?:.+/)sh'), re.compile('^#!(?:.+/)bash')]
grammar = ShGrammar
tabbercls = ShTabber
opentokens = ('delimiter', 'sh.reserved', 'sh.case.start')
opentags = {'(': ')', '[': ']', '{': '}', 'do': 'done', 'then': 'fi',
2008-09-12 09:31:48 -04:00
'case': 'esac'}
closetokens = ('delimiter', 'sh.reserved', 'sh.case.end')
2008-09-12 09:31:48 -04:00
closetags = {')': '(', ']': '[', '}': '{', 'done': 'do', 'fi': 'then',
'esac': 'case'}
2007-07-21 11:40:53 -04:00
colors = {
'sh.builtin': hi_cyan,
'sh.function': hi_magenta,
'sh.reserved': hi_magenta,
'sh.variable': hi_yellow,
2019-11-13 08:22:41 -05:00
'string.sh.variable': hi_orange,
'sh.param.start': lo_yellow,
'sh.param.null': lo_yellow,
'sh.param.end': lo_yellow,
2007-07-21 11:40:53 -04:00
# case statements
'sh.case.start': hi_magenta,
'sh.case.stanza.start': lo_cyan,
'sh.case.stanza.start_cont': lo_cyan,
'sh.case.stanza.middle0': lo_cyan,
'sh.case.middle0': hi_magenta,
'sh.case.end': hi_magenta,
'test.start': hi_cyan,
'binop': hi_magenta,
'unop': hi_magenta,
'sh.eval.start': lo_cyan,
'sh.eval.sh.variable': hi_yellow,
'sh.eval.data': hi_cyan,
'sh.eval.null': hi_cyan,
'sh.eval.end': lo_cyan,
'sh.neval.start': lo_cyan,
'sh.neval.sh.variable': hi_yellow,
'sh.neval.data': hi_cyan,
'sh.neval.null': hi_cyan,
'sh.neval.end': lo_cyan,
2007-07-21 11:40:53 -04:00
}
actions = [ShCheckSyntax, BashStart, BashLoadFile]
2009-02-15 12:06:35 -05:00
_bindings = {
'sh-check-syntax': ('C-c s',),
}
2007-10-19 02:41:33 -04:00
install = Sh.install