20
20
from pygments .formatters import TerminalFormatter
21
21
22
22
from wcwidth import wcswidth
23
+ from math import ceil
23
24
24
25
import blessings
25
26
@@ -395,14 +396,22 @@ def __init__(
395
396
# current line of output - stdout and stdin go here
396
397
self .current_stdouterr_line = ""
397
398
398
- # lines separated whenever logical line
399
- # length goes over what the terminal width
400
- # was at the time of original output
399
+
400
+ # this is every line that's been displayed (input and output)
401
+ # as with formatting applied. Logical lines that exceeded the terminal width
402
+ # at the time of output are split across multiple entries in this list.
401
403
self .display_lines = []
402
404
403
405
# this is every line that's been executed; it gets smaller on rewind
404
406
self .history = []
405
407
408
+ # This is every logical line that's been displayed, both input and output.
409
+ # Like self.history, lines are unwrapped, uncolored, and without prompt.
410
+ # Entries are tuples, where
411
+ # the first element a string of the line
412
+ # the second element is one of 2 strings: "input" or "output".
413
+ self .all_logical_lines = []
414
+
406
415
# formatted version of lines in the buffer kept around so we can
407
416
# unhighlight parens using self.reprint_line as called by bpython.Repl
408
417
self .display_buffer = []
@@ -411,7 +420,7 @@ def __init__(
411
420
# because there wasn't room to display everything
412
421
self .scroll_offset = 0
413
422
414
- # from the left , 0 means first char
423
+ # cursor position relative to start of current_line , 0 is first char
415
424
self ._cursor_offset = 0
416
425
417
426
self .orig_tcattrs = orig_tcattrs
@@ -872,8 +881,10 @@ def on_enter(self, new_code=True, reset_rl_history=True):
872
881
self .rl_history .reset ()
873
882
874
883
self .history .append (self .current_line )
884
+ self .all_logical_lines .append ((self .current_line , "input" ))
875
885
self .push (self .current_line , insert_into_history = new_code )
876
886
887
+
877
888
def on_tab (self , back = False ):
878
889
"""Do something on tab key
879
890
taken from bpython.cli
@@ -982,6 +993,9 @@ def process_simple_keypress(self, e):
982
993
self .add_normal_character (e )
983
994
984
995
def send_current_block_to_external_editor (self , filename = None ):
996
+ """"
997
+ Sends the current code block to external editor to be edited. Usually bound to C-x.
998
+ """
985
999
text = self .send_to_external_editor (self .get_current_block ())
986
1000
lines = [line for line in text .split ("\n " )]
987
1001
while lines and not lines [- 1 ].split ():
@@ -994,15 +1008,20 @@ def send_current_block_to_external_editor(self, filename=None):
994
1008
self .cursor_offset = len (self .current_line )
995
1009
996
1010
def send_session_to_external_editor (self , filename = None ):
1011
+ """
1012
+ Sends entire bpython session to external editor to be edited. Usually bound to F7.
1013
+
1014
+ Loops through self.all_logical_lines so that lines aren't wrapped in the editor.
1015
+
1016
+ """
997
1017
for_editor = EDIT_SESSION_HEADER
998
1018
for_editor += "\n " .join (
999
- line [len (self .ps1 ) :]
1000
- if line .startswith (self .ps1 )
1001
- else line [len (self .ps2 ) :]
1002
- if line .startswith (self .ps2 )
1003
- else "### " + line
1004
- for line in self .getstdout ().split ("\n " )
1019
+ line [0 ] if line [1 ] == "input"
1020
+ else "### " + line [0 ]
1021
+ for line in self .all_logical_lines
1005
1022
)
1023
+ for_editor += "\n " + "### " + self .current_line
1024
+
1006
1025
text = self .send_to_external_editor (for_editor )
1007
1026
if text == for_editor :
1008
1027
self .status_bar .message (
@@ -1170,7 +1189,9 @@ def push(self, line, insert_into_history=True):
1170
1189
if c :
1171
1190
logger .debug ("finished - buffer cleared" )
1172
1191
self .cursor_offset = 0
1192
+
1173
1193
self .display_lines .extend (self .display_buffer_lines )
1194
+
1174
1195
self .display_buffer = []
1175
1196
self .buffer = []
1176
1197
@@ -1237,13 +1258,17 @@ def clear_current_block(self, remove_from_history=True):
1237
1258
if remove_from_history :
1238
1259
for unused in self .buffer :
1239
1260
self .history .pop ()
1261
+ self .all_logical_lines .pop ()
1240
1262
self .buffer = []
1241
1263
self .cursor_offset = 0
1242
1264
self .saved_indent = 0
1243
1265
self .current_line = ""
1244
1266
self .cursor_offset = len (self .current_line )
1245
1267
1246
1268
def get_current_block (self ):
1269
+ """
1270
+ Returns the current code block as string (without prompts)
1271
+ """
1247
1272
return "\n " .join (self .buffer + [self .current_line ])
1248
1273
1249
1274
def send_to_stdouterr (self , output ):
@@ -1271,6 +1296,15 @@ def send_to_stdouterr(self, output):
1271
1296
[],
1272
1297
)
1273
1298
)
1299
+
1300
+ # These can be FmtStrs, but self.all_logical_lines only wants strings
1301
+ for line in [self .current_stdouterr_line ] + lines [1 :- 1 ]:
1302
+ if isinstance (line , FmtStr ):
1303
+ self .all_logical_lines .append ((line .s , "output" ))
1304
+ else :
1305
+ self .all_logical_lines .append ((line , "output" ))
1306
+
1307
+
1274
1308
self .current_stdouterr_line = lines [- 1 ]
1275
1309
logger .debug ("display_lines: %r" , self .display_lines )
1276
1310
@@ -1804,11 +1838,15 @@ def take_back_buffer_line(self):
1804
1838
self .display_buffer .pop ()
1805
1839
self .buffer .pop ()
1806
1840
self .history .pop ()
1841
+ self .all_logical_lines .pop ()
1807
1842
1808
1843
def take_back_empty_line (self ):
1809
1844
assert self .history and not self .history [- 1 ]
1810
1845
self .history .pop ()
1811
1846
self .display_lines .pop ()
1847
+ self .all_logical_lines .pop ()
1848
+
1849
+
1812
1850
1813
1851
def prompt_undo (self ):
1814
1852
if self .buffer :
@@ -1826,8 +1864,9 @@ def prompt_for_undo():
1826
1864
def redo (self ):
1827
1865
if (self .redo_stack ):
1828
1866
temp = self .redo_stack .pop ()
1829
- self .push (temp )
1830
1867
self .history .append (temp )
1868
+ self .all_logical_lines .append ((temp , "input" ))
1869
+ self .push (temp )
1831
1870
else :
1832
1871
self .status_bar .message ("Nothing to redo." )
1833
1872
@@ -1839,6 +1878,7 @@ def reevaluate(self, new_code=False):
1839
1878
old_display_lines = self .display_lines
1840
1879
self .history = []
1841
1880
self .display_lines = []
1881
+ self .all_logical_lines = []
1842
1882
1843
1883
if not self .weak_rewind :
1844
1884
self .interp = self .interp .__class__ ()
@@ -1903,6 +1943,9 @@ def initialize_interp(self):
1903
1943
del self .coderunner .interp .locals ["_Helper" ]
1904
1944
1905
1945
def getstdout (self ):
1946
+ """
1947
+ Returns a string of the current bpython session, formatted and wrapped.
1948
+ """
1906
1949
lines = self .lines_for_display + [self .current_line_formatted ]
1907
1950
s = (
1908
1951
"\n " .join (x .s if isinstance (x , FmtStr ) else x for x in lines )
@@ -1911,6 +1954,7 @@ def getstdout(self):
1911
1954
)
1912
1955
return s
1913
1956
1957
+
1914
1958
def focus_on_subprocess (self , args ):
1915
1959
prev_sigwinch_handler = signal .getsignal (signal .SIGWINCH )
1916
1960
try :
0 commit comments