Fix line-at-once selection not extending wrapped lines into scrollback

Fixes #9437
This commit is contained in:
Kovid Goyal 2026-01-30 20:29:33 +05:30
parent bc5c349d53
commit 625e984b12
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
3 changed files with 59 additions and 4 deletions

View file

@ -208,6 +208,8 @@ Detailed list of changes
- kitten choose-files: Fix TAB completion in the choose save file name prompt
not working with respect to the current working directory (:iss:`9387`)
- Fix line-at-once selection not extending wrapped lines into scrollback (:iss:`9437`)
0.45.0 [2025-12-24]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View file

@ -4922,14 +4922,18 @@ cell_is_blank(const CPUCell *c) {
return !cell_has_text(c) || cell_is_char(c, ' ');
}
bool
screen_selection_range_for_line(Screen *self, index_type y, index_type *start, index_type *end) {
if (y >= self->lines) { return false; }
Line *line = visual_line_(self, y);
static void
screen_selection_range_for_line_(Line *line, index_type *start, index_type *end) {
index_type xlimit = line->xnum, xstart = 0;
while (xlimit > 0 && cell_is_blank(line->cpu_cells + xlimit - 1)) xlimit--;
while (xstart < xlimit && cell_is_blank(line->cpu_cells + xstart)) xstart++;
*start = xstart; *end = xlimit > 0 ? xlimit - 1 : 0;
}
bool
screen_selection_range_for_line(Screen *self, index_type y, index_type *start, index_type *end) {
if (y >= self->lines) { return false; }
screen_selection_range_for_line_(visual_line_(self, y), start, end);
return true;
}
@ -5260,6 +5264,18 @@ continue_line_upwards(Screen *self, index_type top_line, SelectionBoundary *star
return top_line;
}
static index_type
continue_line_upwards_scrollback(Screen *self, int top_line, SelectionBoundary *start, SelectionBoundary *end) {
index_type num_in_scrollback = 0;
Line *line = NULL;
while (range_line_is_continued(self, top_line) && (line = range_line_(self, top_line-1))) {
screen_selection_range_for_line_(line, &start->x, &end->x) ;
top_line--; num_in_scrollback++;
}
return num_in_scrollback;
}
static index_type
continue_line_downwards(Screen *self, index_type bottom_line, SelectionBoundary *start, SelectionBoundary *end) {
while (bottom_line + 1 < self->lines && visual_line_is_continued(self, bottom_line + 1)) {
@ -5400,6 +5416,15 @@ do_update_selection(Screen *self, Selection *s, index_type x, index_type y, bool
} else {
top_line = continue_line_upwards(self, top_line, &up_start, &up_end);
S;
// extend into scrollback if needed
if (top_line == 0 && self->linebuf == self->main_linebuf) {
index_type num_in_scrollback = continue_line_upwards_scrollback(
self, top_line, &up_start, &up_end);
if (num_in_scrollback) {
s->start_scrolled_by += num_in_scrollback;
s->start.x = up_start.x;
}
}
}
}
#undef S
@ -5413,6 +5438,15 @@ do_update_selection(Screen *self, Selection *s, index_type x, index_type y, bool
if (!s->adjusting_start) { a = &s->end; b = &s->start; }
if (adjusted_boundary_is_before) {
a->in_left_half_of_cell = true; a->x = up_start.x; a->y = top_line;
// extend into scrollback if needed
if (top_line == 0 && self->linebuf == self->main_linebuf) {
index_type num_in_scrollback = continue_line_upwards_scrollback(
self, top_line, &up_start, &up_end);
if (num_in_scrollback) {
s->start_scrolled_by += num_in_scrollback;
s->start.x = up_start.x;
}
}
} else {
a->in_left_half_of_cell = false; a->x = down_end.x; a->y = bottom_line;
}

View file

@ -235,6 +235,25 @@ class TestMouse(BaseTest):
self.ae(sel(), ' 123\n 456')
release(button=GLFW_MOUSE_BUTTON_RIGHT)
# line select for wrapped lines in scrollback
s.reset()
s.draw('ABCDE12345')
s.linefeed(), s.carriage_return()
s.draw(('X' * s.columns) * (s.lines-1))
multi_click(x=1, count=3)
self.ae(sel(), 'ABCDE12345')
s.reset()
s.draw('ABCDE12345')
s.linefeed(), s.carriage_return()
s.draw('678')
s.linefeed(), s.carriage_return()
s.draw(('X' * s.columns) * (s.lines-2))
multi_click(x=1, y=1, count=3)
self.ae(sel(), '678')
press(x=2, button=GLFW_MOUSE_BUTTON_RIGHT)
release(x=2, button=GLFW_MOUSE_BUTTON_RIGHT)
self.ae(sel(), 'ABCDE12345\n678')
# Rectangle select
init()
press(x=1, y=1, modifiers=GLFW_MOD_ALT | GLFW_MOD_CONTROL)