Fix cursor moving one line up when resizing

This commit is contained in:
Kovid Goyal 2017-09-20 10:59:31 +05:30
parent 564f19d248
commit 3ca45ab241
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
7 changed files with 85 additions and 44 deletions

View File

@ -143,6 +143,22 @@ line(HistoryBuf *self, PyObject *val) {
return (PyObject*)self->line;
}
static PyObject*
__str__(HistoryBuf *self) {
PyObject *lines = PyTuple_New(self->ynum);
if (lines == NULL) return PyErr_NoMemory();
for (index_type i = 0; i < self->count; i++) {
init_line(self, index_of(self, i), self->line);
PyObject *t = PyObject_Str((PyObject*)self->line);
if (t == NULL) { Py_CLEAR(lines); return NULL; }
PyTuple_SET_ITEM(lines, i, t);
}
PyObject *sep = PyUnicode_FromString("\n");
PyObject *ans = PyUnicode_Join(sep, lines);
Py_CLEAR(lines); Py_CLEAR(sep);
return ans;
}
static PyObject*
push(HistoryBuf *self, PyObject *args) {
#define push_doc "Push a line into this buffer, removing the oldest line, if necessary"
@ -203,6 +219,7 @@ PyTypeObject HistoryBuf_Type = {
.tp_doc = "History buffers",
.tp_methods = methods,
.tp_members = members,
.tp_str = (reprfunc)__str__,
.tp_new = new
};

View File

@ -367,6 +367,22 @@ linebuf_refresh_sprite_positions(LineBuf *self) {
}
}
static PyObject*
__str__(LineBuf *self) {
PyObject *lines = PyTuple_New(self->ynum);
if (lines == NULL) return PyErr_NoMemory();
for (index_type i = 0; i < self->ynum; i++) {
init_line(self, self->line, self->line_map[i]);
PyObject *t = PyObject_Str((PyObject*)self->line);
if (t == NULL) { Py_CLEAR(lines); return NULL; }
PyTuple_SET_ITEM(lines, i, t);
}
PyObject *sep = PyUnicode_FromString("\n");
PyObject *ans = PyUnicode_Join(sep, lines);
Py_CLEAR(lines); Py_CLEAR(sep);
return ans;
}
// Boilerplate {{{
static PyObject*
copy_old(LineBuf *self, PyObject *y);
@ -410,6 +426,7 @@ PyTypeObject LineBuf_Type = {
.tp_doc = "Line buffers",
.tp_methods = methods,
.tp_members = members,
.tp_str = (reprfunc)__str__,
.tp_new = new
};
@ -437,7 +454,7 @@ copy_old(LineBuf *self, PyObject *y) {
#include "rewrap.h"
void
linebuf_rewrap(LineBuf *self, LineBuf *other, int *cursor_y_out, HistoryBuf *historybuf) {
linebuf_rewrap(LineBuf *self, LineBuf *other, index_type *num_content_lines_before, index_type *num_content_lines_after, HistoryBuf *historybuf) {
index_type first, i;
bool is_empty = true;
@ -446,34 +463,41 @@ linebuf_rewrap(LineBuf *self, LineBuf *other, int *cursor_y_out, HistoryBuf *his
memcpy(other->line_map, self->line_map, sizeof(index_type) * self->ynum);
memcpy(other->continued_map, self->continued_map, sizeof(bool) * self->ynum);
memcpy(other->buf, self->buf, self->xnum * self->ynum * sizeof(Cell));
*num_content_lines_before = self->ynum; *num_content_lines_after = self->ynum;
return;
}
// Find the first line that contains some content
for (first = self->ynum - 1; true; first--) {
Cell *cells = lineptr(self, first);
first = self->ynum;
do {
first--;
Cell *cells = lineptr(self, self->line_map[first]);
for(i = 0; i < self->xnum; i++) {
if ((cells[i].ch) != BLANK_CHAR) { is_empty = false; break; }
}
if (!is_empty || !first) break;
} while(is_empty && first > 0);
if (is_empty) { // All lines are empty
*num_content_lines_after = 0;
*num_content_lines_before = 0;
return;
}
if (first == 0) { *cursor_y_out = 0; return; } // All lines are empty
rewrap_inner(self, other, first + 1, historybuf);
*cursor_y_out = other->line->ynum;
*num_content_lines_after = other->line->ynum + 1;
*num_content_lines_before = first + 1;
}
static PyObject*
rewrap(LineBuf *self, PyObject *args) {
LineBuf* other;
HistoryBuf *historybuf;
int cursor_y = -1;
unsigned int nclb, ncla;
if (!PyArg_ParseTuple(args, "O!O!", &LineBuf_Type, &other, &HistoryBuf_Type, &historybuf)) return NULL;
linebuf_rewrap(self, other, &cursor_y, historybuf);
linebuf_rewrap(self, other, &nclb, &ncla, historybuf);
return Py_BuildValue("i", cursor_y);
return Py_BuildValue("II", nclb, ncla);
}
LineBuf *alloc_linebuf(unsigned int lines, unsigned int columns) {

View File

@ -78,7 +78,7 @@ void linebuf_clear_line(LineBuf *self, index_type y);
void linebuf_insert_lines(LineBuf *self, unsigned int num, unsigned int y, unsigned int bottom);
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, int *cursor_y_out, HistoryBuf *);
void linebuf_rewrap(LineBuf *self, LineBuf *other, index_type *, index_type *, HistoryBuf *);
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

@ -28,7 +28,7 @@
if (dest_y >= dest->ynum - 1) { \
linebuf_index(dest, 0, dest->ynum - 1); \
if (historybuf != NULL) { \
linebuf_init_line(dest, dest->ynum - 1); \
init_dest_line(dest->ynum - 1); \
historybuf_add_line(historybuf, dest->line); \
}\
linebuf_clear_line(dest, dest->ynum - 1); \

View File

@ -123,44 +123,40 @@ realloc_hb(HistoryBuf *old, unsigned int lines, unsigned int columns) {
}
static inline LineBuf*
realloc_lb(LineBuf *old, unsigned int lines, unsigned int columns, int *cursor_y, HistoryBuf *hb) {
realloc_lb(LineBuf *old, unsigned int lines, unsigned int columns, index_type *nclb, index_type *ncla, HistoryBuf *hb) {
LineBuf *ans = alloc_linebuf(lines, columns);
if (ans == NULL) { PyErr_NoMemory(); return NULL; }
linebuf_rewrap(old, ans, cursor_y, hb);
linebuf_rewrap(old, ans, nclb, ncla, hb);
return ans;
}
static inline void
adjust_cursor_position_after_resize(Cursor *c, unsigned int lines, unsigned int columns, index_type num_content_lines_before, index_type num_content_lines_after) {
if (c->y >= num_content_lines_before - 1) {
index_type delta = c->y - (num_content_lines_before - 1);
c->y = num_content_lines_after - 1 + delta;
}
c->y = MIN(lines - 1, c->y); c->x = MIN(columns - 1, c->x);
}
static bool
screen_resize(Screen *self, unsigned int lines, unsigned int columns) {
lines = MAX(1, lines); columns = MAX(1, columns);
bool is_main = self->linebuf == self->main_linebuf, is_x_shrink = columns < self->columns;
int cursor_y = -1; unsigned int cursor_x = self->cursor->x;
bool is_main = self->linebuf == self->main_linebuf;
index_type num_content_lines_before, num_content_lines_after;
HistoryBuf *nh = realloc_hb(self->historybuf, self->historybuf->ynum, columns);
if (nh == NULL) return false;
Py_CLEAR(self->historybuf); self->historybuf = nh;
LineBuf *n = realloc_lb(self->main_linebuf, lines, columns, &cursor_y, self->historybuf);
LineBuf *n = realloc_lb(self->main_linebuf, lines, columns, &num_content_lines_before, &num_content_lines_after, self->historybuf);
if (n == NULL) return false;
Py_CLEAR(self->main_linebuf); self->main_linebuf = n;
bool index_after_resize = false;
if (is_main) {
index_type cy = MIN(self->cursor->y, lines - 1);
linebuf_init_line(self->main_linebuf, cy);
if (is_x_shrink && (self->main_linebuf->continued_map[cy] || line_length(self->main_linebuf->line) > columns)) {
// If the client is in line drawing mode, it will redraw the cursor
// line, this can cause rendering artifacts, so ensure that the
// cursor is on a new line
index_after_resize = true;
}
self->cursor->y = MAX(0, cursor_y);
}
cursor_y = -1;
n = realloc_lb(self->alt_linebuf, lines, columns, &cursor_y, NULL);
if (is_main) adjust_cursor_position_after_resize(self->cursor, lines, columns, num_content_lines_before, num_content_lines_after);
n = realloc_lb(self->alt_linebuf, lines, columns, &num_content_lines_before, &num_content_lines_after, NULL);
if (n == NULL) return false;
Py_CLEAR(self->alt_linebuf); self->alt_linebuf = n;
if (!is_main) self->cursor->y = MAX(0, cursor_y);
if (!is_main) adjust_cursor_position_after_resize(self->cursor, lines, columns, num_content_lines_before, num_content_lines_after);
self->linebuf = is_main ? self->main_linebuf : self->alt_linebuf;
if (is_x_shrink && cursor_x >= columns) self->cursor->x = columns - 1;
self->lines = lines; self->columns = columns;
self->margin_top = 0; self->margin_bottom = self->lines - 1;
@ -175,7 +171,6 @@ screen_resize(Screen *self, unsigned int lines, unsigned int columns) {
self->is_dirty = true;
self->selection = EMPTY_SELECTION;
self->url_range = EMPTY_SELECTION;
if (index_after_resize) screen_index(self);
return true;
}

View File

@ -257,7 +257,7 @@ class TestDataTypes(BaseTest):
def rewrap(self, lb, lb2):
hb = HistoryBuf(lb2.ynum, lb2.xnum)
cy = lb.rewrap(lb2, hb)
return hb, cy
return hb, cy[1]
def test_rewrap_simple(self):
' Same width buffers '
@ -268,7 +268,7 @@ class TestDataTypes(BaseTest):
self.ae(lb2.line(i), lb.line(i))
lb2 = LineBuf(8, 5)
cy = self.rewrap(lb, lb2)[1]
self.ae(cy, 4)
self.ae(cy, 5)
for i in range(lb.ynum):
self.ae(lb2.line(i), lb.line(i))
empty = LineBuf(1, lb2.xnum)
@ -276,7 +276,7 @@ class TestDataTypes(BaseTest):
self.ae(str(lb2.line(i)), str(empty.line(0)))
lb2 = LineBuf(3, 5)
cy = self.rewrap(lb, lb2)[1]
self.ae(cy, 2)
self.ae(cy, 3)
for i in range(lb2.ynum):
self.ae(lb2.line(i), lb.line(i + 2))

View File

@ -223,14 +223,19 @@ class TestScreen(BaseTest):
s.resize(5, 1)
self.ae(str(s.line(0)), '4')
hb = s.historybuf
for i in range(hb.ynum):
self.ae(str(hb.line(i)), '4' if i == 0 else '3')
s = self.create_screen(scrollback=6)
s.draw(''.join([str(i) * s.columns for i in range(s.lines*2)]))
self.ae(str(s.line(4)), '9'*5)
self.ae(str(hb), '3\n3\n3\n3\n3\n2')
s = self.create_screen(scrollback=20)
s.draw(''.join(str(i) * s.columns for i in range(s.lines*2)))
self.ae(str(s.linebuf), '55555\n66666\n77777\n88888\n99999')
s.resize(5, 2)
self.ae(str(s.line(3)), '9')
self.ae(str(s.line(4)), '')
self.ae(str(s.linebuf), '88\n88\n99\n99\n9')
def test_cursor_after_resize(self):
s = self.create_screen()
s.draw('123'), s.linefeed(), s.carriage_return(), s.draw('123'), s.linefeed(), s.carriage_return()
y_before = s.cursor.y
s.resize(s.lines, s.columns-1)
self.ae(y_before, s.cursor.y)
def test_tab_stops(self):
# Taken from vttest/main.c