Skip to content

Commit

Permalink
Implement restitch during rewrap
Browse files Browse the repository at this point in the history
Faster, less code and gets us multicell handling during restitching for
free.
  • Loading branch information
kovidgoyal committed Dec 30, 2024
1 parent 7f3feef commit dfa452c
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 102 deletions.
92 changes: 1 addition & 91 deletions kitty/line-buf.c
Original file line number Diff line number Diff line change
Expand Up @@ -596,81 +596,6 @@ copy_old(LineBuf *self, PyObject *y) {
Py_RETURN_NONE;
}

static index_type
restitch(Line *dest, index_type at, LineBuf *src, const index_type src_y, TrackCursor *cursors) {
index_type y = src_y, num_of_lines_removed = 0, num_cells_removed_on_last_line = 0;
bool last_char_has_wrapped_flag = true;
CPUCell *cp; GPUCell *gp;
// copy the cells into dest
while (at < dest->xnum && y < src->ynum) {
linebuf_init_cells(src, y, &cp, &gp);
index_type x = 0;
bool found_text = false;
while (at < dest->xnum && x < src->xnum) {
if (!found_text && cell_has_text(&cp[x])) found_text = true;
if (cp[x].is_multicell) {
// TODO: handle multiline chars
if (dest->xnum - at > cp[x].width) {
for (index_type i = 0; i < cp[x].width; i++) {
dest->cpu_cells[at] = cp[x];
dest->gpu_cells[at++] = gp[x++];
}
} else {
dest->cpu_cells[at] = (CPUCell){0};
dest->gpu_cells[at] = (GPUCell){0};
at = dest->xnum;
}
} else {
dest->cpu_cells[at] = cp[x];
dest->gpu_cells[at] = gp[x];
at++; x++;
}
}
if (!found_text) {
last_char_has_wrapped_flag = false;
break;
}
if (x >= src->xnum) { // Entire line was copied
num_of_lines_removed++;
bool line_ends_with_newline = !linebuf_line_ends_with_continuation(src, y);
if (line_ends_with_newline) {
last_char_has_wrapped_flag = false;
break;
}
} else {
num_cells_removed_on_last_line = x;
break;
}
y++;
}
dest->cpu_cells[dest->xnum - 1].next_char_was_wrapped = last_char_has_wrapped_flag;
// remove the copied lines and cells from src
if (num_of_lines_removed) {
for (index_type i = 0; i < num_of_lines_removed; i++) {
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->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->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]));
memmove(gp, gp + num_cells_removed_on_last_line, remaining_cells * sizeof(gp[0]));
memset(gp + remaining_cells, 0, num_cells_removed_on_last_line * sizeof(gp[0]));
if (line_is_continued_to_next && remaining_cells < src->xnum && src->ynum > src_y + 1) {
Line next_dest = {.xnum=src->xnum};
init_line(src, &next_dest, src_y);
num_of_lines_removed += restitch(&next_dest, remaining_cells, src, src_y + 1, cursors);
}
}
return num_of_lines_removed;
}


void
linebuf_rewrap(
LineBuf *self, LineBuf *other, index_type *num_content_lines_before, index_type *num_content_lines_after,
Expand Down Expand Up @@ -711,24 +636,9 @@ linebuf_rewrap(
{.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);
*num_content_lines_after = 1 + linebuf_rewrap_inner(self, other, *num_content_lines_before, historybuf, (TrackCursor*)tcarr, as_ansi_buf, history_buf_last_line_is_split && historybuf != NULL);
*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, .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].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;
}
}
for (i = 0; i < *num_content_lines_after; i++) other->line_attrs[i].has_dirty_text = true;
}

Expand Down
41 changes: 32 additions & 9 deletions kitty/rewrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,13 @@ typedef struct Rewrap {
index_type src_limit;

Line src, dest, src_scratch, dest_scratch;
index_type src_y, src_x, dest_x, dest_y, num, src_x_limit;
index_type src_y, src_x, dest_x, dest_y, num, src_x_limit, history_buf_last_line_xlimit;
init_line_func_t init_line;
first_dest_line_func_t first_dest_line;
next_dest_line_func_t next_dest_line;
LineBuf *scratch;
bool current_dest_line_has_multiline_cells, current_src_line_has_multline_cells, prev_src_line_ended_with_wrap;
bool current_dest_line_is_last_history_line;
} Rewrap;

