Show More
@@ -0,0 +1,101 b'' | |||||
|
1 | """ Provides bracket matching for Q[Plain]TextEdit widgets. | |||
|
2 | """ | |||
|
3 | ||||
|
4 | # System library imports | |||
|
5 | from PyQt4 import QtCore, QtGui | |||
|
6 | ||||
|
7 | ||||
|
8 | class BracketMatcher(QtCore.QObject): | |||
|
9 | """ Matches square brackets, braces, and parentheses based on cursor | |||
|
10 | position. | |||
|
11 | """ | |||
|
12 | ||||
|
13 | # Protected class variables. | |||
|
14 | _opening_map = { '(':')', '{':'}', '[':']' } | |||
|
15 | _closing_map = { ')':'(', '}':'{', ']':'[' } | |||
|
16 | ||||
|
17 | #-------------------------------------------------------------------------- | |||
|
18 | # 'QObject' interface | |||
|
19 | #-------------------------------------------------------------------------- | |||
|
20 | ||||
|
21 | def __init__(self, parent, multiline=True): | |||
|
22 | """ Create a call tip manager that is attached to the specified Qt | |||
|
23 | text edit widget. | |||
|
24 | """ | |||
|
25 | assert isinstance(parent, (QtGui.QTextEdit, QtGui.QPlainTextEdit)) | |||
|
26 | QtCore.QObject.__init__(self, parent) | |||
|
27 | ||||
|
28 | # The format to apply to matching brackets. | |||
|
29 | self.format = QtGui.QTextCharFormat() | |||
|
30 | self.format.setBackground(QtGui.QColor('silver')) | |||
|
31 | ||||
|
32 | parent.cursorPositionChanged.connect(self._cursor_position_changed) | |||
|
33 | ||||
|
34 | #-------------------------------------------------------------------------- | |||
|
35 | # Protected interface | |||
|
36 | #-------------------------------------------------------------------------- | |||
|
37 | ||||
|
38 | def _find_match(self, position): | |||
|
39 | """ Given a valid position in the text document, try to find the | |||
|
40 | position of the matching bracket. Returns -1 if unsuccessful. | |||
|
41 | """ | |||
|
42 | # Decide what character to search for and what direction to search in. | |||
|
43 | document = self.parent().document() | |||
|
44 | qchar = document.characterAt(position) | |||
|
45 | start_char = qchar.toAscii() | |||
|
46 | search_char = self._opening_map.get(start_char) | |||
|
47 | if search_char: | |||
|
48 | increment = 1 | |||
|
49 | else: | |||
|
50 | search_char = self._closing_map.get(start_char) | |||
|
51 | if search_char: | |||
|
52 | increment = -1 | |||
|
53 | else: | |||
|
54 | return -1 | |||
|
55 | ||||
|
56 | # Search for the character. | |||
|
57 | depth = 0 | |||
|
58 | while position >= 0 and position < document.characterCount(): | |||
|
59 | char = qchar.toAscii() | |||
|
60 | if char == start_char: | |||
|
61 | depth += 1 | |||
|
62 | elif char == search_char: | |||
|
63 | depth -= 1 | |||
|
64 | if depth == 0: | |||
|
65 | break | |||
|
66 | position += increment | |||
|
67 | qchar = document.characterAt(position) | |||
|
68 | else: | |||
|
69 | position = -1 | |||
|
70 | return position | |||
|
71 | ||||
|
72 | def _selection_for_character(self, position): | |||
|
73 | """ Convenience method for selecting a character. | |||
|
74 | """ | |||
|
75 | selection = QtGui.QTextEdit.ExtraSelection() | |||
|
76 | cursor = self.parent().textCursor() | |||
|
77 | cursor.setPosition(position) | |||
|
78 | cursor.movePosition(QtGui.QTextCursor.NextCharacter, | |||
|
79 | QtGui.QTextCursor.KeepAnchor) | |||
|
80 | selection.cursor = cursor | |||
|
81 | selection.format = self.format | |||
|
82 | return selection | |||
|
83 | ||||
|
84 | #------ Signal handlers ---------------------------------------------------- | |||
|
85 | ||||
|
86 | def _cursor_position_changed(self): | |||
|
87 | """ Updates the document formatting based on the new cursor position. | |||
|
88 | """ | |||
|
89 | # Clear out the old formatting. | |||
|
90 | text_edit = self.parent() | |||
|
91 | text_edit.setExtraSelections([]) | |||
|
92 | ||||
|
93 | # Attempt to match a bracket for the new cursor position. | |||
|
94 | cursor = text_edit.textCursor() | |||
|
95 | if not cursor.hasSelection(): | |||
|
96 | position = cursor.position() - 1 | |||
|
97 | match_position = self._find_match(position) | |||
|
98 | if match_position != -1: | |||
|
99 | extra_selections = [ self._selection_for_character(pos) | |||
|
100 | for pos in (position, match_position) ] | |||
|
101 | text_edit.setExtraSelections(extra_selections) |
@@ -9,7 +9,8 b' from PyQt4 import QtCore, QtGui' | |||||
9 | # Local imports |
|
9 | # Local imports | |
10 | from IPython.core.inputsplitter import InputSplitter |
|
10 | from IPython.core.inputsplitter import InputSplitter | |
11 | from IPython.frontend.qt.base_frontend_mixin import BaseFrontendMixin |
|
11 | from IPython.frontend.qt.base_frontend_mixin import BaseFrontendMixin | |
12 |
from IPython.utils.traitlets import Bool |
|
12 | from IPython.utils.traitlets import Bool | |
|
13 | from bracket_matcher import BracketMatcher | |||
13 | from call_tip_widget import CallTipWidget |
|
14 | from call_tip_widget import CallTipWidget | |
14 | from completion_lexer import CompletionLexer |
|
15 | from completion_lexer import CompletionLexer | |
15 | from console_widget import HistoryConsoleWidget |
|
16 | from console_widget import HistoryConsoleWidget | |
@@ -88,8 +89,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||||
88 | executed = QtCore.pyqtSignal(object) |
|
89 | executed = QtCore.pyqtSignal(object) | |
89 |
|
90 | |||
90 | # Protected class variables. |
|
91 | # Protected class variables. | |
91 | _highlighter_class = Type(FrontendHighlighter) |
|
92 | _input_splitter_class = InputSplitter | |
92 | _input_splitter_class = Type(InputSplitter) |
|
|||
93 |
|
93 | |||
94 | #--------------------------------------------------------------------------- |
|
94 | #--------------------------------------------------------------------------- | |
95 | # 'object' interface |
|
95 | # 'object' interface | |
@@ -99,10 +99,11 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||||
99 | super(FrontendWidget, self).__init__(*args, **kw) |
|
99 | super(FrontendWidget, self).__init__(*args, **kw) | |
100 |
|
100 | |||
101 | # FrontendWidget protected variables. |
|
101 | # FrontendWidget protected variables. | |
|
102 | self._bracket_matcher = BracketMatcher(self._control) | |||
102 | self._call_tip_widget = CallTipWidget(self._control) |
|
103 | self._call_tip_widget = CallTipWidget(self._control) | |
103 | self._completion_lexer = CompletionLexer(PythonLexer()) |
|
104 | self._completion_lexer = CompletionLexer(PythonLexer()) | |
104 | self._hidden = False |
|
105 | self._hidden = False | |
105 |
self._highlighter = |
|
106 | self._highlighter = FrontendHighlighter(self) | |
106 | self._input_splitter = self._input_splitter_class(input_mode='block') |
|
107 | self._input_splitter = self._input_splitter_class(input_mode='block') | |
107 | self._kernel_manager = None |
|
108 | self._kernel_manager = None | |
108 |
|
109 |
@@ -110,14 +110,12 b' class PygmentsHighlighter(QtGui.QSyntaxHighlighter):' | |||||
110 | elif hasattr(self._lexer, '_saved_state_stack'): |
|
110 | elif hasattr(self._lexer, '_saved_state_stack'): | |
111 | del self._lexer._saved_state_stack |
|
111 | del self._lexer._saved_state_stack | |
112 |
|
112 | |||
113 | index = 0 |
|
|||
114 | # Lex the text using Pygments |
|
113 | # Lex the text using Pygments | |
|
114 | index = 0 | |||
115 | for token, text in self._lexer.get_tokens(qstring): |
|
115 | for token, text in self._lexer.get_tokens(qstring): | |
116 | l = len(text) |
|
116 | length = len(text) | |
117 |
format |
|
117 | self.setFormat(index, length, self._get_format(token)) | |
118 | if format is not None: |
|
118 | index += length | |
119 | self.setFormat(index, l, format) |
|
|||
120 | index += l |
|
|||
121 |
|
119 | |||
122 | if hasattr(self._lexer, '_saved_state_stack'): |
|
120 | if hasattr(self._lexer, '_saved_state_stack'): | |
123 | data = PygmentsBlockUserData( |
|
121 | data = PygmentsBlockUserData( | |
@@ -185,11 +183,9 b' class PygmentsHighlighter(QtGui.QSyntaxHighlighter):' | |||||
185 | def _get_format_from_style(self, token, style): |
|
183 | def _get_format_from_style(self, token, style): | |
186 | """ Returns a QTextCharFormat for token by reading a Pygments style. |
|
184 | """ Returns a QTextCharFormat for token by reading a Pygments style. | |
187 | """ |
|
185 | """ | |
188 | result = None |
|
186 | result = QtGui.QTextCharFormat() | |
189 | for key, value in style.style_for_token(token).items(): |
|
187 | for key, value in style.style_for_token(token).items(): | |
190 | if value: |
|
188 | if value: | |
191 | if result is None: |
|
|||
192 | result = QtGui.QTextCharFormat() |
|
|||
193 | if key == 'color': |
|
189 | if key == 'color': | |
194 | result.setForeground(self._get_brush(value)) |
|
190 | result.setForeground(self._get_brush(value)) | |
195 | elif key == 'bgcolor': |
|
191 | elif key == 'bgcolor': |
General Comments 0
You need to be logged in to leave comments.
Login now