editor: keep wrapped search matches visible with context - #3013
Conversation
Code reviewNo blocking issues found. Checked for bugs, CLAUDE.md compliance, git history context, and comment consistency. The root cause fix looks right: Two optional, non-blocking notes:
gpui-kit/crates/base/src/input/base/element.rs Lines 463 to 468 in 9c3e6be gpui-kit/crates/base/src/input/base/state.rs Lines 2244 to 2249 in 9c3e6be
gpui-kit/crates/base/src/input/base/state.rs Lines 2238 to 2241 in 9c3e6be 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
|
Thanks |
…3013) ## Description Keep search matches visible with surrounding context when long lines wrap. Resolve matches to their display rows, including offscreen matches, and calculate padding from viewport capacity instead of visible logical-line count. ## Screenshot | Before | After | | ------ | ----- | | <img width="592" height="863" alt="Screenshot 2026-09-08 at 7 33 40 PM" src="https://github.com/user-attachments/assets/cafa15d3-d78f-460f-a200-e5f0f2fd913b" /> | <img width="592" height="863" alt="Screenshot 2026-09-08 at 7 34 56 PM" src="https://github.com/user-attachments/assets/d0ee0ddf-3d00-41df-834f-5f11a10502f6" /> | ## How to Test - `cargo test -p gpui-base --lib input:: --offline` — 220 tests passed, including the wrapped-match regression test. - `cargo run -p example-editor --offline` — built and launched on macOS. - With soft wrapping enabled, search for a word near the end of a long offscreen line. Navigate to the match and verify it stays visible with surrounding rows. E.g. `"path": "relative_path:asdfasfafadsf[?(@['username'] == 237842985798579834534536363365")]"` ## Checklist - [x] I have read the [CONTRIBUTING](../CONTRIBUTING.md) document and followed the guidelines. - [ ] Reviewed the changes in this PR and confirmed AI generated code (If any) is accurate. - [x] Passed `cargo run` for story tests related to the changes. - [ ] Tested macOS, Windows and Linux platforms performance (if the change is platform-specific) --------- Co-authored-by: Jason Lee <huacnlee@gmail.com>
The search state and the public search API this branch introduced were implemented on main in the meantime (longbridge#2691, longbridge#2716, longbridge#2932, longbridge#2953, longbridge#2955, longbridge#3013), so the conflicting files take main's version. The example and the documentation are ported to the new API in follow-up commits.
## Description An application that wants its own search UI on the editor — keeping the editor's matching, highlighting, scrolling and replacing, but drawing the search bar itself — had no supported way to do it: search could only be switched on or off with `.searchable(true)`, and everything else lived inside the built-in `SearchPanel`. Since this PR was opened, the engine moved into `gpui-base` and its API became public on the editor state (#2691, #2716, #2932, #2953, #2955, #3013): `set_search_query`, `close_search`, `next_search_match`, `previous_search_match`, the replace methods and `search_session()`. This PR, rebased onto that, closes the gaps that still tied a search to the built-in panel and documents the custom-UI path. ### Highlights without the panel Match highlights were painted only while `search_session.open`, which also shows the panel, so a query set through `set_search_query` counted matches the editor never showed. `SearchSession` now tracks `active` separately from `open`: opening the panel or setting a query activates the session, `close_search` ends it, and the element highlights while it is active. The panel keeps keying its own visibility on `open`. `SearchSession` is `#[non_exhaustive]`, since it gains fields; build it with `Default`. ### The shortcut reaches the application An editor that is not `searchable` swallowed `Search` and `Replace` instead of propagating them, so a parent view could not bind `Ctrl-F` / `Cmd-F` to its own field. Both handlers now `cx.propagate()` when the editor is not searchable. `searchable` is no longer `#[doc(hidden)]` and is documented for what it gates: the built-in panel and its shortcut, not the search API. ### `current()` `SearchMatcher::current()` reports the current match as `Option<usize>`, so "no match" is not mistaken for the first one; `current_match_index()` stays. ### Docs The Editor guide gains a "Custom search UI" section, in English and Chinese: `set_search_query` from the application's own field, navigating and describing the matches, replacing, ending with `close_search`, and taking `Ctrl-F` / `Cmd-F` on the view that owns the field. ## Breaking Changes None. `SearchSession` is now `#[non_exhaustive]`, so it must be built with `Default` rather than a struct literal; no code in this repository or its examples did otherwise. ## How to Test - `cargo test -p gpui-base -p gpui-component --lib -- search`: covers the session staying active after `set_search_query` without the panel, the shortcut reaching a host view when the editor is not searchable and opening the panel when it is, and the match label/`current()` semantics. - `cargo run -p example-editor`: the built-in panel behaves as before, including resuming its last query on reopen. The rebase, the Base changes and the documentation were written with Claude Code and reviewed by hand; the original API proposal is by @calmondev. --------- Co-authored-by: Jason Lee <huacnlee@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Description
Keep search matches visible with surrounding context when long lines wrap. Resolve matches to their display rows, including offscreen matches, and calculate padding from viewport capacity instead of visible logical-line count.
Screenshot
How to Test
cargo test -p gpui-base --lib input:: --offline— 220 tests passed, including the wrapped-match regression test.cargo run -p example-editor --offline— built and launched on macOS."path": "relative_path:asdfasfafadsf[?(@['username'] == 237842985798579834534536363365")]"Checklist
cargo runfor story tests related to the changes.