diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ca77fb5..ea3d2d24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,8 @@ - `DECSTBM` - _Set Top and Bottom Margins_ - `SGR` - _Set Graphic Rendition_ - `DECSCUSR` - _Set Cursor Style_ +* Support for searching for the last searched-for string in scrollback + search (search for next/prev match with an empty search string). ### Changed diff --git a/doc/foot.1.scd b/doc/foot.1.scd index 6843af1e..e990ca0d 100644 --- a/doc/foot.1.scd +++ b/doc/foot.1.scd @@ -160,13 +160,12 @@ In all other cases, the exit code is that of the client application # KEYBOARD SHORTCUTS -The following keyboard shortcuts are available. +The following keyboard shortcuts are available by default. They can be +changed in *foot.ini*(5). There are also more actions (disabled by +default) available; see *foot.ini*(5). ## NORMAL MODE -Note that these are just the defaults; they can be changed in -*foot.ini(5)*. - *shift*+*page up*/*page down* Scroll up/down in history @@ -203,10 +202,12 @@ Note that these are just the defaults; they can be changed in ## SCROLLBACK SEARCH *ctrl*+*r* - Search _backward_ for the next match + Search _backward_ for the next match. If the search string is + empty, the last searched-for string is used. *ctrl*+*s* - Search _forward_ for the next match + Search _forward_ for the next match. If the search string is + empty, the last searched-for string is used. *ctrl*+*w* Extend current selection (and thus the search criteria) to the end diff --git a/search.c b/search.c index 817f18b5..8c5051af 100644 --- a/search.c +++ b/search.c @@ -94,10 +94,16 @@ search_cancel_keep_selection(struct terminal *term) struct wl_window *win = term->window; wayl_win_subsurface_destroy(&win->search); - free(term->search.buf); + if (term->search.len > 0) { + free(term->search.last.buf); + term->search.last.buf = term->search.buf; + term->search.last.len = term->search.len; + } else + free(term->search.buf); + term->search.buf = NULL; - term->search.len = 0; - term->search.sz = 0; + term->search.len = term->search.sz = 0; + term->search.cursor = 0; term->search.match = (struct coord){-1, -1}; term->search.match_len = 0; @@ -398,6 +404,33 @@ search_find_next(struct terminal *term) #undef ROW_DEC } +static void +add_wchars(struct terminal *term, wchar_t *src, size_t count) +{ + /* Strip non-printable characters */ + for (size_t i = 0, j = 0, orig_count = count; i < orig_count; i++) { + if (iswprint(src[i])) + src[j++] = src[i]; + else + count--; + } + + if (!search_ensure_size(term, term->search.len + count)) + return; + + xassert(term->search.len + count < term->search.sz); + + memmove(&term->search.buf[term->search.cursor + count], + &term->search.buf[term->search.cursor], + (term->search.len - term->search.cursor) * sizeof(wchar_t)); + + memcpy(&term->search.buf[term->search.cursor], src, count * sizeof(wchar_t)); + + term->search.len += count; + term->search.cursor += count; + term->search.buf[term->search.len] = L'\0'; +} + void search_add_chars(struct terminal *term, const char *src, size_t count) { @@ -414,29 +447,7 @@ search_add_chars(struct terminal *term, const char *src, size_t count) ps = (mbstate_t){0}; wchar_t wcs[wchars + 1]; mbsnrtowcs(wcs, &_src, count, wchars, &ps); - - /* Strip non-printable characters */ - for (size_t i = 0, j = 0, orig_wchars = wchars; i < orig_wchars; i++) { - if (iswprint(wcs[i])) - wcs[j++] = wcs[i]; - else - wchars--; - } - - if (!search_ensure_size(term, term->search.len + wchars)) - return; - - xassert(term->search.len + wchars < term->search.sz); - - memmove(&term->search.buf[term->search.cursor + wchars], - &term->search.buf[term->search.cursor], - (term->search.len - term->search.cursor) * sizeof(wchar_t)); - - memcpy(&term->search.buf[term->search.cursor], wcs, wchars * sizeof(wchar_t)); - - term->search.len += wchars; - term->search.cursor += wchars; - term->search.buf[term->search.len] = L'\0'; + add_wchars(term, wcs, wchars); } static void @@ -625,7 +636,15 @@ execute_binding(struct seat *seat, struct terminal *term, return true; case BIND_ACTION_SEARCH_FIND_PREV: - if (term->search.match_len > 0) { + if (term->search.last.buf != NULL && term->search.len == 0) { + add_wchars(term, term->search.last.buf, term->search.last.len); + + free(term->search.last.buf); + term->search.last.buf = NULL; + term->search.last.len = 0; + } + + else if (term->search.match_len > 0) { int new_col = term->search.match.col - 1; int new_row = term->search.match.row; @@ -643,7 +662,15 @@ execute_binding(struct seat *seat, struct terminal *term, return true; case BIND_ACTION_SEARCH_FIND_NEXT: - if (term->search.match_len > 0) { + if (term->search.last.buf != NULL && term->search.len == 0) { + add_wchars(term, term->search.last.buf, term->search.last.len); + + free(term->search.last.buf); + term->search.last.buf = NULL; + term->search.last.len = 0; + } + + else if (term->search.match_len > 0) { int new_col = term->search.match.col + 1; int new_row = term->search.match.row; diff --git a/terminal.c b/terminal.c index c639d3d1..84a43290 100644 --- a/terminal.c +++ b/terminal.c @@ -1638,6 +1638,7 @@ term_destroy(struct terminal *term) &term->custom_glyphs.legacy, GLYPH_LEGACY_COUNT); free(term->search.buf); + free(term->search.last.buf); if (term->render.workers.threads != NULL) { for (size_t i = 0; i < term->render.workers.count; i++) { diff --git a/terminal.h b/terminal.h index e6c82f41..1ff4b3b6 100644 --- a/terminal.h +++ b/terminal.h @@ -514,6 +514,11 @@ struct terminal { bool view_followed_offset; struct coord match; size_t match_len; + + struct { + wchar_t *buf; + size_t len; + } last; } search; struct wayland *wl;