File: bracket_matcher.py

package info (click to toggle)
python-qtconsole 5.6.1-4
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 1,184 kB
  • sloc: python: 7,222; makefile: 180; sh: 36
file content (100 lines) | stat: -rw-r--r-- 3,733 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
""" Provides bracket matching for Q[Plain]TextEdit widgets.
"""

# System library imports
from qtpy import QtCore, QtGui, QtWidgets


class BracketMatcher(QtCore.QObject):
    """ Matches square brackets, braces, and parentheses based on cursor
        position.
    """

    # Protected class variables.
    _opening_map = { '(':')', '{':'}', '[':']' }
    _closing_map = { ')':'(', '}':'{', ']':'[' }

    #--------------------------------------------------------------------------
    # 'QObject' interface
    #--------------------------------------------------------------------------

    def __init__(self, text_edit):
        """ Create a call tip manager that is attached to the specified Qt
            text edit widget.
        """
        assert isinstance(text_edit, (QtWidgets.QTextEdit, QtWidgets.QPlainTextEdit))
        super().__init__()

        # The format to apply to matching brackets.
        self.format = QtGui.QTextCharFormat()
        self.format.setBackground(QtGui.QColor('silver'))

        self._text_edit = text_edit
        text_edit.cursorPositionChanged.connect(self._cursor_position_changed)

    #--------------------------------------------------------------------------
    # Protected interface
    #--------------------------------------------------------------------------

    def _find_match(self, position):
        """ Given a valid position in the text document, try to find the
            position of the matching bracket. Returns -1 if unsuccessful.
        """
        # Decide what character to search for and what direction to search in.
        document = self._text_edit.document()
        start_char = document.characterAt(position)
        search_char = self._opening_map.get(start_char)
        if search_char:
            increment = 1
        else:
            search_char = self._closing_map.get(start_char)
            if search_char:
                increment = -1
            else:
                return -1

        # Search for the character.
        char = start_char
        depth = 0
        while position >= 0 and position < document.characterCount():
            if char == start_char:
                depth += 1
            elif char == search_char:
                depth -= 1
            if depth == 0:
                break
            position += increment
            char = document.characterAt(position)
        else:
            position = -1
        return position

    def _selection_for_character(self, position):
        """ Convenience method for selecting a character.
        """
        selection = QtWidgets.QTextEdit.ExtraSelection()
        cursor = self._text_edit.textCursor()
        cursor.setPosition(position)
        cursor.movePosition(QtGui.QTextCursor.NextCharacter,
                            QtGui.QTextCursor.KeepAnchor)
        selection.cursor = cursor
        selection.format = self.format
        return selection

    #------ Signal handlers ----------------------------------------------------

    def _cursor_position_changed(self):
        """ Updates the document formatting based on the new cursor position.
        """
        # Clear out the old formatting.
        self._text_edit.setExtraSelections([])

        # Attempt to match a bracket for the new cursor position.
        cursor = self._text_edit.textCursor()
        if not cursor.hasSelection():
            position = cursor.position() - 1
            match_position = self._find_match(position)
            if match_position != -1:
                extra_selections = [ self._selection_for_character(pos)
                                     for pos in (position, match_position) ]
                self._text_edit.setExtraSelections(extra_selections)