mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-03-14 05:33:59 -04:00
search: wip: initial search matching
* match search buffer against scrollback content * adjust view to ensure matched content is visible * create selection on a successful match * finalize selection when user presses enter (to "commit" the search) * ctrl+r searches for the next match. Needs more work though.
This commit is contained in:
parent
61cabdac13
commit
aee5045395
3 changed files with 187 additions and 3 deletions
16
grid.h
16
grid.h
|
|
@ -7,12 +7,24 @@ void grid_swap_row(struct grid *grid, int row_a, int row_b, bool initialize);
|
||||||
struct row *grid_row_alloc(int cols, bool initialize);
|
struct row *grid_row_alloc(int cols, bool initialize);
|
||||||
void grid_row_free(struct row *row);
|
void grid_row_free(struct row *row);
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
grid_row_absolute(const struct grid *grid, int row_no)
|
||||||
|
{
|
||||||
|
return (grid->offset + row_no) & (grid->num_rows - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
grid_row_absolute_in_view(const struct grid *grid, int row_no)
|
||||||
|
{
|
||||||
|
return (grid->view + row_no) & (grid->num_rows - 1);
|
||||||
|
}
|
||||||
|
|
||||||
static inline struct row *
|
static inline struct row *
|
||||||
_grid_row_maybe_alloc(struct grid *grid, int row_no, bool alloc_if_null)
|
_grid_row_maybe_alloc(struct grid *grid, int row_no, bool alloc_if_null)
|
||||||
{
|
{
|
||||||
assert(grid->offset >= 0);
|
assert(grid->offset >= 0);
|
||||||
|
|
||||||
int real_row = (grid->offset + row_no) & (grid->num_rows - 1);
|
int real_row = grid_row_absolute(grid, row_no);
|
||||||
struct row *row = grid->rows[real_row];
|
struct row *row = grid->rows[real_row];
|
||||||
|
|
||||||
if (row == NULL && alloc_if_null) {
|
if (row == NULL && alloc_if_null) {
|
||||||
|
|
@ -41,7 +53,7 @@ grid_row_in_view(struct grid *grid, int row_no)
|
||||||
{
|
{
|
||||||
assert(grid->view >= 0);
|
assert(grid->view >= 0);
|
||||||
|
|
||||||
int real_row = (grid->view + row_no) & (grid->num_rows - 1);
|
int real_row = grid_row_absolute_in_view(grid, row_no);
|
||||||
struct row *row = grid->rows[real_row];
|
struct row *row = grid->rows[real_row];
|
||||||
|
|
||||||
assert(row != NULL);
|
assert(row != NULL);
|
||||||
|
|
|
||||||
169
search.c
169
search.c
|
|
@ -7,6 +7,8 @@
|
||||||
#define LOG_ENABLE_DBG 1
|
#define LOG_ENABLE_DBG 1
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "render.h"
|
#include "render.h"
|
||||||
|
#include "selection.h"
|
||||||
|
#include "grid.h"
|
||||||
|
|
||||||
void
|
void
|
||||||
search_begin(struct terminal *term)
|
search_begin(struct terminal *term)
|
||||||
|
|
@ -17,6 +19,10 @@ search_begin(struct terminal *term)
|
||||||
term->search.buf = NULL;
|
term->search.buf = NULL;
|
||||||
term->search.len = 0;
|
term->search.len = 0;
|
||||||
term->search.sz = 0;
|
term->search.sz = 0;
|
||||||
|
term->search.match = (struct coord){-1, -1};
|
||||||
|
term->search.match_len = 0;
|
||||||
|
term->search.original_view = term->grid->view;
|
||||||
|
term->search.view_followed_offset = term->grid->view == term->grid->offset;
|
||||||
term->is_searching = true;
|
term->is_searching = true;
|
||||||
|
|
||||||
render_refresh(term);
|
render_refresh(term);
|
||||||
|
|
@ -36,6 +42,133 @@ search_cancel(struct terminal *term)
|
||||||
render_refresh(term);
|
render_refresh(term);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
search_update(struct terminal *term)
|
||||||
|
{
|
||||||
|
if (term->search.len == 0) {
|
||||||
|
term->search.match = (struct coord){-1, -1};
|
||||||
|
term->search.match_len = 0;
|
||||||
|
selection_cancel(term);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int start_row = term->search.match.row;
|
||||||
|
int start_col = term->search.match.col;
|
||||||
|
size_t len = term->search.match_len;
|
||||||
|
|
||||||
|
assert((len == 0 && start_row == -1 && start_col == -1) ||
|
||||||
|
(len > 0 && start_row >= 0 && start_col >= 0));
|
||||||
|
|
||||||
|
if (len == 0) {
|
||||||
|
start_row = grid_row_absolute(term->grid, term->rows - 1);
|
||||||
|
start_col = term->cols - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("search: update: starting at row=%d col=%d", start_row, start_col);
|
||||||
|
|
||||||
|
/* Scan backward from current end-of-output */
|
||||||
|
for (; start_row >= 0; start_row--) {
|
||||||
|
const struct row *row = term->grid->rows[start_row];
|
||||||
|
if (row == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
for (; start_col >= 0; start_col--) {
|
||||||
|
if (row->cells[start_col].wc != term->search.buf[0])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Got a match on the first letter. Now we'll see if the
|
||||||
|
* rest of the search buffer matches.
|
||||||
|
*/
|
||||||
|
|
||||||
|
LOG_DBG("search: initial match at row=%d, col=%d", start_row, start_col);
|
||||||
|
|
||||||
|
int end_row = start_row;
|
||||||
|
int end_col = start_col;
|
||||||
|
size_t match_len = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < term->search.len; i++, match_len++) {
|
||||||
|
if (row->cells[end_col].wc != term->search.buf[i])
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (++end_col >= term->cols) {
|
||||||
|
if (end_row + 1 > grid_row_absolute(term->grid, term->grid->offset + term->rows - 1)) {
|
||||||
|
/* Don't continue past end of the world */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
end_row++;
|
||||||
|
end_col = 0;
|
||||||
|
row = term->grid->rows[end_row];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match_len != term->search.len) {
|
||||||
|
/* Didn't match (completely) */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We matched the entire buffer. Move view to ensure the
|
||||||
|
* match is visible, create a selection and return.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int old_view = term->grid->view;
|
||||||
|
int new_view = start_row;
|
||||||
|
|
||||||
|
/* Prevent scrolling in uninitialized rows */
|
||||||
|
bool all_initialized = false;
|
||||||
|
do {
|
||||||
|
all_initialized = true;
|
||||||
|
|
||||||
|
for (int i = 0; i < term->rows; i++) {
|
||||||
|
int row_no = (new_view + i) % term->grid->num_rows;
|
||||||
|
if (term->grid->rows[row_no] == NULL) {
|
||||||
|
all_initialized = false;
|
||||||
|
new_view--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (!all_initialized);
|
||||||
|
|
||||||
|
/* Don't scroll past scrollback history */
|
||||||
|
int end = (term->grid->offset + term->rows - 1) % term->grid->num_rows;
|
||||||
|
if (end >= term->grid->offset) {
|
||||||
|
/* Not wrapped */
|
||||||
|
if (new_view >= term->grid->offset && new_view <= end)
|
||||||
|
new_view = term->grid->offset;
|
||||||
|
} else {
|
||||||
|
if (new_view >= term->grid->offset || new_view <= end)
|
||||||
|
new_view = term->grid->offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update view */
|
||||||
|
term->grid->view = new_view;
|
||||||
|
if (new_view != old_view)
|
||||||
|
term_damage_view(term);
|
||||||
|
|
||||||
|
/* Selection endpoint is inclusive */
|
||||||
|
if (--end_col < 0) {
|
||||||
|
end_col = term->cols - 1;
|
||||||
|
start_row--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Begin a selection (it's finalized when "committing" the search) */
|
||||||
|
selection_start(term, start_col, start_row - term->grid->view);
|
||||||
|
selection_update(term, end_col, end_row - term->grid->view);
|
||||||
|
|
||||||
|
/* Update match state */
|
||||||
|
term->search.match.row = start_row;
|
||||||
|
term->search.match.col = start_col;
|
||||||
|
term->search.match_len = match_len;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
start_col = term->cols - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, xkb_mod_mask_t mods)
|
search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, xkb_mod_mask_t mods)
|
||||||
{
|
{
|
||||||
|
|
@ -49,8 +182,41 @@ search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, xkb_mod_mask
|
||||||
enum xkb_compose_status compose_status = xkb_compose_state_get_status(
|
enum xkb_compose_status compose_status = xkb_compose_state_get_status(
|
||||||
term->kbd.xkb_compose_state);
|
term->kbd.xkb_compose_state);
|
||||||
|
|
||||||
if ((mods == 0 && sym == XKB_KEY_Escape) || (mods == ctrl && sym == XKB_KEY_g))
|
/* Cancel search */
|
||||||
|
if ((mods == 0 && sym == XKB_KEY_Escape) ||
|
||||||
|
(mods == ctrl && sym == XKB_KEY_g))
|
||||||
|
{
|
||||||
|
if (term->search.view_followed_offset)
|
||||||
|
term->grid->view = term->grid->offset;
|
||||||
|
else
|
||||||
|
term->grid->view = term->search.original_view;
|
||||||
|
term_damage_view(term);
|
||||||
search_cancel(term);
|
search_cancel(term);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* "Commit" search - copy selection to primary and cancel search */
|
||||||
|
else if (mods == 0 && sym == XKB_KEY_Return) {
|
||||||
|
selection_finalize(term, term->input_serial);
|
||||||
|
search_cancel(term);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (mods == ctrl && sym == XKB_KEY_r) {
|
||||||
|
if (term->search.match_len > 0) {
|
||||||
|
int new_col = term->search.match.col - 1;
|
||||||
|
int new_row = term->search.match.row;
|
||||||
|
|
||||||
|
if (new_col < 0) {
|
||||||
|
new_col = term->cols - 1;
|
||||||
|
new_row--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_row >= 0) {
|
||||||
|
term->search.match.col = new_col;
|
||||||
|
term->search.match.row = new_row;
|
||||||
|
search_update(term);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else if (mods == 0 && sym == XKB_KEY_BackSpace) {
|
else if (mods == 0 && sym == XKB_KEY_BackSpace) {
|
||||||
if (term->search.len > 0)
|
if (term->search.len > 0)
|
||||||
|
|
@ -103,5 +269,6 @@ search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, xkb_mod_mask
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DBG("search: buffer: %S", term->search.buf);
|
LOG_DBG("search: buffer: %S", term->search.buf);
|
||||||
|
search_update(term);
|
||||||
render_refresh(term);
|
render_refresh(term);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -336,6 +336,11 @@ struct terminal {
|
||||||
wchar_t *buf;
|
wchar_t *buf;
|
||||||
size_t len;
|
size_t len;
|
||||||
size_t sz;
|
size_t sz;
|
||||||
|
|
||||||
|
int original_view;
|
||||||
|
bool view_followed_offset;
|
||||||
|
struct coord match;
|
||||||
|
size_t match_len;
|
||||||
} search;
|
} search;
|
||||||
|
|
||||||
struct grid normal;
|
struct grid normal;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue