##// 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 class ConsoleWidget(QtGui.QWidget):
268 268 A boolean indicating whether the source was executed.
269 269 """
270 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 276 if source is not None:
272 277 self.input_buffer = source
273 278
@@ -284,12 +289,20 class ConsoleWidget(QtGui.QWidget):
284 289 # This ensures that _prompt_pos does not become invalid due to
285 290 # text truncation.
286 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 297 self._execute(real_source, hidden)
288 298 elif hidden:
289 299 raise RuntimeError('Incomplete noninteractive input: "%s"' % source)
290 300 else:
291 301 self._show_continuation_prompt()
292 302
303 if not hidden:
304 cursor.endEditBlock()
305
293 306 return complete
294 307
295 308 def _get_input_buffer(self):
@@ -546,6 +559,7 class ConsoleWidget(QtGui.QWidget):
546 559 control.redoAvailable.connect(self.redo_available)
547 560 control.undoAvailable.connect(self.undo_available)
548 561 control.setReadOnly(True)
562 control.setUndoRedoEnabled(False)
549 563 control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
550 564 return control
551 565
@@ -555,6 +569,7 class ConsoleWidget(QtGui.QWidget):
555 569 control = QtGui.QPlainTextEdit()
556 570 control.installEventFilter(self)
557 571 control.setReadOnly(True)
572 control.setUndoRedoEnabled(False)
558 573 control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
559 574 return control
560 575
@@ -664,10 +679,12 class ConsoleWidget(QtGui.QWidget):
664 679 if not self._reading and \
665 680 cursor.columnNumber() == len_prompt and \
666 681 position != self._prompt_pos:
682 cursor.beginEditBlock()
667 683 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
668 684 QtGui.QTextCursor.KeepAnchor)
669 685 cursor.removeSelectedText()
670 686 cursor.deletePreviousChar()
687 cursor.endEditBlock()
671 688 intercepted = True
672 689
673 690 # Regular backwards deletion
@@ -679,8 +696,23 class ConsoleWidget(QtGui.QWidget):
679 696 intercepted = not self._in_buffer(min(anchor, position))
680 697
681 698 elif key == QtCore.Qt.Key_Delete:
682 anchor = cursor.anchor()
683 intercepted = not self._in_buffer(min(anchor, position))
699
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 717 # Don't move the cursor if control is down to allow copy-paste using
686 718 # the keyboard in any part of the buffer.
@@ -1026,7 +1058,10 class ConsoleWidget(QtGui.QWidget):
1026 1058 """
1027 1059 # Temporarily disable the maximum block count to permit undo/redo and
1028 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 1065 self._control.setUndoRedoEnabled(True)
1031 1066
1032 1067 self._control.setReadOnly(False)
@@ -1039,7 +1074,6 class ConsoleWidget(QtGui.QWidget):
1039 1074 """ Called immediately after a prompt is finished, i.e. when some input
1040 1075 will be processed and a new prompt displayed.
1041 1076 """
1042 self._control.setUndoRedoEnabled(False)
1043 1077 self._control.setReadOnly(True)
1044 1078 self._prompt_finished_hook()
1045 1079
@@ -296,8 +296,7 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):
296 296 # Decide if it makes sense to show a call tip
297 297 cursor = self._get_cursor()
298 298 cursor.movePosition(QtGui.QTextCursor.Left)
299 document = self._control.document()
300 if document.characterAt(cursor.position()).toAscii() != '(':
299 if cursor.document().characterAt(cursor.position()).toAscii() != '(':
301 300 return False
302 301 context = self._get_context(cursor)
303 302 if not context:
@@ -312,14 +311,6 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):
312 311 def _complete(self):
313 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 314 # We let the kernel split the input line, so we *always* send an empty
324 315 # text field. Readline-based frontends do get a real text field which
325 316 # they can use.
@@ -327,12 +318,11 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):
327 318
328 319 # Send the completion request to the kernel
329 320 self._complete_id = self.kernel_manager.xreq_channel.complete(
330 text, # text
331 line, # line
321 text, # text
322 self._get_input_buffer_cursor_line(), # line
332 323 self._get_input_buffer_cursor_column(), # cursor_pos
333 324 self.input_buffer) # block
334 325 self._complete_pos = self._get_cursor().position()
335 return True
336 326
337 327 def _get_banner(self):
338 328 """ Gets a banner to display at the beginning of a session.
@@ -342,7 +332,8 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):
342 332 return banner % (sys.version, sys.platform)
343 333
344 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 338 if cursor is None:
348 339 cursor = self._get_cursor()
General Comments 0
You need to be logged in to leave comments. Login now