Make selection smarter

It now does not add trailing blank cells on selected lines to the
selection.
This commit is contained in:
Kovid Goyal
2017-09-10 09:09:56 +05:30
parent 5103381c27
commit 63882e1fdc
4 changed files with 94 additions and 81 deletions

View File

@@ -90,37 +90,6 @@ class Selection: # {{{
b = coord(self.end_x, self.end_y, self.end_scrolled_by)
return (a, b) if a[1] < b[1] or (a[1] == b[1] and a[0] <= b[0]) else (b, a)
def text(self, linebuf, historybuf):
sy = self.start_y - self.start_scrolled_by
ey = self.end_y - self.end_scrolled_by
if sy == ey and self.start_x == self.end_x:
return ''
a, b = (sy, self.start_x), (ey, self.end_x)
if a > b:
a, b = b, a
def line(y):
if y < 0:
return historybuf.line(-1 - y)
return linebuf.line(y)
lines = []
for y in range(a[0], b[0] + 1):
startx, endx = 0, linebuf.xnum - 1
if y == a[0]:
startx = max(0, min(a[1], endx))
if y == b[0]:
endx = max(0, min(b[1], endx))
l = line(y)
is_continued = l.is_continued()
if endx - startx >= linebuf.xnum - 1:
l = str(l).rstrip(' ')
else:
l = ''.join(l[x] for x in range(startx, endx + 1))
if not is_continued and startx == 0 and len(lines) > 0:
l = '\n' + l
lines.append(l)
return ''.join(lines)
# }}}
@@ -312,21 +281,7 @@ class CharGrid:
s.start_scrolled_by = s.end_scrolled_by = self.scrolled_by
s.start_y = s.end_y = y
s.in_progress = False
if count == 3:
for i in range(self.screen.columns):
if line[i] != ' ':
s.start_x = i
break
else:
s.start_x = 0
for i in range(self.screen.columns):
c = self.screen.columns - 1 - i
if line[c] != ' ':
s.end_x = c
break
else:
s.end_x = self.screen.columns - 1
elif count == 2:
if count == 2:
i = x
while i >= 0 and self.word_pat.match(line[i]) is not None:
i -= 1
@@ -335,6 +290,9 @@ class CharGrid:
while i < self.screen.columns and self.word_pat.match(line[i]) is not None:
i += 1
s.end_x = i if i == x else i - 1
elif count == 3:
s.start_x, xlimit = self.screen.selection_range_for_line(y, self.scrolled_by)
s.end_x = max(s.start_x, xlimit - 1)
ps = self.text_for_selection()
if ps:
set_primary_selection(ps)
@@ -347,7 +305,8 @@ class CharGrid:
def text_for_selection(self, sel=None):
s = sel or self.current_selection
return s.text(self.screen.linebuf, self.screen.historybuf)
start, end = s.limits(self.scrolled_by, self.screen.lines, self.screen.columns)
return ''.join(self.screen.text_for_selection(self.scrolled_by, *start, *end))
def prepare_for_render(self, cell_program):
if self.vao_id is None:
@@ -356,11 +315,12 @@ class CharGrid:
self.update_cell_data(cell_program)
self.scroll_changed = False
sg = self.render_data
start, end = sel = self.current_selection.limits(self.scrolled_by, self.screen.lines, self.screen.columns)
start, end = self.current_selection.limits(self.scrolled_by, self.screen.lines, self.screen.columns)
sel = start, end, self.scrolled_by
selection_changed = sel != self.last_rendered_selection
if selection_changed:
with cell_program.mapped_vertex_data(self.vao_id, self.selection_buffer_size, bufnum=1) as address:
self.screen.apply_selection(address, start[0], start[1], end[0], end[1], self.selection_buffer_size)
self.screen.apply_selection(address, self.selection_buffer_size, self.scrolled_by, *start, *end)
self.last_rendered_selection = sel
return sg

View File

