diff --git a/kitty/data-types.h b/kitty/data-types.h index 9607eac77..86ea41e05 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -254,6 +254,7 @@ typedef struct { SavepointBuffer main_savepoints, alt_savepoints; PyObject *callbacks; LineBuf *linebuf, *main_linebuf, *alt_linebuf; + HistoryBuf *historybuf; bool *tabstops; ChangeTracker *change_tracker; ScreenModes modes; @@ -317,7 +318,9 @@ void linebuf_clear_line(LineBuf *self, index_type y); 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_set_attribute(LineBuf *, unsigned int , unsigned int ); -bool linebuf_rewrap(LineBuf *self, LineBuf *other, int *cursor_y_out, PyObject *extra_lines); +void linebuf_rewrap(LineBuf *self, LineBuf *other, int *cursor_y_out, HistoryBuf *); +bool historybuf_resize(HistoryBuf *self, index_type lines); +void historybuf_add_line(HistoryBuf *self, const Line *line); void screen_restore_cursor(Screen *); void screen_save_cursor(Screen *); diff --git a/kitty/history.c b/kitty/history.c index c9b06994b..3b9e4e009 100644 --- a/kitty/history.c +++ b/kitty/history.c @@ -41,9 +41,9 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { } static void -dealloc(LineBuf* self) { - PyMem_Free(self->buf); +dealloc(HistoryBuf* self) { Py_CLEAR(self->line); + PyMem_Free(self->buf); Py_TYPE(self)->tp_free((PyObject*)self); } @@ -55,7 +55,7 @@ static inline index_type index_of(HistoryBuf *self, index_type lnum) { return (self->start_of_data + idx) % self->ynum; } -static inline void* start_of(HistoryBuf *self, index_type num) { +static inline uint8_t* start_of(HistoryBuf *self, index_type num) { // Pointer to the start of the line with index (buffer position) num return self->buf + CELL_SIZE_H * num * self->xnum; } @@ -70,25 +70,26 @@ static inline void init_line(HistoryBuf *self, index_type num, Line *l) { l->combining_chars = (combining_type*)(l->decoration_fg + self->xnum); } -static inline void historybuf_push(HistoryBuf *self) { - init_line(self, (self->start_of_data + self->count) % self->ynum, self->line); +static inline index_type historybuf_push(HistoryBuf *self) { + index_type idx = (self->start_of_data + self->count) % self->ynum; + init_line(self, idx, self->line); if (self->count == self->ynum) self->start_of_data = (self->start_of_data + 1) % self->ynum; else self->count++; + return idx; } -static PyObject* -change_num_of_lines(HistoryBuf *self, PyObject *val) { -#define change_num_of_lines_doc "Change the number of lines in this buffer" +bool +historybuf_resize(HistoryBuf *self, index_type lines) { HistoryBuf t = {0}; t.xnum=self->xnum; - t.ynum=(index_type) PyLong_AsUnsignedLong(val); + t.ynum=lines; if (t.ynum > 0 && t.ynum != self->ynum) { t.buf = PyMem_Calloc(t.xnum * t.ynum, CELL_SIZE_H); - if (t.buf == NULL) return PyErr_NoMemory(); + if (t.buf == NULL) { PyErr_NoMemory(); return false; } t.count = MIN(self->count, t.ynum); if (t.count > 0) { for (index_type s=0; s < t.count; s++) { - void *src = start_of(self, index_of(self, s)), *dest = start_of(&t, index_of(&t, s)); + uint8_t *src = start_of(self, index_of(self, s)), *dest = start_of(&t, index_of(&t, s)); memcpy(dest, src, CELL_SIZE_H * t.xnum); } } @@ -98,6 +99,19 @@ change_num_of_lines(HistoryBuf *self, PyObject *val) { PyMem_Free(self->buf); self->buf = t.buf; } + return true; +} + +void historybuf_add_line(HistoryBuf *self, const Line *line) { + index_type idx = historybuf_push(self); + COPY_LINE(line, self->line); + *(start_of(self, idx)) = line->continued; +} + +static PyObject* +change_num_of_lines(HistoryBuf *self, PyObject *val) { +#define change_num_of_lines_doc "Change the number of lines in this buffer" + if(!historybuf_resize(self, (index_type)PyLong_AsUnsignedLong(val))) return NULL; Py_RETURN_NONE; } diff --git a/kitty/line-buf.c b/kitty/line-buf.c index 5bbc37d8a..2acb01d72 100644 --- a/kitty/line-buf.c +++ b/kitty/line-buf.c @@ -345,7 +345,7 @@ copy_old(LineBuf *self, PyObject *y); #define copy_old_doc "Copy the contents of the specified LineBuf to this LineBuf. Both must have the same number of columns, but the number of lines can be different, in which case the bottom lines are copied." static PyObject* -rewrap(LineBuf *self, PyObject *val); +rewrap(LineBuf *self, PyObject *args); #define rewrap_doc "rewrap(new_screen) -> Fill up new screen (which can have different size to this screen) with as much of the contents of this screen as will fit. Return lines that overflow." static PyMethodDef methods[] = { @@ -354,7 +354,7 @@ static PyMethodDef methods[] = { METHOD(copy_old, METH_O) METHOD(copy_line_to, METH_VARARGS) METHOD(create_line_copy, METH_O) - METHOD(rewrap, METH_O) + METHOD(rewrap, METH_VARARGS) METHOD(clear, METH_NOARGS) METHOD(set_attribute, METH_VARARGS) METHOD(set_continued, METH_VARARGS) @@ -415,11 +415,9 @@ static inline void copy_range(Line *src, index_type src_at, Line* dest, index_ty #define next_dest_line(continued) {\ if (dest_y >= dest->ynum - 1) { \ linebuf_index(dest, 0, dest->ynum - 1); \ - if (extra_lines != NULL) {\ - PyObject *l = create_line_copy_inner(dest, dest_y); \ - if (l == NULL) return false; \ - if (PyList_Append(extra_lines, l) != 0) { Py_CLEAR(l); return false; } \ - Py_CLEAR(l); \ + if (historybuf != NULL) { \ + linebuf_init_line(dest, dest->ynum - 1); \ + historybuf_add_line(historybuf, dest->line); \ }\ } else dest_y++; \ INIT_LINE(dest, dest->line, dest->line_map[dest_y]); \ @@ -427,7 +425,7 @@ static inline void copy_range(Line *src, index_type src_at, Line* dest, index_ty dest_x = 0; \ } -static bool rewrap_inner(LineBuf *src, LineBuf *dest, const index_type src_limit, PyObject *extra_lines) { +static void rewrap_inner(LineBuf *src, LineBuf *dest, const index_type src_limit, HistoryBuf *historybuf) { // TODO: Change this to put the extra lines into the history buf bool src_line_is_continued = false; index_type src_y = 0, src_x = 0, dest_x = 0, dest_y = 0, num = 0, src_x_limit = 0; @@ -452,10 +450,9 @@ static bool rewrap_inner(LineBuf *src, LineBuf *dest, const index_type src_limit if (!src_line_is_continued && src_y < src_limit) next_dest_line(false); } while (src_y < src_limit); dest->line->ynum = dest_y; - return true; } -bool linebuf_rewrap(LineBuf *self, LineBuf *other, int *cursor_y_out, PyObject *extra_lines) { +void linebuf_rewrap(LineBuf *self, LineBuf *other, int *cursor_y_out, HistoryBuf *historybuf) { index_type first, i; bool is_empty = true; @@ -466,7 +463,7 @@ bool linebuf_rewrap(LineBuf *self, LineBuf *other, int *cursor_y_out, PyObject * memcpy(other->continued_map, self->continued_map, sizeof(bool) * self->ynum); memcpy(other->buf, self->buf, self->xnum * self->ynum * CELL_SIZE); Py_END_ALLOW_THREADS; - return true; + return; } // Find the first line that contains some content @@ -480,28 +477,22 @@ bool linebuf_rewrap(LineBuf *self, LineBuf *other, int *cursor_y_out, PyObject * } Py_END_ALLOW_THREADS; - if (first == 0) { *cursor_y_out = 0; return true; } // All lines are empty + if (first == 0) { *cursor_y_out = 0; return; } // All lines are empty - if (!rewrap_inner(self, other, first + 1, extra_lines)) { - PyErr_NoMemory(); - return false; - } + rewrap_inner(self, other, first + 1, historybuf); *cursor_y_out = other->line->ynum; - return true; } static PyObject* -rewrap(LineBuf *self, PyObject *val) { +rewrap(LineBuf *self, PyObject *args) { LineBuf* other; + HistoryBuf *historybuf; int cursor_y = -1; - if (!PyObject_TypeCheck(val, &LineBuf_Type)) { PyErr_SetString(PyExc_TypeError, "Not a LineBuf object"); return NULL; } - other = (LineBuf*) val; - PyObject *ret = PyList_New(0); - if (ret == NULL) return PyErr_NoMemory(); - if(!linebuf_rewrap(self, other, &cursor_y, ret)) return NULL; + if (!PyArg_ParseTuple(args, "O!O!", &LineBuf_Type, &other, &HistoryBuf_Type, &historybuf)) return NULL; + linebuf_rewrap(self, other, &cursor_y, historybuf); - return Py_BuildValue("Ni", ret, cursor_y); + return Py_BuildValue("i", cursor_y); } LineBuf *alloc_linebuf(unsigned int lines, unsigned int columns) { diff --git a/kitty/screen.c b/kitty/screen.c index 9d27b1b5f..ee602f775 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -35,8 +35,9 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { self->main_linebuf = alloc_linebuf(lines, columns); self->alt_linebuf = alloc_linebuf(lines, columns); self->linebuf = self->main_linebuf; self->change_tracker = alloc_change_tracker(lines, columns); + self->historybuf = alloc_historybuf(lines, columns); self->tabstops = PyMem_Calloc(self->columns, sizeof(bool)); - if (self->cursor == NULL || self->main_linebuf == NULL || self->alt_linebuf == NULL || self->change_tracker == NULL || self->tabstops == NULL) { + if (self->cursor == NULL || self->main_linebuf == NULL || self->alt_linebuf == NULL || self->change_tracker == NULL || self->tabstops == NULL || self->historybuf == NULL) { Py_CLEAR(self); return NULL; } } @@ -63,10 +64,10 @@ void screen_reset(Screen *self) { tracker_update_screen(self->change_tracker); } -static inline LineBuf* realloc_lb(LineBuf *old, unsigned int lines, unsigned int columns, int *cursor_y) { +static inline LineBuf* realloc_lb(LineBuf *old, unsigned int lines, unsigned int columns, int *cursor_y, HistoryBuf *hb) { LineBuf *ans = alloc_linebuf(lines, columns); if (ans == NULL) { PyErr_NoMemory(); return NULL; } - if(!linebuf_rewrap(old, ans, cursor_y, NULL)) return NULL; + linebuf_rewrap(old, ans, cursor_y, hb); return ans; } @@ -75,12 +76,12 @@ static bool screen_resize(Screen *self, unsigned int lines, unsigned int columns bool is_main = self->linebuf == self->main_linebuf; int cursor_y = -1; - LineBuf *n = realloc_lb(self->main_linebuf, lines, columns, &cursor_y); + LineBuf *n = realloc_lb(self->main_linebuf, lines, columns, &cursor_y, self->historybuf); if (n == NULL) return false; Py_CLEAR(self->main_linebuf); self->main_linebuf = n; if (is_main) self->cursor->y = MAX(0, cursor_y); cursor_y = -1; - n = realloc_lb(self->alt_linebuf, lines, columns, &cursor_y); + n = realloc_lb(self->alt_linebuf, lines, columns, &cursor_y, NULL); if (n == NULL) return false; Py_CLEAR(self->alt_linebuf); self->alt_linebuf = n; if (!is_main) self->cursor->y = MAX(0, cursor_y); @@ -99,9 +100,8 @@ static bool screen_resize(Screen *self, unsigned int lines, unsigned int columns return true; } -static bool screen_change_scrollback_size(Screen UNUSED *self, unsigned int UNUSED size) { - // TODO: Implement this - return true; +static bool screen_change_scrollback_size(Screen *self, unsigned int size) { + return historybuf_resize(self->historybuf, size); } @@ -524,8 +524,10 @@ void screen_index(Screen *self) { unsigned int top = self->margin_top, bottom = self->margin_bottom; if (self->cursor->y == bottom) { linebuf_index(self->linebuf, top, bottom); - if (self->linebuf == self->main_linebuf) { - // TODO: Add line to tophistorybuf + if (self->linebuf == self->main_linebuf && bottom == self->lines - 1) { + // Only add to history when no page margins have been set + linebuf_init_line(self->linebuf, bottom); + historybuf_add_line(self->historybuf, self->linebuf->line); tracker_line_added_to_history(self->change_tracker); } linebuf_clear_line(self->linebuf, bottom); diff --git a/kitty_tests/datatypes.py b/kitty_tests/datatypes.py index 5f872f4e6..27e19c947 100644 --- a/kitty_tests/datatypes.py +++ b/kitty_tests/datatypes.py @@ -8,7 +8,7 @@ from kitty.config import build_ansi_color_table, defaults from kitty.utils import is_simple_string, wcwidth, sanitize_title -from kitty.fast_data_types import LineBuf, Cursor as C, REVERSE, ColorProfile, SpriteMap +from kitty.fast_data_types import LineBuf, Cursor as C, REVERSE, ColorProfile, SpriteMap, HistoryBuf def create_lbuf(*lines): @@ -210,15 +210,20 @@ def test_line(self): l.set_char(0, 'x', 1, q) self.assertEqualAttributes(l.cursor_from(0), q) + def rewrap(self, lb, lb2): + hb = HistoryBuf(lb2.ynum, lb2.xnum) + cy = lb.rewrap(lb2, hb) + return hb, cy + def test_rewrap_simple(self): ' Same width buffers ' lb = filled_line_buf(5, 5) lb2 = LineBuf(lb.ynum, lb.xnum) - lb.rewrap(lb2) + self.rewrap(lb, lb2) for i in range(lb.ynum): self.ae(lb2.line(i), lb.line(i)) lb2 = LineBuf(8, 5) - cy = lb.rewrap(lb2)[1] + cy = self.rewrap(lb, lb2)[1] self.ae(cy, 4) for i in range(lb.ynum): self.ae(lb2.line(i), lb.line(i)) @@ -226,14 +231,14 @@ def test_rewrap_simple(self): for i in range(lb.ynum, lb2.ynum): self.ae(str(lb2.line(i)), str(empty.line(0))) lb2 = LineBuf(3, 5) - extra, cy = lb.rewrap(lb2) + cy = self.rewrap(lb, lb2)[1] self.ae(cy, 2) for i in range(lb2.ynum): self.ae(lb2.line(i), lb.line(i + 2)) def line_comparison(self, lb, *lines): lb2 = LineBuf(len(lines), max(map(len, lines))) - lb.rewrap(lb2) + self.rewrap(lb, lb2) for i, l in enumerate(lines): l2 = lb2.line(i) self.ae(l, str(l2))