Skip to content

Commit fe8ace3

Browse files
authored
gh-156207: Fix curses Textbox.gather() for double-width characters (GH-156208)
gather() read the window cell by cell, so a double-width character, which occupies two cells, was returned twice. Read the whole line with in_wstr(), which returns every character once.
1 parent 8e3271d commit fe8ace3

2 files changed

Lines changed: 27 additions & 8 deletions

File tree

Lib/curses/textpad.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -200,14 +200,16 @@ def gather(self):
200200
result = ""
201201
self._update_max_yx()
202202
for y in range(self.maxy+1):
203-
self.win.move(y, 0)
204-
stop = self._end_of_line(y)
205-
if stop == 0 and self.stripspaces:
206-
continue
207-
for x in range(self.maxx+1):
208-
if self.stripspaces and x > stop:
209-
break
210-
result = result + str(self.win.in_wch(y, x))
203+
# The whole line: in_wstr() reads a double-width character once,
204+
# skipping the continuation cell that holds its other half.
205+
line = self.win.in_wstr(y, 0)
206+
if self.stripspaces:
207+
stripped = line.rstrip(' ')
208+
if not stripped:
209+
continue
210+
# Keep the blank the cursor rests on past the last character.
211+
line = stripped + ' ' if len(stripped) < len(line) else stripped
212+
result = result + line
211213
if self.maxy > 0:
212214
result = result + "\n"
213215
return result

Lib/test/test_curses.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2694,6 +2694,23 @@ def test_textbox_combining(self):
26942694
box.do_command(ch)
26952695
self.assertEqual(box.gather(), text + ' ')
26962696

2697+
@requires_wide_build
2698+
def test_textbox_double_width(self):
2699+
# A double-width (East Asian) character occupies two cells. gather()
2700+
# reads a whole line at a time so that the second cell, which holds
2701+
# the same character, is not reported as another one.
2702+
text = '你好'
2703+
if not self._encodable(text):
2704+
self.skipTest('the locale cannot encode %r' % text)
2705+
box, win = self._make_textbox(1, 12)
2706+
for ch in text:
2707+
box.do_command(ch)
2708+
self.assertEqual(box.gather(), text + ' ')
2709+
box, win = self._make_textbox(1, 12, stripspaces=False)
2710+
for ch in text:
2711+
box.do_command(ch)
2712+
self.assertEqual(box.gather(), text + ' ' * 8)
2713+
26972714
def test_textbox_edit_wide(self):
26982715
# edit() reads characters through get_wch(). Each character is pushed
26992716
# with unget_wch(), which on a narrow build requires it to encode to a

0 commit comments

Comments
 (0)