2828import re
2929import time
3030import sys
31+ import unicodedata
3132from fcntl import ioctl
3233from . import curses
3334from .fancy_termios import tcgetattr , tcsetattr
@@ -44,6 +45,13 @@ class InvalidTerminal(RuntimeError):
4445except NameError :
4546 unicode = str
4647
48+
49+ def width (c ):
50+ return 2 if unicodedata .east_asian_width (c ) in "FW" else 1
51+ def wlen (s ):
52+ return sum (map (width , s ))
53+
54+
4755_error = (termios .error , curses .error , InvalidTerminal )
4856
4957# there are arguments for changing this to "refresh"
@@ -247,46 +255,56 @@ def __write_changed_line(self, y, oldline, newline, px):
247255 # structuring this function are equally painful (I'm trying to
248256 # avoid writing code generators these days...)
249257 x = 0
250- minlen = min (len (oldline ), len (newline ))
258+ i = 0
259+ minlen = min (wlen (oldline ), wlen (newline ))
260+ pi = 0
261+ xx = 0
262+ for c in oldline :
263+ xx += width (c )
264+ pi += 1
265+ if xx >= px : break
251266 #
252267 # reuse the oldline as much as possible, but stop as soon as we
253268 # encounter an ESCAPE, because it might be the start of an escape
254269 # sequene
255- #XXX unicode check!
256- while x < minlen and oldline [ x ] == newline [x ] and newline [ x ] != ' \x1b ' :
257- x += 1
258- if oldline [x :] == newline [x + 1 :] and self .ich1 :
270+ while x < minlen and oldline [ i ] == newline [ i ] and newline [ i ] != ' \x1b ' :
271+ x += width ( newline [i ])
272+ i += 1
273+ if oldline [i :] == newline [i + 1 :] and self .ich1 :
259274 if (y == self .__posxy [1 ] and x > self .__posxy [0 ] and
260- oldline [px :x ] == newline [px + 1 :x + 1 ]):
275+ oldline [pi :i ] == newline [pi + 1 :i + 1 ]):
276+ i = pi
261277 x = px
262278 self .__move (x , y )
263- self .__write_code (self .ich1 )
264- self .__write (newline [x ])
265- self .__posxy = x + 1 , y
266- elif x < minlen and oldline [x + 1 :] == newline [x + 1 :]:
279+ cw = width (newline [i ])
280+ self .__write_code (cw * self .ich1 )
281+ self .__write (newline [i ])
282+ self .__posxy = x + cw , y
283+ elif (x < minlen and oldline [i + 1 :] == newline [i + 1 :]
284+ and width (oldline [i ]) == width (newline [i ])):
267285 self .__move (x , y )
268- self .__write (newline [x ])
269- self .__posxy = x + 1 , y
270- elif (self .dch1 and self .ich1 and len (newline ) == self .width
271- and x < len (newline ) - 2
272- and newline [x + 1 :- 1 ] == oldline [x :- 2 ]):
286+ self .__write (newline [i ])
287+ self .__posxy = x + width (newline [i ]), y
288+ elif (self .dch1 and self .ich1 and wlen (newline ) == self .width
289+ and x < wlen (newline ) - 2
290+ and newline [i + 1 :- 1 ] == oldline [i :- 2 ]):
291+ raise NotImplementedError () # FIXME
273292 self .__hide_cursor ()
274293 self .__move (self .width - 2 , y )
275294 self .__posxy = self .width - 2 , y
276295 self .__write_code (self .dch1 )
277296 self .__move (x , y )
278297 self .__write_code (self .ich1 )
279- self .__write (newline [x ])
280- self .__posxy = x + 1 , y
298+ self .__write (newline [i ])
299+ self .__posxy = x + width ( newline [ i ]) , y
281300 else :
282301 self .__hide_cursor ()
283302 self .__move (x , y )
284- if len (oldline ) > len (newline ):
303+ if wlen (oldline ) > wlen (newline ):
285304 self .__write_code (self ._el )
286- self .__write (newline [x :])
287- self .__posxy = len (newline ), y
305+ self .__write (newline [i :])
306+ self .__posxy = wlen (newline ), y
288307
289- #XXX: check for unicode mess
290308 if '\x1b ' in newline :
291309 # ANSI escape characters are present, so we can't assume
292310 # anything about the position of the cursor. Moving the cursor
0 commit comments