##// END OF EJS Templates
* The ConsoleWidget now has full undo/redo support. Previously, the undo/redo history was cleared after every continuation prompt. This is no longer the case....
epatters -
Show More
@@ -268,6 +268,11 b' class ConsoleWidget(QtGui.QWidget):'
268 A boolean indicating whether the source was executed.
268 A boolean indicating whether the source was executed.
269 """
269 """
270 if not hidden:
270 if not hidden:
271 # Do everything here inside an edit block so continuation prompts
272 # are removed seamlessly via undo/redo.
273 cursor = self._control.textCursor()
274 cursor.beginEditBlock()
275
271 if source is not None:
276 if source is not None:
272 self.input_buffer = source
277 self.input_buffer = source
273
278
@@ -284,12 +289,20 b' class ConsoleWidget(QtGui.QWidget):'
284 # This ensures that _prompt_pos does not become invalid due to
289 # This ensures that _prompt_pos does not become invalid due to
285 # text truncation.
290 # text truncation.
286 self._control.document().setMaximumBlockCount(self.buffer_size)
291 self._control.document().setMaximumBlockCount(self.buffer_size)
292
293 # Setting a positive maximum block count will automatically
294 # disable the undo/redo history, but just to be safe:
295 self._control.setUndoRedoEnabled(False)
296
287 self._execute(real_source, hidden)
297 self._execute(real_source, hidden)
288 elif hidden:
298 elif hidden:
289 raise RuntimeError('Incomplete noninteractive input: "%s"' % source)
299 raise RuntimeError('Incomplete noninteractive input: "%s"' % source)
290 else:
300 else:
291 self._show_continuation_prompt()
301 self._show_continuation_prompt()
292
302
303 if not hidden:
304 cursor.endEditBlock()
305
293 return complete
306 return complete
294
307
295 def _get_input_buffer(self):
308 def _get_input_buffer(self):
@@ -546,6 +559,7 b' class ConsoleWidget(QtGui.QWidget):'
546 control.redoAvailable.connect(self.redo_available)
559 control.redoAvailable.connect(self.redo_available)
547 control.undoAvailable.connect(self.undo_available)
560 control.undoAvailable.connect(self.undo_available)
548 control.setReadOnly(True)
561 control.setReadOnly(True)
562 control.setUndoRedoEnabled(False)
549 control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
563 control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
550 return control
564 return control
551
565
@@ -555,6 +569,7 b' class ConsoleWidget(QtGui.QWidget):'
555 control = QtGui.QPlainTextEdit()
569 control = QtGui.QPlainTextEdit()
556 control.installEventFilter(self)
570 control.installEventFilter(self)
557 control.setReadOnly(True)
571 control.setReadOnly(True)
572 control.setUndoRedoEnabled(False)
558 control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
573 control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
559 return control
574 return control
560
575
@@ -664,10 +679,12 b' class ConsoleWidget(QtGui.QWidget):'
664 if not self._reading and \
679 if not self._reading and \
665 cursor.columnNumber() == len_prompt and \
680 cursor.columnNumber() == len_prompt and \
666 position != self._prompt_pos:
681 position != self._prompt_pos:
682 cursor.beginEditBlock()
667 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
683 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
668 QtGui.QTextCursor.KeepAnchor)
684 QtGui.QTextCursor.KeepAnchor)
669 cursor.removeSelectedText()
685 cursor.removeSelectedText()
670 cursor.deletePreviousChar()
686 cursor.deletePreviousChar()
687 cursor.endEditBlock()
671 intercepted = True
688 intercepted = True
672
689
673 # Regular backwards deletion
690 # Regular backwards deletion
@@ -679,8 +696,23 b' class ConsoleWidget(QtGui.QWidget):'
679 intercepted = not self._in_buffer(min(anchor, position))
696 intercepted = not self._in_buffer(min(anchor, position))
680
697
681 elif key == QtCore.Qt.Key_Delete:
698 elif key == QtCore.Qt.Key_Delete:
682 anchor = cursor.anchor()
699
683 intercepted = not self._in_buffer(min(anchor, position))
700 # Line deletion (remove continuation prompt)
701 if not self._reading and cursor.atBlockEnd() and not \
702 cursor.hasSelection():
703 cursor.movePosition(QtGui.QTextCursor.NextBlock,
704 QtGui.QTextCursor.KeepAnchor)
705 cursor.movePosition(QtGui.QTextCursor.Right,
706 QtGui.QTextCursor.KeepAnchor,
707 len(self._continuation_prompt))
708 cursor.removeSelectedText()
709 intercepted = True
710
711 # Regular forwards deletion:
712 else:
713 anchor = cursor.anchor()
714 intercepted = (not self._in_buffer(anchor) or
715 not self._in_buffer(position))
684
716
685 # Don't move the cursor if control is down to allow copy-paste using
717 # Don't move the cursor if control is down to allow copy-paste using
686 # the keyboard in any part of the buffer.
718 # the keyboard in any part of the buffer.
@@ -1026,7 +1058,10 b' class ConsoleWidget(QtGui.QWidget):'
1026 """
1058 """
1027 # Temporarily disable the maximum block count to permit undo/redo and
1059 # Temporarily disable the maximum block count to permit undo/redo and
1028 # to ensure that the prompt position does not change due to truncation.
1060 # to ensure that the prompt position does not change due to truncation.
1029 self._control.document().setMaximumBlockCount(0)
1061 # Because setting this property clears the undo/redo history, we only
1062 # set it if we have to.
1063 if self._control.document().maximumBlockCount() > 0:
1064 self._control.document().setMaximumBlockCount(0)
1030 self._control.setUndoRedoEnabled(True)
1065 self._control.setUndoRedoEnabled(True)
1031
1066
1032 self._control.setReadOnly(False)
1067 self._control.setReadOnly(False)
@@ -1039,7 +1074,6 b' class ConsoleWidget(QtGui.QWidget):'
1039 """ Called immediately after a prompt is finished, i.e. when some input
1074 """ Called immediately after a prompt is finished, i.e. when some input
1040 will be processed and a new prompt displayed.
1075 will be processed and a new prompt displayed.
1041 """
1076 """
1042 self._control.setUndoRedoEnabled(False)
1043 self._control.setReadOnly(True)
1077 self._control.setReadOnly(True)
1044 self._prompt_finished_hook()
1078 self._prompt_finished_hook()
1045
1079
@@ -296,8 +296,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
296 # Decide if it makes sense to show a call tip
296 # Decide if it makes sense to show a call tip
297 cursor = self._get_cursor()
297 cursor = self._get_cursor()
298 cursor.movePosition(QtGui.QTextCursor.Left)
298 cursor.movePosition(QtGui.QTextCursor.Left)
299 document = self._control.document()
299 if cursor.document().characterAt(cursor.position()).toAscii() != '(':
300 if document.characterAt(cursor.position()).toAscii() != '(':
301 return False
300 return False
302 context = self._get_context(cursor)
301 context = self._get_context(cursor)
303 if not context:
302 if not context:
@@ -312,14 +311,6 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
312 def _complete(self):
311 def _complete(self):
313 """ Performs completion at the current cursor location.
312 """ Performs completion at the current cursor location.
314 """
313 """
315 # Decide if it makes sense to do completion
316
317 # We should return only if the line is empty. Otherwise, let the
318 # kernel split the line up.
319 line = self._get_input_buffer_cursor_line()
320 if not line:
321 return False
322
323 # We let the kernel split the input line, so we *always* send an empty
314 # We let the kernel split the input line, so we *always* send an empty
324 # text field. Readline-based frontends do get a real text field which
315 # text field. Readline-based frontends do get a real text field which
325 # they can use.
316 # they can use.
@@ -327,12 +318,11 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
327
318
328 # Send the completion request to the kernel
319 # Send the completion request to the kernel
329 self._complete_id = self.kernel_manager.xreq_channel.complete(
320 self._complete_id = self.kernel_manager.xreq_channel.complete(
330 text, # text
321 text, # text
331 line, # line
322 self._get_input_buffer_cursor_line(), # line
332 self._get_input_buffer_cursor_column(), # cursor_pos
323 self._get_input_buffer_cursor_column(), # cursor_pos
333 self.input_buffer) # block
324 self.input_buffer) # block
334 self._complete_pos = self._get_cursor().position()
325 self._complete_pos = self._get_cursor().position()
335 return True
336
326
337 def _get_banner(self):
327 def _get_banner(self):
338 """ Gets a banner to display at the beginning of a session.
328 """ Gets a banner to display at the beginning of a session.
@@ -342,7 +332,8 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
342 return banner % (sys.version, sys.platform)
332 return banner % (sys.version, sys.platform)
343
333
344 def _get_context(self, cursor=None):
334 def _get_context(self, cursor=None):
345 """ Gets the context at the current cursor location.
335 """ Gets the context for the specified cursor (or the current cursor
336 if none is specified).
346 """
337 """
347 if cursor is None:
338 if cursor is None:
348 cursor = self._get_cursor()
339 cursor = self._get_cursor()
General Comments 0
You need to be logged in to leave comments. Login now