@@ -58,31 +58,38 @@ text_at(Line* self, Py_ssize_t xval) {
return line_text_at(self->cells[xval].ch & CHAR_MASK, self->cells[xval].cc);
}
static PyObject *
as_unicode(Line* self) {
Py_ssize_t n = 0;
PyObject*
unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc, char leading_char) {
size_t n = 0;
static Py_UCS4 buf[4096];
index_type xlimit = MIN(sizeof(buf)/sizeof(buf[0]), xlimit_for_line(self));
if (leading_char) buf[n++] = leading_char;
char_type previous_width = 0;
for(index_type i = 0; i < xlimit; i++) {
for(index_type i = start; i < limit && n < sizeof(buf)/sizeof(buf[0]) - 4; i++) {
char_type ch = self->cells[i].ch & CHAR_MASK;
if (ch == 0) {
if (previous_width == 2) { previous_width = 0; continue; };
ch = ' ';
}
buf[n++] = ch;
char_type cc = self->cells[i].cc;
Py_UCS4 cc1 = cc & CC_MASK, cc2;
if (cc1) {
buf[n++] = cc1;
cc2 = cc >> 16;
if (cc2) buf[n++] = cc2;
if (include_cc) {
char_type cc = self->cells[i].cc;
Py_UCS4 cc1 = cc & CC_MASK, cc2;
if (cc1) {
buf[n++] = cc1;
cc2 = cc >> 16;
if (cc2) buf[n++] = cc2;
}
}
previous_width = (self->cells[i].ch >> ATTRS_SHIFT) & WIDTH_MASK;
}
return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, n);
}
static PyObject *
as_unicode(Line* self) {
return unicode_in_range(self, 0, xlimit_for_line(self), true, 0);
}
static inline bool
write_sgr(unsigned int val, Py_UCS4 *buf, index_type buflen, index_type *i) {
static char s[20] = {0};

View File

@@ -50,10 +50,7 @@ static inline index_type
xlimit_for_line(Line *line) {
index_type xlimit = line->xnum;
if (BLANK_CHAR == 0) {
while (xlimit != 0) {
if ((line->cells[xlimit - 1].ch & CHAR_MASK) != BLANK_CHAR) break;
xlimit--;
}
while (xlimit > 0 && (line->cells[xlimit - 1].ch & CHAR_MASK) == BLANK_CHAR) xlimit--;
}
return xlimit;
}
@@ -66,6 +63,7 @@ void line_right_shift(Line *, unsigned int , unsigned int );
void line_add_combining_char(Line *, uint32_t , unsigned int );
index_type line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen);
unsigned int line_length(Line *self);
PyObject* unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc, char leading_char);
void linebuf_init_line(LineBuf *, index_type);
void linebuf_clear(LineBuf *, char_type ch);

View File

