search: add more key bindings to extend the current match

This patch adds the following new search key bindings:

* extend-char (shift+right)
* extend-line-down (shift+down)
* extend-backward-char (shift+left)
* extend-backward-to-word-boundary (ctrl+shift+left)
* extend-backward-to-next-whitespace (ctrl+shift+alt+left)
* extend-line-up (shift+up)

They can be used to extend the search match (i.e. the selection).

This patch also adds an initial set of key bindings to scroll in the
scrollback history:

* scrollback-up-page
* scrollback-down-page

These work just like the key bindings for the normal mode. Also note
that it was already possible to scroll using the mouse.

This patch also fixes a couple of search mode bugs:

* crashing when a search match ends in the last column
* grapheme clusters not being matched correctly
* Search match not being "extendable" after a pointer leave event
* A few others, related to either large matches, or extending matches
  after moving the viewport.

There are still a couple of (known) issues:

* A search match isn't correctly highlighted if its *starting* point
  is outside the viewport.
* Extending the match to end of the scrollback (i.e. the most recent
  output) is simply buggy.

Related to #419
This commit is contained in:
Daniel Eklöf 2023-09-26 17:54:03 +02:00
parent 56d5d4cc21
commit 78665a7e80
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
6 changed files with 457 additions and 106 deletions

View file

