@@ -297,6 +297,10 @@ def __init__(self, parent=None, **kw):
297
297
self ._reading_callback = None
298
298
self ._tab_width = 4
299
299
300
+ # Cursor position of where to insert text.
301
+ # Control characters allow this to move around on the current line.
302
+ self ._insert_text_cursor = self ._control .textCursor ()
303
+
300
304
# List of strings pending to be appended as plain text in the widget.
301
305
# The text is not immediately inserted when available to not
302
306
# choke the Qt event loop with paint events for the widget in
@@ -695,6 +699,9 @@ def do_execute(self, source, complete, indent):
695
699
# effect when using a QTextEdit. I believe this is a Qt bug.
696
700
self ._control .moveCursor (QtGui .QTextCursor .End )
697
701
702
+ # Advance where text is inserted
703
+ self ._insert_text_cursor .movePosition (QtGui .QTextCursor .End )
704
+
698
705
def export_html (self ):
699
706
""" Shows a dialog to export HTML/XML in various formats.
700
707
"""
@@ -712,6 +719,9 @@ def _finalize_input_request(self):
712
719
self ._append_before_prompt_cursor .setPosition (
713
720
self ._get_end_cursor ().position ())
714
721
722
+ self ._insert_text_cursor .setPosition (
723
+ self ._get_end_cursor ().position ())
724
+
715
725
# The maximum block count is only in effect during execution.
716
726
# This ensures that _prompt_pos does not become invalid due to
717
727
# text truncation.
@@ -841,12 +851,12 @@ def paste(self, mode=QtGui.QClipboard.Clipboard):
841
851
842
852
self ._insert_plain_text_into_buffer (cursor , dedent (text ))
843
853
844
- def print_ (self , printer = None ):
854
+ def print_ (self , printer = None ):
845
855
""" Print the contents of the ConsoleWidget to the specified QPrinter.
846
856
"""
847
- if ( not printer ) :
857
+ if not printer :
848
858
printer = QtPrintSupport .QPrinter ()
849
- if ( QtPrintSupport .QPrintDialog (printer ).exec_ () != QtPrintSupport .QPrintDialog .Accepted ) :
859
+ if QtPrintSupport .QPrintDialog (printer ).exec_ () != QtPrintSupport .QPrintDialog .Accepted :
850
860
return
851
861
self ._control .print_ (printer )
852
862
@@ -998,18 +1008,40 @@ def _append_custom(self, insert, input, before_prompt=False, *args, **kwargs):
998
1008
current prompt, if there is one.
999
1009
"""
1000
1010
# Determine where to insert the content.
1001
- cursor = self ._control . textCursor ()
1011
+ cursor = self ._insert_text_cursor
1002
1012
if before_prompt and (self ._reading or not self ._executing ):
1003
1013
self ._flush_pending_stream ()
1004
- cursor ._insert_mode = True
1005
- cursor .setPosition (self ._append_before_prompt_pos )
1014
+
1015
+ # Jump to before prompt, if there is one
1016
+ if cursor .position () >= self ._append_before_prompt_pos \
1017
+ and self ._append_before_prompt_pos != self ._get_end_pos ():
1018
+ cursor .setPosition (self ._append_before_prompt_pos )
1019
+
1020
+ # If we appending on the same line as the prompt, use insert mode
1021
+ # If so, the character at self._append_before_prompt_pos will not be a newline
1022
+ cursor .movePosition (QtGui .QTextCursor .Right ,
1023
+ QtGui .QTextCursor .KeepAnchor )
1024
+ if cursor .selection ().toPlainText () != '\n ' :
1025
+ cursor ._insert_mode = True
1026
+ cursor .movePosition (QtGui .QTextCursor .Left )
1006
1027
else :
1028
+ # Insert at current printing point
1029
+ # If cursor is before prompt jump to end, but only if there
1030
+ # is a prompt (before_prompt_pos != end)
1031
+ if cursor .position () <= self ._append_before_prompt_pos \
1032
+ and self ._append_before_prompt_pos != self ._get_end_pos ():
1033
+ cursor .movePosition (QtGui .QTextCursor .End )
1034
+
1007
1035
if insert != self ._insert_plain_text :
1008
1036
self ._flush_pending_stream ()
1009
- cursor .movePosition (QtGui .QTextCursor .End )
1010
1037
1011
1038
# Perform the insertion.
1012
1039
result = insert (cursor , input , * args , ** kwargs )
1040
+
1041
+ # Remove insert mode tag
1042
+ if hasattr (cursor , '_insert_mode' ):
1043
+ del cursor ._insert_mode
1044
+
1013
1045
return result
1014
1046
1015
1047
def _append_block (self , block_format = None , before_prompt = False ):
@@ -1045,7 +1077,7 @@ def _clear_temporary_buffer(self):
1045
1077
# Select and remove all text below the input buffer.
1046
1078
cursor = self ._get_prompt_cursor ()
1047
1079
prompt = self ._continuation_prompt .lstrip ()
1048
- if ( self ._temp_buffer_filled ) :
1080
+ if self ._temp_buffer_filled :
1049
1081
self ._temp_buffer_filled = False
1050
1082
while cursor .movePosition (QtGui .QTextCursor .NextBlock ):
1051
1083
temp_cursor = QtGui .QTextCursor (cursor )
@@ -1657,24 +1689,23 @@ def _event_filter_page_keypress(self, event):
1657
1689
return False
1658
1690
1659
1691
def _on_flush_pending_stream_timer (self ):
1660
- """ Flush the pending stream output and change the
1661
- prompt position appropriately.
1692
+ """ Flush pending text into the widget on console timer trigger.
1662
1693
"""
1663
- cursor = self ._control .textCursor ()
1664
- cursor .movePosition (QtGui .QTextCursor .End )
1665
1694
self ._flush_pending_stream ()
1666
- cursor .movePosition (QtGui .QTextCursor .End )
1667
1695
1668
1696
def _flush_pending_stream (self ):
1669
- """ Flush out pending text into the widget. """
1697
+ """ Flush pending text into the widget. Only applies to text that is pending
1698
+ when the console is in the running state. Text printed when console is
1699
+ not running is shown immediately, and does not wait to be flushed.
1700
+ """
1670
1701
text = self ._pending_insert_text
1671
1702
self ._pending_insert_text = []
1672
1703
buffer_size = self ._control .document ().maximumBlockCount ()
1673
1704
if buffer_size > 0 :
1674
1705
text = self ._get_last_lines_from_list (text , buffer_size )
1675
1706
text = '' .join (text )
1676
1707
t = time .time ()
1677
- self ._insert_plain_text (self ._get_end_cursor () , text , flush = True )
1708
+ self ._insert_plain_text (self ._insert_text_cursor , text , flush = True )
1678
1709
# Set the flush interval to equal the maximum time to update text.
1679
1710
self ._pending_text_flush_interval .setInterval (
1680
1711
int (max (100 , (time .time () - t ) * 1000 ))
@@ -2093,12 +2124,12 @@ def _insert_plain_text(self, cursor, text, flush=False):
2093
2124
2094
2125
if (self ._executing and not flush and
2095
2126
self ._pending_text_flush_interval .isActive () and
2096
- cursor .position () == self ._get_end_pos ()):
2127
+ cursor .position () == self ._insert_text_cursor . position ()):
2097
2128
# Queue the text to insert in case it is being inserted at end
2098
2129
self ._pending_insert_text .append (text )
2099
2130
if buffer_size > 0 :
2100
2131
self ._pending_insert_text = self ._get_last_lines_from_list (
2101
- self ._pending_insert_text , buffer_size )
2132
+ self ._pending_insert_text , buffer_size )
2102
2133
return
2103
2134
2104
2135
if self ._executing and not self ._pending_text_flush_interval .isActive ():
@@ -2123,7 +2154,7 @@ def _insert_plain_text(self, cursor, text, flush=False):
2123
2154
cursor .select (QtGui .QTextCursor .Document )
2124
2155
remove = True
2125
2156
if act .area == 'line' :
2126
- if act .erase_to == 'all' :
2157
+ if act .erase_to == 'all' :
2127
2158
cursor .select (QtGui .QTextCursor .LineUnderCursor )
2128
2159
remove = True
2129
2160
elif act .erase_to == 'start' :
@@ -2137,7 +2168,7 @@ def _insert_plain_text(self, cursor, text, flush=False):
2137
2168
QtGui .QTextCursor .EndOfLine ,
2138
2169
QtGui .QTextCursor .KeepAnchor )
2139
2170
remove = True
2140
- if remove :
2171
+ if remove :
2141
2172
nspace = cursor .selectionEnd ()- cursor .selectionStart () if fill else 0
2142
2173
cursor .removeSelectedText ()
2143
2174
if nspace > 0 : cursor .insertText (' ' * nspace ) # replace text by space, to keep cursor position as specified
@@ -2174,15 +2205,17 @@ def _insert_plain_text(self, cursor, text, flush=False):
2174
2205
# simulate replacement mode
2175
2206
if substring is not None :
2176
2207
format = self ._ansi_processor .get_format ()
2177
- if not (hasattr (cursor ,'_insert_mode' ) and cursor ._insert_mode ):
2208
+
2209
+ # Note that using _insert_mode means the \r ANSI sequence will not swallow characters.
2210
+ if not (hasattr (cursor , '_insert_mode' ) and cursor ._insert_mode ):
2178
2211
pos = cursor .position ()
2179
2212
cursor2 = QtGui .QTextCursor (cursor ) # self._get_line_end_pos() is the previous line, don't use it
2180
2213
cursor2 .movePosition (QtGui .QTextCursor .EndOfLine )
2181
2214
remain = cursor2 .position () - pos # number of characters until end of line
2182
2215
n = len (substring )
2183
2216
swallow = min (n , remain ) # number of character to swallow
2184
- cursor .setPosition (pos + swallow ,QtGui .QTextCursor .KeepAnchor )
2185
- cursor .insertText (substring ,format )
2217
+ cursor .setPosition (pos + swallow , QtGui .QTextCursor .KeepAnchor )
2218
+ cursor .insertText (substring , format )
2186
2219
else :
2187
2220
cursor .insertText (text )
2188
2221
cursor .endEditBlock ()
@@ -2399,7 +2432,7 @@ def _readline(self, prompt='', callback=None, password=False):
2399
2432
2400
2433
self ._reading = True
2401
2434
if password :
2402
- self ._show_prompt ('Warning: QtConsole does not support password mode, ' \
2435
+ self ._show_prompt ('Warning: QtConsole does not support password mode, '
2403
2436
'the text you type will be visible.' , newline = True )
2404
2437
2405
2438
if 'ipdb' not in prompt .lower ():
@@ -2531,6 +2564,9 @@ def _show_prompt(self, prompt=None, html=False, newline=True,
2531
2564
if move_forward :
2532
2565
self ._append_before_prompt_cursor .setPosition (
2533
2566
self ._append_before_prompt_cursor .position () + 1 )
2567
+ else :
2568
+ # cursor position was 0, set before prompt cursor
2569
+ self ._append_before_prompt_cursor .setPosition (0 )
2534
2570
self ._prompt_started ()
2535
2571
2536
2572
#------ Signal handlers ----------------------------------------------------
0 commit comments