Skip to content

Commit

Permalink
Refactor rewrap code, again
Browse files Browse the repository at this point in the history
Now we do the rewrap of history and line buffers together. This is
faster and deals with multiline chars split between the two buffers
correctly. Also, considerably simpler code.
  • Loading branch information
kovidgoyal committed Dec 31, 2024
1 parent e2b6166 commit 60d812b
Show file tree
Hide file tree
Showing 10 changed files with 435 additions and 475 deletions.
60 changes: 37 additions & 23 deletions kitty/history.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include "wcswidth.h"
#include "lineops.h"
#include "charsets.h"
#include "rewrap.h"
#include "resize.h"
#include <structmember.h>
#include "../3rdparty/ringbuf/ringbuf.h"

Expand Down Expand Up @@ -599,6 +599,7 @@ INIT_TYPE(HistoryBuf)
HistoryBuf *alloc_historybuf(unsigned int lines, unsigned int columns, unsigned int pagerhist_sz, TextCache *tc) {
return create_historybuf(&HistoryBuf_Type, columns, lines, pagerhist_sz, tc);
}

// }}}

static void
Expand All @@ -617,34 +618,47 @@ historybuf_next_dest_line(HistoryBuf *self, ANSIBuf *as_ansi_buf, Line *src_line
return dest_y + 1;
}

