Add multicell tests for various edit operations
This commit is contained in:
parent
e10df382f8
commit
394404a1ab
4 changed files with 129 additions and 8 deletions
|
|
@ -83,8 +83,8 @@ text_in_cell_ansi(const CPUCell *c, TextCache *tc, ANSIBuf *output) {
|
|||
if (c->x || c->y) return 0;
|
||||
MultiCellData mcd = cell_multicell_data(c, tc);
|
||||
unsigned n = write_multicell_ansi_prefix(mcd, output);
|
||||
n += tc_chars_at_index_ansi(tc, c->ch_or_idx, output);
|
||||
output->buf[output->len] = '\a';
|
||||
n += tc_chars_at_index_ansi(tc, c->ch_or_idx, output) - 1;
|
||||
output->buf[output->len - 1] = '\a';
|
||||
return n;
|
||||
}
|
||||
ensure_space_for(output, buf, output->buf[0], output->len + 1, capacity, 2048, false);
|
||||
|
|
@ -956,7 +956,6 @@ hyperlink_ids(Line *self, PyObject *args UNUSED) {
|
|||
return ans;
|
||||
}
|
||||
|
||||
|
||||
static PyObject *
|
||||
richcmp(PyObject *obj1, PyObject *obj2, int op);
|
||||
|
||||
|
|
|
|||
|
|
@ -185,7 +185,6 @@ cell_first_char(const CPUCell *c, const TextCache *tc) {
|
|||
return c->ch_or_idx;
|
||||
}
|
||||
|
||||
|
||||
static inline CellAttrs
|
||||
cursor_to_attrs(const Cursor *c) {
|
||||
CellAttrs ans = {
|
||||
|
|
|
|||
|
|
@ -2231,6 +2231,7 @@ screen_erase_in_line(Screen *self, unsigned int how, bool private) {
|
|||
break;
|
||||
}
|
||||
if (n > 0) {
|
||||
nuke_multicell_char_intersecting_with(self, s, n, self->cursor->y, self->cursor->y + 1, false);
|
||||
screen_dirty_line_graphics(self, self->cursor->y, self->cursor->y, self->linebuf == self->main_linebuf);
|
||||
linebuf_init_line(self->linebuf, self->cursor->y);
|
||||
if (private) {
|
||||
|
|
@ -2257,6 +2258,15 @@ screen_clear_scrollback(Screen *self) {
|
|||
self->scrolled_by = 0;
|
||||
dirty_scroll(self);
|
||||
}
|
||||
LineBuf *orig = self->linebuf; self->linebuf = self->main_linebuf;
|
||||
CPUCell *cells = linebuf_cpu_cells_for_line(self->linebuf, 0);
|
||||
for (index_type x = 0; x < self->columns; x++) {
|
||||
CPUCell *c = cells + x;
|
||||
if (c->is_multicell && c->y > 0) { // multiline char that extended into scrollback
|
||||
nuke_multicell_char_at(self, x, 0, false);
|
||||
}
|
||||
}
|
||||
self->linebuf = orig;
|
||||
}
|
||||
|
||||
static Line* visual_line_(Screen *self, int y_);
|
||||
|
|
@ -2298,6 +2308,7 @@ screen_erase_in_display(Screen *self, unsigned int how, bool private) {
|
|||
:param bool private: when ``True`` character attributes are left unchanged
|
||||
*/
|
||||
unsigned int a, b;
|
||||
bool nuke_multicell_chars = true;
|
||||
switch(how) {
|
||||
case 0:
|
||||
a = self->cursor->y + 1; b = self->lines; break;
|
||||
|
|
@ -2305,12 +2316,14 @@ screen_erase_in_display(Screen *self, unsigned int how, bool private) {
|
|||
a = 0; b = self->cursor->y; break;
|
||||
case 22:
|
||||
screen_move_into_scrollback(self);
|
||||
nuke_multicell_chars = false; // they have been moved into scrollback and we would get double deletions
|
||||
how = 2;
|
||||
/* fallthrough */
|
||||
case 2:
|
||||
case 3:
|
||||
grman_clear(self->grman, how == 3, self->cell_size);
|
||||
a = 0; b = self->lines; break;
|
||||
a = 0; b = self->lines; nuke_multicell_chars = false;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
|
@ -2324,6 +2337,7 @@ screen_erase_in_display(Screen *self, unsigned int how, bool private) {
|
|||
linebuf_clear_attrs_and_dirty(self->linebuf, i);
|
||||
}
|
||||
} else linebuf_clear_lines(self->linebuf, self->cursor, a, b);
|
||||
if (nuke_multicell_chars) nuke_multicell_char_intersecting_with(self, 0, self->columns, a, b, false);
|
||||
self->is_dirty = true;
|
||||
if (selection_intersects_screen_lines(&self->selections, a, b)) clear_selection(&self->selections);
|
||||
}
|
||||
|
|
@ -2341,11 +2355,30 @@ 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) {
|
||||
// remove split multiline chars at top edge
|
||||
CPUCell *cells = linebuf_cpu_cells_for_line(self->linebuf, self->cursor->y);
|
||||
for (index_type x = 0; x < self->columns; x++) {
|
||||
if (cells[x].is_multicell && cells[x].y) nuke_multicell_char_at(self, x, self->cursor->y, false);
|
||||
}
|
||||
screen_dirty_line_graphics(self, top, bottom, self->linebuf == self->main_linebuf);
|
||||
linebuf_insert_lines(self->linebuf, count, self->cursor->y, bottom);
|
||||
self->is_dirty = true;
|
||||
clear_selection(&self->selections);
|
||||
screen_carriage_return(self);
|
||||
// remove split multiline chars at bottom of screen
|
||||
cells = linebuf_cpu_cells_for_line(self->linebuf, bottom);
|
||||
for (index_type x = 0; x < self->columns; x++) {
|
||||
if (cells[x].is_multicell) {
|
||||
MultiCellData mcd = cell_multicell_data(cells + x, self->text_cache);
|
||||
index_type y_limit = mcd.scale;
|
||||
if (cells[x].y + 1u < y_limit) {
|
||||
index_type orig = self->lines;
|
||||
self->lines = bottom + 1;
|
||||
nuke_multicell_char_at(self, x, bottom, false);
|
||||
self->lines = orig;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2368,6 +2401,11 @@ 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) {
|
||||
index_type y = self->cursor->y;
|
||||
nuke_multiline_char_intersecting_with(self, 0, self->columns, y, y + 1, false);
|
||||
y += count;
|
||||
y = MIN(bottom, y);
|
||||
nuke_multiline_char_intersecting_with(self, 0, self->columns, y, y + 1, false);
|
||||
screen_dirty_line_graphics(self, top, bottom, self->linebuf == self->main_linebuf);
|
||||
linebuf_delete_lines(self->linebuf, count, self->cursor->y, bottom);
|
||||
self->is_dirty = true;
|
||||
|
|
@ -5158,16 +5196,23 @@ static PyObject*
|
|||
cpu_cells(Screen *self, PyObject *args) {
|
||||
int y, x = -1;
|
||||
if (!PyArg_ParseTuple(args, "i|i", &y, &x)) return NULL;
|
||||
if (y < 0 || y >= (int)self->lines) { PyErr_SetString(PyExc_IndexError, "y out of bounds"); return NULL; }
|
||||
if (y >= (int)self->lines) { PyErr_SetString(PyExc_IndexError, "y out of bounds"); return NULL; }
|
||||
CPUCell *cells;
|
||||
if (y >= 0) cells = linebuf_cpu_cells_for_line(self->linebuf, y);
|
||||
else {
|
||||
Line *l = self->linebuf == self->main_linebuf ? checked_range_line(self, y) : NULL;
|
||||
if (!l) { PyErr_SetString(PyExc_IndexError, "y out of bounds"); return NULL; }
|
||||
cells = l->cpu_cells;
|
||||
}
|
||||
if (x > -1) {
|
||||
if (x >= (int)self->columns) { PyErr_SetString(PyExc_IndexError, "x out of bounds"); return NULL; }
|
||||
return cpu_cell_as_dict(linebuf_cpu_cell_at(self->linebuf, x, y), self->text_cache, self->lc, self->hyperlink_pool);
|
||||
return cpu_cell_as_dict(cells + x, self->text_cache, self->lc, self->hyperlink_pool);
|
||||
}
|
||||
index_type start_x = 0, x_limit = self->columns;
|
||||
RAII_PyObject(ans, PyTuple_New(x_limit - start_x));
|
||||
if (ans) {
|
||||
for (index_type x = start_x; x < x_limit; x++) {
|
||||
PyObject *d = cpu_cell_as_dict(linebuf_cpu_cell_at(self->linebuf, x, y), self->text_cache, self->lc, self->hyperlink_pool);
|
||||
PyObject *d = cpu_cell_as_dict(cells + x, self->text_cache, self->lc, self->hyperlink_pool);
|
||||
if (!d) return NULL;
|
||||
PyTuple_SET_ITEM(ans, x, d);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -253,3 +253,81 @@ def big_a(x, y):
|
|||
s.erase_characters(3)
|
||||
assert_line('\0\0\0\0c\0')
|
||||
assert_line('\0\0\0\0\0\0', 1)
|
||||
|
||||
# Erase in line
|
||||
for x in (1, 2):
|
||||
s.reset()
|
||||
s.draw('a'), multicell(s, 'b', scale=2), s.draw('c')
|
||||
s.cursor.x = x
|
||||
s.erase_in_line(0)
|
||||
assert_line('a\0\0\0\0\0')
|
||||
s.reset()
|
||||
s.draw('a'), multicell(s, 'b', width=2), s.draw('c')
|
||||
s.cursor.x = x
|
||||
s.erase_in_line(1)
|
||||
assert_line('\0\0\0c\0\0')
|
||||
s.reset()
|
||||
s.draw('a'), multicell(s, 'b', scale=2), s.draw('c')
|
||||
s.erase_in_line(2)
|
||||
for y in (0, 1):
|
||||
assert_line('\0\0\0\0\0\0', y)
|
||||
s.reset()
|
||||
s.draw('a'), multicell(s, 'b', scale=2), s.draw('c')
|
||||
s.cursor.y = 1
|
||||
s.erase_in_line(2)
|
||||
assert_line('a\0\0c\0\0', 0)
|
||||
assert_line('\0\0\0\0\0\0', 1)
|
||||
|
||||
# Clear scrollback
|
||||
s.reset()
|
||||
s.draw('a'), multicell(s, 'b', scale=2), s.draw('c')
|
||||
for i in range(s.lines):
|
||||
s.index()
|
||||
s.cursor.y = 0
|
||||
assert_line('\0__\0\0\0')
|
||||
s.clear_scrollback()
|
||||
assert_line('\0\0\0\0\0\0')
|
||||
|
||||
# Erase in display
|
||||
for x in (1, 2):
|
||||
s.reset(), s.draw('a'), multicell(s, 'b', scale=2), s.draw('c')
|
||||
s.cursor.x = x
|
||||
s.erase_in_display(0)
|
||||
assert_line('a\0\0\0\0\0')
|
||||
s.reset(), s.draw('a'), multicell(s, 'b', scale=2), s.draw('c')
|
||||
s.cursor.x, s.cursor.y = 2, 1
|
||||
s.erase_in_display(0)
|
||||
assert_line('a\0\0c\0\0', 0)
|
||||
assert_line('\0\0\0\0\0\0', 1)
|
||||
s.reset(), s.draw('a'), multicell(s, 'b', scale=2), s.draw('c')
|
||||
for i in range(s.lines):
|
||||
s.index()
|
||||
s.erase_in_display(22)
|
||||
assert_line('ab_c\0\0', -2)
|
||||
assert_line('\0__\0\0\0', -1)
|
||||
self.ae(s.historybuf.line(1).as_ansi(), f'a\x1b]{TEXT_SIZE_CODE};s=2;b\x07c')
|
||||
self.ae(s.historybuf.line(0).as_ansi(), ' ')
|
||||
|
||||
# Insert lines
|
||||
s.reset()
|
||||
multicell(s, 'a', scale=2)
|
||||
s.cursor.x, s.cursor.y = 0, s.lines - 2
|
||||
multicell(s, 'b', scale=2)
|
||||
s.cursor.x, s.cursor.y = 0, 1
|
||||
s.insert_lines(1)
|
||||
for y in range(s.lines):
|
||||
assert_line('\0' * s.columns, y)
|
||||
s.reset()
|
||||
multicell(s, 'a', scale=2)
|
||||
s.insert_lines(2)
|
||||
assert_line('\0' * s.columns, 0)
|
||||
assert_line('a_\0\0\0\0', 2)
|
||||
|
||||
# Delete lines
|
||||
s.reset()
|
||||
multicell(s, 'a', scale=2)
|
||||
s.cursor.y = 1
|
||||
multicell(s, 'b', scale=2)
|
||||
s.delete_lines(1)
|
||||
for y in range(s.lines):
|
||||
assert_line('\0' * s.columns, y)
|
||||
|
|
|
|||
Loading…
Reference in a new issue