Cursor tracking tests

Also fix extra x movement for tracked cursors
This commit is contained in:
Kovid Goyal 2024-12-30 15:14:04 +05:30
parent c4a32862d1
commit 97449dfddb
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
5 changed files with 51 additions and 29 deletions

View file

@ -11,7 +11,6 @@
// TODO: Handle selection with multicell
// TODO: URL detection with multicell
// TODO: Handle restitch of multiline chars
typedef union CellAttrs {
struct {

View file

@ -155,16 +155,15 @@ init_src_line(Rewrap *r) {
}
static void
update_tracked_cursors(Rewrap *r, index_type num_cells, index_type y, index_type x_limit) {
update_tracked_cursors(Rewrap *r, index_type num_cells, index_type src_y, index_type dest_y, index_type x_limit) {
for (TrackCursor *t = r->cursors; !t->is_sentinel; t++) {
if (t->y == y && r->src_x <= t->x && (t->x < r->src_x + num_cells || t->x >= x_limit)) {
if (t->y == src_y && r->src_x <= t->x && (t->x < r->src_x + num_cells || t->x >= x_limit)) {
if (r->current_dest_line_is_last_history_line) {
t->dest_x = 0; t->dest_y = 0;
} else {
index_type x = t->x;
if (x >= x_limit) x = MAX(1u, x_limit) - 1;
t->dest_y = r->dest_y;
t->dest_x = r->dest_x + (x - r->src_x + (x > 0));
t->dest_y = dest_y;
t->dest_x = r->dest_x + (t->x - r->src_x);
if (t->dest_x > r->dest_xnum) t->dest_x = r->dest_xnum;
}
}
}
@ -197,7 +196,7 @@ copy_multiline_extra_lines(Rewrap *r, CPUCell *src_cell, index_type mc_width) {
linebuf_init_line_at(r->scratch, i - 1, &r->dest_scratch);
linebuf_mark_line_dirty(r->scratch, i - 1);
copy_range(&r->src_scratch, r->src_x, &r->dest_scratch, r->dest_x, mc_width);
update_tracked_cursors(r, mc_width, r->src_y + i, r->src_xnum + 10000 /* ensure cursor is moved only if in region being copied */);
update_tracked_cursors(r, mc_width, r->src_y + i, r->dest_y + i, r->src_xnum + 10000 /* ensure cursor is moved only if in region being copied */);
}
}
@ -209,15 +208,18 @@ multiline_copy_src_to_dest(Rewrap *r) {
c = &r->src.cpu_cells[r->src_x];
if (c->is_multicell) {
mc_width = mcd_x_limit(c);
if (c->y || mc_width > r->dest_xnum) {
update_tracked_cursors(r, mc_width, r->src_y, r->src_x_limit);
if (mc_width > r->dest_xnum) {
update_tracked_cursors(r, mc_width, r->src_y, r->dest_y, r->src_x_limit);
r->src_x += mc_width;
continue;
} else if (c->y) {
r->src_x += mc_width;
continue;
}
} else mc_width = 1;
find_space_in_dest(r, mc_width);
copy_range(&r->src, r->src_x, &r->dest, r->dest_x, mc_width);
update_tracked_cursors(r, mc_width, r->src_y, r->src_x_limit);
update_tracked_cursors(r, mc_width, r->src_y, r->dest_y, r->src_x_limit);
if (c->scale > 1) copy_multiline_extra_lines(r, c, mc_width);
r->src_x += mc_width; r->dest_x += mc_width;
}
@ -249,7 +251,7 @@ fast_copy_src_to_dest(Rewrap *r) {
}
}
copy_range(&r->src, r->src_x, &r->dest, r->dest_x, num);
update_tracked_cursors(r, num, r->src_y, r->src_x_limit);
update_tracked_cursors(r, num, r->src_y, r->dest_y, r->src_x_limit);
r->src_x += num; r->dest_x += num;
}
}

View file

@ -217,8 +217,8 @@ screen_dirty_sprite_positions(Screen *self) {
}
static HistoryBuf*
realloc_hb(HistoryBuf *old, unsigned int lines, unsigned int columns, ANSIBuf *as_ansi_buf) {
HistoryBuf *ans = alloc_historybuf(lines, columns, 0, old->text_cache);
realloc_hb(HistoryBuf *old, unsigned int columns, ANSIBuf *as_ansi_buf) {
HistoryBuf *ans = alloc_historybuf(old->ynum, columns, 0, old->text_cache);
if (ans == NULL) { PyErr_NoMemory(); return NULL; }
ans->pagerhist = old->pagerhist; old->pagerhist = NULL;
historybuf_rewrap(old, ans, as_ansi_buf);
@ -397,7 +397,7 @@ screen_resize(Screen *self, unsigned int lines, unsigned int columns) {
// Resize main linebuf
bool history_buf_last_line_is_split = history_buf_endswith_wrap(self->historybuf);
HistoryBuf *nh = realloc_hb(self->historybuf, self->historybuf->ynum, columns, &self->as_ansi_buf);
HistoryBuf *nh = realloc_hb(self->historybuf, columns, &self->as_ansi_buf);
if (nh == NULL) return false;
Py_CLEAR(self->historybuf); self->historybuf = nh;
RAII_PyObject(prompt_copy, NULL);

View file

@ -466,6 +466,12 @@ def test_multicell(self: TestMulticell) -> None:
s.resize(*o)
s.reset()
def mc(x=None, y=None):
if x is not None:
s.cursor.x = x
if y is not None:
s.cursor.y = y
reset()
multicell(s, 'a', scale=2)
before = as_ansi()
@ -474,27 +480,43 @@ def test_multicell(self: TestMulticell) -> None:
reset()
s.draw('a' * (s.columns - 2) + '😛' + 'bb')
mc(4, 0)
s.resize(s.lines, s.columns-1)
self.ae('\x1b[maaaa\x1b[m😛bb', as_ansi().rstrip())
assert_cursor_at(0, 1)
reset()
s.draw('a' * (s.columns - 2) + '😛' + 'bb')
mc(0, 1)
s.resize(s.lines, s.columns-2)
assert_cursor_at(2, 1)
self.ae('\x1b[maaaa\x1b[m😛bb', as_ansi().rstrip())
reset()
s.draw('a' * (s.columns - 2) + '😛' + 'bb')
mc(5, 0)
s.resize(s.lines, s.columns-3)
self.ae('\x1b[maaa\x1b[ma😛\x1b[mbb', as_ansi().rstrip()) # ]]]]]]]
assert_cursor_at(2, 1)
def resize(lines, cols, cursorx=None, cursory=None):
mc(cursorx, cursory)
before = s.cursor.x, s.cursor.y
cell = s.cpu_cells(s.cursor.y, s.cursor.x)
cell.pop('next_char_was_wrapped')
s.resize(lines, cols)
ncell = s.cpu_cells(s.cursor.y, s.cursor.x)
ncell.pop('next_char_was_wrapped')
self.ae(cell, ncell, f'Cursor moved from: {before} to {(s.cursor.x, s.cursor.y)}')
reset()
multicell(s, 'a', scale=3), s.draw('b'*(s.columns-3))
s.resize(s.lines, s.columns-1)
resize(s.lines, s.columns-1, 5, 0)
self.ae('\x1b[m\x1b]66;w=1:s=3;a\x07bb\x1b[mb', as_ansi().rstrip()) # ]]
ac(0, 0, is_multicell=True)
ac(0, 1, is_multicell=True)
ac(3, 1, is_multicell=False, text='b')
reset()
s.draw('X'), multicell(s, 'a', scale=3), s.draw('12345')
s.resize(s.lines, s.columns-1)
resize(s.lines, s.columns-1, 4, 0)
self.ae('\x1b[mX\x1b]66;w=1:s=3;a\x071\x1b[m23\x1b[m45', as_ansi().rstrip()) # ]]
for y in (0, 1):
ac(0, y, is_multicell=False), ac(1, y, is_multicell=True), ac(3, y, is_multicell=True)
@ -502,17 +524,17 @@ def test_multicell(self: TestMulticell) -> None:
reset()
s.draw('a'*(s.columns - 2)), s.draw('😛'), s.linefeed(), s.carriage_return(), s.draw('123')
s.resize(s.lines, s.columns-1)
resize(s.lines, s.columns-1, 5, 0)
self.ae('\x1b[maaaa\x1b[m😛\n\x1b[m123', as_ansi().rstrip()) # ]]]]]]]
reset()
s.draw('a'*(s.columns - 1)), s.draw('😛'), s.draw('bcd')
s.resize(s.lines, s.columns + 1)
resize(s.lines, s.columns + 1, 0, 1)
self.ae('\x1b[maaaaa😛\x1b[mbcd', as_ansi().rstrip()) # ]]]]]]]
reset()
s.draw('a'*s.columns), s.draw('😛'), s.draw('bcd')
s.resize(s.lines, s.columns + 1)
resize(s.lines, s.columns + 1, 0, 1)
self.ae('\x1b[maaaaaa\x1b[m😛bcd', as_ansi().rstrip()) # ]]]]]]]
ac(s.columns-1, 0, next_char_was_wrapped=True)
s.resize(s.lines, s.columns + 1)
@ -520,7 +542,7 @@ def test_multicell(self: TestMulticell) -> None:
reset()
s.draw('a'*(s.columns - 1)), multicell(s, 'X', scale=2), s.draw('bcd')
s.resize(s.lines, s.columns + 1)
resize(s.lines, s.columns + 1, 0, 2)
self.ae('\x1b[maaaaa\x1b]66;w=1:s=2;X\x07\x1b[mbcd', as_ansi().rstrip()) # ]]]]]]]
for y in (0, 1):
for x in (1, 2):
@ -539,13 +561,13 @@ def test_multicell(self: TestMulticell) -> None:
reset()
multicell(s, 'X', scale=4), s.draw('abc')
s.resize(3, 3)
resize(3, 3, 5, 0)
self.ae('\x1b[mabc', as_ansi().rstrip()) # ]]]]]]]
reset()
multicell(s, 'X', width=4), s.draw('abc')
s.resize(3, 3)
resize(3, 3, 4, 0)
self.ae('\x1b[mabc', as_ansi().rstrip()) # ]]]]]]]
reset()
s.draw('1'), multicell(s, 'X', width=4), s.draw('abc')
s.resize(3, 3)
resize(3, 3, 5, 0)
self.ae('\x1b[m1ab\x1b[mc', as_ansi().rstrip()) # ]]]]]]]

View file

@ -302,9 +302,8 @@ class TestScreen(BaseTest):
self.assertTrue(s.historybuf.endswith_wrap())
self.ae(str(s.historybuf), '111122')
self.ae(at(), text + '\n')
# for some reason rewrap_inner moves the cursor by one cell to the right
self.ae((s.cursor.x, s.cursor.y), (4, 0))
self.ae(ac(), 'c')
self.ae((s.cursor.x, s.cursor.y), (3, 0))
self.ae(ac(), 'b')
s = self.create_screen(cols=4, lines=4, scrollback=4)
s.draw('1111222'), s.linefeed(), s.carriage_return()
s.draw('333344445555')
@ -324,10 +323,10 @@ class TestScreen(BaseTest):
s.cursor.x, s.cursor.y = 1, 1
self.ae(ac(), 'b')
s.resize(s.lines, s.columns * 2)
self.ae(ac(), 'c')
self.ae(ac(), 'b')
self.ae(str(s.historybuf), '11112222')
self.ae(at(), text + '\n\n')
self.ae((s.cursor.x, s.cursor.y), (2, 0))
self.ae((s.cursor.x, s.cursor.y), (1, 0))
# test that trailing blank line is preserved on resize
s = self.create_screen(cols=5, lines=5, scrollback=15)