77
88KEYS = 'asdghklqwertyuiopzxcvbnmfj'
99
10+ def get_char_width (char ):
11+ """Get visual width of a single character"""
12+ return 2 if unicodedata .east_asian_width (char ) in 'WF' else 1
13+
1014def get_string_width (s ):
1115 """Calculate visual width of string, accounting for double-width characters"""
1216 width = 0
1317 for c in s :
14- # East Asian Width (W, F) characters are full width (2 columns)
15- width += 2 if unicodedata .east_asian_width (c ) in 'WF' else 1
18+ width += get_char_width (c )
1619 return width
1720
21+ def get_true_position (line , target_col ):
22+ """Calculate true position accounting for wide characters"""
23+ visual_pos = 0
24+ true_pos = 0
25+ while true_pos < len (line ) and visual_pos < target_col :
26+ char_width = get_char_width (line [true_pos ])
27+ visual_pos += char_width
28+ true_pos += 1
29+ return true_pos
30+
1831def pyshell (cmd ):
1932 debug = os .environ .get ('TMUX_EASYMOTION_DEBUG' ) == 'true'
2033 if debug :
@@ -140,18 +153,19 @@ def main(stdscr):
140153 lines = captured_pane .splitlines ()
141154 for line_num , line in enumerate (lines ):
142155 for match in re .finditer (search_ch , line .lower ()):
143- col = match .start ()
144- positions .append ((line_num , col ))
156+ visual_col = sum ( get_char_width ( c ) for c in line [: match .start ()] )
157+ positions .append ((line_num , visual_col , line [ match . start ()] ))
145158
146159 # render 1st hints
147- for i , (line_num , col ) in enumerate (positions ):
160+ for i , (line_num , col , char ) in enumerate (positions ):
148161 if i >= len (hints ):
149162 break
150163 y = line_num
151164 x = col
152165 stdscr .addstr (y , x , hints [i ][0 ], curses .color_pair (RED ))
153- if x + 1 < width :
154- stdscr .addstr (y , x + 1 , hints [i ][1 ], curses .color_pair (GREEN ))
166+ char_width = get_char_width (char )
167+ if x + char_width < width :
168+ stdscr .addstr (y , x + char_width , hints [i ][1 ], curses .color_pair (GREEN ))
155169 stdscr .refresh ()
156170
157171 ch1 = stdscr .getkey ()
@@ -164,12 +178,14 @@ def main(stdscr):
164178 stdscr .addstr (y , 0 , line )
165179 except curses .error :
166180 pass
167- for i , (line_num , col ) in enumerate (positions ):
181+ for i , (line_num , col , char ) in enumerate (positions ):
168182 if not hints [i ].startswith (ch1 ) or len (hints [i ]) < 2 :
169183 continue
170184 y = line_num
171185 x = col
172- stdscr .addstr (y , x , hints [i ][1 ], curses .color_pair (GREEN ))
186+ char_width = get_char_width (char )
187+ if x + char_width < width :
188+ stdscr .addstr (y , x + char_width , hints [i ][1 ], curses .color_pair (GREEN ))
173189 stdscr .refresh ()
174190
175191 ch2 = stdscr .getkey ()
@@ -179,7 +195,8 @@ def main(stdscr):
179195 # Calculate final position based on line and column
180196 target_pos = positions [hints_dict [ch1 + ch2 ]]
181197 line_offset = sum (len (line ) + 1 for line in lines [:target_pos [0 ]])
182- final_pos = line_offset + target_pos [1 ]
198+ true_col = get_true_position (lines [target_pos [0 ]], target_pos [1 ])
199+ final_pos = line_offset + true_col # Convert visual position to true position
183200
184201 # Adjust for scroll position
185202 if scroll_position > 0 :
0 commit comments