Implement dirty line tracking

This commit is contained in:
Kovid Goyal 2017-11-09 12:51:02 +05:30
parent 0277be5856
commit 6c838bbc28
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
6 changed files with 118 additions and 14 deletions

View File

@ -185,6 +185,19 @@ as_ansi(HistoryBuf *self, PyObject *callback) {
Py_RETURN_NONE;
}
static PyObject*
dirty_lines(HistoryBuf *self) {
#define dirty_lines_doc "dirty_lines() -> Line numbers of all lines that have dirty text."
PyObject *ans = PyList_New(0);
for (index_type i = 0; i < self->ynum; i++) {
if (self->line_attrs[i] & TEXT_DIRTY_MASK) {
PyList_Append(ans, PyLong_FromUnsignedLong(i));
}
}
return ans;
}
// Boilerplate {{{
static PyObject* rewrap(HistoryBuf *self, PyObject *args);
#define rewrap_doc ""
@ -193,6 +206,7 @@ static PyMethodDef methods[] = {
METHOD(change_num_of_lines, METH_O)
METHOD(line, METH_O)
METHOD(as_ansi, METH_O)
METHOD(dirty_lines, METH_NOARGS)
METHOD(push, METH_VARARGS)
METHOD(rewrap, METH_VARARGS)
{NULL, NULL, 0, NULL} /* Sentinel */
@ -248,7 +262,10 @@ void historybuf_rewrap(HistoryBuf *self, HistoryBuf *other) {
return;
}
other->count = 0; other->start_of_data = 0;
if (self->count > 0) rewrap_inner(self, other, self->count, NULL);
if (self->count > 0) {
rewrap_inner(self, other, self->count, NULL);
for (index_type i = 0; i < other->count; i++) other->line_attrs[(other->start_of_data + i) % other->ynum] |= TEXT_DIRTY_MASK;
}
}
static PyObject*

View File

