From f61b15b28426726de92fc970bf9b218c08913811 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 26 Jul 2025 09:25:01 +0530 Subject: [PATCH] Fix incorrect handling of VS16 when it causes char to wrap to next line and is part of a draw command with more characters following it Needed to initialize full text loop state rather than just segmentation state on wrap. Fixes #8848 --- docs/changelog.rst | 2 ++ kitty/screen.c | 11 ++++++----- kitty_tests/screen.py | 4 ++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index af114e0c1..0053714dd 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -122,6 +122,8 @@ Detailed list of changes - Watchers: A new event for global watchers corresponding to the tab bar being changed (:disc:`8842`) +- Fix a regression in 0.40.0 that broke handing of the VS16 variation selector when it caused a character to reflow to the next line (:iss:`8848`) + 0.42.2 [2025-07-16] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/kitty/screen.c b/kitty/screen.c index 1f18a110a..c9bd6e08e 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -944,7 +944,8 @@ move_cursor_past_multicell(Screen *self, index_type required_width) { } static void -move_widened_char_past_multiline_chars(Screen *self, CPUCell* cpu_cell, GPUCell *gpu_cell, index_type xpos, index_type ypos) { +move_widened_char_past_multiline_chars(Screen *self, text_loop_state *s, CPUCell* cpu_cell, GPUCell *gpu_cell, index_type xpos, index_type ypos) { + index_type before = self->cursor->y; self->cursor->x = xpos; self->cursor->y = ypos; if (move_cursor_past_multicell(self, 2)) { CPUCell *cp; GPUCell *gp; @@ -957,6 +958,8 @@ move_widened_char_past_multiline_chars(Screen *self, CPUCell* cpu_cell, GPUCell self->cursor->x++; } *cpu_cell = (CPUCell){0}; *gpu_cell = (GPUCell){0}; + if (self->cursor->y == before) init_segmentation_state(self, s); + else init_text_loop_line(self, s); } static bool @@ -984,8 +987,7 @@ draw_combining_char(Screen *self, text_loop_state *s, char_type ch) { CPUCell *second = cp + xpos + 1; if (second->is_multicell) { if (second->y) { - move_widened_char_past_multiline_chars(self, cpu_cell, gpu_cell, xpos, s->prev.y); - init_segmentation_state(self, s); + move_widened_char_past_multiline_chars(self, s, cpu_cell, gpu_cell, xpos, s->prev.y); return; } nuke_multicell_char_at(self, xpos + 1, s->prev.y, false); @@ -994,8 +996,7 @@ draw_combining_char(Screen *self, text_loop_state *s, char_type ch) { self->cursor->x++; *second = *cpu_cell; second->x = 1; } else { - move_widened_char_past_multiline_chars(self, cpu_cell, gpu_cell, xpos, s->prev.y); - init_segmentation_state(self, s); + move_widened_char_past_multiline_chars(self, s, cpu_cell, gpu_cell, xpos, s->prev.y); } } } else if (ch == VS15) { diff --git a/kitty_tests/screen.py b/kitty_tests/screen.py index 03ad0d43e..41b81f431 100644 --- a/kitty_tests/screen.py +++ b/kitty_tests/screen.py @@ -732,6 +732,10 @@ class TestScreen(BaseTest): self.ae(s.text_for_selection(), ('a\u00adb',)) def test_variation_selectors(self): + s = self.create_screen(cols=3) + q = '*\ufe0f' + s.draw(q*(s.columns+1)) + self.ae(str(s.line(0)), q*(s.columns//2)) s = self.create_screen(cols=8) def widths(text, *widths): s.reset()