pmacs3/tab_python.py

142 lines
4.6 KiB
Python

import point, regex, util, tab
class PythonTabber(tab.Tabber):
start_tags = {'(': ')',
'{': '}',
'[': ']'}
close_tags = {')': '(',
'}': '{',
']': '['}
def __init__(self, m):
tab.Tabber.__init__(self, m)
self.y = None
self.index = None
self.tab_stack = None
self.line_depth = None
def stack_append(self, item):
self.tab_stack.append(item)
def stack_pop(self):
self.tab_stack.pop(-1)
def base_indentation_level(self, y):
return y == 0
def calculate_tabs(self, start=0, goal=None):
lines = self.mode.window.buffer.lines
tokens = self.mode.highlighter.tokens
buffer = self.mode.window.buffer
if self.levels is None:
self.levels = [None] * (len(lines))
self.index = 0
self.y = start
self.base = 0
self.tab_stack = []
# we want to process every logical line in the file
while self.y < len(lines):
line = lines[self.y]
start_index = self.index
start_point = point.Point(0, self.y)
start_offset = buffer.get_point_offset(start_point)
end_point = point.Point(len(line), self.y)
end_offset = buffer.get_point_offset(end_point)
# we want to find all the tokens on the line we are currently processing
while self.index < len(tokens):
token = tokens[self.index]
if token.end > end_offset:
break
self.index += 1
self.handle_line(line,
start_offset, start_index,
end_offset, self.index)
self.levels[self.y] = self.line_depth
self.y += 1
if goal is not None and self.y > goal:
break
def get_line_depth(self):
if len(self.tab_stack) > 0:
return self.tab_stack[-1][1]
else:
return self.base
def handle_line(self, line, start_offset, start_index, end_offset, end_index):
self.line_depth = self.get_line_depth()
tokens = self.mode.highlighter.tokens
if start_index >= len(tokens):
return
if regex.whitespace.match(line):
return
if len(self.tab_stack) == 0 and tokens[start_index].start >= start_offset:
self.base = util.count_leading_whitespace(line)
for i in range(start_index, end_index):
token = tokens[i]
s = token.string
if s in self.start_tags:
if i < end_index - 1:
i = tokens[i+1].start - start_offset
elif len(self.tab_stack) > 0:
i = self.tab_stack[-1][1] + 4
else:
i = self.base + 4
self.stack_append((s, i))
elif s in self.close_tags:
assert len(self.tab_stack), "Unbalanced closing tag"
assert self.tab_stack[-1][0] == self.close_tags[s], "Unmatched closing tag"
self.stack_pop()
if i == start_index:
self.line_depth = self.get_line_depth()
if tokens[start_index].start < start_offset:
self.line_depth = -1
prebase = self.base
s = tokens[start_index].string
e = tokens[end_index-1].string
if s == "except" or s == "elif" or s == "else":
if self.y > 0 and self.line_depth == self.levels[self.y - 1]:
self.line_depth = max(0, self.line_depth - 4)
elif (s == "return" or s == "raise" or s == "yield" or s == "break" or
s == "pass" or s == 'continue'):
self.base = max(0, self.base - 4)
if e == "\\":
if len(self.tab_stack) and self.tab_stack[-1][0] == "\\":
pass
else:
self.stack_append(("\\", prebase + 4))
return
elif e == ":":
self.base += 4
elif len(self.tab_stack) and self.tab_stack[-1][0] == "\\":
self.stack_pop()
def get_indentation_level(self, y):
if self.levels is not None and self.levels[y] is not None:
result = self.levels[y]
else:
i = max(0, y - 1)
while i > 0:
if self.base_indentation_level(i):
break
i -= 1
self.calculate_tabs(i, y)
result = self.levels[y]
if result == -1:
return None
return result