##// END OF EJS Templates
Merge branch 'newkernel' of github.com:ipython/ipython into upstream-newkernel
Brian Granger -
r2901:61ddddd9 merge
parent child Browse files
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):
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)
@@ -35,14 +35,14 b" def magic_history(self, parameter_s = ''):"
35 for making documentation, and in conjunction with -o, for producing
35 for making documentation, and in conjunction with -o, for producing
36 doctest-ready output.
36 doctest-ready output.
37
37
38 -t: (default) print the 'translated' history, as IPython understands it.
38 -r: (default) print the 'raw' history, i.e. the actual commands you typed.
39 IPython filters your input and converts it all into valid Python source
40 before executing it (things like magics or aliases are turned into
41 function calls, for example). With this option, you'll see the native
42 history instead of the user-entered version: '%cd /' will be seen as
43 '_ip.magic("%cd /")' instead of '%cd /'.
44
39
45 -r: print the 'raw' history, i.e. the actual commands you typed.
40 -t: print the 'translated' history, as IPython understands it. IPython
41 filters your input and converts it all into valid Python source before
42 executing it (things like magics or aliases are turned into function
43 calls, for example). With this option, you'll see the native history
44 instead of the user-entered version: '%cd /' will be seen as
45 'get_ipython().magic("%cd /")' instead of '%cd /'.
46
46
47 -g: treat the arg as a pattern to grep for in (full) history.
47 -g: treat the arg as a pattern to grep for in (full) history.
48 This includes the "shadow history" (almost all commands ever written).
48 This includes the "shadow history" (almost all commands ever written).
@@ -80,7 +80,8 b" def magic_history(self, parameter_s = ''):"
80 elif 'r' in opts:
80 elif 'r' in opts:
81 input_hist = self.input_hist_raw
81 input_hist = self.input_hist_raw
82 else:
82 else:
83 input_hist = self.input_hist
83 # Raw history is the default
84 input_hist = self.input_hist_raw
84
85
85 default_length = 40
86 default_length = 40
86 pattern = None
87 pattern = None
@@ -1828,7 +1828,6 b' class InteractiveShell(Configurable, Magic):'
1828 self.resetbuffer()
1828 self.resetbuffer()
1829 lines = lines.splitlines()
1829 lines = lines.splitlines()
1830 more = 0
1830 more = 0
1831
1832 with nested(self.builtin_trap, self.display_trap):
1831 with nested(self.builtin_trap, self.display_trap):
1833 for line in lines:
1832 for line in lines:
1834 # skip blank lines so we don't mess up the prompt counter, but do
1833 # skip blank lines so we don't mess up the prompt counter, but do
@@ -1837,8 +1836,9 b' class InteractiveShell(Configurable, Magic):'
1837
1836
1838 if line or more:
1837 if line or more:
1839 # push to raw history, so hist line numbers stay in sync
1838 # push to raw history, so hist line numbers stay in sync
1840 self.input_hist_raw.append("# " + line + "\n")
1839 self.input_hist_raw.append(line + '\n')
1841 prefiltered = self.prefilter_manager.prefilter_lines(line,more)
1840 prefiltered = self.prefilter_manager.prefilter_lines(line,
1841 more)
1842 more = self.push_line(prefiltered)
1842 more = self.push_line(prefiltered)
1843 # IPython's runsource returns None if there was an error
1843 # IPython's runsource returns None if there was an error
1844 # compiling the code. This allows us to stop processing right
1844 # compiling the code. This allows us to stop processing right
@@ -101,7 +101,7 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
101 QtCore.Qt.Key_N : QtCore.Qt.Key_Down,
101 QtCore.Qt.Key_N : QtCore.Qt.Key_Down,
102 QtCore.Qt.Key_D : QtCore.Qt.Key_Delete, }
102 QtCore.Qt.Key_D : QtCore.Qt.Key_Delete, }
103 _shortcuts = set(_ctrl_down_remap.keys() +
103 _shortcuts = set(_ctrl_down_remap.keys() +
104 [ QtCore.Qt.Key_C, QtCore.Qt.Key_V ])
104 [ QtCore.Qt.Key_C, QtCore.Qt.Key_V, QtCore.Qt.Key_O ])
105
105
106 #---------------------------------------------------------------------------
106 #---------------------------------------------------------------------------
107 # 'QObject' interface
107 # 'QObject' interface
@@ -164,18 +164,26 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
164
164
165 def eventFilter(self, obj, event):
165 def eventFilter(self, obj, event):
166 """ Reimplemented to ensure a console-like behavior in the underlying
166 """ Reimplemented to ensure a console-like behavior in the underlying
167 text widget.
167 text widgets.
168 """
168 """
169 # Re-map keys for all filtered widgets.
170 etype = event.type()
169 etype = event.type()
171 if etype == QtCore.QEvent.KeyPress and \
170 if etype == QtCore.QEvent.KeyPress:
172 self._control_key_down(event.modifiers()) and \
171
173 event.key() in self._ctrl_down_remap:
172 # Re-map keys for all filtered widgets.
174 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
173 key = event.key()
175 self._ctrl_down_remap[event.key()],
174 if self._control_key_down(event.modifiers()) and \
176 QtCore.Qt.NoModifier)
175 key in self._ctrl_down_remap:
177 QtGui.qApp.sendEvent(obj, new_event)
176 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
178 return True
177 self._ctrl_down_remap[key],
178 QtCore.Qt.NoModifier)
179 QtGui.qApp.sendEvent(obj, new_event)
180 return True
181
182 elif obj == self._control:
183 return self._event_filter_console_keypress(event)
184
185 elif obj == self._page_control:
186 return self._event_filter_page_keypress(event)
179
187
180 # Override shortucts for all filtered widgets. Note that on Mac OS it is
188 # Override shortucts for all filtered widgets. Note that on Mac OS it is
181 # always unnecessary to override shortcuts, hence the check below (users
189 # always unnecessary to override shortcuts, hence the check below (users
@@ -187,12 +195,6 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
187 event.accept()
195 event.accept()
188 return False
196 return False
189
197
190 elif etype == QtCore.QEvent.KeyPress:
191 if obj == self._control:
192 return self._event_filter_console_keypress(event)
193 elif obj == self._page_control:
194 return self._event_filter_page_keypress(event)
195
196 return super(ConsoleWidget, self).eventFilter(obj, event)
198 return super(ConsoleWidget, self).eventFilter(obj, event)
197
199
198 #---------------------------------------------------------------------------
200 #---------------------------------------------------------------------------
@@ -334,12 +336,11 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
334 else:
336 else:
335 # Do this inside an edit block so continuation prompts are
337 # Do this inside an edit block so continuation prompts are
336 # removed seamlessly via undo/redo.
338 # removed seamlessly via undo/redo.
337 cursor = self._control.textCursor()
339 cursor = self._get_end_cursor()
338 cursor.beginEditBlock()
340 cursor.beginEditBlock()
339
341 cursor.insertText('\n')
340 self._append_plain_text('\n')
342 self._insert_continuation_prompt(cursor)
341 self._show_continuation_prompt()
343 self._control.moveCursor(QtGui.QTextCursor.End)
342
343 cursor.endEditBlock()
344 cursor.endEditBlock()
344
345
345 return complete
346 return complete
@@ -431,9 +432,8 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
431 """ Sets the font to the default fixed-width font for this platform.
432 """ Sets the font to the default fixed-width font for this platform.
432 """
433 """
433 # FIXME: font family and size should be configurable by the user.
434 # FIXME: font family and size should be configurable by the user.
434
435 if sys.platform == 'win32':
435 if sys.platform == 'win32':
436 # Fixme: we should test whether Consolas is available and use it
436 # FIXME: we should test whether Consolas is available and use it
437 # first if it is. Consolas ships by default from Vista onwards,
437 # first if it is. Consolas ships by default from Vista onwards,
438 # it's *vastly* more readable and prettier than Courier, and is
438 # it's *vastly* more readable and prettier than Courier, and is
439 # often installed even on XP systems. So we should first check for
439 # often installed even on XP systems. So we should first check for
@@ -632,13 +632,32 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
632 if self._in_buffer(position):
632 if self._in_buffer(position):
633 cursor.movePosition(QtGui.QTextCursor.EndOfLine,
633 cursor.movePosition(QtGui.QTextCursor.EndOfLine,
634 QtGui.QTextCursor.KeepAnchor)
634 QtGui.QTextCursor.KeepAnchor)
635 if not cursor.hasSelection():
636 # Line deletion (remove continuation prompt)
637 cursor.movePosition(QtGui.QTextCursor.NextBlock,
638 QtGui.QTextCursor.KeepAnchor)
639 cursor.movePosition(QtGui.QTextCursor.Right,
640 QtGui.QTextCursor.KeepAnchor,
641 len(self._continuation_prompt))
635 cursor.removeSelectedText()
642 cursor.removeSelectedText()
636 intercepted = True
643 intercepted = True
637
644
638 elif key == QtCore.Qt.Key_L:
645 elif key == QtCore.Qt.Key_L:
646 # It would be better to simply move the prompt block to the top
647 # of the control viewport. QPlainTextEdit has a private method
648 # to do this (setTopBlock), but it cannot be duplicated here
649 # because it requires access to the QTextControl that underlies
650 # both QPlainTextEdit and QTextEdit. In short, this can only be
651 # achieved by appending newlines after the prompt, which is a
652 # gigantic hack and likely to cause other problems.
639 self.clear()
653 self.clear()
640 intercepted = True
654 intercepted = True
641
655
656 elif key == QtCore.Qt.Key_O:
657 if self._page_control and self._page_control.isVisible():
658 self._page_control.setFocus()
659 intercept = True
660
642 elif key == QtCore.Qt.Key_X:
661 elif key == QtCore.Qt.Key_X:
643 # FIXME: Instead of disabling cut completely, only allow it
662 # FIXME: Instead of disabling cut completely, only allow it
644 # when safe.
663 # when safe.
@@ -669,16 +688,44 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
669 cursor.removeSelectedText()
688 cursor.removeSelectedText()
670 intercepted = True
689 intercepted = True
671
690
691 elif key == QtCore.Qt.Key_Greater:
692 self._control.moveCursor(QtGui.QTextCursor.End)
693 intercepted = True
694
695 elif key == QtCore.Qt.Key_Less:
696 self._control.setTextCursor(self._get_prompt_cursor())
697 intercepted = True
698
672 else:
699 else:
673 if key in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter):
700 if key in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter):
674 if self._reading:
675 self._append_plain_text('\n')
676 self._reading = False
677 if self._reading_callback:
678 self._reading_callback()
679 elif not self._executing:
680 self.execute(interactive=True)
681 intercepted = True
701 intercepted = True
702 if self._in_buffer(position):
703 if self._reading:
704 self._append_plain_text('\n')
705 self._reading = False
706 if self._reading_callback:
707 self._reading_callback()
708
709 # If there is only whitespace after the cursor, execute.
710 # Otherwise, split the line with a continuation prompt.
711 elif not self._executing:
712 cursor.movePosition(QtGui.QTextCursor.End,
713 QtGui.QTextCursor.KeepAnchor)
714 if cursor.selectedText().trimmed().isEmpty():
715 self.execute(interactive=True)
716 else:
717 cursor.beginEditBlock()
718 cursor.setPosition(position)
719 cursor.insertText('\n')
720 self._insert_continuation_prompt(cursor)
721
722 # Ensure that the whole input buffer is visible.
723 # FIXME: This will not be usable if the input buffer
724 # is taller than the console widget.
725 self._control.moveCursor(QtGui.QTextCursor.End)
726 self._control.setTextCursor(cursor)
727
728 cursor.endEditBlock()
682
729
683 elif key == QtCore.Qt.Key_Up:
730 elif key == QtCore.Qt.Key_Up:
684 if self._reading or not self._up_pressed():
731 if self._reading or not self._up_pressed():
@@ -702,17 +749,20 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
702 intercepted = not self._in_buffer(position - 1)
749 intercepted = not self._in_buffer(position - 1)
703
750
704 elif key == QtCore.Qt.Key_Home:
751 elif key == QtCore.Qt.Key_Home:
705 cursor.movePosition(QtGui.QTextCursor.StartOfBlock)
706 start_line = cursor.blockNumber()
752 start_line = cursor.blockNumber()
707 if start_line == self._get_prompt_cursor().blockNumber():
753 if start_line == self._get_prompt_cursor().blockNumber():
708 start_pos = self._prompt_pos
754 start_pos = self._prompt_pos
709 else:
755 else:
756 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
757 QtGui.QTextCursor.KeepAnchor)
710 start_pos = cursor.position()
758 start_pos = cursor.position()
711 start_pos += len(self._continuation_prompt)
759 start_pos += len(self._continuation_prompt)
760 cursor.setPosition(position)
712 if shift_down and self._in_buffer(position):
761 if shift_down and self._in_buffer(position):
713 self._set_selection(position, start_pos)
762 cursor.setPosition(start_pos, QtGui.QTextCursor.KeepAnchor)
714 else:
763 else:
715 self._set_position(start_pos)
764 cursor.setPosition(start_pos)
765 self._set_cursor(cursor)
716 intercepted = True
766 intercepted = True
717
767
718 elif key == QtCore.Qt.Key_Backspace:
768 elif key == QtCore.Qt.Key_Backspace:
@@ -769,8 +819,24 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
769 interface.
819 interface.
770 """
820 """
771 key = event.key()
821 key = event.key()
822 ctrl_down = self._control_key_down(event.modifiers())
823 alt_down = event.modifiers() & QtCore.Qt.AltModifier
824
825 if ctrl_down:
826 if key == QtCore.Qt.Key_O:
827 self._control.setFocus()
828 intercept = True
829
830 elif alt_down:
831 if key == QtCore.Qt.Key_Greater:
832 self._page_control.moveCursor(QtGui.QTextCursor.End)
833 intercepted = True
834
835 elif key == QtCore.Qt.Key_Less:
836 self._page_control.moveCursor(QtGui.QTextCursor.Start)
837 intercepted = True
772
838
773 if key in (QtCore.Qt.Key_Q, QtCore.Qt.Key_Escape):
839 elif key in (QtCore.Qt.Key_Q, QtCore.Qt.Key_Escape):
774 if self._splitter:
840 if self._splitter:
775 self._page_control.hide()
841 self._page_control.hide()
776 else:
842 else:
@@ -779,7 +845,14 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
779
845
780 elif key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return):
846 elif key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return):
781 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
847 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
782 QtCore.Qt.Key_Down,
848 QtCore.Qt.Key_PageDown,
849 QtCore.Qt.NoModifier)
850 QtGui.qApp.sendEvent(self._page_control, new_event)
851 return True
852
853 elif key == QtCore.Qt.Key_Backspace:
854 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
855 QtCore.Qt.Key_PageUp,
783 QtCore.Qt.NoModifier)
856 QtCore.Qt.NoModifier)
784 QtGui.qApp.sendEvent(self._page_control, new_event)
857 QtGui.qApp.sendEvent(self._page_control, new_event)
785 return True
858 return True
@@ -964,6 +1037,15 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
964 cursor.setPosition(position)
1037 cursor.setPosition(position)
965 return cursor
1038 return cursor
966
1039
1040 def _insert_continuation_prompt(self, cursor):
1041 """ Inserts new continuation prompt using the specified cursor.
1042 """
1043 if self._continuation_prompt_html is None:
1044 self._insert_plain_text(cursor, self._continuation_prompt)
1045 else:
1046 self._continuation_prompt = self._insert_html_fetching_plain_text(
1047 cursor, self._continuation_prompt_html)
1048
967 def _insert_html(self, cursor, html):
1049 def _insert_html(self, cursor, html):
968 """ Inserts HTML using the specified cursor in such a way that future
1050 """ Inserts HTML using the specified cursor in such a way that future
969 formatting is unaffected.
1051 formatting is unaffected.
@@ -1184,18 +1266,6 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
1184 """
1266 """
1185 self._control.setTextCursor(cursor)
1267 self._control.setTextCursor(cursor)
1186
1268
1187 def _set_position(self, position):
1188 """ Convenience method to set the position of the cursor.
1189 """
1190 cursor = self._control.textCursor()
1191 cursor.setPosition(position)
1192 self._control.setTextCursor(cursor)
1193
1194 def _set_selection(self, start, end):
1195 """ Convenience method to set the current selected text.
1196 """
1197 self._control.setTextCursor(self._get_selection_cursor(start, end))
1198
1199 def _show_context_menu(self, pos):
1269 def _show_context_menu(self, pos):
1200 """ Shows a context menu at the given QPoint (in widget coordinates).
1270 """ Shows a context menu at the given QPoint (in widget coordinates).
1201 """
1271 """
@@ -1259,15 +1329,6 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
1259 self._prompt_pos = self._get_end_cursor().position()
1329 self._prompt_pos = self._get_end_cursor().position()
1260 self._prompt_started()
1330 self._prompt_started()
1261
1331
1262 def _show_continuation_prompt(self):
1263 """ Writes a new continuation prompt at the end of the buffer.
1264 """
1265 if self._continuation_prompt_html is None:
1266 self._append_plain_text(self._continuation_prompt)
1267 else:
1268 self._continuation_prompt = self._append_html_fetching_plain_text(
1269 self._continuation_prompt_html)
1270
1271
1332
1272 class HistoryConsoleWidget(ConsoleWidget):
1333 class HistoryConsoleWidget(ConsoleWidget):
1273 """ A ConsoleWidget that keeps a history of the commands that have been
1334 """ A ConsoleWidget that keeps a history of the commands that have been
@@ -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, Type
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 = self._highlighter_class(self)
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
@@ -170,8 +171,8 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
170 """ Reimplemented to allow execution interruption.
171 """ Reimplemented to allow execution interruption.
171 """
172 """
172 key = event.key()
173 key = event.key()
173 if self._executing and self._control_key_down(event.modifiers()):
174 if self._control_key_down(event.modifiers()):
174 if key == QtCore.Qt.Key_C:
175 if key == QtCore.Qt.Key_C and self._executing:
175 self._kernel_interrupt()
176 self._kernel_interrupt()
176 return True
177 return True
177 elif key == QtCore.Qt.Key_Period:
178 elif key == QtCore.Qt.Key_Period:
@@ -179,13 +180,13 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
179 return True
180 return True
180 return super(FrontendWidget, self)._event_filter_console_keypress(event)
181 return super(FrontendWidget, self)._event_filter_console_keypress(event)
181
182
182 def _show_continuation_prompt(self):
183 def _insert_continuation_prompt(self, cursor):
183 """ Reimplemented for auto-indentation.
184 """ Reimplemented for auto-indentation.
184 """
185 """
185 super(FrontendWidget, self)._show_continuation_prompt()
186 super(FrontendWidget, self)._insert_continuation_prompt(cursor)
186 spaces = self._input_splitter.indent_spaces
187 spaces = self._input_splitter.indent_spaces
187 self._append_plain_text('\t' * (spaces / self.tab_width))
188 cursor.insertText('\t' * (spaces / self.tab_width))
188 self._append_plain_text(' ' * (spaces % self.tab_width))
189 cursor.insertText(' ' * (spaces % self.tab_width))
189
190
190 #---------------------------------------------------------------------------
191 #---------------------------------------------------------------------------
191 # 'BaseFrontendMixin' abstract interface
192 # 'BaseFrontendMixin' abstract interface
@@ -353,15 +354,20 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
353 if self.custom_restart:
354 if self.custom_restart:
354 self.custom_restart_requested.emit()
355 self.custom_restart_requested.emit()
355 elif self.kernel_manager.has_kernel:
356 elif self.kernel_manager.has_kernel:
356 try:
357 message = 'Are you sure you want to restart the kernel?'
357 self.kernel_manager.restart_kernel()
358 buttons = QtGui.QMessageBox.Yes | QtGui.QMessageBox.No
358 except RuntimeError:
359 result = QtGui.QMessageBox.question(self, 'Restart kernel?',
359 message = 'Kernel started externally. Cannot restart.\n'
360 message, buttons)
360 self._append_plain_text(message)
361 if result == QtGui.QMessageBox.Yes:
361 else:
362 try:
362 self._stopped_channels()
363 self.kernel_manager.restart_kernel()
363 self._append_plain_text('Kernel restarting...\n')
364 except RuntimeError:
364 self._show_interpreter_prompt()
365 message = 'Kernel started externally. Cannot restart.\n'
366 self._append_plain_text(message)
367 else:
368 self._stopped_channels()
369 self._append_plain_text('Kernel restarting...\n')
370 self._show_interpreter_prompt()
365 else:
371 else:
366 self._append_plain_text('Kernel process is either remote or '
372 self._append_plain_text('Kernel process is either remote or '
367 'unspecified. Cannot restart.\n')
373 'unspecified. Cannot restart.\n')
@@ -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 = self._get_format(token)
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':
@@ -64,10 +64,8 b' def make_kernel(namespace, kernel_factory,'
64 """ Creates a kernel.
64 """ Creates a kernel.
65 """
65 """
66 # Install minimal exception handling
66 # Install minimal exception handling
67 color_scheme = 'LightBG' if sys.platform == 'darwin' else 'Linux'
67 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
68 sys.excepthook = FormattedTB(
68 ostream=sys.__stdout__)
69 mode='Verbose', color_scheme=color_scheme, ostream=sys.__stdout__
70 )
71
69
72 # Create a context, a session, and the kernel sockets.
70 # Create a context, a session, and the kernel sockets.
73 io.raw_print("Starting the kernel...")
71 io.raw_print("Starting the kernel...")
@@ -101,4 +101,4 b' gitwash-update:'
101 cd source/development/gitwash && rename 's/.rst/.txt/' *.rst
101 cd source/development/gitwash && rename 's/.rst/.txt/' *.rst
102
102
103 nightly: dist
103 nightly: dist
104 rsync -avH --delete dist/ ipython:www/doc/nightly No newline at end of file
104 rsync -avH --delete dist/ ipython:www/doc/nightly
@@ -130,20 +130,9 b' Messages on the XREP/XREQ socket'
130 Execute
130 Execute
131 -------
131 -------
132
132
133 The execution request contains a single string, but this may be a multiline
133 This message type is used by frontends to ask the kernel to execute code on
134 string. The kernel is responsible for splitting this into possibly more than
134 behalf of the user, in a namespace reserved to the user's variables (and thus
135 one block and deciding whether to compile these in 'single' or 'exec' mode.
135 separate from the kernel's own internal code and variables).
136 We're still sorting out this policy. The current inputsplitter is capable of
137 splitting the input for blocks that can all be run as 'single', but in the long
138 run it may prove cleaner to only use 'single' mode for truly single-line
139 inputs, and run all multiline input in 'exec' mode. This would preserve the
140 natural behavior of single-line inputs while allowing long cells to behave more
141 likea a script. This design will be refined as we complete the implementation.
142
143 .. Note::
144
145 What today we call 'prompt requests' will be encoded in the
146 ``state_template`` field.
147
136
148 Message type: ``execute_request``::
137 Message type: ``execute_request``::
149
138
@@ -162,25 +151,75 b' Message type: ``execute_request``::'
162 # The default is False.
151 # The default is False.
163 'silent' : bool,
152 'silent' : bool,
164
153
165 # An optional string to request arbitrary state information from the
154 # A list of variable names from the user's namespace to be retrieved. What
166 # kernel. This string is evaluated via the itpl module, and it can
155 # returns is a JSON string of the variable's repr(), not a python object.
167 # therefore contain arbitrary code for execution.
156 'user_variables' : list,
168
157
169 'state_template' : str,
158 # Similarly, a dict mapping names to expressions to be evaluated in the
159 # user's dict.
160 'user_expressions' : dict,
170 }
161 }
171
162
163 The ``code`` field contains a single string, but this may be a multiline
164 string. The kernel is responsible for splitting this into possibly more than
165 one block and deciding whether to compile these in 'single' or 'exec' mode.
166 We're still sorting out this policy. The current inputsplitter is capable of
167 splitting the input for blocks that can all be run as 'single', but in the long
168 run it may prove cleaner to only use 'single' mode for truly single-line
169 inputs, and run all multiline input in 'exec' mode. This would preserve the
170 natural behavior of single-line inputs while allowing long cells to behave more
171 likea a script. This design will be refined as we complete the implementation.
172
173 The ``user_`` fields deserve a detailed explanation. In the past, IPython had
174 the notion of a prompt string that allowed arbitrary code to be evaluated, and
175 this was put to good use by many in creating prompts that displayed system
176 status, path information, and even more esoteric uses like remote instrument
177 status aqcuired over the network. But now that IPython has a clean separation
178 between the kernel and the clients, the notion of embedding 'prompt'
179 maninpulations into the kernel itself feels awkward. Prompts should be a
180 frontend-side feature, and it should be even possible for different frontends
181 to display different prompts while interacting with the same kernel.
182
183 We have therefore abandoned the idea of a 'prompt string' to be evaluated by
184 the kernel, and instead provide the ability to retrieve from the user's
185 namespace information after the execution of the main ``code``, with two fields
186 of the execution request:
187
188 - ``user_variables``: If only variables from the user's namespace are needed, a
189 list of variable names can be passed and a dict with these names as keys and
190 their :func:`repr()` as values will be returned.
191
192 - ``user_expressions``: For more complex expressions that require function
193 evaluations, a dict can be provided with string keys and arbitrary python
194 expressions as values. The return message will contain also a dict with the
195 same keys and the :func:`repr()` of the evaluated expressions as value.
196
197 With this information, frontends can display any status information they wish
198 in the form that best suits each frontend (a status line, a popup, inline for a
199 terminal, etc).
200
201 .. Note::
202
203 In order to obtain the current execution counter for the purposes of
204 displaying input prompts, frontends simply make an execution request with an
205 empty code string and ``silent=True``.
206
172 Execution semantics
207 Execution semantics
173 Upon execution of the ``code`` field, the kernel *always* sends a reply,
208 Upon completion of the execution request, the kernel *always* sends a
174 with a status code indicating what happened and additional data depending
209 reply, with a status code indicating what happened and additional data
175 on the outcome.
210 depending on the outcome.
211
212 The ``code`` field is executed first, and then the ``user_variables`` and
213 ``user_expressions`` are computed. This ensures that any error in the
214 latter don't harm the main code execution.
176
215
177 Any code in the ``state_template`` string is evaluated, but full exceptions
216 Any error in retrieving the ``user_variables`` or evaluating the
178 that may occur are *not* propagated back. If any error occurs during the
217 ``user_expressions`` will result in a simple error message in the return
179 evaluation, the value of the string will simply be::
218 fields of the form::
180
219
181 [ERROR in <contents of template>: ExceptionType - Exception message]
220 [ERROR] ExceptionType: Exception message
182
221
183 The user can simply send the same code contained in the template for normal
222 The user can simply send the same variable name or expression for
184 evaluation to see a regular traceback.
223 evaluation to see a regular traceback.
185
224
186 Execution counter (old prompt number)
225 Execution counter (old prompt number)
@@ -280,7 +319,7 b' Message type: ``getattr_request``::'
280
319
281 content = {
320 content = {
282 # The (possibly dotted) name of the attribute
321 # The (possibly dotted) name of the attribute
283 'name' : str
322 'name' : str,
284 }
323 }
285
324
286 When a ``getattr_request`` fails, there are two possible error types:
325 When a ``getattr_request`` fails, there are two possible error types:
@@ -296,20 +335,20 b' Message type: ``getattr_reply``::'
296
335
297 content = {
336 content = {
298 # One of ['ok', 'AttributeError', 'AccessError'].
337 # One of ['ok', 'AttributeError', 'AccessError'].
299 'status' : str
338 'status' : str,
300 # If status is 'ok', a JSON object.
339 # If status is 'ok', a JSON object.
301 'value' : object
340 'value' : object,
302 }
341 }
303
342
304 Message type: ``setattr_request``::
343 Message type: ``setattr_request``::
305
344
306 content = {
345 content = {
307 # The (possibly dotted) name of the attribute
346 # The (possibly dotted) name of the attribute
308 'name' : str
347 'name' : str,
309
348
310 # A JSON-encoded object, that will be validated by the Traits
349 # A JSON-encoded object, that will be validated by the Traits
311 # information in the kernel
350 # information in the kernel
312 'value' : object
351 'value' : object,
313 }
352 }
314
353
315 When a ``setattr_request`` fails, there are also two possible error types with
354 When a ``setattr_request`` fails, there are also two possible error types with
@@ -319,7 +358,7 b' Message type: ``setattr_reply``::'
319
358
320 content = {
359 content = {
321 # One of ['ok', 'AttributeError', 'AccessError'].
360 # One of ['ok', 'AttributeError', 'AccessError'].
322 'status' : str
361 'status' : str,
323 }
362 }
324
363
325
364
@@ -391,13 +430,13 b' Message type: ``object_info_reply``::'
391 # The name of the varargs (*args), if any
430 # The name of the varargs (*args), if any
392 varargs : str,
431 varargs : str,
393 # The name of the varkw (**kw), if any
432 # The name of the varkw (**kw), if any
394 varkw : str
433 varkw : str,
395 # The values (as strings) of all default arguments. Note
434 # The values (as strings) of all default arguments. Note
396 # that these must be matched *in reverse* with the 'args'
435 # that these must be matched *in reverse* with the 'args'
397 # list above, since the first positional args have no default
436 # list above, since the first positional args have no default
398 # value at all.
437 # value at all.
399 func_defaults : list
438 func_defaults : list,
400 }
439 },
401
440
402 # For instances, provide the constructor signature (the definition of
441 # For instances, provide the constructor signature (the definition of
403 # the __init__ method):
442 # the __init__ method):
@@ -487,6 +526,7 b' Message type: ``history_reply``::'
487 # respectively.
526 # respectively.
488 'history' : dict,
527 'history' : dict,
489 }
528 }
529
490 Messages on the PUB/SUB socket
530 Messages on the PUB/SUB socket
491 ==============================
531 ==============================
492
532
@@ -539,10 +579,10 b' Message type: ``pyout``::'
539 # The data is typically the repr() of the object.
579 # The data is typically the repr() of the object.
540 'data' : str,
580 'data' : str,
541
581
542 # The prompt number for this execution is also provided so that clients
582 # The counter for this execution is also provided so that clients can
543 # can display it, since IPython automatically creates variables called
583 # display it, since IPython automatically creates variables called _N (for
544 # _N (for prompt N).
584 # prompt N).
545 'prompt_number' : int,
585 'execution_count' : int,
546 }
586 }
547
587
548 Python errors
588 Python errors
General Comments 0
You need to be logged in to leave comments. Login now