123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100 |
- """ 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(BracketMatcher, self).__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)
|