bugfixes and comments for mode_python
--HG-- branch : pmacs2
This commit is contained in:
parent
cec50f0ad7
commit
865b3b33bc
122
mode_python.py
122
mode_python.py
|
@ -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"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"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_])"),
|
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'"""', grammar=StringGrammar(), end=r'"""'),
|
||||||
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'(?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"'"),
|
||||||
PatternRule(name=r'comment', pattern=r'#.*$'),
|
PatternRule(name=r'comment', pattern=r'#.*$'),
|
||||||
PatternRule(name=r'continuation', pattern=r'\\$'),
|
PatternRule(name=r'continuation', pattern=r'\\$'),
|
||||||
]
|
]
|
||||||
|
|
||||||
class PythonTabber(tab2.StackTabber):
|
class PythonTabber(tab2.StackTabber):
|
||||||
unanchored_names = ('null', 'string', 'docstring', 'comment')
|
unanchored_names = ('null', 'string', 'comment')
|
||||||
endlevel_names = ('pass', 'return', 'yield', 'raise', 'break', 'continue')
|
endlevel_names = ('pass', 'return', 'yield', 'raise', 'break', 'continue')
|
||||||
startlevel_names = ('if', 'try', 'class', 'def', 'for', 'while', 'try')
|
startlevel_names = ('if', 'try', 'class', 'def', 'for', 'while', 'try')
|
||||||
def __init__(self, m):
|
def __init__(self, m):
|
||||||
|
@ -45,13 +46,18 @@ class PythonTabber(tab2.StackTabber):
|
||||||
|
|
||||||
def is_base(self, y):
|
def is_base(self, y):
|
||||||
if y == 0:
|
if y == 0:
|
||||||
|
# we always know that line 0 is indented at the 0 level
|
||||||
return True
|
return True
|
||||||
tokens = self.get_tokens(y)
|
tokens = self.get_tokens(y)
|
||||||
if not tokens:
|
if not tokens:
|
||||||
|
# if a line has no tokens, we don't know much about its indentation
|
||||||
return False
|
return False
|
||||||
elif tokens[0].name not in self.unanchored_names:
|
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
|
return True
|
||||||
else:
|
else:
|
||||||
|
# otherwise, we can't be sure that its level is correct
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_level(self, y):
|
def get_level(self, y):
|
||||||
|
@ -59,15 +65,25 @@ class PythonTabber(tab2.StackTabber):
|
||||||
return self.lines.get(y)
|
return self.lines.get(y)
|
||||||
|
|
||||||
def _calc_level(self, y):
|
def _calc_level(self, y):
|
||||||
|
# ok, so first remember where we are going, and find our starting point
|
||||||
target = y
|
target = y
|
||||||
while not self.is_base(y) and y > 0:
|
while not self.is_base(y) and y > 0:
|
||||||
y -= 1
|
y -= 1
|
||||||
|
|
||||||
|
# ok, so clear out our stack and then loop over each line
|
||||||
self.markers = []
|
self.markers = []
|
||||||
while y <= target:
|
while y <= target:
|
||||||
self.popped = False
|
self.continued = False
|
||||||
tokens = self.get_tokens(y)
|
self.popped = False
|
||||||
currlvl = self.get_curr_level()
|
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 y < target and tokens:
|
||||||
if self.token_is_whitespace(y, 0):
|
if self.token_is_whitespace(y, 0):
|
||||||
l = len(tokens[0].string)
|
l = len(tokens[0].string)
|
||||||
|
@ -77,8 +93,10 @@ class PythonTabber(tab2.StackTabber):
|
||||||
self._pop()
|
self._pop()
|
||||||
currlvl = self.get_curr_level()
|
currlvl = self.get_curr_level()
|
||||||
self.popped = True
|
self.popped = True
|
||||||
|
# ok, having done all that, we can now process each token on the line
|
||||||
for i in range(0, len(tokens)):
|
for i in range(0, len(tokens)):
|
||||||
currlvl = self._handle_token(currlvl, y, i)
|
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.lines[y] = currlvl
|
||||||
self.record[y] = tuple(self.markers)
|
self.record[y] = tuple(self.markers)
|
||||||
y += 1
|
y += 1
|
||||||
|
@ -86,40 +104,50 @@ class PythonTabber(tab2.StackTabber):
|
||||||
def _handle_other_token(self, currlvl, y, i):
|
def _handle_other_token(self, currlvl, y, i):
|
||||||
token = self.get_token(y, i)
|
token = self.get_token(y, i)
|
||||||
fqname = token.fqname()
|
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)
|
self._opt_append('string', None)
|
||||||
elif fqname == 'string.end':
|
elif fqname == 'string.end':
|
||||||
|
# since we're done with the string, resume our indentation level
|
||||||
self._opt_pop('string')
|
self._opt_pop('string')
|
||||||
elif fqname == 'docstring.start':
|
|
||||||
self._opt_append('docstring', None)
|
|
||||||
elif fqname == 'docstring.end':
|
|
||||||
self._opt_pop('docstring')
|
|
||||||
elif fqname == 'delimiter':
|
elif fqname == 'delimiter':
|
||||||
if token.string == ':' and self.markers and self.markers[-1].name in ('[', '{'):
|
# we only reall care about a colon as part of a one-line statement,
|
||||||
# we are in a list range [:] or dictionary key/value {:}
|
# i.e. "while ok: foo()" or "if True: print 3"
|
||||||
pass
|
if token.string == ':':
|
||||||
elif self.is_rightmost_token(y, i):
|
if self.markers and self.markers[-1].name in ('[', '{'):
|
||||||
# we are at the end of a block
|
pass
|
||||||
pass
|
elif self.is_rightmost_token(y, i):
|
||||||
else:
|
pass
|
||||||
# we are doing a one-liner
|
else:
|
||||||
self._pop()
|
self._pop()
|
||||||
elif fqname == 'keyword':
|
elif fqname == 'keyword':
|
||||||
if token.string in self.endlevel_names:
|
if token.string in self.endlevel_names:
|
||||||
|
# we know we'll unindent at least once
|
||||||
self._pop()
|
self._pop()
|
||||||
elif token.string in self.startlevel_names and self.is_leftmost_token(y, i):
|
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)
|
self._append(token.string, currlvl + 4)
|
||||||
elif token.string in ('elif', 'else') and self.is_leftmost_token(y, i):
|
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:
|
if not self.popped:
|
||||||
self._pop_until('if', 'elif')
|
self._pop_until('if', 'elif')
|
||||||
currlvl = self.get_curr_level()
|
currlvl = self.get_curr_level()
|
||||||
self._append(token.string, currlvl + 4)
|
self._append(token.string, currlvl + 4)
|
||||||
elif token.string == 'except' and self.is_leftmost_token(y, i):
|
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:
|
if not self.popped:
|
||||||
self._pop_until('try')
|
self._pop_until('try')
|
||||||
currlvl = self.get_curr_level()
|
currlvl = self.get_curr_level()
|
||||||
self._append(token.string, currlvl + 4)
|
self._append(token.string, currlvl + 4)
|
||||||
elif token.string == 'finally' and self.is_leftmost_token(y, i):
|
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:
|
if not self.popped:
|
||||||
self._pop_until('try', 'except')
|
self._pop_until('try', 'except')
|
||||||
currlvl = self.get_curr_level()
|
currlvl = self.get_curr_level()
|
||||||
|
@ -127,53 +155,43 @@ class PythonTabber(tab2.StackTabber):
|
||||||
return currlvl
|
return currlvl
|
||||||
|
|
||||||
class Python(mode2.Fundamental):
|
class Python(mode2.Fundamental):
|
||||||
tabbercls = PythonTabber
|
tabbercls = PythonTabber
|
||||||
grammar = PythonGrammar()
|
grammar = PythonGrammar()
|
||||||
opentoken = 'delimiter'
|
opentoken = 'delimiter'
|
||||||
opentags = {'(': ')', '[': ']', '{': '}'}
|
opentags = {'(': ')', '[': ']', '{': '}'}
|
||||||
closetoken = 'delimiter'
|
closetoken = 'delimiter'
|
||||||
closetags = {')': '(', ']': '[', '}': '{'}
|
closetags = {')': '(', ']': '[', '}': '{'}
|
||||||
def __init__(self, w):
|
def __init__(self, w):
|
||||||
mode2.Fundamental.__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
|
# add python-specific methods
|
||||||
self.add_action_and_bindings(PythonCheckSyntax(), ('C-c s',))
|
self.add_action_and_bindings(PythonCheckSyntax(), ('C-c s',))
|
||||||
self.add_action_and_bindings(PythonDictCleanup(), ('C-c h',))
|
self.add_action_and_bindings(PythonDictCleanup(), ('C-c h',))
|
||||||
self.add_action_and_bindings(PythonUpdateTags(), ('C-c t',))
|
self.add_action_and_bindings(PythonUpdateTags(), ('C-c t',))
|
||||||
self.add_action_and_bindings(PythonTagComplete(), ('C-c k',))
|
self.add_action_and_bindings(PythonTagComplete(), ('C-c k',))
|
||||||
|
# highlighting
|
||||||
# we want to do these kinds of tag matching
|
|
||||||
self.add_bindings('close-paren', (')',))
|
|
||||||
self.add_bindings('close-brace', ('}',))
|
|
||||||
self.add_bindings('close-bracket', (']',))
|
|
||||||
|
|
||||||
self.colors = {
|
self.colors = {
|
||||||
'keyword': color.build('cyan', 'default'),
|
'keyword': color.build('cyan', 'default'),
|
||||||
'reserved': color.build('cyan', 'default'),
|
'reserved': color.build('cyan', 'default'),
|
||||||
'builtin': color.build('cyan', 'default'),
|
'builtin': color.build('cyan', 'default'),
|
||||||
'functionname': color.build('blue', 'default'),
|
'functionname': color.build('blue', 'default'),
|
||||||
'classname': color.build('green', 'default'),
|
'classname': color.build('green', 'default'),
|
||||||
|
'string.start': color.build('green', 'default'),
|
||||||
'string.start': color.build('green', 'default'),
|
'string.null': color.build('green', 'default'),
|
||||||
'string.null': color.build('green', 'default'),
|
'string.octal': color.build('magenta', 'default'),
|
||||||
'string.escaped': color.build('magenta', 'default'),
|
'string.escaped': color.build('magenta', 'default'),
|
||||||
'string.octal': color.build('magenta', 'default'),
|
'string.format': color.build('yellow', 'default'),
|
||||||
'string.format': color.build('yellow', 'default'),
|
'string.end': color.build('green', 'default'),
|
||||||
'string.end': color.build('green', 'default'),
|
'integer': color.build('default', 'default'),
|
||||||
|
'float': color.build('default', 'default'),
|
||||||
'integer': color.build('default', 'default'),
|
'imaginary': 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'),
|
|
||||||
|
|
||||||
'comment': color.build('red', 'default'),
|
'comment': color.build('red', 'default'),
|
||||||
'continuation': color.build('red', 'default'),
|
'continuation': color.build('red', 'default'),
|
||||||
'system_identifier': color.build('cyan', 'default'),
|
'system_identifier': color.build('cyan', 'default'),
|
||||||
}
|
}
|
||||||
|
|
||||||
def name(self):
|
def name(self):
|
||||||
return "Python"
|
return "Python"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue