diff --git a/kitty/data-types.h b/kitty/data-types.h index c939236ec..3394dc8c6 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -24,6 +24,7 @@ typedef unsigned int index_type; #define CHAR_MASK 0xFFFFFF #define ATTRS_SHIFT 24 +#define ATTRS_MASK_WITHOUT_WIDTH 0xFC000000 #define WIDTH_MASK 0xFF #define DECORATION_SHIFT 2 #define BOLD_SHIFT 4 diff --git a/kitty/line.c b/kitty/line.c index 6f331e018..4eeef9a1c 100644 --- a/kitty/line.c +++ b/kitty/line.c @@ -159,6 +159,20 @@ cursor_from(Line* self, PyObject *args) { return (PyObject*)ans; } +static PyObject* +clear_text(Line* self, PyObject *args) { +#define clear_text_doc "clear_text(at, num, ch=' ') -> Clear characters in the specified range, preserving formatting." + unsigned int at, num; + int ch = 32; + if (!PyArg_ParseTuple(args, "II|C", &at, &num, &ch)) return NULL; + const char_type repl = ((char_type)ch & CHAR_MASK) | (1 << ATTRS_SHIFT); + for (index_type i = at; i < MIN(self->xnum, at + num); i++) { + self->chars[i] = (self->chars[i] & ATTRS_MASK_WITHOUT_WIDTH) | repl; + } + memset(self->combining_chars + at, 0, MIN(num, self->xnum - at) * sizeof(combining_type)); + Py_RETURN_NONE; +} + static PyObject* apply_cursor(Line* self, PyObject *args) { #define apply_cursor_doc "apply_cursor(cursor, at=0, num=1, clear_char=False) -> Apply the formatting attributes from cursor to the specified characters in this line." @@ -293,6 +307,7 @@ static PyMethodDef methods[] = { METHOD(set_text, METH_VARARGS) METHOD(cursor_from, METH_VARARGS) METHOD(apply_cursor, METH_VARARGS) + METHOD(clear_text, METH_VARARGS) METHOD(copy_char, METH_VARARGS) METHOD(right_shift, METH_VARARGS) METHOD(left_shift, METH_VARARGS) diff --git a/kitty/screen.py b/kitty/screen.py index ee1495331..866afcf15 100644 --- a/kitty/screen.py +++ b/kitty/screen.py @@ -607,7 +607,7 @@ class Screen: # TODO: Check what to do if x is on the second char of a wide char # pair. num = min(self.columns - x, count) - line = self.linebuf[y] + line = self.linebuf.line(y) line.right_shift(x, num) line.apply_cursor(self.cursor, x, num, clear_char=True) self.update_cell_range(y, x, self.columns - 1) @@ -631,7 +631,7 @@ class Screen: # for this control code. Also, what happens if we start deleting # at the second cell of a wide character, or delete only the first # cell of a wide character? - line = self.linebuf[y] + line = self.linebuf.line(y) line.left_shift(x, num) line.apply_cursor(self.cursor, self.columns - num, num, clear_char=True) self.update_cell_range(y, x, self.columns - 1) @@ -654,7 +654,8 @@ class Screen: x, y = self.cursor.x, self.cursor.y # TODO: Same set of wide character questions as for delete_characters() num = min(self.columns - x, count) - self.linebuf[y].apply_cursor(self.cursor, x, num, clear_char=True) + l = self.linebuf.line(y) + l.apply_cursor(self.cursor, x, num, clear_char=True) self.update_cell_range(y, x, min(x + num, self.columns) - 1) def erase_in_line(self, how=0, private=False): @@ -685,7 +686,7 @@ class Screen: if n - s: # TODO: Same set of questions as for delete_characters() y = self.cursor.y - line = self.linebuf[y] + line = self.linebuf.line(y) c = None if private else self.cursor if private: line.clear_text(s, n) @@ -723,9 +724,9 @@ class Screen: if interval[1] > interval[0]: for line in range(*interval): if private: - self.linebuf[line].clear_text(0, self.columns) + self.linebuf.line(line).clear_text(0, self.columns) else: - self.linebuf[line].apply_cursor(self.cursor, 0, self.columns, clear_char=True) + self.linebuf.line(line).apply_cursor(self.cursor, 0, self.columns, clear_char=True) self.update_line_range(interval[0], interval[1] - 1) # In case of 0 or 1 we have to erase the line with the cursor also @@ -894,7 +895,7 @@ class Screen: def alignment_display(self): """Fills screen with uppercase E's for screen focus and alignment.""" for i in range(self.lines): - self.linebuf[i].clear_text(0, self.columns, 'E') + self.linebuf.line(i).clear_text(0, self.columns, 'E') def select_graphic_rendition(self, *attrs): """Set display attributes. diff --git a/kitty_tests/datatypes.py b/kitty_tests/datatypes.py index 1902ed5a2..d18a71cf1 100644 --- a/kitty_tests/datatypes.py +++ b/kitty_tests/datatypes.py @@ -107,6 +107,13 @@ class TestDataTypes(BaseTest): self.ae(lb.line(3), lb2.line(0)) self.ae(lb.line(4), clb.line(4)) + lb = filled_line_buf(5, 5, filled_cursor()) + l = lb.line(0) + l.add_combining_char(1, 'a') + l.clear_text(1, 2) + self.ae(str(l), '0 00') + self.assertEqualAttributes(l.cursor_from(1), l.cursor_from(0)) + def test_line(self): lb = LineBuf(2, 3) for y in range(lb.ynum):