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