diff --git a/docs/changelog.rst b/docs/changelog.rst index 8721be262..bfee3f786 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -128,6 +128,8 @@ Detailed list of changes - Fix a regression in 0.40.0 that broke rendering of VS15 variation selectors in some circumstances (:iss:`8731`) +- Fix a regression in 0.40.0 that broke serialization of tab characters as ANSI text (:iss:`8741`) + 0.42.1 [2025-05-17] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/kitty/line.c b/kitty/line.c index 66540a77f..0a0fa26f6 100644 --- a/kitty/line.c +++ b/kitty/line.c @@ -111,10 +111,11 @@ multicell_is_continuation_of_previous(const CPUCell *prev, const CPUCell *curr) return prev->width == curr->width && !curr->natural_width; } -static void +static index_type text_in_cell_ansi(ANSILineState *s, const CPUCell *c, TextCache *tc, bool skip_multiline_non_zero_lines) { + index_type num_cells_to_skip_for_tab = 0; if (c->is_multicell) { - if (c->x || (skip_multiline_non_zero_lines && c->y)) return; + if (c->x || (skip_multiline_non_zero_lines && c->y)) return num_cells_to_skip_for_tab; if (s->current_multicell_state) { if (!multicell_is_continuation_of_previous(s->current_multicell_state, c)) { close_multicell(s); @@ -134,18 +135,15 @@ text_in_cell_ansi(ANSILineState *s, const CPUCell *c, TextCache *tc, bool skip_m switch (s->output_buf->buf[pos]) { case 0: s->output_buf->buf[pos] = ' '; break; case '\t': { - unsigned num_cells_to_skip_for_tab = 0, n = s->output_buf->len - pos; - if (n - pos > 1) { + index_type n = s->output_buf->len - pos; + if (n > 1) { num_cells_to_skip_for_tab = s->output_buf->buf[s->output_buf->len - n + 1]; s->output_buf->len -= n - 1; } - const CPUCell *next = c + 1; - while (num_cells_to_skip_for_tab && pos + 1 < s->limit && cell_is_char(next, ' ')) { - num_cells_to_skip_for_tab--; pos++; next++; - } } break; } } + return num_cells_to_skip_for_tab; } @@ -596,8 +594,13 @@ line_as_ansi(Line *self, ANSILineState *s, index_type start_at, index_type stop_ if (*sgr) write_sgr_to_ansi_buf(s, sgr); } - text_in_cell_ansi(s, self->cpu_cells + s->pos, self->text_cache, skip_multiline_non_zero_lines); + index_type num_cells_to_skip_for_tab = text_in_cell_ansi( + s, self->cpu_cells + s->pos, self->text_cache, skip_multiline_non_zero_lines); s->prev_gpu_cell = cell; + const CPUCell *next = self->cpu_cells + s->pos + 1; + while (num_cells_to_skip_for_tab && s->pos + 1 < s->limit && cell_is_char(next, ' ')) { + num_cells_to_skip_for_tab--; s->pos++; next++; + } } close_multicell(s); return s->escape_code_written; diff --git a/kitty_tests/datatypes.py b/kitty_tests/datatypes.py index ef01cac6b..d65cd007e 100644 --- a/kitty_tests/datatypes.py +++ b/kitty_tests/datatypes.py @@ -535,8 +535,6 @@ class TestDataTypes(BaseTest): if saved[k] is not None: os.environ[k] = saved[k] - - def test_historybuf(self): lb = filled_line_buf() hb = HistoryBuf(5, 5) diff --git a/kitty_tests/screen.py b/kitty_tests/screen.py index 1845c1117..01f51de81 100644 --- a/kitty_tests/screen.py +++ b/kitty_tests/screen.py @@ -105,6 +105,14 @@ class TestScreen(BaseTest): self.ae(str(s.line(4)), 'a\u0306b1\u030623') self.ae((s.cursor.x, s.cursor.y), (2, 4)) + # Test drawing of tabs + s = self.create_screen(cols=32) + txt = 'a\tb' + s.draw(txt) + ln = s.line(0) + self.ae(txt, ln.as_ansi()) + + def test_rep(self): s = self.create_screen() s.draw('a')