##// END OF EJS Templates
Made use of plain text consistent.
epatters -
Show More
@@ -1,74 +1,74 b''
1 # System library imports
1 # System library imports
2 from pygments.token import Token, is_token_subtype
2 from pygments.token import Token, is_token_subtype
3
3
4
4
5 class CompletionLexer(object):
5 class CompletionLexer(object):
6 """ Uses Pygments and some auxillary information to lex code snippets for
6 """ Uses Pygments and some auxillary information to lex code snippets for
7 symbol contexts.
7 symbol contexts.
8 """
8 """
9
9
10 # Maps Lexer names to a list of possible name separators
10 # Maps Lexer names to a list of possible name separators
11 separator_map = { 'C' : [ '.', '->' ],
11 separator_map = { 'C' : [ '.', '->' ],
12 'C++' : [ '.', '->', '::' ],
12 'C++' : [ '.', '->', '::' ],
13 'Python' : [ '.' ] }
13 'Python' : [ '.' ] }
14
14
15 def __init__(self, lexer):
15 def __init__(self, lexer):
16 """ Create a CompletionLexer using the specified Pygments lexer.
16 """ Create a CompletionLexer using the specified Pygments lexer.
17 """
17 """
18 self.lexer = lexer
18 self.lexer = lexer
19
19
20 def get_context(self, string):
20 def get_context(self, string):
21 """ Assuming the cursor is at the end of the specified string, get the
21 """ Assuming the cursor is at the end of the specified string, get the
22 context (a list of names) for the symbol at cursor position.
22 context (a list of names) for the symbol at cursor position.
23 """
23 """
24 context = []
24 context = []
25 reversed_tokens = list(self._lexer.get_tokens(string))
25 reversed_tokens = list(self._lexer.get_tokens(string))
26 reversed_tokens.reverse()
26 reversed_tokens.reverse()
27
27
28 # Pygments often tacks on a newline when none is specified in the input.
28 # Pygments often tacks on a newline when none is specified in the input.
29 # Remove this newline.
29 # Remove this newline.
30 if reversed_tokens and reversed_tokens[0][1].endswith('\n') and \
30 if reversed_tokens and reversed_tokens[0][1].endswith('\n') and \
31 not string.endswith('\n'):
31 not string.endswith('\n'):
32 reversed_tokens.pop(0)
32 reversed_tokens.pop(0)
33
33
34 current_op = unicode()
34 current_op = ''
35 for token, text in reversed_tokens:
35 for token, text in reversed_tokens:
36
36
37 if is_token_subtype(token, Token.Name):
37 if is_token_subtype(token, Token.Name):
38
38
39 # Handle a trailing separator, e.g 'foo.bar.'
39 # Handle a trailing separator, e.g 'foo.bar.'
40 if current_op in self._name_separators:
40 if current_op in self._name_separators:
41 if not context:
41 if not context:
42 context.insert(0, unicode())
42 context.insert(0, '')
43
43
44 # Handle non-separator operators and punction.
44 # Handle non-separator operators and punction.
45 elif current_op:
45 elif current_op:
46 break
46 break
47
47
48 context.insert(0, text)
48 context.insert(0, text)
49 current_op = unicode()
49 current_op = ''
50
50
51 # Pygments doesn't understand that, e.g., '->' is a single operator
51 # Pygments doesn't understand that, e.g., '->' is a single operator
52 # in C++. This is why we have to build up an operator from
52 # in C++. This is why we have to build up an operator from
53 # potentially several tokens.
53 # potentially several tokens.
54 elif token is Token.Operator or token is Token.Punctuation:
54 elif token is Token.Operator or token is Token.Punctuation:
55 current_op = text + current_op
55 current_op = text + current_op
56
56
57 # Break on anything that is not a Operator, Punctuation, or Name.
57 # Break on anything that is not a Operator, Punctuation, or Name.
58 else:
58 else:
59 break
59 break
60
60
61 return context
61 return context
62
62
63 def get_lexer(self, lexer):
63 def get_lexer(self, lexer):
64 return self._lexer
64 return self._lexer
65
65
66 def set_lexer(self, lexer, name_separators=None):
66 def set_lexer(self, lexer, name_separators=None):
67 self._lexer = lexer
67 self._lexer = lexer
68 if name_separators is None:
68 if name_separators is None:
69 self._name_separators = self.separator_map.get(lexer.name, ['.'])
69 self._name_separators = self.separator_map.get(lexer.name, ['.'])
70 else:
70 else:
71 self._name_separators = list(name_separators)
71 self._name_separators = list(name_separators)
72
72
73 lexer = property(get_lexer, set_lexer)
73 lexer = property(get_lexer, set_lexer)
74
74
@@ -1,124 +1,124 b''
1 # System library imports
1 # System library imports
2 from PyQt4 import QtCore, QtGui
2 from PyQt4 import QtCore, QtGui
3
3
4
4
5 class CompletionWidget(QtGui.QListWidget):
5 class CompletionWidget(QtGui.QListWidget):
6 """ A widget for GUI tab completion.
6 """ A widget for GUI tab completion.
7 """
7 """
8
8
9 #--------------------------------------------------------------------------
9 #--------------------------------------------------------------------------
10 # 'QWidget' interface
10 # 'QWidget' interface
11 #--------------------------------------------------------------------------
11 #--------------------------------------------------------------------------
12
12
13 def __init__(self, parent):
13 def __init__(self, parent):
14 """ Create a completion widget that is attached to the specified Qt
14 """ Create a completion widget that is attached to the specified Qt
15 text edit widget.
15 text edit widget.
16 """
16 """
17 assert isinstance(parent, (QtGui.QTextEdit, QtGui.QPlainTextEdit))
17 assert isinstance(parent, (QtGui.QTextEdit, QtGui.QPlainTextEdit))
18 QtGui.QListWidget.__init__(self, parent)
18 QtGui.QListWidget.__init__(self, parent)
19
19
20 self.setWindowFlags(QtCore.Qt.ToolTip | QtCore.Qt.WindowStaysOnTopHint)
20 self.setWindowFlags(QtCore.Qt.ToolTip | QtCore.Qt.WindowStaysOnTopHint)
21 self.setAttribute(QtCore.Qt.WA_StaticContents)
21 self.setAttribute(QtCore.Qt.WA_StaticContents)
22
22
23 # Ensure that parent keeps focus when widget is displayed.
23 # Ensure that parent keeps focus when widget is displayed.
24 self.setFocusProxy(parent)
24 self.setFocusProxy(parent)
25
25
26 self.setFrameShadow(QtGui.QFrame.Plain)
26 self.setFrameShadow(QtGui.QFrame.Plain)
27 self.setFrameShape(QtGui.QFrame.StyledPanel)
27 self.setFrameShape(QtGui.QFrame.StyledPanel)
28
28
29 self.itemActivated.connect(self._complete_current)
29 self.itemActivated.connect(self._complete_current)
30
30
31 def hideEvent(self, event):
31 def hideEvent(self, event):
32 """ Reimplemented to disconnect the cursor movement handler.
32 """ Reimplemented to disconnect the cursor movement handler.
33 """
33 """
34 QtGui.QListWidget.hideEvent(self, event)
34 QtGui.QListWidget.hideEvent(self, event)
35 try:
35 try:
36 self.parent().cursorPositionChanged.disconnect(self._update_current)
36 self.parent().cursorPositionChanged.disconnect(self._update_current)
37 except TypeError:
37 except TypeError:
38 pass
38 pass
39
39
40 def keyPressEvent(self, event):
40 def keyPressEvent(self, event):
41 """ Reimplemented to update the list.
41 """ Reimplemented to update the list.
42 """
42 """
43 key, text = event.key(), event.text()
43 key, text = event.key(), event.text()
44
44
45 if key in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter,
45 if key in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter,
46 QtCore.Qt.Key_Tab):
46 QtCore.Qt.Key_Tab):
47 self._complete_current()
47 self._complete_current()
48 event.accept()
48 event.accept()
49
49
50 elif key == QtCore.Qt.Key_Escape:
50 elif key == QtCore.Qt.Key_Escape:
51 self.hide()
51 self.hide()
52 event.accept()
52 event.accept()
53
53
54 elif key in (QtCore.Qt.Key_Up, QtCore.Qt.Key_Down,
54 elif key in (QtCore.Qt.Key_Up, QtCore.Qt.Key_Down,
55 QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown,
55 QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown,
56 QtCore.Qt.Key_Home, QtCore.Qt.Key_End):
56 QtCore.Qt.Key_Home, QtCore.Qt.Key_End):
57 QtGui.QListWidget.keyPressEvent(self, event)
57 QtGui.QListWidget.keyPressEvent(self, event)
58 event.accept()
58 event.accept()
59
59
60 else:
60 else:
61 event.ignore()
61 event.ignore()
62
62
63 def showEvent(self, event):
63 def showEvent(self, event):
64 """ Reimplemented to connect the cursor movement handler.
64 """ Reimplemented to connect the cursor movement handler.
65 """
65 """
66 QtGui.QListWidget.showEvent(self, event)
66 QtGui.QListWidget.showEvent(self, event)
67 self.parent().cursorPositionChanged.connect(self._update_current)
67 self.parent().cursorPositionChanged.connect(self._update_current)
68
68
69 #--------------------------------------------------------------------------
69 #--------------------------------------------------------------------------
70 # 'CompletionWidget' interface
70 # 'CompletionWidget' interface
71 #--------------------------------------------------------------------------
71 #--------------------------------------------------------------------------
72
72
73 def show_items(self, cursor, items):
73 def show_items(self, cursor, items):
74 """ Shows the completion widget with 'items' at the position specified
74 """ Shows the completion widget with 'items' at the position specified
75 by 'cursor'.
75 by 'cursor'.
76 """
76 """
77 text_edit = self.parent()
77 text_edit = self.parent()
78 point = text_edit.cursorRect(cursor).bottomRight()
78 point = text_edit.cursorRect(cursor).bottomRight()
79 point = text_edit.mapToGlobal(point)
79 point = text_edit.mapToGlobal(point)
80 screen_rect = QtGui.QApplication.desktop().availableGeometry(self)
80 screen_rect = QtGui.QApplication.desktop().availableGeometry(self)
81 if screen_rect.size().height() - point.y() - self.height() < 0:
81 if screen_rect.size().height() - point.y() - self.height() < 0:
82 point = text_edit.mapToGlobal(text_edit.cursorRect().topRight())
82 point = text_edit.mapToGlobal(text_edit.cursorRect().topRight())
83 point.setY(point.y() - self.height())
83 point.setY(point.y() - self.height())
84 self.move(point)
84 self.move(point)
85
85
86 self._start_position = cursor.position()
86 self._start_position = cursor.position()
87 self.clear()
87 self.clear()
88 self.addItems(items)
88 self.addItems(items)
89 self.setCurrentRow(0)
89 self.setCurrentRow(0)
90 self.show()
90 self.show()
91
91
92 #--------------------------------------------------------------------------
92 #--------------------------------------------------------------------------
93 # Protected interface
93 # Protected interface
94 #--------------------------------------------------------------------------
94 #--------------------------------------------------------------------------
95
95
96 def _complete_current(self):
96 def _complete_current(self):
97 """ Perform the completion with the currently selected item.
97 """ Perform the completion with the currently selected item.
98 """
98 """
99 self._current_text_cursor().insertText(self.currentItem().text())
99 self._current_text_cursor().insertText(self.currentItem().text())
100 self.hide()
100 self.hide()
101
101
102 def _current_text_cursor(self):
102 def _current_text_cursor(self):
103 """ Returns a cursor with text between the start position and the
103 """ Returns a cursor with text between the start position and the
104 current position selected.
104 current position selected.
105 """
105 """
106 cursor = self.parent().textCursor()
106 cursor = self.parent().textCursor()
107 if cursor.position() >= self._start_position:
107 if cursor.position() >= self._start_position:
108 cursor.setPosition(self._start_position,
108 cursor.setPosition(self._start_position,
109 QtGui.QTextCursor.KeepAnchor)
109 QtGui.QTextCursor.KeepAnchor)
110 return cursor
110 return cursor
111
111
112 def _update_current(self):
112 def _update_current(self):
113 """ Updates the current item based on the current text.
113 """ Updates the current item based on the current text.
114 """
114 """
115 prefix = self._current_text_cursor().selectedText()
115 prefix = self._current_text_cursor().selection().toPlainText()
116 if prefix:
116 if prefix:
117 items = self.findItems(prefix, (QtCore.Qt.MatchStartsWith |
117 items = self.findItems(prefix, (QtCore.Qt.MatchStartsWith |
118 QtCore.Qt.MatchCaseSensitive))
118 QtCore.Qt.MatchCaseSensitive))
119 if items:
119 if items:
120 self.setCurrentItem(items[0])
120 self.setCurrentItem(items[0])
121 else:
121 else:
122 self.hide()
122 self.hide()
123 else:
123 else:
124 self.hide()
124 self.hide()
@@ -1,380 +1,380 b''
1 # Standard library imports
1 # Standard library imports
2 import signal
2 import signal
3 import sys
3 import sys
4
4
5 # System library imports
5 # System library imports
6 from pygments.lexers import PythonLexer
6 from pygments.lexers import PythonLexer
7 from PyQt4 import QtCore, QtGui
7 from PyQt4 import QtCore, QtGui
8 import zmq
8 import zmq
9
9
10 # Local imports
10 # Local imports
11 from IPython.core.inputsplitter import InputSplitter
11 from IPython.core.inputsplitter import InputSplitter
12 from call_tip_widget import CallTipWidget
12 from call_tip_widget import CallTipWidget
13 from completion_lexer import CompletionLexer
13 from completion_lexer import CompletionLexer
14 from console_widget import HistoryConsoleWidget
14 from console_widget import HistoryConsoleWidget
15 from pygments_highlighter import PygmentsHighlighter
15 from pygments_highlighter import PygmentsHighlighter
16
16
17
17
18 class FrontendHighlighter(PygmentsHighlighter):
18 class FrontendHighlighter(PygmentsHighlighter):
19 """ A PygmentsHighlighter that can be turned on and off and that ignores
19 """ A PygmentsHighlighter that can be turned on and off and that ignores
20 prompts.
20 prompts.
21 """
21 """
22
22
23 def __init__(self, frontend):
23 def __init__(self, frontend):
24 super(FrontendHighlighter, self).__init__(frontend.document())
24 super(FrontendHighlighter, self).__init__(frontend.document())
25 self._current_offset = 0
25 self._current_offset = 0
26 self._frontend = frontend
26 self._frontend = frontend
27 self.highlighting_on = False
27 self.highlighting_on = False
28
28
29 def highlightBlock(self, qstring):
29 def highlightBlock(self, qstring):
30 """ Highlight a block of text. Reimplemented to highlight selectively.
30 """ Highlight a block of text. Reimplemented to highlight selectively.
31 """
31 """
32 if not self.highlighting_on:
32 if not self.highlighting_on:
33 return
33 return
34
34
35 # The input to this function is unicode string that may contain
35 # The input to this function is unicode string that may contain
36 # paragraph break characters, non-breaking spaces, etc. Here we acquire
36 # paragraph break characters, non-breaking spaces, etc. Here we acquire
37 # the string as plain text so we can compare it.
37 # the string as plain text so we can compare it.
38 current_block = self.currentBlock()
38 current_block = self.currentBlock()
39 string = self._frontend._get_block_plain_text(current_block)
39 string = self._frontend._get_block_plain_text(current_block)
40
40
41 # Decide whether to check for the regular or continuation prompt.
41 # Decide whether to check for the regular or continuation prompt.
42 if current_block.contains(self._frontend._prompt_pos):
42 if current_block.contains(self._frontend._prompt_pos):
43 prompt = self._frontend._prompt
43 prompt = self._frontend._prompt
44 else:
44 else:
45 prompt = self._frontend._continuation_prompt
45 prompt = self._frontend._continuation_prompt
46
46
47 # Don't highlight the part of the string that contains the prompt.
47 # Don't highlight the part of the string that contains the prompt.
48 if string.startswith(prompt):
48 if string.startswith(prompt):
49 self._current_offset = len(prompt)
49 self._current_offset = len(prompt)
50 qstring.remove(0, len(prompt))
50 qstring.remove(0, len(prompt))
51 else:
51 else:
52 self._current_offset = 0
52 self._current_offset = 0
53
53
54 PygmentsHighlighter.highlightBlock(self, qstring)
54 PygmentsHighlighter.highlightBlock(self, qstring)
55
55
56 def setFormat(self, start, count, format):
56 def setFormat(self, start, count, format):
57 """ Reimplemented to highlight selectively.
57 """ Reimplemented to highlight selectively.
58 """
58 """
59 start += self._current_offset
59 start += self._current_offset
60 PygmentsHighlighter.setFormat(self, start, count, format)
60 PygmentsHighlighter.setFormat(self, start, count, format)
61
61
62
62
63 class FrontendWidget(HistoryConsoleWidget):
63 class FrontendWidget(HistoryConsoleWidget):
64 """ A Qt frontend for a generic Python kernel.
64 """ A Qt frontend for a generic Python kernel.
65 """
65 """
66
66
67 # Emitted when an 'execute_reply' is received from the kernel.
67 # Emitted when an 'execute_reply' is received from the kernel.
68 executed = QtCore.pyqtSignal(object)
68 executed = QtCore.pyqtSignal(object)
69
69
70 #---------------------------------------------------------------------------
70 #---------------------------------------------------------------------------
71 # 'QObject' interface
71 # 'QObject' interface
72 #---------------------------------------------------------------------------
72 #---------------------------------------------------------------------------
73
73
74 def __init__(self, parent=None):
74 def __init__(self, parent=None):
75 super(FrontendWidget, self).__init__(parent)
75 super(FrontendWidget, self).__init__(parent)
76
76
77 # FrontendWidget protected variables.
77 # FrontendWidget protected variables.
78 self._call_tip_widget = CallTipWidget(self)
78 self._call_tip_widget = CallTipWidget(self)
79 self._completion_lexer = CompletionLexer(PythonLexer())
79 self._completion_lexer = CompletionLexer(PythonLexer())
80 self._hidden = True
80 self._hidden = True
81 self._highlighter = FrontendHighlighter(self)
81 self._highlighter = FrontendHighlighter(self)
82 self._input_splitter = InputSplitter(input_mode='replace')
82 self._input_splitter = InputSplitter(input_mode='replace')
83 self._kernel_manager = None
83 self._kernel_manager = None
84
84
85 # Configure the ConsoleWidget.
85 # Configure the ConsoleWidget.
86 self._set_continuation_prompt('... ')
86 self._set_continuation_prompt('... ')
87
87
88 self.document().contentsChange.connect(self._document_contents_change)
88 self.document().contentsChange.connect(self._document_contents_change)
89
89
90 #---------------------------------------------------------------------------
90 #---------------------------------------------------------------------------
91 # 'QWidget' interface
91 # 'QWidget' interface
92 #---------------------------------------------------------------------------
92 #---------------------------------------------------------------------------
93
93
94 def focusOutEvent(self, event):
94 def focusOutEvent(self, event):
95 """ Reimplemented to hide calltips.
95 """ Reimplemented to hide calltips.
96 """
96 """
97 self._call_tip_widget.hide()
97 self._call_tip_widget.hide()
98 super(FrontendWidget, self).focusOutEvent(event)
98 super(FrontendWidget, self).focusOutEvent(event)
99
99
100 def keyPressEvent(self, event):
100 def keyPressEvent(self, event):
101 """ Reimplemented to allow calltips to process events and to send
101 """ Reimplemented to allow calltips to process events and to send
102 signals to the kernel.
102 signals to the kernel.
103 """
103 """
104 if self._executing and event.key() == QtCore.Qt.Key_C and \
104 if self._executing and event.key() == QtCore.Qt.Key_C and \
105 self._control_down(event.modifiers()):
105 self._control_down(event.modifiers()):
106 self._interrupt_kernel()
106 self._interrupt_kernel()
107 else:
107 else:
108 if self._call_tip_widget.isVisible():
108 if self._call_tip_widget.isVisible():
109 self._call_tip_widget.keyPressEvent(event)
109 self._call_tip_widget.keyPressEvent(event)
110 super(FrontendWidget, self).keyPressEvent(event)
110 super(FrontendWidget, self).keyPressEvent(event)
111
111
112 #---------------------------------------------------------------------------
112 #---------------------------------------------------------------------------
113 # 'ConsoleWidget' abstract interface
113 # 'ConsoleWidget' abstract interface
114 #---------------------------------------------------------------------------
114 #---------------------------------------------------------------------------
115
115
116 def _is_complete(self, source, interactive):
116 def _is_complete(self, source, interactive):
117 """ Returns whether 'source' can be completely processed and a new
117 """ Returns whether 'source' can be completely processed and a new
118 prompt created. When triggered by an Enter/Return key press,
118 prompt created. When triggered by an Enter/Return key press,
119 'interactive' is True; otherwise, it is False.
119 'interactive' is True; otherwise, it is False.
120 """
120 """
121 complete = self._input_splitter.push(source.replace('\t', ' '))
121 complete = self._input_splitter.push(source.replace('\t', ' '))
122 if interactive:
122 if interactive:
123 complete = not self._input_splitter.push_accepts_more()
123 complete = not self._input_splitter.push_accepts_more()
124 return complete
124 return complete
125
125
126 def _execute(self, source, hidden):
126 def _execute(self, source, hidden):
127 """ Execute 'source'. If 'hidden', do not show any output.
127 """ Execute 'source'. If 'hidden', do not show any output.
128 """
128 """
129 self.kernel_manager.xreq_channel.execute(source)
129 self.kernel_manager.xreq_channel.execute(source)
130 self._hidden = hidden
130 self._hidden = hidden
131
131
132 def _prompt_started_hook(self):
132 def _prompt_started_hook(self):
133 """ Called immediately after a new prompt is displayed.
133 """ Called immediately after a new prompt is displayed.
134 """
134 """
135 if not self._reading:
135 if not self._reading:
136 self._highlighter.highlighting_on = True
136 self._highlighter.highlighting_on = True
137
137
138 # Auto-indent if this is a continuation prompt.
138 # Auto-indent if this is a continuation prompt.
139 if self._get_prompt_cursor().blockNumber() != \
139 if self._get_prompt_cursor().blockNumber() != \
140 self._get_end_cursor().blockNumber():
140 self._get_end_cursor().blockNumber():
141 spaces = self._input_splitter.indent_spaces
141 spaces = self._input_splitter.indent_spaces
142 self.appendPlainText('\t' * (spaces / 4) + ' ' * (spaces % 4))
142 self.appendPlainText('\t' * (spaces / 4) + ' ' * (spaces % 4))
143
143
144 def _prompt_finished_hook(self):
144 def _prompt_finished_hook(self):
145 """ Called immediately after a prompt is finished, i.e. when some input
145 """ Called immediately after a prompt is finished, i.e. when some input
146 will be processed and a new prompt displayed.
146 will be processed and a new prompt displayed.
147 """
147 """
148 if not self._reading:
148 if not self._reading:
149 self._highlighter.highlighting_on = False
149 self._highlighter.highlighting_on = False
150
150
151 def _tab_pressed(self):
151 def _tab_pressed(self):
152 """ Called when the tab key is pressed. Returns whether to continue
152 """ Called when the tab key is pressed. Returns whether to continue
153 processing the event.
153 processing the event.
154 """
154 """
155 self._keep_cursor_in_buffer()
155 self._keep_cursor_in_buffer()
156 cursor = self.textCursor()
156 cursor = self.textCursor()
157 return not self._complete()
157 return not self._complete()
158
158
159 #---------------------------------------------------------------------------
159 #---------------------------------------------------------------------------
160 # 'FrontendWidget' interface
160 # 'FrontendWidget' interface
161 #---------------------------------------------------------------------------
161 #---------------------------------------------------------------------------
162
162
163 def execute_file(self, path, hidden=False):
163 def execute_file(self, path, hidden=False):
164 """ Attempts to execute file with 'path'. If 'hidden', no output is
164 """ Attempts to execute file with 'path'. If 'hidden', no output is
165 shown.
165 shown.
166 """
166 """
167 self.execute('execfile("%s")' % path, hidden=hidden)
167 self.execute('execfile("%s")' % path, hidden=hidden)
168
168
169 def _get_kernel_manager(self):
169 def _get_kernel_manager(self):
170 """ Returns the current kernel manager.
170 """ Returns the current kernel manager.
171 """
171 """
172 return self._kernel_manager
172 return self._kernel_manager
173
173
174 def _set_kernel_manager(self, kernel_manager):
174 def _set_kernel_manager(self, kernel_manager):
175 """ Disconnect from the current kernel manager (if any) and set a new
175 """ Disconnect from the current kernel manager (if any) and set a new
176 kernel manager.
176 kernel manager.
177 """
177 """
178 # Disconnect the old kernel manager, if necessary.
178 # Disconnect the old kernel manager, if necessary.
179 if self._kernel_manager is not None:
179 if self._kernel_manager is not None:
180 self._kernel_manager.started_channels.disconnect(
180 self._kernel_manager.started_channels.disconnect(
181 self._started_channels)
181 self._started_channels)
182 self._kernel_manager.stopped_channels.disconnect(
182 self._kernel_manager.stopped_channels.disconnect(
183 self._stopped_channels)
183 self._stopped_channels)
184
184
185 # Disconnect the old kernel manager's channels.
185 # Disconnect the old kernel manager's channels.
186 sub = self._kernel_manager.sub_channel
186 sub = self._kernel_manager.sub_channel
187 xreq = self._kernel_manager.xreq_channel
187 xreq = self._kernel_manager.xreq_channel
188 rep = self._kernel_manager.rep_channel
188 rep = self._kernel_manager.rep_channel
189 sub.message_received.disconnect(self._handle_sub)
189 sub.message_received.disconnect(self._handle_sub)
190 xreq.execute_reply.disconnect(self._handle_execute_reply)
190 xreq.execute_reply.disconnect(self._handle_execute_reply)
191 xreq.complete_reply.disconnect(self._handle_complete_reply)
191 xreq.complete_reply.disconnect(self._handle_complete_reply)
192 xreq.object_info_reply.disconnect(self._handle_object_info_reply)
192 xreq.object_info_reply.disconnect(self._handle_object_info_reply)
193 rep.readline_requested.disconnect(self._handle_req)
193 rep.readline_requested.disconnect(self._handle_req)
194
194
195 # Handle the case where the old kernel manager is still listening.
195 # Handle the case where the old kernel manager is still listening.
196 if self._kernel_manager.channels_running:
196 if self._kernel_manager.channels_running:
197 self._stopped_channels()
197 self._stopped_channels()
198
198
199 # Set the new kernel manager.
199 # Set the new kernel manager.
200 self._kernel_manager = kernel_manager
200 self._kernel_manager = kernel_manager
201 if kernel_manager is None:
201 if kernel_manager is None:
202 return
202 return
203
203
204 # Connect the new kernel manager.
204 # Connect the new kernel manager.
205 kernel_manager.started_channels.connect(self._started_channels)
205 kernel_manager.started_channels.connect(self._started_channels)
206 kernel_manager.stopped_channels.connect(self._stopped_channels)
206 kernel_manager.stopped_channels.connect(self._stopped_channels)
207
207
208 # Connect the new kernel manager's channels.
208 # Connect the new kernel manager's channels.
209 sub = kernel_manager.sub_channel
209 sub = kernel_manager.sub_channel
210 xreq = kernel_manager.xreq_channel
210 xreq = kernel_manager.xreq_channel
211 rep = kernel_manager.rep_channel
211 rep = kernel_manager.rep_channel
212 sub.message_received.connect(self._handle_sub)
212 sub.message_received.connect(self._handle_sub)
213 xreq.execute_reply.connect(self._handle_execute_reply)
213 xreq.execute_reply.connect(self._handle_execute_reply)
214 xreq.complete_reply.connect(self._handle_complete_reply)
214 xreq.complete_reply.connect(self._handle_complete_reply)
215 xreq.object_info_reply.connect(self._handle_object_info_reply)
215 xreq.object_info_reply.connect(self._handle_object_info_reply)
216 rep.readline_requested.connect(self._handle_req)
216 rep.readline_requested.connect(self._handle_req)
217
217
218 # Handle the case where the kernel manager started channels before
218 # Handle the case where the kernel manager started channels before
219 # we connected.
219 # we connected.
220 if kernel_manager.channels_running:
220 if kernel_manager.channels_running:
221 self._started_channels()
221 self._started_channels()
222
222
223 kernel_manager = property(_get_kernel_manager, _set_kernel_manager)
223 kernel_manager = property(_get_kernel_manager, _set_kernel_manager)
224
224
225 #---------------------------------------------------------------------------
225 #---------------------------------------------------------------------------
226 # 'FrontendWidget' protected interface
226 # 'FrontendWidget' protected interface
227 #---------------------------------------------------------------------------
227 #---------------------------------------------------------------------------
228
228
229 def _call_tip(self):
229 def _call_tip(self):
230 """ Shows a call tip, if appropriate, at the current cursor location.
230 """ Shows a call tip, if appropriate, at the current cursor location.
231 """
231 """
232 # Decide if it makes sense to show a call tip
232 # Decide if it makes sense to show a call tip
233 cursor = self.textCursor()
233 cursor = self.textCursor()
234 cursor.movePosition(QtGui.QTextCursor.Left)
234 cursor.movePosition(QtGui.QTextCursor.Left)
235 document = self.document()
235 document = self.document()
236 if document.characterAt(cursor.position()).toAscii() != '(':
236 if document.characterAt(cursor.position()).toAscii() != '(':
237 return False
237 return False
238 context = self._get_context(cursor)
238 context = self._get_context(cursor)
239 if not context:
239 if not context:
240 return False
240 return False
241
241
242 # Send the metadata request to the kernel
242 # Send the metadata request to the kernel
243 name = '.'.join(context)
243 name = '.'.join(context)
244 self._calltip_id = self.kernel_manager.xreq_channel.object_info(name)
244 self._calltip_id = self.kernel_manager.xreq_channel.object_info(name)
245 self._calltip_pos = self.textCursor().position()
245 self._calltip_pos = self.textCursor().position()
246 return True
246 return True
247
247
248 def _complete(self):
248 def _complete(self):
249 """ Performs completion at the current cursor location.
249 """ Performs completion at the current cursor location.
250 """
250 """
251 # Decide if it makes sense to do completion
251 # Decide if it makes sense to do completion
252 context = self._get_context()
252 context = self._get_context()
253 if not context:
253 if not context:
254 return False
254 return False
255
255
256 # Send the completion request to the kernel
256 # Send the completion request to the kernel
257 text = '.'.join(context)
257 text = '.'.join(context)
258 self._complete_id = self.kernel_manager.xreq_channel.complete(
258 self._complete_id = self.kernel_manager.xreq_channel.complete(
259 text, self.input_buffer_cursor_line, self.input_buffer)
259 text, self.input_buffer_cursor_line, self.input_buffer)
260 self._complete_pos = self.textCursor().position()
260 self._complete_pos = self.textCursor().position()
261 return True
261 return True
262
262
263 def _get_banner(self):
263 def _get_banner(self):
264 """ Gets a banner to display at the beginning of a session.
264 """ Gets a banner to display at the beginning of a session.
265 """
265 """
266 banner = 'Python %s on %s\nType "help", "copyright", "credits" or ' \
266 banner = 'Python %s on %s\nType "help", "copyright", "credits" or ' \
267 '"license" for more information.'
267 '"license" for more information.'
268 return banner % (sys.version, sys.platform)
268 return banner % (sys.version, sys.platform)
269
269
270 def _get_context(self, cursor=None):
270 def _get_context(self, cursor=None):
271 """ Gets the context at the current cursor location.
271 """ Gets the context at the current cursor location.
272 """
272 """
273 if cursor is None:
273 if cursor is None:
274 cursor = self.textCursor()
274 cursor = self.textCursor()
275 cursor.movePosition(QtGui.QTextCursor.StartOfLine,
275 cursor.movePosition(QtGui.QTextCursor.StartOfLine,
276 QtGui.QTextCursor.KeepAnchor)
276 QtGui.QTextCursor.KeepAnchor)
277 text = unicode(cursor.selectedText())
277 text = str(cursor.selection().toPlainText())
278 return self._completion_lexer.get_context(text)
278 return self._completion_lexer.get_context(text)
279
279
280 def _interrupt_kernel(self):
280 def _interrupt_kernel(self):
281 """ Attempts to the interrupt the kernel.
281 """ Attempts to the interrupt the kernel.
282 """
282 """
283 if self.kernel_manager.has_kernel:
283 if self.kernel_manager.has_kernel:
284 self.kernel_manager.signal_kernel(signal.SIGINT)
284 self.kernel_manager.signal_kernel(signal.SIGINT)
285 else:
285 else:
286 self.appendPlainText('Kernel process is either remote or '
286 self.appendPlainText('Kernel process is either remote or '
287 'unspecified. Cannot interrupt.\n')
287 'unspecified. Cannot interrupt.\n')
288
288
289 def _show_interpreter_prompt(self):
289 def _show_interpreter_prompt(self):
290 """ Shows a prompt for the interpreter.
290 """ Shows a prompt for the interpreter.
291 """
291 """
292 self._show_prompt('>>> ')
292 self._show_prompt('>>> ')
293
293
294 #------ Signal handlers ----------------------------------------------------
294 #------ Signal handlers ----------------------------------------------------
295
295
296 def _started_channels(self):
296 def _started_channels(self):
297 """ Called when the kernel manager has started listening.
297 """ Called when the kernel manager has started listening.
298 """
298 """
299 self._reset()
299 self._reset()
300 self.appendPlainText(self._get_banner())
300 self.appendPlainText(self._get_banner())
301 self._show_interpreter_prompt()
301 self._show_interpreter_prompt()
302
302
303 def _stopped_channels(self):
303 def _stopped_channels(self):
304 """ Called when the kernel manager has stopped listening.
304 """ Called when the kernel manager has stopped listening.
305 """
305 """
306 # FIXME: Print a message here?
306 # FIXME: Print a message here?
307 pass
307 pass
308
308
309 def _document_contents_change(self, position, removed, added):
309 def _document_contents_change(self, position, removed, added):
310 """ Called whenever the document's content changes. Display a calltip
310 """ Called whenever the document's content changes. Display a calltip
311 if appropriate.
311 if appropriate.
312 """
312 """
313 # Calculate where the cursor should be *after* the change:
313 # Calculate where the cursor should be *after* the change:
314 position += added
314 position += added
315
315
316 document = self.document()
316 document = self.document()
317 if position == self.textCursor().position():
317 if position == self.textCursor().position():
318 self._call_tip()
318 self._call_tip()
319
319
320 def _handle_req(self, req):
320 def _handle_req(self, req):
321 # Make sure that all output from the SUB channel has been processed
321 # Make sure that all output from the SUB channel has been processed
322 # before entering readline mode.
322 # before entering readline mode.
323 self.kernel_manager.sub_channel.flush()
323 self.kernel_manager.sub_channel.flush()
324
324
325 def callback(line):
325 def callback(line):
326 self.kernel_manager.rep_channel.readline(line)
326 self.kernel_manager.rep_channel.readline(line)
327 self._readline(callback=callback)
327 self._readline(callback=callback)
328
328
329 def _handle_sub(self, omsg):
329 def _handle_sub(self, omsg):
330 if self._hidden:
330 if self._hidden:
331 return
331 return
332 handler = getattr(self, '_handle_%s' % omsg['msg_type'], None)
332 handler = getattr(self, '_handle_%s' % omsg['msg_type'], None)
333 if handler is not None:
333 if handler is not None:
334 handler(omsg)
334 handler(omsg)
335
335
336 def _handle_pyout(self, omsg):
336 def _handle_pyout(self, omsg):
337 self.appendPlainText(omsg['content']['data'] + '\n')
337 self.appendPlainText(omsg['content']['data'] + '\n')
338
338
339 def _handle_stream(self, omsg):
339 def _handle_stream(self, omsg):
340 self.appendPlainText(omsg['content']['data'])
340 self.appendPlainText(omsg['content']['data'])
341 self.moveCursor(QtGui.QTextCursor.End)
341 self.moveCursor(QtGui.QTextCursor.End)
342
342
343 def _handle_execute_reply(self, reply):
343 def _handle_execute_reply(self, reply):
344 if self._hidden:
344 if self._hidden:
345 return
345 return
346
346
347 # Make sure that all output from the SUB channel has been processed
347 # Make sure that all output from the SUB channel has been processed
348 # before writing a new prompt.
348 # before writing a new prompt.
349 self.kernel_manager.sub_channel.flush()
349 self.kernel_manager.sub_channel.flush()
350
350
351 status = reply['content']['status']
351 status = reply['content']['status']
352 if status == 'error':
352 if status == 'error':
353 self._handle_execute_error(reply)
353 self._handle_execute_error(reply)
354 elif status == 'aborted':
354 elif status == 'aborted':
355 text = "ERROR: ABORTED\n"
355 text = "ERROR: ABORTED\n"
356 self.appendPlainText(text)
356 self.appendPlainText(text)
357 self._hidden = True
357 self._hidden = True
358 self._show_interpreter_prompt()
358 self._show_interpreter_prompt()
359 self.executed.emit(reply)
359 self.executed.emit(reply)
360
360
361 def _handle_execute_error(self, reply):
361 def _handle_execute_error(self, reply):
362 content = reply['content']
362 content = reply['content']
363 traceback = ''.join(content['traceback'])
363 traceback = ''.join(content['traceback'])
364 self.appendPlainText(traceback)
364 self.appendPlainText(traceback)
365
365
366 def _handle_complete_reply(self, rep):
366 def _handle_complete_reply(self, rep):
367 cursor = self.textCursor()
367 cursor = self.textCursor()
368 if rep['parent_header']['msg_id'] == self._complete_id and \
368 if rep['parent_header']['msg_id'] == self._complete_id and \
369 cursor.position() == self._complete_pos:
369 cursor.position() == self._complete_pos:
370 text = '.'.join(self._get_context())
370 text = '.'.join(self._get_context())
371 cursor.movePosition(QtGui.QTextCursor.Left, n=len(text))
371 cursor.movePosition(QtGui.QTextCursor.Left, n=len(text))
372 self._complete_with_items(cursor, rep['content']['matches'])
372 self._complete_with_items(cursor, rep['content']['matches'])
373
373
374 def _handle_object_info_reply(self, rep):
374 def _handle_object_info_reply(self, rep):
375 cursor = self.textCursor()
375 cursor = self.textCursor()
376 if rep['parent_header']['msg_id'] == self._calltip_id and \
376 if rep['parent_header']['msg_id'] == self._calltip_id and \
377 cursor.position() == self._calltip_pos:
377 cursor.position() == self._calltip_pos:
378 doc = rep['content']['docstring']
378 doc = rep['content']['docstring']
379 if doc:
379 if doc:
380 self._call_tip_widget.show_docstring(doc)
380 self._call_tip_widget.show_docstring(doc)
General Comments 0
You need to be logged in to leave comments. Login now