diff --git a/kitty/line-buf.c b/kitty/line-buf.c index 44e7c57ce..fea6b518e 100644 --- a/kitty/line-buf.c +++ b/kitty/line-buf.c @@ -249,24 +249,56 @@ 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; + index_type i; 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++) { + for (i = ylimit - num; i < ylimit; i++) { self->scratch[i] = self->line_map[i]; } - for (index_type i = ylimit - 1; i >= y + num; i--) { + for (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++) { + for (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++) { + for (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; +} + +static PyObject* +delete_lines(LineBuf *self, PyObject *args) { +#define delete_lines_doc "delete_lines(num, y, bottom) -> Delete num blank lines at y, only changing lines in the range [y, bottom]." + unsigned int y, num, bottom; + index_type i; + 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 (i = y; i < y + num; i++) { + self->scratch[i] = self->line_map[i]; + } + for (i = y; i < ylimit; i++) { + self->line_map[i] = self->line_map[i + num]; + self->continued_map[i] = self->continued_map[i + num]; + } + self->continued_map[y] = 0; + for (i = 0; i < num; i++) { + self->line_map[ylimit - num + i] = self->scratch[y + i]; + } + Line l; + for (i = ylimit - num; i < ylimit; i++) { INIT_LINE(self, &l, self->line_map[i]); CLEAR_LINE(l); self->continued_map[i] = 0; @@ -275,7 +307,6 @@ insert_lines(LineBuf *self, PyObject *args) { Py_RETURN_NONE; } - // Boilerplate {{{ static PyObject* copy_old(LineBuf *self, PyObject *y); @@ -293,6 +324,7 @@ static PyMethodDef methods[] = { METHOD(index, METH_VARARGS) METHOD(reverse_index, METH_VARARGS) METHOD(insert_lines, METH_VARARGS) + METHOD(delete_lines, METH_VARARGS) METHOD(is_continued, METH_O) {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/kitty/screen.py b/kitty/screen.py index 679a9cce7..ee1495331 100644 --- a/kitty/screen.py +++ b/kitty/screen.py @@ -586,12 +586,8 @@ class Screen: # If cursor is outside scrolling margins it -- do nothin'. if top <= self.cursor.y <= bottom: - # v -- +1 to include the bottom margin. - for _ in range(min(bottom - self.cursor.y + 1, count)): - self.linebuf.pop(self.cursor.y) - self.linebuf.insert(bottom, Line(self.columns)) + self.linebuf.delete_lines(count, self.cursor.y, bottom) self.update_line_range(self.cursor.y, bottom) - self.carriage_return() def insert_characters(self, count=1): diff --git a/kitty_tests/datatypes.py b/kitty_tests/datatypes.py index 4b6a9aa66..1902ed5a2 100644 --- a/kitty_tests/datatypes.py +++ b/kitty_tests/datatypes.py @@ -36,7 +36,7 @@ class TestDataTypes(BaseTest): lb2.copy_old(lb) lb.index(0, 4) for i in range(0, 4): - self.ae(lb.line(i), lb2.line(i+1)) + self.ae(lb.line(i), lb2.line(i + 1)) self.ae(lb.line(4), lb2.line(0)) lb = filled_line_buf(5, 5, filled_cursor()) lb.index(1, 3) @@ -55,7 +55,7 @@ class TestDataTypes(BaseTest): lb.reverse_index(0, 4) self.ae(lb.line(0), lb2.line(4)) for i in range(1, 5): - self.ae(lb.line(i), lb2.line(i-1)) + 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()) @@ -83,6 +83,30 @@ class TestDataTypes(BaseTest): self.ae(lb.line(3), clb.line(2)) self.ae(lb.line(4), clb.line(4)) + lb = filled_line_buf(5, 5, filled_cursor()) + lb.delete_lines(2, 1, lb.ynum - 1) + self.ae(lb.line(0), clb.line(0)) + self.ae(lb.line(1), clb.line(3)) + self.ae(lb.line(2), clb.line(4)) + self.ae(lb.line(3), lb2.line(0)) + self.ae(lb.line(4), lb2.line(0)) + lb = filled_line_buf(5, 5, filled_cursor()) + lb.delete_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.delete_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.delete_lines(1, 1, 3) + self.ae(lb.line(0), clb.line(0)) + self.ae(lb.line(1), clb.line(2)) + self.ae(lb.line(2), clb.line(3)) + self.ae(lb.line(3), lb2.line(0)) + self.ae(lb.line(4), clb.line(4)) + def test_line(self): lb = LineBuf(2, 3) for y in range(lb.ynum):