Skip to content

Commit 9da6783

Browse files
committed
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 84d581a commit 9da6783

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
@@ -402,7 +402,7 @@ def __init__(
402402
# so we're just using the same object
403403
self.interact = self.status_bar
404404

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

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

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