static void
Expand All @@ -102,9 +103,13 @@ setup_line(TextCache *tc, index_type xnum, Line *l) {

static void
next_dest_line(Rewrap *r, bool continued) {
r->dest_y = r->next_dest_line(r->dest_buf, r->historybuf, r->as_ansi_buf, &r->src, r->dest_y, &r->dest, continued);
r->dest_x = 0;
r->current_dest_line_has_multiline_cells = false;
if (r->current_dest_line_is_last_history_line) {
r->dest.cpu_cells[r->dest_xnum-1].next_char_was_wrapped = continued;
r->current_dest_line_is_last_history_line = false;
r->dest_y = r->first_dest_line(r->dest_buf, r->as_ansi_buf, &r->src, &r->dest);
} else r->dest_y = r->next_dest_line(r->dest_buf, r->historybuf, r->as_ansi_buf, &r->src, r->dest_y, &r->dest, continued);
if (r->scratch->line_attrs[0].has_dirty_text) {
CPUCell *cpu_cells; GPUCell *gpu_cells;
linebuf_init_cells(r->scratch, 0, &cpu_cells, &gpu_cells);
Expand All @@ -120,7 +125,15 @@ next_dest_line(Rewrap *r, bool continued) {

static void
first_dest_line(Rewrap *r) {
r->dest_y = r->first_dest_line(r->dest_buf, r->as_ansi_buf, &r->src, &r->dest);
if (r->history_buf_last_line_xlimit < r->dest_xnum && r->historybuf) {
r->dest_y = 0;
r->dest_x = r->history_buf_last_line_xlimit;
r->current_dest_line_has_multiline_cells = true;
r->current_dest_line_is_last_history_line = true;
historybuf_init_line(r->historybuf, 0, &r->dest);
} else {
r->dest_y = r->first_dest_line(r->dest_buf, r->as_ansi_buf, &r->src, &r->dest);
}
}

static bool
Expand All @@ -145,10 +158,14 @@ 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));
if (r->current_dest_line_is_last_history_line) {
t->dest_x = 0; t->dest_y = 0;
} else {
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));
}
}
}
}
Expand Down Expand Up @@ -257,10 +274,16 @@ rewrap_inner(Rewrap *r) {
}

index_type
linebuf_rewrap_inner(LineBuf *src, LineBuf *dest, const index_type src_limit, HistoryBuf *historybuf, TrackCursor *track, ANSIBuf *as_ansi_buf) {
linebuf_rewrap_inner(LineBuf *src, LineBuf *dest, const index_type src_limit, HistoryBuf *historybuf, TrackCursor *track, ANSIBuf *as_ansi_buf, bool history_buf_last_line_is_split) {
index_type xlimit = dest->xnum;
if (history_buf_last_line_is_split && historybuf) {
historybuf_init_line(historybuf, 0, historybuf->line);
xlimit = xlimit_for_line(historybuf->line);
}
Rewrap r = {
.src_buf = src, .dest_buf = dest, .as_ansi_buf = as_ansi_buf, .text_cache = src->text_cache, .cursors = track,
.src_limit = src_limit, .historybuf=historybuf, .src_xnum = src->xnum, .dest_xnum = dest->xnum,
.history_buf_last_line_xlimit = xlimit,

.init_line = LineBuf_init_line, .next_dest_line = LineBuf_next_dest_line, .first_dest_line = LineBuf_first_dest_line,
};
Expand All @@ -272,7 +295,7 @@ historybuf_rewrap_inner(HistoryBuf *src, HistoryBuf *dest, const index_type src_
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, .cursors=&t,
.src_limit = src_limit, .src_xnum = src->xnum, .dest_xnum = dest->xnum, .cursors=&t, .history_buf_last_line_xlimit = dest->xnum,

.init_line = HistoryBuf_init_line, .next_dest_line = HistoryBuf_next_dest_line, .first_dest_line = HistoryBuf_first_dest_line,
};
Expand Down
2 changes: 1 addition & 1 deletion kitty/rewrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ typedef struct TrackCursor {
} TrackCursor;


index_type linebuf_rewrap_inner(LineBuf *src, LineBuf *dest, const index_type src_limit, HistoryBuf *historybuf, TrackCursor *track, ANSIBuf *as_ansi_buf);
index_type linebuf_rewrap_inner(LineBuf *src, LineBuf *dest, const index_type src_limit, HistoryBuf *historybuf, TrackCursor *track, ANSIBuf *as_ansi_buf, bool history_buf_last_line_is_split);

index_type historybuf_rewrap_inner(HistoryBuf *src, HistoryBuf *dest, const index_type src_limit, ANSIBuf *as_ansi_buf);

4 changes: 3 additions & 1 deletion kitty_tests/screen.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,8 +357,10 @@ def ac():
s = self.create_screen(scrollback=20)
s.draw(''.join(str(i) * s.columns for i in range(s.lines*2)))
self.ae(str(s.linebuf), '55555\n66666\n77777\n88888\n99999')
before = at()
s.resize(5, 2)
self.ae(str(s.linebuf), '88\n88\n99\n99\n9')
self.ae(before, at())
self.ae(str(s.linebuf), '88\n88\n89\n99\n99')
s = self.create_screen()
s.draw('a' * s.columns)
s.linefeed(), s.carriage_return()
Expand Down

0 comments on commit dfa452c

Please sign in to comment.