void
historybuf_rewrap(HistoryBuf *self, HistoryBuf *other, ANSIBuf *as_ansi_buf) {
while(other->num_segments < self->num_segments) add_segment(other);
if (other->xnum == self->xnum && other->ynum == self->ynum) {
// Fast path
for (index_type i = 0; i < self->num_segments; i++) {
memcpy(other->segments[i].cpu_cells, self->segments[i].cpu_cells, SEGMENT_SIZE * self->xnum * sizeof(CPUCell));
memcpy(other->segments[i].gpu_cells, self->segments[i].gpu_cells, SEGMENT_SIZE * self->xnum * sizeof(GPUCell));
memcpy(other->segments[i].line_attrs, self->segments[i].line_attrs, SEGMENT_SIZE * sizeof(LineAttrs));
}
other->count = self->count; other->start_of_data = self->start_of_data;
return;
HistoryBuf*
historybuf_alloc_for_rewrap(unsigned int columns, HistoryBuf *self) {
if (!self) return NULL;
HistoryBuf *ans = alloc_historybuf(self->ynum, columns, 0, self->text_cache);
if (ans) {
while(ans->num_segments < self->num_segments) add_segment(ans);
ans->count = 0; ans->start_of_data = 0;
}
if (other->pagerhist && other->xnum != self->xnum && ringbuf_bytes_used(other->pagerhist->ringbuf))
other->pagerhist->rewrap_needed = true;
other->count = 0; other->start_of_data = 0;
if (self->count > 0) {
historybuf_rewrap_inner(self, other, self->count, as_ansi_buf);
for (index_type i = 0; i < other->count; i++) attrptr(other, (other->start_of_data + i) % other->ynum)->has_dirty_text = true;
return ans;
}

void
historybuf_finish_rewrap(HistoryBuf *dest, HistoryBuf *src) {
for (index_type i = 0; i < dest->count; i++) attrptr(dest, (dest->start_of_data + i) % dest->ynum)->has_dirty_text = true;
dest->pagerhist = src->pagerhist; src->pagerhist = NULL;
if (dest->pagerhist && dest->xnum != src->xnum && ringbuf_bytes_used(dest->pagerhist->ringbuf)) dest->pagerhist->rewrap_needed = true;
}

void
historybuf_fast_rewrap(HistoryBuf *dest, HistoryBuf *src) {
for (index_type i = 0; i < src->num_segments; i++) {
memcpy(dest->segments[i].cpu_cells, src->segments[i].cpu_cells, SEGMENT_SIZE * src->xnum * sizeof(CPUCell));
memcpy(dest->segments[i].gpu_cells, src->segments[i].gpu_cells, SEGMENT_SIZE * src->xnum * sizeof(GPUCell));
memcpy(dest->segments[i].line_attrs, src->segments[i].line_attrs, SEGMENT_SIZE * sizeof(LineAttrs));
}
dest->count = src->count; dest->start_of_data = src->start_of_data;
}


static PyObject*
rewrap(HistoryBuf *self, PyObject *args) {
HistoryBuf *other;
if (!PyArg_ParseTuple(args, "O!", &HistoryBuf_Type, &other)) return NULL;
unsigned xnum;
if (!PyArg_ParseTuple(args, "I", &xnum)) return NULL;
ANSIBuf as_ansi_buf = {0};
historybuf_rewrap(self, other, &as_ansi_buf);
LineBuf *dummy = alloc_linebuf(4, self->xnum, self->text_cache);
if (!dummy) return PyErr_NoMemory();
RAII_PyObject(cleanup, (PyObject*)dummy);
TrackCursor cursors[1] = {{.is_sentinel=true}};
ResizeResult r = resize_screen_buffers(dummy, self, 8, xnum, &as_ansi_buf, cursors);
free(as_ansi_buf.buf);
Py_RETURN_NONE;
if (!r.ok) return PyErr_NoMemory();
Py_CLEAR(r.lb);
return (PyObject*)r.hb;
}
3 changes: 3 additions & 0 deletions kitty/history.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,7 @@ typedef struct {


HistoryBuf* alloc_historybuf(unsigned int, unsigned int, unsigned int, TextCache *tc);
HistoryBuf *historybuf_alloc_for_rewrap(unsigned int columns, HistoryBuf *self);
void historybuf_finish_rewrap(HistoryBuf *dest, HistoryBuf *src);
void historybuf_fast_rewrap(HistoryBuf *dest, HistoryBuf *src);
index_type historybuf_next_dest_line(HistoryBuf *self, ANSIBuf *as_ansi_buf, Line *src_line, index_type dest_y, Line *dest_line, bool continued);
63 changes: 7 additions & 56 deletions kitty/line-buf.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

#include "data-types.h"
#include "lineops.h"
#include "rewrap.h"
#include "resize.h"

#include <structmember.h>

Expand Down Expand Up @@ -596,65 +596,16 @@ copy_old(LineBuf *self, PyObject *y) {
Py_RETURN_NONE;
}

void
linebuf_rewrap(
LineBuf *self, LineBuf *other, index_type *num_content_lines_before, index_type *num_content_lines_after,
HistoryBuf *historybuf, index_type *track_x, index_type *track_y, index_type *track_x2, index_type *track_y2,
ANSIBuf *as_ansi_buf, bool history_buf_last_line_is_split
) {
index_type first, i;
bool is_empty = true;

// Fast path
if (other->xnum == self->xnum && other->ynum == self->ynum) {
memcpy(other->line_map, self->line_map, sizeof(index_type) * self->ynum);
memcpy(other->line_attrs, self->line_attrs, sizeof(LineAttrs) * self->ynum);
memcpy(other->cpu_cell_buf, self->cpu_cell_buf, (size_t)self->xnum * self->ynum * sizeof(CPUCell));
memcpy(other->gpu_cell_buf, self->gpu_cell_buf, (size_t)self->xnum * self->ynum * sizeof(GPUCell));
*num_content_lines_before = self->ynum; *num_content_lines_after = self->ynum;
return;
}

// Find the first line that contains some content
first = self->ynum;
do {
first--;
CPUCell *cells = cpu_lineptr(self, self->line_map[first]);
for(i = 0; i < self->xnum; i++) {
if (cells[i].ch_or_idx || cells[i].ch_is_idx) { is_empty = false; break; }
}
} while(is_empty && first > 0);

if (is_empty) { // All lines are empty
*num_content_lines_after = 0;
*num_content_lines_before = 0;
return;
}
*num_content_lines_before = first + 1;
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, 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;
for (i = 0; i < *num_content_lines_after; i++) other->line_attrs[i].has_dirty_text = true;
}

static PyObject*
rewrap(LineBuf *self, PyObject *args) {
LineBuf* other;
HistoryBuf *historybuf;
unsigned int nclb, ncla;

if (!PyArg_ParseTuple(args, "O!O!", &LineBuf_Type, &other, &HistoryBuf_Type, &historybuf)) return NULL;
index_type x = 0, y = 0, x2 = 0, y2 = 0;
unsigned int lines, columns;
if (!PyArg_ParseTuple(args, "II", &lines, &columns)) return NULL;
TrackCursor cursors[1] = {{.is_sentinel=true}};
ANSIBuf as_ansi_buf = {0};
linebuf_rewrap(self, other, &nclb, &ncla, historybuf, &x, &y, &x2, &y2, &as_ansi_buf, false);
ResizeResult r = resize_screen_buffers(self, NULL, lines, columns, &as_ansi_buf, cursors);
free(as_ansi_buf.buf);

return Py_BuildValue("II", nclb, ncla);
if (!r.ok) return PyErr_NoMemory();
return Py_BuildValue("NII", r.lb, r.num_content_lines_before, r.num_content_lines_after);
}


Expand Down
2 changes: 0 additions & 2 deletions kitty/lineops.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ void linebuf_clear_line(LineBuf *self, index_type y, bool clear_attrs);
void linebuf_insert_lines(LineBuf *self, unsigned int num, unsigned int y, unsigned int bottom);
void linebuf_delete_lines(LineBuf *self, index_type num, index_type y, index_type bottom);
void linebuf_copy_line_to(LineBuf *, Line *, index_type);
void linebuf_rewrap(LineBuf *self, LineBuf *other, index_type *, index_type *, HistoryBuf *, index_type *, index_type *, index_type *, index_type *, ANSIBuf*, bool);
void linebuf_mark_line_dirty(LineBuf *self, index_type y);
void linebuf_clear_attrs_and_dirty(LineBuf *self, index_type y);
void linebuf_mark_line_clean(LineBuf *self, index_type y);
Expand All @@ -102,7 +101,6 @@ bool linebuf_line_ends_with_continuation(LineBuf *self, index_type y);
void linebuf_refresh_sprite_positions(LineBuf *self);
void historybuf_add_line(HistoryBuf *self, const Line *line, ANSIBuf*);
bool historybuf_pop_line(HistoryBuf *, Line *);
void historybuf_rewrap(HistoryBuf *self, HistoryBuf *other, ANSIBuf*);
void historybuf_init_line(HistoryBuf *self, index_type num, Line *l);
bool history_buf_endswith_wrap(HistoryBuf *self);
CPUCell* historybuf_cpu_cells(HistoryBuf *self, index_type num);
Expand Down
Loading

0 comments on commit 60d812b

Please sign in to comment.