Skip to content

Commit 8839681

Browse files
rybarczykjthomasballinger
authored andcommitted
add/integrate all_logical_lines to fix editor wrapping
- send_session_to_editor no longer wraps in the middle of a logical line - added better documentation to some relevant existing methods
1 parent 8e25e01 commit 8839681

File tree

2 files changed

+57
-11
lines changed

2 files changed

+57
-11
lines changed

bpython/curtsiesfrontend/repl.py

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from pygments.formatters import TerminalFormatter
2121

2222
from wcwidth import wcswidth
23+
from math import ceil
2324

2425
import 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:

bpython/test/test_curtsies_repl.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ def test_external_communication(self):
7878
def test_external_communication_encoding(self):
7979
with captured_output():
8080
self.repl.display_lines.append('>>> "åß∂ƒ"')
81+
self.repl.history.append('"åß∂ƒ"')
82+
self.repl.all_logical_lines.append(('"åß∂ƒ"',"input"))
8183
self.repl.send_session_to_external_editor()
8284

8385
def test_get_last_word(self):

0 commit comments

Comments
 (0)