28
28
import re
29
29
import time
30
30
import sys
31
+ import unicodedata
31
32
from fcntl import ioctl
32
33
from . import curses
33
34
from .fancy_termios import tcgetattr , tcsetattr
@@ -44,6 +45,13 @@ class InvalidTerminal(RuntimeError):
44
45
except NameError :
45
46
unicode = str
46
47
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
+
47
55
_error = (termios .error , curses .error , InvalidTerminal )
48
56
49
57
# there are arguments for changing this to "refresh"
@@ -247,46 +255,56 @@ def __write_changed_line(self, y, oldline, newline, px):
247
255
# structuring this function are equally painful (I'm trying to
248
256
# avoid writing code generators these days...)
249
257
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
251
266
#
252
267
# reuse the oldline as much as possible, but stop as soon as we
253
268
# encounter an ESCAPE, because it might be the start of an escape
254
269
# 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 :
259
274
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
261
277
x = px
262
278
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 ])):
267
285
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
273
292
self .__hide_cursor ()
274
293
self .__move (self .width - 2 , y )
275
294
self .__posxy = self .width - 2 , y
276
295
self .__write_code (self .dch1 )
277
296
self .__move (x , y )
278
297
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
281
300
else :
282
301
self .__hide_cursor ()
283
302
self .__move (x , y )
284
- if len (oldline ) > len (newline ):
303
+ if wlen (oldline ) > wlen (newline ):
285
304
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
288
307
289
- #XXX: check for unicode mess
290
308
if '\x1b ' in newline :
291
309
# ANSI escape characters are present, so we can't assume
292
310
# anything about the position of the cursor. Moving the cursor
0 commit comments