2020from pygments .formatters import TerminalFormatter
2121
2222from wcwidth import wcswidth
23+ from math import ceil
2324
2425import blessings
2526
@@ -395,14 +396,22 @@ def __init__(
395396 # current line of output - stdout and stdin go here
396397 self .current_stdouterr_line = ""
397398
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.
401403 self .display_lines = []
402404
403405 # this is every line that's been executed; it gets smaller on rewind
404406 self .history = []
405407
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+
406415 # formatted version of lines in the buffer kept around so we can
407416 # unhighlight parens using self.reprint_line as called by bpython.Repl
408417 self .display_buffer = []
@@ -411,7 +420,7 @@ def __init__(
411420 # because there wasn't room to display everything
412421 self .scroll_offset = 0
413422
414- # from the left , 0 means first char
423+ # cursor position relative to start of current_line , 0 is first char
415424 self ._cursor_offset = 0
416425
417426 self .orig_tcattrs = orig_tcattrs
@@ -872,8 +881,10 @@ def on_enter(self, new_code=True, reset_rl_history=True):
872881 self .rl_history .reset ()
873882
874883 self .history .append (self .current_line )
884+ self .all_logical_lines .append ((self .current_line , "input" ))
875885 self .push (self .current_line , insert_into_history = new_code )
876886
887+
877888 def on_tab (self , back = False ):
878889 """Do something on tab key
879890 taken from bpython.cli
@@ -982,6 +993,9 @@ def process_simple_keypress(self, e):
982993 self .add_normal_character (e )
983994
984995 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+ """
985999 text = self .send_to_external_editor (self .get_current_block ())
9861000 lines = [line for line in text .split ("\n " )]
9871001 while lines and not lines [- 1 ].split ():
@@ -994,15 +1008,20 @@ def send_current_block_to_external_editor(self, filename=None):
9941008 self .cursor_offset = len (self .current_line )
9951009
9961010 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+ """
9971017 for_editor = EDIT_SESSION_HEADER
9981018 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
10051022 )
1023+ for_editor += "\n " + "### " + self .current_line
1024+
10061025 text = self .send_to_external_editor (for_editor )
10071026 if text == for_editor :
10081027 self .status_bar .message (
@@ -1170,7 +1189,9 @@ def push(self, line, insert_into_history=True):
11701189 if c :
11711190 logger .debug ("finished - buffer cleared" )
11721191 self .cursor_offset = 0
1192+
11731193 self .display_lines .extend (self .display_buffer_lines )
1194+
11741195 self .display_buffer = []
11751196 self .buffer = []
11761197
@@ -1237,13 +1258,17 @@ def clear_current_block(self, remove_from_history=True):
12371258 if remove_from_history :
12381259 for unused in self .buffer :
12391260 self .history .pop ()
1261+ self .all_logical_lines .pop ()
12401262 self .buffer = []
12411263 self .cursor_offset = 0
12421264 self .saved_indent = 0
12431265 self .current_line = ""
12441266 self .cursor_offset = len (self .current_line )
12451267
12461268 def get_current_block (self ):
1269+ """
1270+ Returns the current code block as string (without prompts)
1271+ """
12471272 return "\n " .join (self .buffer + [self .current_line ])
12481273
12491274 def send_to_stdouterr (self , output ):
@@ -1271,6 +1296,15 @@ def send_to_stdouterr(self, output):
12711296 [],
12721297 )
12731298 )
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+
12741308 self .current_stdouterr_line = lines [- 1 ]
12751309 logger .debug ("display_lines: %r" , self .display_lines )
12761310
@@ -1804,11 +1838,15 @@ def take_back_buffer_line(self):
18041838 self .display_buffer .pop ()
18051839 self .buffer .pop ()
18061840 self .history .pop ()
1841+ self .all_logical_lines .pop ()
18071842
18081843 def take_back_empty_line (self ):
18091844 assert self .history and not self .history [- 1 ]
18101845 self .history .pop ()
18111846 self .display_lines .pop ()
1847+ self .all_logical_lines .pop ()
1848+
1849+
18121850
18131851 def prompt_undo (self ):
18141852 if self .buffer :
@@ -1826,8 +1864,9 @@ def prompt_for_undo():
18261864 def redo (self ):
18271865 if (self .redo_stack ):
18281866 temp = self .redo_stack .pop ()
1829- self .push (temp )
18301867 self .history .append (temp )
1868+ self .all_logical_lines .append ((temp , "input" ))
1869+ self .push (temp )
18311870 else :
18321871 self .status_bar .message ("Nothing to redo." )
18331872
@@ -1839,6 +1878,7 @@ def reevaluate(self, new_code=False):
18391878 old_display_lines = self .display_lines
18401879 self .history = []
18411880 self .display_lines = []
1881+ self .all_logical_lines = []
18421882
18431883 if not self .weak_rewind :
18441884 self .interp = self .interp .__class__ ()
@@ -1903,6 +1943,9 @@ def initialize_interp(self):
19031943 del self .coderunner .interp .locals ["_Helper" ]
19041944
19051945 def getstdout (self ):
1946+ """
1947+ Returns a string of the current bpython session, formatted and wrapped.
1948+ """
19061949 lines = self .lines_for_display + [self .current_line_formatted ]
19071950 s = (
19081951 "\n " .join (x .s if isinstance (x , FmtStr ) else x for x in lines )
@@ -1911,6 +1954,7 @@ def getstdout(self):
19111954 )
19121955 return s
19131956
1957+
19141958 def focus_on_subprocess (self , args ):
19151959 prev_sigwinch_handler = signal .getsignal (signal .SIGWINCH )
19161960 try :
0 commit comments