def _get_paren_pos(self, tc, column): pos, char = self._get_first_open_paren(tc, column) mapping = {'(': PAREN, '[': SQUARE, '{': BRACE} tc2 = QTextCursor(tc) tc2.setPosition(pos) import sys ol, oc = self.editor.modes.get(SymbolMatcherMode).symbol_pos( tc2, OPEN, mapping[char]) cl, cc = self.editor.modes.get(SymbolMatcherMode).symbol_pos( tc2, CLOSE, mapping[char]) return (ol, oc), (cl, cc)
def _get_indent(self, cursor): ln, column = self._helper.cursor_position() fullline = self._get_full_line(cursor).rstrip() line = fullline[:column] pre, post = AutoIndentMode._get_indent(self, cursor) if self._at_block_start(cursor, line): return pre, post # return pressed in comments c2 = QTextCursor(cursor) if c2.atBlockEnd(): c2.movePosition(c2.Left) if (self._helper.is_comment_or_string( c2, formats=['comment', 'docstring']) or fullline.endswith(('"""', "'''"))): if line.strip().startswith("#") and column != len(fullline): post += '# ' return pre, post # between parens elif self._between_paren(cursor, column): return self._handle_indent_between_paren( column, line, (pre, post), cursor) else: lastword = self._get_last_word(cursor) lastwordu = self._get_last_word_unstripped(cursor) end_with_op = fullline.endswith( ('+', '-', '*', '/', '=', ' and', ' or', '%')) in_string_def, char = self._is_in_string_def(fullline, column) if in_string_def: post, pre = self._handle_indent_inside_string( char, cursor, fullline, post) elif (fullline.rstrip().endswith(":") and lastword.rstrip().endswith(':') and self._at_block_end(cursor, fullline)): post = self._handle_new_scope_indentation( cursor, fullline) elif line.endswith("\\"): # if user typed \ and press enter -> indent is always # one level higher post += self.editor.tab_length * " " elif (fullline.endswith((')', '}', ']')) and lastword.endswith((')', '}', ']'))): post = self._handle_indent_after_paren(cursor, post) elif (not fullline.endswith("\\") and (end_with_op or not self._at_block_end(cursor, fullline))): post, pre = self._handle_indent_in_statement( fullline, lastwordu, post, pre) elif ((self._at_block_end(cursor, fullline) and fullline.strip().startswith('return')) or lastword == "pass"): post = post[:-self.editor.tab_length] return pre, post
def _handle_indent_between_paren(self, column, line, parent_impl, tc): """ Handle indent between symbols such as parenthesis, braces,... """ pre, post = parent_impl next_char = self._get_next_char(tc) prev_char = self._get_prev_char(tc) prev_open = prev_char in ['[', '(', '{'] next_close = next_char in [']', ')', '}'] (open_line, open_symbol_col), (close_line, close_col) = \ self._get_paren_pos(tc, column) open_line_txt = self._helper.line_text(open_line) open_line_indent = len(open_line_txt) - len(open_line_txt.lstrip()) if prev_open: post = (open_line_indent + self.editor.tab_length) * ' ' elif next_close and prev_char != ',': post = open_line_indent * ' ' elif tc.block().blockNumber() == open_line: post = open_symbol_col * ' ' # adapt indent if cursor on closing line and next line have same # indent -> PEP8 compliance if close_line and close_col: txt = self._helper.line_text(close_line) bn = tc.block().blockNumber() flg = bn == close_line next_indent = self._helper.line_indent(bn + 1) * ' ' if flg and txt.strip().endswith(':') and next_indent == post: # | look at how the previous line ( ``':'):`` ) was # over-indented, this is actually what we are trying to # achieve here post += self.editor.tab_length * ' ' # breaking string if next_char in ['"', "'"]: tc.movePosition(tc.Left) is_string = self._helper.is_comment_or_string(tc, formats=['string']) if next_char in ['"', "'"]: tc.movePosition(tc.Right) if is_string: trav = QTextCursor(tc) while self._helper.is_comment_or_string( trav, formats=['string']): trav.movePosition(trav.Left) trav.movePosition(trav.Right) symbol = '%s' % self._get_next_char(trav) pre += symbol post += symbol return pre, post
def _get_indent(self, cursor): ln, column = self._helper.cursor_position() fullline = self._get_full_line(cursor).rstrip() line = fullline[:column] pre, post = AutoIndentMode._get_indent(self, cursor) if self._at_block_start(cursor, line): return pre, post # return pressed in comments c2 = QTextCursor(cursor) if c2.atBlockEnd(): c2.movePosition(c2.Left) if (self._helper.is_comment_or_string(c2, formats=['comment', 'docstring']) or fullline.endswith(('"""', "'''"))): if line.strip().startswith("#") and column != len(fullline): post += '# ' return pre, post # between parens elif self._between_paren(cursor, column): return self._handle_indent_between_paren(column, line, (pre, post), cursor) else: lastword = self._get_last_word(cursor) lastwordu = self._get_last_word_unstripped(cursor) end_with_op = fullline.endswith( ('+', '-', '*', '/', '=', ' and', ' or', '%')) in_string_def, char = self._is_in_string_def(fullline, column) if in_string_def: post, pre = self._handle_indent_inside_string( char, cursor, fullline, post) elif (fullline.rstrip().endswith(":") and lastword.rstrip().endswith(':') and self._at_block_end(cursor, fullline)): post = self._handle_new_scope_indentation(cursor, fullline) elif line.endswith("\\"): # if user typed \ and press enter -> indent is always # one level higher post += self.editor.tab_length * " " elif (fullline.endswith((')', '}', ']')) and lastword.endswith( (')', '}', ']'))): post = self._handle_indent_after_paren(cursor, post) elif (not fullline.endswith("\\") and (end_with_op or not self._at_block_end(cursor, fullline))): post, pre = self._handle_indent_in_statement( fullline, lastwordu, post, pre) elif ((self._at_block_end(cursor, fullline) and fullline.strip().startswith('return')) or lastword == "pass"): post = post[:-self.editor.tab_length] return pre, post
def _get_prev_char(tc): tc2 = QTextCursor(tc) tc2.movePosition(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor) char = tc2.selectedText() while char == ' ': tc2.movePosition(tc2.PreviousCharacter, tc2.KeepAnchor) char = tc2.selectedText() return char.strip()
def _get_prev_char(tc): tc2 = QTextCursor(tc) tc2.movePosition(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor) char = tc2.selectedText() while char == ' ': tc2.movePosition(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor) char = tc2.selectedText() return char.strip()
def _handle_indent_between_paren(self, column, line, parent_impl, tc): """ Handle indent between symbols such as parenthesis, braces,... """ pre, post = parent_impl next_char = self._get_next_char(tc) prev_char = self._get_prev_char(tc) prev_open = prev_char in ['[', '(', '{'] next_close = next_char in [']', ')', '}'] (open_line, open_symbol_col), (close_line, close_col) = \ self._get_paren_pos(tc, column) open_line_txt = self._helper.line_text(open_line) open_line_indent = len(open_line_txt) - len(open_line_txt.lstrip()) if prev_open: post = (open_line_indent + self.editor.tab_length) * ' ' elif next_close and prev_char != ',': post = open_line_indent * ' ' elif tc.block().blockNumber() == open_line: post = open_symbol_col * ' ' # adapt indent if cursor on closing line and next line have same # indent -> PEP8 compliance if close_line and close_col: txt = self._helper.line_text(close_line) bn = tc.block().blockNumber() flg = bn == close_line next_indent = self._helper.line_indent(bn + 1) * ' ' if flg and txt.strip().endswith(':') and next_indent == post: # | look at how the previous line ( ``':'):`` ) was # over-indented, this is actually what we are trying to # achieve here post += self.editor.tab_length * ' ' # breaking string if next_char in ['"', "'"]: tc.movePosition(tc.Left) is_string = self._helper.is_comment_or_string(tc, formats=['string']) if next_char in ['"', "'"]: tc.movePosition(tc.Right) if is_string: trav = QTextCursor(tc) while self._helper.is_comment_or_string(trav, formats=['string']): trav.movePosition(trav.Left) trav.movePosition(trav.Right) symbol = '%s' % self._get_next_char(trav) pre += symbol post += symbol return pre, post
def _get_full_line(tc): tc2 = QTextCursor(tc) tc2.select(QTextCursor.LineUnderCursor) full_line = tc2.selectedText() return full_line
def _get_next_char(tc): tc2 = QTextCursor(tc) tc2.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor) char = tc2.selectedText() return char
def _get_first_open_paren(self, tc, column): pos = None char = None ln = tc.blockNumber() tc_trav = QTextCursor(tc) mapping = { '(': (CLOSE, PAREN), '[': (CLOSE, SQUARE), '{': (CLOSE, BRACE) } while ln >= 0 and tc.block().text().strip(): tc_trav.movePosition(tc_trav.StartOfLine, tc_trav.MoveAnchor) lists = get_block_symbol_data(self.editor, tc_trav.block()) all_symbols = [] for symbols in lists: all_symbols += [s for s in symbols] symbols = sorted(all_symbols, key=lambda x: x.position) for paren in reversed(symbols): if paren.position < column: if self._is_paren_open(paren): if paren.position > column: continue else: pos = tc_trav.position() + paren.position char = paren.character # ensure it does not have a closing paren on # the same line tc3 = QTextCursor(tc) tc3.setPosition(pos) try: ch, ch_type = mapping[paren.character] l, c = self.editor.modes.get( SymbolMatcherMode).symbol_pos( tc3, ch, ch_type) except KeyError: continue if l == ln and c < column: continue return pos, char # check previous line tc_trav.movePosition(tc_trav.Up, tc_trav.MoveAnchor) ln = tc_trav.blockNumber() column = len(self._helper.line_text(ln)) return pos, char
def _get_last_word_unstripped(tc): tc2 = QTextCursor(tc) tc2.movePosition(QTextCursor.Left, tc.KeepAnchor, 1) tc2.movePosition(QTextCursor.WordLeft, tc.KeepAnchor) return tc2.selectedText()