bugfixes and comments for mode_python

--HG--
branch : pmacs2
This commit is contained in:
moculus 2007-06-27 03:01:49 +00:00
parent cec50f0ad7
commit 865b3b33bc
1 changed files with 70 additions and 52 deletions

View File

@ -28,15 +28,16 @@ class PythonGrammar(Grammar):
PatternRule(name=r"integer", pattern=r"(?<![\.0-9a-zA-Z_])(?:0|[1-9][0-9]*|0[0-7]+|0[xX][0-9a-fA-F]+)[lL]?(?![\.0-9a-zA-Z_])"),
PatternRule(name=r"float", pattern=r"(?<![\.0-9a-zA-Z_])(?:[0-9]+\.[0-9]*|\.[0-9]+|(?:[0-9]|[0-9]+\.[0-9]*|\.[0-9]+)[eE][\+-]?[0-9]+)(?![\.0-9a-zA-Z_])"),
PatternRule(name=r"imaginary", pattern=r"(?<![\.0-9a-zA-Z_])(?:[0-9]+|(?:[0-9]+\.[0-9]*|\.[0-9]+|(?:[0-9]|[0-9]+\.[0-9]*|\.[0-9]+)[eE][\+-]?[0-9]+)[jJ])(?![\.0-9a-zA-Z_])"),
RegionRule(name=r'docstring', start=r'^ *(?P<tag>"""|\'\'\')', grammar=Grammar(), end=r'%(tag)s'),
RegionRule(name=r'string', start=r'(?P<tag>"""|\'\'\')', grammar=StringGrammar(), end=r'%(tag)s'),
RegionRule(name=r'string', start=r'(?P<tag>"|\')', grammar=StringGrammar(), end=r'%(tag)s'),
RegionRule(name=r'string', start=r'"""', grammar=StringGrammar(), end=r'"""'),
RegionRule(name=r'string', start=r"'''", grammar=StringGrammar(), end=r"'''"),
RegionRule(name=r'string', start=r'"', grammar=StringGrammar(), end=r'"'),
RegionRule(name=r'string', start=r"'", grammar=StringGrammar(), end=r"'"),
PatternRule(name=r'comment', pattern=r'#.*$'),
PatternRule(name=r'continuation', pattern=r'\\$'),
]
class PythonTabber(tab2.StackTabber):
unanchored_names = ('null', 'string', 'docstring', 'comment')
unanchored_names = ('null', 'string', 'comment')
endlevel_names = ('pass', 'return', 'yield', 'raise', 'break', 'continue')
startlevel_names = ('if', 'try', 'class', 'def', 'for', 'while', 'try')
def __init__(self, m):
@ -45,13 +46,18 @@ class PythonTabber(tab2.StackTabber):
def is_base(self, y):
if y == 0:
# we always know that line 0 is indented at the 0 level
return True
tokens = self.get_tokens(y)
if not tokens:
# if a line has no tokens, we don't know much about its indentation
return False
elif tokens[0].name not in self.unanchored_names:
# if a line has no whitespace and beings with something like
# 'while','class','def','if',etc. then we can start at it
return True
else:
# otherwise, we can't be sure that its level is correct
return False
def get_level(self, y):
@ -59,15 +65,25 @@ class PythonTabber(tab2.StackTabber):
return self.lines.get(y)
def _calc_level(self, y):
# ok, so first remember where we are going, and find our starting point
target = y
while not self.is_base(y) and y > 0:
y -= 1
# ok, so clear out our stack and then loop over each line
self.markers = []
while y <= target:
self.popped = False
tokens = self.get_tokens(y)
currlvl = self.get_curr_level()
self.continued = False
self.popped = False
tokens = self.get_tokens(y)
currlvl = self.get_curr_level()
# if we were continuing, let's pop that previous continuation token
# and note that we're continuing
if self.markers and self.markers[-1].name == 'cont':
self.continued = True
self._pop()
# if we haven't reached the target-line yet, we can detect how many
# levels of unindention, if any, the user chose on previous lines
if y < target and tokens:
if self.token_is_whitespace(y, 0):
l = len(tokens[0].string)
@ -77,8 +93,10 @@ class PythonTabber(tab2.StackTabber):
self._pop()
currlvl = self.get_curr_level()
self.popped = True
# ok, having done all that, we can now process each token on the line
for i in range(0, len(tokens)):
currlvl = self._handle_token(currlvl, y, i)
# so let's store the level for this line, as well as some debugging
self.lines[y] = currlvl
self.record[y] = tuple(self.markers)
y += 1
@ -86,40 +104,50 @@ class PythonTabber(tab2.StackTabber):
def _handle_other_token(self, currlvl, y, i):
token = self.get_token(y, i)
fqname = token.fqname()
if fqname == 'string.start':
if fqname == 'continuation':
# we need to pop the indentation level over, unless last line was
# also a continued line
if self.continued:
self._opt_append('cont', currlvl)
else:
self._opt_append('cont', currlvl + 4)
elif fqname == 'string.start':
# while inside of a string, there is no indention leve
self._opt_append('string', None)
elif fqname == 'string.end':
# since we're done with the string, resume our indentation level
self._opt_pop('string')
elif fqname == 'docstring.start':
self._opt_append('docstring', None)
elif fqname == 'docstring.end':
self._opt_pop('docstring')
elif fqname == 'delimiter':
if token.string == ':' and self.markers and self.markers[-1].name in ('[', '{'):
# we are in a list range [:] or dictionary key/value {:}
pass
elif self.is_rightmost_token(y, i):
# we are at the end of a block
pass
else:
# we are doing a one-liner
self._pop()
# we only reall care about a colon as part of a one-line statement,
# i.e. "while ok: foo()" or "if True: print 3"
if token.string == ':':
if self.markers and self.markers[-1].name in ('[', '{'):
pass
elif self.is_rightmost_token(y, i):
pass
else:
self._pop()
elif fqname == 'keyword':
if token.string in self.endlevel_names:
# we know we'll unindent at least once
self._pop()
elif token.string in self.startlevel_names and self.is_leftmost_token(y, i):
# we know we will indent exactly once
self._append(token.string, currlvl + 4)
elif token.string in ('elif', 'else') and self.is_leftmost_token(y, i):
# we know we'll unindent at least to the first if/elif
if not self.popped:
self._pop_until('if', 'elif')
currlvl = self.get_curr_level()
self._append(token.string, currlvl + 4)
elif token.string == 'except' and self.is_leftmost_token(y, i):
# we know we'll unindent at least to the first try
if not self.popped:
self._pop_until('try')
currlvl = self.get_curr_level()
self._append(token.string, currlvl + 4)
elif token.string == 'finally' and self.is_leftmost_token(y, i):
# we know we'll unindent at least to the first try/except
if not self.popped:
self._pop_until('try', 'except')
currlvl = self.get_curr_level()
@ -127,53 +155,43 @@ class PythonTabber(tab2.StackTabber):
return currlvl
class Python(mode2.Fundamental):
tabbercls = PythonTabber
grammar = PythonGrammar()
tabbercls = PythonTabber
grammar = PythonGrammar()
opentoken = 'delimiter'
opentags = {'(': ')', '[': ']', '{': '}'}
closetoken = 'delimiter'
closetags = {')': '(', ']': '[', '}': '{'}
def __init__(self, w):
mode2.Fundamental.__init__(self, w)
# tag matching
self.add_bindings('close-paren', (')',))
self.add_bindings('close-brace', ('}',))
self.add_bindings('close-bracket', (']',))
# add python-specific methods
self.add_action_and_bindings(PythonCheckSyntax(), ('C-c s',))
self.add_action_and_bindings(PythonDictCleanup(), ('C-c h',))
self.add_action_and_bindings(PythonUpdateTags(), ('C-c t',))
self.add_action_and_bindings(PythonTagComplete(), ('C-c k',))
# we want to do these kinds of tag matching
self.add_bindings('close-paren', (')',))
self.add_bindings('close-brace', ('}',))
self.add_bindings('close-bracket', (']',))
# highlighting
self.colors = {
'keyword': color.build('cyan', 'default'),
'reserved': color.build('cyan', 'default'),
'builtin': color.build('cyan', 'default'),
'functionname': color.build('blue', 'default'),
'classname': color.build('green', 'default'),
'string.start': color.build('green', 'default'),
'string.null': color.build('green', 'default'),
'string.escaped': color.build('magenta', 'default'),
'string.octal': color.build('magenta', 'default'),
'string.format': color.build('yellow', 'default'),
'string.end': color.build('green', 'default'),
'integer': color.build('default', 'default'),
'float': color.build('default', 'default'),
'imaginary': color.build('default', 'default'),
'docstring.start': color.build('green', 'default'),
'docstring.null': color.build('green', 'default'),
'docstring.end': color.build('green', 'default'),
'keyword': color.build('cyan', 'default'),
'reserved': color.build('cyan', 'default'),
'builtin': color.build('cyan', 'default'),
'functionname': color.build('blue', 'default'),
'classname': color.build('green', 'default'),
'string.start': color.build('green', 'default'),
'string.null': color.build('green', 'default'),
'string.octal': color.build('magenta', 'default'),
'string.escaped': color.build('magenta', 'default'),
'string.format': color.build('yellow', 'default'),
'string.end': color.build('green', 'default'),
'integer': color.build('default', 'default'),
'float': color.build('default', 'default'),
'imaginary': color.build('default', 'default'),
'comment': color.build('red', 'default'),
'continuation': color.build('red', 'default'),
'system_identifier': color.build('cyan', 'default'),
}
def name(self):
return "Python"