Skip to content

Commit 4ede389

Browse files
rybarczykjsebastinas
authored andcommitted
Integrate width awareness
- update display_linize to use curtsies 3.0 width functionality - correct cursor positioning on wrapped fullwidth chars by adding new method - add test coverage
1 parent 46270e0 commit 4ede389

File tree

3 files changed

+64
-15
lines changed

3 files changed

+64
-15
lines changed

bpython/curtsiesfrontend/repl.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ def __init__(
403403
# so we're just using the same object
404404
self.interact = self.status_bar
405405

406-
# line currently being edited, without ps1 (usually '>>> ')
406+
# logical line currently being edited, without ps1 (usually '>>> ')
407407
self._current_line = ""
408408

409409
# current line of output - stdout and stdin go here
@@ -745,8 +745,9 @@ def process_key_event(self, e):
745745
)
746746
and self.config.curtsies_right_arrow_completion
747747
and self.cursor_offset == len(self.current_line)
748+
# if at end of current line and user presses RIGHT (to autocomplete)
748749
):
749-
750+
# then autocomplete
750751
self.current_line += self.current_suggestion
751752
self.cursor_offset = len(self.current_line)
752753
elif e in ("<UP>",) + key_dispatch[self.config.up_one_line_key]:
@@ -1436,6 +1437,23 @@ def current_output_line(self, value):
14361437
self.current_stdouterr_line = ""
14371438
self.stdin.current_line = "\n"
14381439

1440+
def number_of_padding_chars_on_current_cursor_line(self):
1441+
""" To avoid cutting off two-column characters at the end of lines where
1442+
there's only one column left, curtsies adds a padding char (u' ').
1443+
It's important to know about these for cursor positioning.
1444+
1445+
Should return zero unless there are fullwidth characters. """
1446+
full_line = self.current_cursor_line_without_suggestion
1447+
line_with_padding = "".join(
1448+
line.s
1449+
for line in paint.display_linize(
1450+
self.current_cursor_line_without_suggestion.s, self.width
1451+
)
1452+
)
1453+
1454+
# the difference in length here is how much padding there is
1455+
return len(line_with_padding) - len(full_line)
1456+
14391457
def paint(
14401458
self,
14411459
about_to_exit=False,
@@ -1621,7 +1639,8 @@ def move_screen_up(current_line_start_row):
16211639
wcswidth(self.current_cursor_line_without_suggestion.s)
16221640
- wcswidth(self.current_line)
16231641
+ wcswidth(self.current_line[: self.cursor_offset])
1624-
),
1642+
)
1643+
+ self.number_of_padding_chars_on_current_cursor_line(),
16251644
width,
16261645
)
16271646
assert cursor_column >= 0, (

bpython/curtsiesfrontend/replpainter.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,24 @@ def display_linize(msg, columns, blank_line=False):
2626
"""Returns lines obtained by splitting msg over multiple lines.
2727
2828
Warning: if msg is empty, returns an empty list of lines"""
29-
display_lines = (
30-
[
31-
msg[start:end]
32-
for start, end in zip(
33-
range(0, len(msg), columns),
34-
range(columns, len(msg) + columns, columns),
35-
)
36-
]
37-
if msg
38-
else ([""] if blank_line else [])
39-
)
29+
if not msg:
30+
return [''] if blank_line else []
31+
msg = fmtstr(msg)
32+
try:
33+
display_lines = list(msg.width_aware_splitlines(columns))
34+
# use old method if wcwidth can't determine width of msg
35+
except ValueError:
36+
display_lines = (
37+
[
38+
msg[start:end]
39+
for start, end in zip(
40+
range(0, len(msg), columns),
41+
range(columns, len(msg) + columns, columns),
42+
)
43+
]
44+
)
4045
return display_lines
4146

42-
4347
def paint_history(rows, columns, display_lines):
4448
lines = []
4549
for r, line in zip(range(rows), display_lines[-rows:]):

bpython/test/test_curtsies_painting.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,32 @@ def send_key(self, key):
271271
self.repl.process_event("<SPACE>" if key == " " else key)
272272
self.repl.paint() # has some side effects we need to be wary of
273273

274+
class TestWidthAwareness(HigherLevelCurtsiesPaintingTest):
275+
276+
def test_cursor_position_with_fullwidth_char(self):
277+
self.repl.add_normal_character("間")
278+
279+
cursor_pos = self.repl.paint()[1]
280+
self.assertEqual(cursor_pos, (0,6))
281+
282+
def test_cursor_position_with_padding_char(self):
283+
# odd numbered so fullwidth chars don't wrap evenly
284+
self.repl.width = 11
285+
[self.repl.add_normal_character(c) for c in "width"]
286+
287+
cursor_pos = self.repl.paint()[1]
288+
self.assertEqual(cursor_pos, (1,4))
289+
290+
def test_display_of_padding_chars(self):
291+
self.repl.width = 11
292+
[self.repl.add_normal_character(c) for c in "width"]
293+
294+
self.enter()
295+
expected = [
296+
'>>> wid ', # <--- note the added trailing space
297+
'th']
298+
result = [d.s for d in self.repl.display_lines[0:2]]
299+
self.assertEqual(result, expected)
274300

275301
class TestCurtsiesRewindRedraw(HigherLevelCurtsiesPaintingTest):
276302
def test_rewind(self):

0 commit comments

Comments
 (0)