diff --git a/IPython/frontend/qt/console/console_widget.py b/IPython/frontend/qt/console/console_widget.py
index 93ccdf5..7308116 100644
--- a/IPython/frontend/qt/console/console_widget.py
+++ b/IPython/frontend/qt/console/console_widget.py
@@ -214,11 +214,11 @@ class ConsoleWidget(QtGui.QPlainTextEdit):
elif key == QtCore.Qt.Key_Home:
cursor.movePosition(QtGui.QTextCursor.StartOfLine)
- start_pos = cursor.position()
start_line = cursor.blockNumber()
if start_line == self._get_prompt_cursor().blockNumber():
- start_pos += len(self._prompt)
+ start_pos = self._prompt_pos
else:
+ start_pos = cursor.position()
start_pos += len(self._continuation_prompt)
if shift_down and self._in_buffer(position):
self._set_selection(position, start_pos)
@@ -266,22 +266,7 @@ class ConsoleWidget(QtGui.QPlainTextEdit):
make sense for a console widget.
"""
cursor = self._get_end_cursor()
- cursor.insertHtml(html)
-
- # After appending HTML, the text document "remembers" the current
- # formatting, which means that subsequent calls to 'appendPlainText'
- # will be formatted similarly, a behavior that we do not want. To
- # prevent this, we make sure that the last character has no formatting.
- cursor.movePosition(QtGui.QTextCursor.Left,
- QtGui.QTextCursor.KeepAnchor)
- if cursor.selection().toPlainText().trimmed().isEmpty():
- # If the last character is whitespace, it doesn't matter how it's
- # formatted, so just clear the formatting.
- cursor.setCharFormat(QtGui.QTextCharFormat())
- else:
- # Otherwise, add an unformatted space.
- cursor.movePosition(QtGui.QTextCursor.Right)
- cursor.insertText(' ', QtGui.QTextCharFormat())
+ self._insert_html(cursor, html)
def appendPlainText(self, text):
""" Reimplemented to not append text as a new paragraph, which doesn't
@@ -712,6 +697,27 @@ class ConsoleWidget(QtGui.QPlainTextEdit):
cursor.setPosition(position)
return cursor
+ def _insert_html(self, cursor, html):
+ """ Insert HTML using the specified cursor in such a way that future
+ formatting is unaffected.
+ """
+ cursor.insertHtml(html)
+
+ # After inserting HTML, the text document "remembers" the current
+ # formatting, which means that subsequent calls adding plain text
+ # will result in similar formatting, a behavior that we do not want. To
+ # prevent this, we make sure that the last character has no formatting.
+ cursor.movePosition(QtGui.QTextCursor.Left,
+ QtGui.QTextCursor.KeepAnchor)
+ if cursor.selection().toPlainText().trimmed().isEmpty():
+ # If the last character is whitespace, it doesn't matter how it's
+ # formatted, so just clear the formatting.
+ cursor.setCharFormat(QtGui.QTextCharFormat())
+ else:
+ # Otherwise, add an unformatted space.
+ cursor.movePosition(QtGui.QTextCursor.Right)
+ cursor.insertText(' ', QtGui.QTextCharFormat())
+
def _prompt_started(self):
""" Called immediately after a new prompt is displayed.
"""
diff --git a/IPython/frontend/qt/console/ipython_widget.py b/IPython/frontend/qt/console/ipython_widget.py
index 0fa6e85..5db2a3d 100644
--- a/IPython/frontend/qt/console/ipython_widget.py
+++ b/IPython/frontend/qt/console/ipython_widget.py
@@ -30,6 +30,10 @@ class IPythonWidget(FrontendWidget):
.out-prompt-number { color: red; font-weight: bold; }
"""
+ # Default prompts.
+ in_prompt = '
In [%i]: '
+ out_prompt = 'Out[%i]: '
+
#---------------------------------------------------------------------------
# 'QObject' interface
#---------------------------------------------------------------------------
@@ -38,6 +42,7 @@ class IPythonWidget(FrontendWidget):
super(IPythonWidget, self).__init__(parent)
# Initialize protected variables.
+ self._previous_prompt_blocks = []
self._prompt_count = 0
# Set a default stylesheet.
@@ -64,17 +69,29 @@ class IPythonWidget(FrontendWidget):
def _show_interpreter_prompt(self):
""" Reimplemented for IPython-style prompts.
"""
+ # Update old prompt numbers if necessary.
+ previous_prompt_number = self._prompt_count
+ if previous_prompt_number != self._prompt_count:
+ for i, (block, length) in enumerate(self._previous_prompt_blocks):
+ if block.isValid():
+ cursor = QtGui.QTextCursor(block)
+ cursor.movePosition(QtGui.QTextCursor.Right,
+ QtGui.QTextCursor.KeepAnchor, length-1)
+ if i == 0:
+ prompt = self._make_in_prompt(previous_prompt_number)
+ else:
+ prompt = self._make_out_prompt(previous_prompt_number)
+ self._insert_html(cursor, prompt)
+ self._previous_prompt_blocks = []
+
+ # Show a new prompt.
self._prompt_count += 1
- prompt_template = '%s'
- prompt_body = '
In [%i]: '
- prompt = (prompt_template % prompt_body) % self._prompt_count
- self._show_prompt(prompt, html=True)
+ self._show_prompt(self._make_in_prompt(self._prompt_count), html=True)
+ self._save_prompt_block()
# Update continuation prompt to reflect (possibly) new prompt length.
- cont_prompt_chars = '...: '
- space_count = len(self._prompt.lstrip()) - len(cont_prompt_chars)
- cont_prompt_body = ' ' * space_count + cont_prompt_chars
- self._continuation_prompt_html = prompt_template % cont_prompt_body
+ self._set_continuation_prompt(
+ self._make_continuation_prompt(self._prompt), html=True)
#------ Signal handlers ----------------------------------------------------
@@ -96,10 +113,9 @@ class IPythonWidget(FrontendWidget):
def _handle_pyout(self, omsg):
""" Reimplemented for IPython-style "display hook".
"""
- prompt_template = '%s'
- prompt_body = 'Out[%i]: '
- prompt = (prompt_template % prompt_body) % self._prompt_count
- self.appendHtml(prompt)
+ self.appendHtml(self._make_out_prompt(self._prompt_count))
+ self._save_prompt_block()
+
self.appendPlainText(omsg['content']['data'] + '\n')
#---------------------------------------------------------------------------
@@ -135,6 +151,38 @@ class IPythonWidget(FrontendWidget):
else:
self._highlighter.set_style(syntax_style)
+ #---------------------------------------------------------------------------
+ # 'IPythonWidget' protected interface
+ #---------------------------------------------------------------------------
+
+ def _make_in_prompt(self, number):
+ """ Given a prompt number, returns an HTML In prompt.
+ """
+ body = self.in_prompt % number
+ return '%s' % body
+
+ def _make_continuation_prompt(self, prompt):
+ """ Given a plain text version of an In prompt, returns an HTML
+ continuation prompt.
+ """
+ end_chars = '...: '
+ space_count = len(prompt.lstrip('\n')) - len(end_chars)
+ body = ' ' * space_count + end_chars
+ return '%s' % body
+
+ def _make_out_prompt(self, number):
+ """ Given a prompt number, returns an HTML Out prompt.
+ """
+ body = self.out_prompt % number
+ return '%s' % body
+
+ def _save_prompt_block(self):
+ """ Assuming a prompt has just been written at the end of the buffer,
+ store the QTextBlock that contains it and its length.
+ """
+ block = self.document().lastBlock()
+ self._previous_prompt_blocks.append((block, block.length()))
+
if __name__ == '__main__':
from IPython.frontend.qt.kernelmanager import QtKernelManager