@ -35,6 +35,16 @@ linebuf_clear(LineBuf *self, char_type ch) {
}
}
void
linebuf_mark_line_dirty(LineBuf *self, index_type y) {
self->line_attrs[y] |= TEXT_DIRTY_MASK;
}
void
linebuf_mark_line_clean(LineBuf *self, index_type y) {
self->line_attrs[y] &= ~TEXT_DIRTY_MASK;
}
static PyObject*
clear(LineBuf *self) {
#define clear_doc "Clear all lines in this LineBuf"
@ -156,6 +166,18 @@ set_continued(LineBuf *self, PyObject *args) {
Py_RETURN_NONE;
}
static PyObject*
dirty_lines(LineBuf *self) {
#define dirty_lines_doc "dirty_lines() -> Line numbers of all lines that have dirty text."
PyObject *ans = PyList_New(0);
for (index_type i = 0; i < self->ynum; i++) {
if (self->line_attrs[i] & TEXT_DIRTY_MASK) {
PyList_Append(ans, PyLong_FromUnsignedLong(i));
}
}
return ans;
}
static inline bool
allocate_line_storage(Line *line, bool initialize) {
if (initialize) {
@ -215,7 +237,8 @@ clear_line_(Line *l, index_type xnum) {
l->has_dirty_text = false;
}
void linebuf_clear_line(LineBuf *self, index_type y) {
void
linebuf_clear_line(LineBuf *self, index_type y) {
Line l;
init_line(self, &l, self->line_map[y]);
clear_line_(&l, self->xnum);
@ -231,7 +254,8 @@ clear_line(LineBuf *self, PyObject *val) {
Py_RETURN_NONE;
}
void linebuf_index(LineBuf* self, index_type top, index_type bottom) {
void
linebuf_index(LineBuf* self, index_type top, index_type bottom) {
if (top >= self->ynum - 1 || bottom >= self->ynum || bottom <= top) return;
index_type old_top = self->line_map[top];
line_attrs_type old_attrs = self->line_attrs[top];
@ -252,7 +276,8 @@ index(LineBuf *self, PyObject *args) {
Py_RETURN_NONE;
}
void linebuf_reverse_index(LineBuf *self, index_type top, index_type bottom) {
void
linebuf_reverse_index(LineBuf *self, index_type top, index_type bottom) {
if (top >= self->ynum - 1 || bottom >= self->ynum || bottom <= top) return;
index_type old_bottom = self->line_map[bottom];
line_attrs_type old_attrs = self->line_attrs[bottom];
@ -297,7 +322,7 @@ linebuf_insert_lines(LineBuf *self, unsigned int num, unsigned int y, unsigned i
self->line_map[i] = self->line_map[i - num];
self->line_attrs[i] = self->line_attrs[i - num];
}
if (y + num < self->ynum) self->line_attrs[y + num] = 0;
if (y + num < self->ynum) self->line_attrs[y + num] &= ~CONTINUED_MASK;
for (i = 0; i < num; i++) {
self->line_map[y + i] = self->scratch[ylimit - num + i];
}
@ -332,7 +357,7 @@ linebuf_delete_lines(LineBuf *self, index_type num, index_type y, index_type bot
self->line_map[i] = self->line_map[i + num];
self->line_attrs[i] = self->line_attrs[i + num];
}
self->line_attrs[y] = 0;
self->line_attrs[y] &= ~CONTINUED_MASK;
for (i = 0; i < num; i++) {
self->line_map[ylimit - num + i] = self->scratch[y + i];
}
@ -346,7 +371,7 @@ linebuf_delete_lines(LineBuf *self, index_type num, index_type y, index_type bot
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]."
#define delete_lines_doc "delete_lines(num, y, bottom) -> Delete num 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;
linebuf_delete_lines(self, num, y, bottom);
@ -409,6 +434,7 @@ static PyMethodDef methods[] = {
METHOD(as_ansi, METH_O)
METHOD(set_attribute, METH_VARARGS)
METHOD(set_continued, METH_VARARGS)
METHOD(dirty_lines, METH_NOARGS)
METHOD(index, METH_VARARGS)
METHOD(reverse_index, METH_VARARGS)
METHOD(insert_lines, METH_VARARGS)
@ -491,6 +517,7 @@ linebuf_rewrap(LineBuf *self, LineBuf *other, index_type *num_content_lines_befo
rewrap_inner(self, other, first + 1, historybuf);
*num_content_lines_after = other->line->ynum + 1;
for (i = 0; i < *num_content_lines_after; i++) other->line_attrs[i] |= TEXT_DIRTY_MASK;
*num_content_lines_before = first + 1;
}

View File

@ -67,6 +67,8 @@ void linebuf_insert_lines(LineBuf *self, unsigned int num, unsigned int y, unsig
void linebuf_delete_lines(LineBuf *self, index_type num, index_type y, index_type bottom);
void linebuf_set_attribute(LineBuf *, unsigned int , unsigned int );
void linebuf_rewrap(LineBuf *self, LineBuf *other, index_type *, index_type *, HistoryBuf *);
void linebuf_mark_line_dirty(LineBuf *self, index_type y);
void linebuf_mark_line_clean(LineBuf *self, index_type y);
unsigned int linebuf_char_width_at(LineBuf *self, index_type x, index_type y);
void linebuf_refresh_sprite_positions(LineBuf *self);
bool historybuf_resize(HistoryBuf *self, index_type lines);

View File

@ -292,15 +292,18 @@ screen_draw(Screen *self, uint32_t och) {
self->cursor->x++;
}
self->is_dirty = true;
linebuf_mark_line_dirty(self->linebuf, self->cursor->y);
} else if (is_combining_char(ch)) {
if (self->cursor->x > 0) {
linebuf_init_line(self->linebuf, self->cursor->y);
line_add_combining_char(self->linebuf->line, ch, self->cursor->x - 1);
self->is_dirty = true;
linebuf_mark_line_dirty(self->linebuf, self->cursor->y);
} else if (self->cursor->y > 0) {
linebuf_init_line(self->linebuf, self->cursor->y - 1);
line_add_combining_char(self->linebuf->line, ch, self->columns - 1);
self->is_dirty = true;
linebuf_mark_line_dirty(self->linebuf, self->cursor->y);
}
}
}
@ -324,6 +327,7 @@ screen_alignment_display(Screen *self) {
for (unsigned int y = 0; y < self->linebuf->ynum; y++) {
linebuf_init_line(self->linebuf, y);
line_clear_text(self->linebuf->line, 0, self->linebuf->xnum, 'E');
linebuf_mark_line_dirty(self->linebuf, y);
}
}
@ -820,7 +824,8 @@ screen_cursor_to_line(Screen *self, unsigned int line) {
// Editing {{{
void screen_erase_in_line(Screen *self, unsigned int how, bool private) {
void
screen_erase_in_line(Screen *self, unsigned int how, bool private) {
/*Erases a line in a specific way.
:param int how: defines the way the line should be erased in:
@ -856,10 +861,12 @@ void screen_erase_in_line(Screen *self, unsigned int how, bool private) {
line_apply_cursor(self->linebuf->line, self->cursor, s, n, true);
}
self->is_dirty = true;
linebuf_mark_line_dirty(self->linebuf, self->cursor->y);
}
}
void screen_erase_in_display(Screen *self, unsigned int how, bool private) {
void
screen_erase_in_display(Screen *self, unsigned int how, bool private) {
/* Erases display in a specific way.
:param int how: defines the way the line should be erased in:
@ -892,6 +899,7 @@ void screen_erase_in_display(Screen *self, unsigned int how, bool private) {
} else {
line_apply_cursor(self->linebuf->line, self->cursor, 0, self->columns, true);
}
linebuf_mark_line_dirty(self->linebuf, i);
}
self->is_dirty = true;
}
@ -900,7 +908,8 @@ void screen_erase_in_display(Screen *self, unsigned int how, bool private) {
}
}
void screen_insert_lines(Screen *self, unsigned int count) {
void
screen_insert_lines(Screen *self, unsigned int count) {
unsigned int top = self->margin_top, bottom = self->margin_bottom;
if (count == 0) count = 1;
if (top <= self->cursor->y && self->cursor->y <= bottom) {
@ -910,7 +919,8 @@ void screen_insert_lines(Screen *self, unsigned int count) {
}
}
void screen_delete_lines(Screen *self, unsigned int count) {
void
screen_delete_lines(Screen *self, unsigned int count) {
unsigned int top = self->margin_top, bottom = self->margin_bottom;
if (count == 0) count = 1;
if (top <= self->cursor->y && self->cursor->y <= bottom) {
@ -920,7 +930,8 @@ void screen_delete_lines(Screen *self, unsigned int count) {
}
}
void screen_insert_characters(Screen *self, unsigned int count) {
void
screen_insert_characters(Screen *self, unsigned int count) {
unsigned int top = self->margin_top, bottom = self->margin_bottom;
if (count == 0) count = 1;
if (top <= self->cursor->y && self->cursor->y <= bottom) {
@ -929,11 +940,13 @@ void screen_insert_characters(Screen *self, unsigned int count) {
linebuf_init_line(self->linebuf, self->cursor->y);
line_right_shift(self->linebuf->line, x, num);
line_apply_cursor(self->linebuf->line, self->cursor, x, num, true);
linebuf_mark_line_dirty(self->linebuf, self->cursor->y);
self->is_dirty = true;
}
}
void screen_delete_characters(Screen *self, unsigned int count) {
void
screen_delete_characters(Screen *self, unsigned int count) {
// Delete characters, later characters are moved left
unsigned int top = self->margin_top, bottom = self->margin_bottom;
if (count == 0) count = 1;
@ -943,17 +956,20 @@ void screen_delete_characters(Screen *self, unsigned int count) {
linebuf_init_line(self->linebuf, self->cursor->y);
left_shift_line(self->linebuf->line, x, num);
line_apply_cursor(self->linebuf->line, self->cursor, self->columns - num, num, true);
linebuf_mark_line_dirty(self->linebuf, self->cursor->y);
self->is_dirty = true;
}
}
void screen_erase_characters(Screen *self, unsigned int count) {
void
screen_erase_characters(Screen *self, unsigned int count) {
// Delete characters replacing them by spaces
if (count == 0) count = 1;
unsigned int x = self->cursor->x;
unsigned int num = MIN(self->columns - x, count);
linebuf_init_line(self->linebuf, self->cursor->y);
line_apply_cursor(self->linebuf->line, self->cursor, x, num, true);
linebuf_mark_line_dirty(self->linebuf, self->cursor->y);
self->is_dirty = true;
}

View File

@ -278,6 +278,8 @@ class TestDataTypes(BaseTest):
self.ae(cy, 3)
for i in range(lb2.ynum):
self.ae(lb2.line(i), lb.line(i + 2))
self.assertFalse(lb.dirty_lines())
self.ae(lb2.dirty_lines(), list(range(lb2.ynum)))
def line_comparison(self, buf, *lines):
for i, l0 in enumerate(lines):
@ -298,6 +300,7 @@ class TestDataTypes(BaseTest):
lb = create_lbuf('0123 ', '56789')
lb2 = self.line_comparison_rewrap(lb, '0123 5', '6789', '')
self.assertContinued(lb2, False, True)
self.ae(lb2.dirty_lines(), [0, 1])
lb = create_lbuf('12', 'abc')
lb2 = self.line_comparison_rewrap(lb, '12', 'abc')
@ -357,6 +360,7 @@ class TestDataTypes(BaseTest):
hb.rewrap(hb2)
for i in range(hb2.ynum):
self.ae(hb2.line(i), hb.line(i))
self.ae(hb2.dirty_lines(), list(range(hb2.ynum)))
hb = filled_history_buf(5, 5)
hb2 = HistoryBuf(hb.ynum, hb.xnum * 2)
hb.rewrap(hb2)

View File

@ -324,3 +324,41 @@ class TestScreen(BaseTest):
s.cursor_visible = False
s.toggle_alt_screen()
self.assertFalse(s.cursor_visible)
def test_dirty_lines(self):
s = self.create_screen()
self.assertFalse(s.linebuf.dirty_lines())
s.draw('a' * (s.columns * 2))
self.ae(s.linebuf.dirty_lines(), [0, 1])
self.assertFalse(s.historybuf.dirty_lines())
while not s.historybuf.count:
s.draw('a' * (s.columns * 2))
self.ae(s.historybuf.dirty_lines(), list(range(s.historybuf.count)))
self.ae(s.linebuf.dirty_lines(), list(range(s.lines)))
s.cursor.x, s.cursor.y = 0, 1
s.insert_lines(2)
self.ae(s.linebuf.dirty_lines(), [0, 3, 4])
s.draw('a' * (s.columns * s.lines))
self.ae(s.linebuf.dirty_lines(), list(range(s.lines)))
s.cursor.x, s.cursor.y = 0, 1
s.delete_lines(2)
self.ae(s.linebuf.dirty_lines(), [0, 1, 2])
s = self.create_screen()
self.assertFalse(s.linebuf.dirty_lines())
s.erase_in_line(0, False)
self.ae(s.linebuf.dirty_lines(), [0])
s.index(), s.index()
s.erase_in_display(0, False)
self.ae(s.linebuf.dirty_lines(), [0, 2, 3, 4])
s = self.create_screen()
self.assertFalse(s.linebuf.dirty_lines())
s.insert_characters(2)
self.ae(s.linebuf.dirty_lines(), [0])
s.cursor.y = 1
s.delete_characters(2)
self.ae(s.linebuf.dirty_lines(), [0, 1])
s.cursor.y = 2
s.erase_characters(2)
self.ae(s.linebuf.dirty_lines(), [0, 1, 2])