Implement dirty line tracking
This commit is contained in:
parent
0277be5856
commit
6c838bbc28
@ -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*
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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])
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user