From 90c91d61482920881bb0c1ecf4babc001b3f598d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 9 Apr 2022 17:57:29 +0200 Subject: [PATCH] search/render: initial support for highlighting all visible matches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this patch, only the currently “selected” match was highlighted (by having the “selected” attribute, and by *not* dimming it, like the rest of the grid during a scrollback search). With this patch, we now highlight matches within the viewport. While searching, only the “primary” match is searched-for, and tracked. Then, when rendering a frame, we find all “secondary” matches as well. “holes” are added to the search-mode overlay by the means of an search-match iterator. The iterator’s text matching logic is *very* similar to what we do when the search criteria has been updated, and we re-search the scrollback. It should be possible to refactor this, and share code. --- render.c | 36 ++++++++++++++++-------- search.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ search.h | 8 ++++++ 3 files changed, 117 insertions(+), 11 deletions(-) diff --git a/render.c b/render.c index 8093b747..5a05e354 100644 --- a/render.c +++ b/render.c @@ -35,6 +35,7 @@ #include "hsl.h" #include "ime.h" #include "quirks.h" +#include "search.h" #include "selection.h" #include "shm.h" #include "sixel.h" @@ -1539,17 +1540,30 @@ render_overlay(struct terminal *term) pixman_region32_clear(see_through); - struct grid *grid = term->grid; - for (int r = 0; r < term->rows; r++) { - struct row *row = grid_row_in_view(grid, r); - int y = term->margins.top + r * term->cell_height; - for (int c = 0; c < term->cols; c++) { - if (row->cells[c].attrs.selected) { - int x = term->margins.left + c * term->cell_width; - pixman_region32_union_rect( - see_through, see_through, - x, y, term->cell_width, term->cell_height); - } + struct search_match_iterator iter = search_matches_new_iter(term); + + for (struct range match = search_matches_next(&iter); + match.start.row >= 0; + match = search_matches_next(&iter)) + { + int r = match.start.row; + int start_col = match.start.col; + const int end_row = match.end.row; + + while (true) { + const int end_col = + r == end_row ? match.end.col : term->cols - 1; + + int x = term->margins.left + start_col * term->cell_width; + int y = term->margins.top + r * term->cell_height; + int width = (end_col + 1 - start_col) * term->cell_width; + int height = 1 * term->cell_height; + + pixman_region32_union_rect( + see_through, see_through, x, y, width, height); + + if (++r > end_row) + break; } } diff --git a/search.c b/search.c index 69dbcccd..52849502 100644 --- a/search.c +++ b/search.c @@ -409,6 +409,90 @@ search_find_next(struct terminal *term) #undef ROW_DEC } +struct search_match_iterator +search_matches_new_iter(struct terminal *term) +{ + return (struct search_match_iterator){ + .term = term, + .start = {0, 0}, + }; +} + +struct range +search_matches_next(struct search_match_iterator *iter) +{ + xassert(iter->start.row >= 0); + xassert(iter->start.col >= 0); + + struct terminal *term = iter->term; + //xassert(term->is_searching); + + if (term->search.match_len == 0) + goto done; + + int start_col = iter->start.col; + + for (int r = iter->start.row; r < term->rows; r++) { + const struct row *row = grid_row_in_view(term->grid, r); + + for (int c = start_col; c < term->cols; c++) { + if (matches_cell(term, &row->cells[c], 0) < 0) + continue; + + struct range match = { + .start = {c, r}, + .end = {-1, -1}, + }; + size_t match_len = 0; + + for (size_t i = 0; i < term->search.len;) { + if (c >= term->cols) { + if (++r >= term->rows) + break; + + row = grid_row_in_view(term->grid, r); + c = 0; + } + + if (row->cells[c].wc >= CELL_SPACER) { + c++; + continue; + } + + ssize_t additional_chars = matches_cell(term, &row->cells[c], i); + if (additional_chars < 0) + break; + + i += additional_chars; + match_len += additional_chars; + c++; + } + + if (match_len != term->search.len) { + /* Didn't match (completely) */ + continue; + } + + match.end = (struct coord){c - 1, r}; + + LOG_DBG("match at %dx%d-%dx%d", + match.start.row, match.start.col, + match.end.row, match.end.col); + + iter->start.row = r; + iter->start.col = c; + return match; + } + + start_col = 0; + } + +done: + iter->start.row = -1; + iter->start.col = -1; + return (struct range){{-1, -1}, {-1, -1}}; +} + static void add_wchars(struct terminal *term, char32_t *src, size_t count) { diff --git a/search.h b/search.h index 18af0d62..24828512 100644 --- a/search.h +++ b/search.h @@ -14,3 +14,11 @@ void search_input( void search_add_chars(struct terminal *term, const char *text, size_t len); void search_selection_cancelled(struct terminal *term); + +struct search_match_iterator { + struct terminal *term; + struct coord start; +}; + +struct search_match_iterator search_matches_new_iter(struct terminal *term); +struct range search_matches_next(struct search_match_iterator *iter);