From 17e59ccc22016239a8f6f8eb8f4e02583ee613e0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 7 Nov 2016 16:15:46 +0530 Subject: [PATCH] LineBuf.insert_lines --- kitty/data-types.h | 2 +- kitty/line-buf.c | 47 +++++++++++++++++++++++++++++++++++----- kitty/screen.py | 7 +----- kitty_tests/datatypes.py | 26 ++++++++++++++++++++++ 4 files changed, 69 insertions(+), 13 deletions(-) diff --git a/kitty/data-types.h b/kitty/data-types.h index 16f4ad59d..c939236ec 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -108,7 +108,7 @@ typedef struct { PyObject_HEAD uint8_t *buf; - index_type xnum, ynum, *line_map; + index_type xnum, ynum, *line_map, *scratch; index_type block_size; uint8_t *continued_map; Line *line; diff --git a/kitty/line-buf.c b/kitty/line-buf.c index 36b43f372..44e7c57ce 100644 --- a/kitty/line-buf.c +++ b/kitty/line-buf.c @@ -51,9 +51,10 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { self->block_size = xnum * ynum; self->buf = PyMem_Calloc(xnum * ynum, CELL_SIZE); self->line_map = PyMem_Calloc(ynum, sizeof(index_type)); + self->scratch = PyMem_Calloc(ynum, sizeof(index_type)); self->continued_map = PyMem_Calloc(ynum, sizeof(uint8_t)); self->line = alloc_line(); - if (self->buf == NULL || self->line_map == NULL || self->continued_map == NULL || self->line == NULL) { + if (self->buf == NULL || self->line_map == NULL || self->scratch == NULL || self->continued_map == NULL || self->line == NULL) { PyErr_NoMemory(); PyMem_Free(self->buf); PyMem_Free(self->line_map); PyMem_Free(self->continued_map); Py_CLEAR(self->line); Py_CLEAR(self); @@ -75,7 +76,7 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { static void dealloc(LineBuf* self) { - PyMem_Free(self->buf); PyMem_Free(self->line_map); PyMem_Free(self->continued_map); + PyMem_Free(self->buf); PyMem_Free(self->line_map); PyMem_Free(self->continued_map); PyMem_Free(self->scratch); Py_CLEAR(self->line); Py_TYPE(self)->tp_free((PyObject*)self); } @@ -182,6 +183,12 @@ copy_line_to(LineBuf *self, PyObject *args) { Py_RETURN_NONE; } +#define CLEAR_LINE(l) \ + for (index_type i = 0; i < self->xnum; i++) l.chars[i] = (1 << ATTRS_SHIFT) | 32; \ + memset(l.colors, 0, self->xnum * sizeof(color_type)); \ + memset(l.decoration_fg, 0, self->xnum * sizeof(decoration_type)); \ + memset(l.combining_chars, 0, self->xnum * sizeof(combining_type)); + static PyObject* clear_line(LineBuf *self, PyObject *val) { #define clear_line_doc "clear_line(y) -> Clear the specified line" @@ -189,10 +196,7 @@ clear_line(LineBuf *self, PyObject *val) { Line l; if (y >= self->ynum) { PyErr_SetString(PyExc_ValueError, "Out of bounds"); return NULL; } INIT_LINE(self, &l, self->line_map[y]); - for (index_type i = 0; i < self->xnum; i++) l.chars[i] = (1 << ATTRS_SHIFT) | 32; - memset(l.colors, 0, self->xnum * sizeof(color_type)); - memset(l.decoration_fg, 0, self->xnum * sizeof(decoration_type)); - memset(l.combining_chars, 0, self->xnum * sizeof(combining_type)); + CLEAR_LINE(l); self->continued_map[y] = 0; Py_RETURN_NONE; } @@ -241,6 +245,36 @@ is_continued(LineBuf *self, PyObject *val) { Py_RETURN_FALSE; } +static PyObject* +insert_lines(LineBuf *self, PyObject *args) { +#define insert_lines_doc "insert_lines(num, y, bottom) -> Insert num blank lines at y, only changing lines in the range [y, bottom]." + unsigned int y, num, bottom; + if (!PyArg_ParseTuple(args, "III", &num, &y, &bottom)) return NULL; + if (y >= self->ynum || y > bottom || bottom >= self->ynum) { PyErr_SetString(PyExc_ValueError, "Out of bounds"); return NULL; } + index_type ylimit = bottom + 1; + num = MIN(ylimit - y, num); + if (num > 0) { + for (index_type i = ylimit - num; i < ylimit; i++) { + self->scratch[i] = self->line_map[i]; + } + for (index_type i = ylimit - 1; i >= y + num; i--) { + self->line_map[i] = self->line_map[i - num]; + self->continued_map[i] = self->continued_map[i - num]; + } + if (y + num < self->ynum) self->continued_map[y + num] = 0; + for (index_type i = 0; i < num; i++) { + self->line_map[y + i] = self->scratch[ylimit - num + i]; + } + Line l; + for (index_type i = y; i < y + num; i++) { + INIT_LINE(self, &l, self->line_map[i]); + CLEAR_LINE(l); + self->continued_map[i] = 0; + } + } + Py_RETURN_NONE; +} + // Boilerplate {{{ static PyObject* @@ -258,6 +292,7 @@ static PyMethodDef methods[] = { METHOD(set_continued, METH_VARARGS) METHOD(index, METH_VARARGS) METHOD(reverse_index, METH_VARARGS) + METHOD(insert_lines, METH_VARARGS) METHOD(is_continued, METH_O) {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/kitty/screen.py b/kitty/screen.py index 6a01f20c8..679a9cce7 100644 --- a/kitty/screen.py +++ b/kitty/screen.py @@ -570,13 +570,8 @@ class Screen: # If cursor is outside scrolling margins -- do nothin'. if top <= self.cursor.y <= bottom: - # v +1, because range() is exclusive. - for line in range(self.cursor.y, - min(bottom + 1, self.cursor.y + count)): - self.linebuf.pop(bottom) - self.linebuf.insert(line, Line(self.columns)) + self.linebuf.insert_lines(count, self.cursor.y, bottom) self.update_line_range(self.cursor.y, bottom) - self.carriage_return() def delete_lines(self, count=1): diff --git a/kitty_tests/datatypes.py b/kitty_tests/datatypes.py index db20b9960..4b6a9aa66 100644 --- a/kitty_tests/datatypes.py +++ b/kitty_tests/datatypes.py @@ -57,6 +57,32 @@ class TestDataTypes(BaseTest): for i in range(1, 5): self.ae(lb.line(i), lb2.line(i-1)) + lb = filled_line_buf(5, 5, filled_cursor()) + clb = filled_line_buf(5, 5, filled_cursor()) + lb2 = LineBuf(1, 5) + lb.insert_lines(2, 1, lb.ynum - 1) + self.ae(lb.line(0), clb.line(0)) + self.ae(lb.line(3), clb.line(1)) + self.ae(lb.line(4), clb.line(2)) + self.ae(lb.line(1), lb2.line(0)) + self.ae(lb.line(2), lb2.line(0)) + lb = filled_line_buf(5, 5, filled_cursor()) + lb.insert_lines(10, 0, lb.ynum - 1) + for i in range(lb.ynum): + self.ae(lb.line(i), lb2.line(0)) + lb = filled_line_buf(5, 5, filled_cursor()) + lb.insert_lines(10, 1, lb.ynum - 1) + self.ae(lb.line(0), clb.line(0)) + for i in range(1, lb.ynum): + self.ae(lb.line(i), lb2.line(0)) + lb = filled_line_buf(5, 5, filled_cursor()) + lb.insert_lines(1, 1, 3) + self.ae(lb.line(0), clb.line(0)) + self.ae(lb.line(1), lb2.line(0)) + self.ae(lb.line(2), clb.line(1)) + self.ae(lb.line(3), clb.line(2)) + self.ae(lb.line(4), clb.line(4)) + def test_line(self): lb = LineBuf(2, 3) for y in range(lb.ynum):