From a48ac5e4d93639627accc925d66e73fca5db6c22 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 29 Dec 2024 15:19:51 +0530 Subject: [PATCH] More work on rewrapping multicells --- kitty/line-buf.c | 24 +++++--- kitty/rewrap.c | 140 ++++++++++++++++++++++++++++++++++++----------- kitty/rewrap.h | 5 +- 3 files changed, 128 insertions(+), 41 deletions(-) diff --git a/kitty/line-buf.c b/kitty/line-buf.c index b21e1ded154..05736041a97 100644 --- a/kitty/line-buf.c +++ b/kitty/line-buf.c @@ -650,12 +650,12 @@ restitch(Line *dest, index_type at, LineBuf *src, const index_type src_y, TrackC linebuf_clear_line(src, src_y, true); linebuf_index(src, src_y, src->ynum - 1); } - for (TrackCursor *tc = cursors; !tc->is_sentinel; tc++) if (tc->y >= src_y) tc->y -= num_of_lines_removed; + for (TrackCursor *tc = cursors; !tc->is_sentinel; tc++) if (tc->dest_y >= src_y) tc->dest_y -= num_of_lines_removed; } if (num_cells_removed_on_last_line) { bool line_is_continued_to_next = linebuf_line_ends_with_continuation(src, src_y); linebuf_init_cells(src, src_y, &cp, &gp); - for (TrackCursor *tc = cursors; !tc->is_sentinel; tc++) if (tc->y == src_y && tc->x >= num_cells_removed_on_last_line) tc->x -= num_cells_removed_on_last_line; + for (TrackCursor *tc = cursors; !tc->is_sentinel; tc++) if (tc->dest_y == src_y && tc->dest_x >= num_cells_removed_on_last_line) tc->dest_x -= num_cells_removed_on_last_line; index_type remaining_cells = src->xnum - num_cells_removed_on_last_line; memmove(cp, cp + num_cells_removed_on_last_line, remaining_cells * sizeof(cp[0])); memset(cp + remaining_cells, 0, num_cells_removed_on_last_line * sizeof(cp[0])); @@ -706,18 +706,26 @@ linebuf_rewrap( return; } *num_content_lines_before = first + 1; - TrackCursor tcarr[3] = {{.x = *track_x, .y = *track_y }, {.x = *track_x2, .y = *track_y2}, {.is_sentinel = true}}; + TrackCursor tcarr[3] = { + {.x = *track_x, .y = *track_y, .dest_x=*track_x, .dest_y = *track_y }, + {.x = *track_x2, .y = *track_y2, .dest_x=*track_x2, .dest_y = *track_y2 }, + {.is_sentinel = true} + }; *num_content_lines_after = 1 + linebuf_rewrap_inner(self, other, *num_content_lines_before, historybuf, (TrackCursor*)tcarr, as_ansi_buf); - *track_x = tcarr[0].x; *track_y = tcarr[0].y; - *track_x2 = tcarr[1].x; *track_y2 = tcarr[1].y; + *track_x = tcarr[0].dest_x; *track_y = tcarr[0].dest_y; + *track_x2 = tcarr[1].dest_x; *track_y2 = tcarr[1].dest_y; if (history_buf_last_line_is_split && historybuf) { historybuf_init_line(historybuf, 0, historybuf->line); index_type xlimit = xlimit_for_line(historybuf->line); if (xlimit < historybuf->line->xnum) { - TrackCursor tcarr[3] = {{.x = *track_x, .y = *track_y }, {.x = *track_x2, .y = *track_y2}, {.is_sentinel = true}}; + TrackCursor tcarr[3] = { + {.x = *track_x, .y = *track_y, .dest_x=*track_x, .dest_y = *track_y }, + {.x = *track_x2, .y = *track_y2, .dest_x=*track_x2, .dest_y = *track_y2 }, + {.is_sentinel = true} + }; index_type num_of_lines_removed = restitch(historybuf->line, xlimit, other, 0, tcarr); - *track_x = tcarr[0].x; *track_y = tcarr[0].y; - *track_x2 = tcarr[1].x; *track_y2 = tcarr[1].y; + *track_x = tcarr[0].dest_x; *track_y = tcarr[0].dest_y; + *track_x2 = tcarr[1].dest_x; *track_y2 = tcarr[1].dest_y; *num_content_lines_after -= num_of_lines_removed; } } diff --git a/kitty/rewrap.c b/kitty/rewrap.c index 778e76580c6..2d8d9b3a9c1 100644 --- a/kitty/rewrap.c +++ b/kitty/rewrap.c @@ -79,7 +79,7 @@ typedef struct Rewrap { TrackCursor *cursors; index_type src_limit; - Line src, dest; + Line src, dest, src_scratch, dest_scratch; index_type src_y, src_x, dest_x, dest_y, num, src_x_limit; init_line_func_t init_line; first_dest_line_func_t first_dest_line; @@ -135,42 +135,119 @@ init_src_line(Rewrap *r) { r->prev_src_line_ended_with_wrap = r->src.cpu_cells[r->src_xnum - 1].next_char_was_wrapped; r->src.cpu_cells[r->src_xnum - 1].next_char_was_wrapped = false; r->src_x = 0; - for (TrackCursor *t = r->cursors; !t->is_sentinel; t++) { - if (r->src_y == t->y) { - t->is_tracked_line = true; - if (t->x >= r->src_x_limit) t->x = MAX(1u, r->src_x_limit) - 1; - } else t->is_tracked_line = false; + r->current_src_line_has_multline_cells = false; + for (index_type i = 0; i < r->src_x_limit; i++) if (r->src.cpu_cells[i].is_multicell && r->src.cpu_cells[i].scale > 1) { + r->current_src_line_has_multline_cells = true; + break; } return newline_needed; } -static index_type -rewrap_inner(Rewrap r) { - setup_line(r.text_cache, r.src_xnum, &r.src); setup_line(r.text_cache, r.dest_xnum, &r.dest); - static TrackCursor tc_end = {.is_sentinel = true }; - if (!r.cursors) r.cursors = &tc_end; - r.scratch = alloc_linebuf(SCALE_BITS << 1, r.dest_xnum, r.text_cache); - if (!r.scratch) fatal("Out of memory"); - RAII_PyObject(scratch, (PyObject*)r.scratch); - for (; r.src_y < r.src_limit; r.src_y++) { - if (init_src_line(&r)) { - if (r.src_y) next_dest_line(&r, false); - else first_dest_line(&r); +static void +update_tracked_cursors(Rewrap *r, index_type num_cells, index_type y, index_type x_limit) { + for (TrackCursor *t = r->cursors; !t->is_sentinel; t++) { + if (t->y == y && r->src_x <= t->x && (t->x < r->src_x + num_cells || t->x >= x_limit)) { + index_type x = t->x; + if (x >= x_limit) x = MAX(1u, x_limit) - 1; + t->dest_y = r->dest_y; + t->dest_x = r->dest_x + (x - r->src_x + (x > 0)); } - while (r.src_x < r.src_x_limit) { - if (r.dest_x >= r.dest_xnum) next_dest_line(&r, true); - index_type num = MIN(r.src.xnum - r.src_x, r.dest_xnum - r.dest_x); - copy_range(&r.src, r.src_x, &r.dest, r.dest_x, num); - for (TrackCursor *t = r.cursors; !t->is_sentinel; t++) { - if (t->is_tracked_line && r.src_x <= t->x && t->x < r.src_x + num) { - t->y = r.dest_y; - t->x = r.dest_x + (t->x - r.src_x + (t->x > 0)); + } +} + +static void +fast_copy_src_to_dest(Rewrap *r) { + CPUCell *c; index_type mc_width; + while (r->src_x < r->src_x_limit) { + if (r->dest_x >= r->dest_xnum) next_dest_line(r, true); + index_type num = MIN(r->src_x_limit - r->src_x, r->dest_xnum - r->dest_x); + bool do_copy = true; + if (num && (c = &r->src.cpu_cells[r->src_x + num - 1])->is_multicell && c->x != (mc_width = mcd_x_limit(c)) - 1) { + // we have a split multicell at the right edge of the copy region + if (num > mc_width) num -= mc_width; + else { + if (mc_width > r->dest_xnum) do_copy = false; + else { + r->dest_x = r->dest_xnum; + continue; } } - r.src_x += num; r.dest_x += num; } + if (do_copy) copy_range(&r->src, r->src_x, &r->dest, r->dest_x, num); + update_tracked_cursors(r, num, r->src_y, r->src_x_limit); + r->src_x += num; r->dest_x += num; + } +} + +static bool +find_space_in_dest_line(Rewrap *r, index_type num_cells) { + while (r->dest_x + num_cells <= r->dest_xnum) { + index_type before = r->dest_x; + for (index_type x = r->dest_x; x < r->dest_x + num_cells; x++) { + if (r->dest.cpu_cells[x].is_multicell) { + r->dest_x = x + mcd_x_limit(r->dest.cpu_cells + x); + break; + } + } + if (before == r->dest_x) return true; + } + return false; +} + +static void +find_space_in_dest(Rewrap *r, index_type num_cells) { + while (!find_space_in_dest_line(r, num_cells)) next_dest_line(r, true); +} + +static void +copy_multiline_extra_lines(Rewrap *r, CPUCell *src_cell, index_type mc_width) { + for (index_type i = 1; i < src_cell->scale; i++) { + r->init_line(r->src_buf, r->src_y + i, &r->src_scratch); + linebuf_init_line_at(r->scratch, i - 1, &r->dest_scratch); + linebuf_mark_line_dirty(r->scratch, i - 1); + copy_range(&r->src_scratch, r->src_x, &r->dest_scratch, r->dest_x, mc_width); + update_tracked_cursors(r, mc_width, r->src_y + i, r->src_xnum + 10000 /* ensure cursor is moved only if in region being copied */); + } +} + +static void +multiline_copy_src_to_dest(Rewrap *r) { + CPUCell *c; index_type mc_width; + while (r->src_x < r->src_x_limit) { + c = &r->src.cpu_cells[r->src_x]; + if (c->is_multicell) { + mc_width = mcd_x_limit(c); + if (c->y || mc_width > r->dest_xnum) { + update_tracked_cursors(r, mc_width, r->src_y, r->src_x_limit); + r->src_x += mc_width; + continue; + } + } else mc_width = 1; + find_space_in_dest(r, mc_width); + copy_range(&r->src, r->src_x, &r->dest, r->dest_x, mc_width); + update_tracked_cursors(r, mc_width, r->src_y, r->src_x_limit); + if (c->scale > 1) copy_multiline_extra_lines(r, c, mc_width); + r->src_x += mc_width; r->dest_x += mc_width; + } +} + +static index_type +rewrap_inner(Rewrap *r) { + setup_line(r->text_cache, r->src_xnum, &r->src); setup_line(r->text_cache, r->dest_xnum, &r->dest); + setup_line(r->text_cache, r->src_xnum, &r->src_scratch); setup_line(r->text_cache, r->dest_xnum, &r->dest_scratch); + + r->scratch = alloc_linebuf(SCALE_BITS << 1, r->dest_xnum, r->text_cache); + if (!r->scratch) fatal("Out of memory"); + RAII_PyObject(scratch, (PyObject*)r->scratch); + for (; r->src_y < r->src_limit; r->src_y++) { + if (init_src_line(r)) { + if (r->src_y) next_dest_line(r, false); + else first_dest_line(r); + } + if (r->current_src_line_has_multline_cells || r->current_dest_line_has_multiline_cells) multiline_copy_src_to_dest(r); + else fast_copy_src_to_dest(r); } - return r.dest_y; + return r->dest_y; } index_type @@ -181,16 +258,17 @@ linebuf_rewrap_inner(LineBuf *src, LineBuf *dest, const index_type src_limit, Hi .init_line = LineBuf_init_line, .next_dest_line = LineBuf_next_dest_line, .first_dest_line = LineBuf_first_dest_line, }; - return rewrap_inner(r); + return rewrap_inner(&r); } index_type historybuf_rewrap_inner(HistoryBuf *src, HistoryBuf *dest, const index_type src_limit, ANSIBuf *as_ansi_buf) { + static TrackCursor t = {.is_sentinel = true }; Rewrap r = { .src_buf = src, .dest_buf = dest, .as_ansi_buf = as_ansi_buf, .text_cache = src->text_cache, - .src_limit = src_limit, .src_xnum = src->xnum, .dest_xnum = dest->xnum, + .src_limit = src_limit, .src_xnum = src->xnum, .dest_xnum = dest->xnum, .cursors=&t, .init_line = HistoryBuf_init_line, .next_dest_line = HistoryBuf_next_dest_line, .first_dest_line = HistoryBuf_first_dest_line, }; - return rewrap_inner(r); + return rewrap_inner(&r); } diff --git a/kitty/rewrap.h b/kitty/rewrap.h index 849ae490b77..fb9bb9559a6 100644 --- a/kitty/rewrap.h +++ b/kitty/rewrap.h @@ -10,8 +10,9 @@ #include "history.h" typedef struct TrackCursor { - index_type x, y; - bool is_tracked_line, is_sentinel; + const index_type x, y; + index_type dest_x, dest_y; + bool is_sentinel; } TrackCursor;