142 lines
4.6 KiB
Python
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
|