@@ -1078,23 +1078,26 @@ line(Screen *self, PyObject *val) {
return (PyObject*) self->linebuf->line;
}
static inline Line*
visual_line_(Screen *self, index_type y, index_type scrolled_by) {
if (scrolled_by) {
if (y < scrolled_by) {
historybuf_init_line(self->historybuf, scrolled_by - 1 - y, self->historybuf->line);
return self->historybuf->line;
}
y -= scrolled_by;
}
linebuf_init_line(self->linebuf, y);
return self->linebuf->line;
}
static PyObject*
visual_line(Screen *self, PyObject *args) {
// The line corresponding to the yth visual line, taking into account scrolling
unsigned int y, scrolled_by;
if (!PyArg_ParseTuple(args, "II", &y, &scrolled_by)) return NULL;
if (y >= self->lines) { Py_RETURN_NONE; }
if (scrolled_by) {
if (y < scrolled_by) {
historybuf_init_line(self->historybuf, scrolled_by - 1 - y, self->historybuf->line);
Py_INCREF(self->historybuf->line);
return (PyObject*) self->historybuf->line;
}
y -= scrolled_by;
}
linebuf_init_line(self->linebuf, y);
Py_INCREF(self->linebuf->line);
return (PyObject*) self->linebuf->line;
return Py_BuildValue("O", visual_line_(self, y, scrolled_by));
}
static PyObject*
@@ -1229,20 +1232,63 @@ update_cell_data(Screen *self, PyObject *args) {
return Py_BuildValue("OIO", cursor_changed, scrolled_by, self->modes.mDECSCNM ? Py_True : Py_False);
}
static inline bool
is_selection_empty(Screen *self, unsigned int start_x, unsigned int start_y, unsigned int end_x, unsigned int end_y) {
return (start_x >= self->columns || start_y >= self->lines || end_x >= self->columns || end_y >= self->lines || (start_x == end_x && start_y == end_y)) ? true : false;
}
static PyObject*
apply_selection(Screen *self, PyObject *args) {
unsigned int size, startx, endx, starty, endy, i, end;
unsigned int size, start_x, end_x, start_y, end_y, scrolled_by;
PyObject *l;
if (!PyArg_ParseTuple(args, "O!IIIII", &PyLong_Type, &l, &startx, &starty, &endx, &endy, &size)) return NULL;
if (startx >= self->columns || starty >= self->lines || endx >= self->columns || endy >= self->lines) { Py_RETURN_NONE; }
if (!PyArg_ParseTuple(args, "O!IIIIII", &PyLong_Type, &l, &size, &scrolled_by, &start_x, &start_y, &end_x, &end_y)) return NULL;
float *data = PyLong_AsVoidPtr(l);
memset(data, 0, size);
end = endy * self->columns + endx;
i = starty * self->columns + startx;
if (i != end) { for(; i <= end; i++) data[i] = 1; }
if (is_selection_empty(self, start_x, start_y, end_x, end_y)) { Py_RETURN_NONE; }
for (index_type y = start_y; y <= end_y; y++) {
Line *line = visual_line_(self, y, scrolled_by);
index_type xlimit = xlimit_for_line(line);
if (y == end_y) xlimit = MIN(end_x + 1, xlimit);
float *line_start = data + self->columns * y;
for (index_type x = (y == start_y ? start_x : 0); x < xlimit; x++) line_start[x] = 1.0;
}
Py_RETURN_NONE;
}
static PyObject*
text_for_selection(Screen *self, PyObject *args) {
unsigned int start_x, end_x, start_y, end_y, scrolled_by;
if (!PyArg_ParseTuple(args, "IIIII", &scrolled_by, &start_x, &start_y, &end_x, &end_y)) return NULL;
if (is_selection_empty(self, start_x, start_y, end_x, end_y)) return PyTuple_New(0);
Py_ssize_t i = 0, num_of_lines = end_y - start_y + 1;
PyObject *ans = PyTuple_New(num_of_lines);
if (ans == NULL) return PyErr_NoMemory();
for (index_type y = start_y; y <= end_y; y++, i++) {
Line *line = visual_line_(self, y, scrolled_by);
index_type xlimit = xlimit_for_line(line);
if (y == end_y) xlimit = MIN(end_x + 1, xlimit);
index_type xstart = (y == start_y ? start_x : 0);
char leading_char = i > 0 && !line->continued ? '\n' : 0;
PyObject *text = unicode_in_range(line, xstart, xlimit, true, leading_char);
if (text == NULL) { Py_DECREF(ans); return PyErr_NoMemory(); }
PyTuple_SET_ITEM(ans, i, text);
}
return ans;
}
static PyObject*
selection_range_for_line(Screen *self, PyObject *args) {
unsigned int y, scrolled_by;
if (!PyArg_ParseTuple(args, "II", &y, &scrolled_by)) return NULL;
if (y >= self->lines) { PyErr_SetString(PyExc_ValueError, "y larger than lines"); return NULL; }
Line *line = visual_line_(self, y, scrolled_by);
index_type xlimit = line->xnum, xstart = 0;
while (xlimit > 0 && CHAR_IS_BLANK(line->cells[xlimit - 1].ch)) xlimit--;
while (xstart < xlimit && CHAR_IS_BLANK(line->cells[xstart].ch)) xstart++;
return Py_BuildValue("II", (unsigned int)xstart, (unsigned int)xlimit);
}
static
PyObject* mark_as_dirty(Screen *self) {
self->is_dirty = Py_True;
@@ -1328,6 +1374,8 @@ static PyMethodDef methods[] = {
MND(resize, METH_VARARGS)
MND(set_margins, METH_VARARGS)
MND(apply_selection, METH_VARARGS)
MND(selection_range_for_line, METH_VARARGS)
MND(text_for_selection, METH_VARARGS)
MND(toggle_alt_screen, METH_NOARGS)
MND(reset_callbacks, METH_NOARGS)
{"update_cell_data", (PyCFunction)update_cell_data, METH_VARARGS, ""},