From a9f5519d116ca7e4606233b71810df1f53cd95ef Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 16 Nov 2023 13:13:31 +0530 Subject: [PATCH] Add tests for writing with cursor on trailer of wide char --- kitty/screen.c | 21 +++++++++------------ kitty_tests/screen.py | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/kitty/screen.c b/kitty/screen.c index c2db02bd0..1d6fe4dce 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -658,22 +658,20 @@ screen_on_input(Screen *self) { } static void -ensure_cursor_not_on_wide_char_trailer_for_insert(Screen *self) { - if (self->cursor->x == 0) return; - CPUCell *c; GPUCell *g; - linebuf_init_cells(self->linebuf, self->cursor->y, &c, &g); - if (g[self->cursor->x - 1].attrs.width == 2) { - g[self->cursor->x-1].attrs.width = 1; - c[self->cursor->x-1].ch = ' '; - c[self->cursor->x].ch = 0; - memset(c[self->cursor->x-1].cc_idx, 0, sizeof(c[0].cc_idx)); - memset(c[self->cursor->x].cc_idx, 0, sizeof(c[0].cc_idx)); +ensure_cursor_not_on_wide_char_trailer_for_insert(Screen *self, text_loop_state *s) { + if (UNLIKELY(self->cursor->x > 0 && s->gp[self->cursor->x - 1].attrs.width == 2)) { + memcpy(s->gp + self->cursor->x - 1, &s->g, sizeof(s->g)); + memcpy(s->cp + self->cursor->x - 1, &s->cc, sizeof(s->cc)); + s->cp[self->cursor->x-1].ch = ' '; + memcpy(s->gp + self->cursor->x, &s->g, sizeof(s->g)); + memcpy(s->cp + self->cursor->x, &s->cc, sizeof(s->cc)); } } static void draw_text_loop(Screen *self, const uint32_t *chars, size_t num_chars, text_loop_state *s) { init_text_loop_line(self, s); + if (chars[0] < 0x7f || !is_combining_char(chars[0])) ensure_cursor_not_on_wide_char_trailer_for_insert(self, s); for (size_t i = 0; i < num_chars; i++) { uint32_t ch = chars[i]; if (ch < ' ') continue; @@ -701,7 +699,7 @@ draw_text_loop(Screen *self, const uint32_t *chars, size_t num_chars, text_loop_ init_text_loop_line(self, s); } else { self->cursor->x = self->columns - char_width; - ensure_cursor_not_on_wide_char_trailer_for_insert(self); + ensure_cursor_not_on_wide_char_trailer_for_insert(self, s); } } if (self->modes.mIRM) line_right_shift(self->linebuf->line, self->cursor->x, char_width); @@ -738,7 +736,6 @@ draw_text(Screen *self, const uint32_t *chars, size_t num_chars) { .decoration_fg=force_underline ? ((OPT(url_color) & COL_MASK) << 8) | 2 : self->cursor->decoration_fg & COL_MASK, } }; - ensure_cursor_not_on_wide_char_trailer_for_insert(self); draw_text_loop(self, chars, num_chars, &s); } diff --git a/kitty_tests/screen.py b/kitty_tests/screen.py index d1ea5d0dd..57b59b6c2 100644 --- a/kitty_tests/screen.py +++ b/kitty_tests/screen.py @@ -600,6 +600,21 @@ class TestScreen(BaseTest): s.draw('\u25b6\ufe0f') self.ae(s.cursor.x, 2) + def test_writing_with_cursor_on_trailer_of_wide_character(self): + s = self.create_screen() + def r(x, pos, expected): + s.reset() + s.draw('😸') + self.ae(s.cursor.x, 2) + s.cursor.x = 1 + s.draw(x) + self.ae(s.cursor.x, pos) + self.ae(str(s.line(0)), expected) + + r('a', 2, ' a') + r('😸', 3, ' 😸') + r('\u0304', 1, '😸\u0304') + def test_serialize(self): from kitty.window import as_text s = self.create_screen()