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 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
|
# (C) Copyright 2005-2023 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!
""" Provides bracket matching for Q[Plain]TextEdit widgets.
"""
from pyface.qt import QtCore, QtGui
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, (QtGui.QTextEdit, QtGui.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)
def _remove_event_listeners(self):
self._text_edit.cursorPositionChanged.disconnect(
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 = QtGui.QTextEdit.ExtraSelection()
cursor = self._text_edit.textCursor()
cursor.setPosition(position)
cursor.movePosition(
QtGui.QTextCursor.MoveOperation.NextCharacter, QtGui.QTextCursor.MoveMode.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)
|