@ -340,16 +340,18 @@ selection_to_text(const struct terminal *term)
return extract_finish(ctx, &text, NULL) ? text : NULL;
}
/* Coordinates are in *absolute* row numbers (NOT view local) */
void
selection_find_word_boundary_left(struct terminal *term, struct coord *pos,
selection_find_word_boundary_left(const struct terminal *term, struct coord *pos,
bool spaces_only)
{
xassert(pos->row >= 0);
xassert(pos->row < term->rows);
xassert(pos->row < term->grid->num_rows);
xassert(pos->col >= 0);
xassert(pos->col < term->cols);
const struct row *r = grid_row_in_view(term->grid, pos->row);
const struct grid *grid = term->grid;
const struct row *r = grid->rows[pos->row];
char32_t c = r->cells[pos->col].wc;
while (c >= CELL_SPACER) {
@ -373,15 +375,22 @@ selection_find_word_boundary_left(struct terminal *term, struct coord *pos,
int next_col = pos->col - 1;
int next_row = pos->row;
const struct row *row = grid_row_in_view(term->grid, next_row);
const struct row *row = grid->rows[next_row];
/* Linewrap */
if (next_col < 0) {
next_col = term->cols - 1;
if (--next_row < 0)
break;
row = grid_row_in_view(term->grid, next_row);
next_row = (next_row - 1 + grid->num_rows) & (grid->num_rows - 1);
if (grid_row_abs_to_sb(grid, term->rows, next_row) == term->grid->num_rows - 1 ||
grid->rows[next_row] == NULL)
{
/* Scrollback wrap-around */
break;
}
row = grid->rows[next_row];
if (row->linebreak) {
/* Hard linebreak, treat as space. I.e. break selection */
@ -418,17 +427,19 @@ selection_find_word_boundary_left(struct terminal *term, struct coord *pos,
}
}
/* Coordinates are in *absolute* row numbers (NOT view local) */
void
selection_find_word_boundary_right(struct terminal *term, struct coord *pos,
selection_find_word_boundary_right(const struct terminal *term, struct coord *pos,
bool spaces_only,
bool stop_on_space_to_word_boundary)
{
xassert(pos->row >= 0);
xassert(pos->row < term->rows);
xassert(pos->row < term->grid->num_rows);
xassert(pos->col >= 0);
xassert(pos->col < term->cols);
const struct row *r = grid_row_in_view(term->grid, pos->row);
const struct grid *grid = term->grid;
const struct row *r = grid->rows[pos->row];
char32_t c = r->cells[pos->col].wc;
while (c >= CELL_SPACER) {
@ -453,7 +464,7 @@ selection_find_word_boundary_right(struct terminal *term, struct coord *pos,
int next_col = pos->col + 1;
int next_row = pos->row;
const struct row *row = grid_row_in_view(term->grid, next_row);
const struct row *row = term->grid->rows[next_row];
/* Linewrap */
if (next_col >= term->cols) {
@ -463,10 +474,14 @@ selection_find_word_boundary_right(struct terminal *term, struct coord *pos,
}
next_col = 0;
if (++next_row >= term->rows)
break;
next_row = (next_row + 1) & (grid->num_rows - 1);
row = grid_row_in_view(term->grid, next_row);
if (grid_row_abs_to_sb(grid, term->rows, next_row) == 0) {
/* Scrollback wrap-around */
break;
}
row = grid->rows[next_row];
}
c = row->cells[next_col].wc;
@ -659,15 +674,15 @@ selection_start(struct terminal *term, int col, int row,
break;
case SELECTION_WORD_WISE: {
struct coord start = {col, row}, end = {col, row};
struct coord start = {col, term->grid->view + row};
struct coord end = {col, term->grid->view + row};
selection_find_word_boundary_left(term, &start, spaces_only);
selection_find_word_boundary_right(term, &end, spaces_only, true);
term->selection.coords.start = (struct coord){
start.col, term->grid->view + start.row};
term->selection.coords.start = start;
term->selection.pivot.start = term->selection.coords.start;
term->selection.pivot.end = (struct coord){end.col, term->grid->view + end.row};
term->selection.pivot.end = end;
selection_update(term, end.col, end.row);
break;
@ -1085,22 +1100,26 @@ set_pivot_point_for_block_and_char_wise(struct terminal *term,
void
selection_update(struct terminal *term, int col, int row)
{
if (term->selection.coords.start.row < 0)
if (term->selection.coords.start.row < 0) {
LOG_ERR("NO SELECTION");
return;
}
if (!term->selection.ongoing)
if (!term->selection.ongoing) {
LOG_ERR("NOT ON-GOING");
return;
LOG_DBG("selection updated: start = %d,%d, end = %d,%d -> %d, %d",
term->selection.coords.start.row, term->selection.coords.start.col,
term->selection.coords.end.row, term->selection.coords.end.col,
row, col);
}
xassert(term->grid->view + row != -1);
struct coord new_start = term->selection.coords.start;
struct coord new_end = {col, term->grid->view + row};
LOG_DBG("selection updated: start = %d,%d, end = %d,%d -> %d, %d",
term->selection.coords.start.row, term->selection.coords.start.col,
term->selection.coords.end.row, term->selection.coords.end.col,
new_end.row, new_end.col);
/* Adjust start point if the selection has changed 'direction' */
if (!(new_end.row == new_start.row && new_end.col == new_start.col)) {
enum selection_direction new_direction = term->selection.direction;
@ -1160,21 +1179,17 @@ selection_update(struct terminal *term, int col, int row)
case SELECTION_WORD_WISE:
switch (term->selection.direction) {
case SELECTION_LEFT: {
struct coord end = {col, row};
case SELECTION_LEFT:
new_end = (struct coord){col, term->grid->view + row};
selection_find_word_boundary_left(
term, &end, term->selection.spaces_only);
new_end = (struct coord){end.col, term->grid->view + end.row};
term, &new_end, term->selection.spaces_only);
break;
}
case SELECTION_RIGHT: {
struct coord end = {col, row};
case SELECTION_RIGHT:
new_end = (struct coord){col, term->grid->view + row};
selection_find_word_boundary_right(
term, &end, term->selection.spaces_only, true);
new_end = (struct coord){end.col, term->grid->view + end.row};
term, &new_end, term->selection.spaces_only, true);
break;
}
case SELECTION_UNDIR:
break;
@ -1346,16 +1361,14 @@ selection_extend_normal(struct terminal *term, int col, int row,
xassert(new_kind == SELECTION_CHAR_WISE ||
new_kind == SELECTION_WORD_WISE);
struct coord pivot_start = {new_start.col, new_start.row - term->grid->view};
struct coord pivot_start = {new_start.col, new_start.row};
struct coord pivot_end = pivot_start;
selection_find_word_boundary_left(term, &pivot_start, spaces_only);
selection_find_word_boundary_right(term, &pivot_end, spaces_only, true);
term->selection.pivot.start =
(struct coord){pivot_start.col, term->grid->view + pivot_start.row};
term->selection.pivot.end =
(struct coord){pivot_end.col, term->grid->view + pivot_end.row};
term->selection.pivot.start = pivot_start;
term->selection.pivot.end = pivot_end;
break;
}