Skip to content

text_view: Add range highlights - #3215

Merged
huacnlee merged 6 commits into
longbridge:mainfrom
white07S:text-view-range-highlights
Sep 24, 2026
Merged

huacnlee merged 6 commits into
longbridge:mainfrom
white07S:text-view-range-highlights

Conversation

@white07S

@white07S white07S commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Refs #3214

Description

Adds range highlights to the Markdown TextView. This covers highlighting in #3214; scrolling to a range will follow in a separate PR.

Applications can search rendered_text() and paint results or citations behind the rendered text without reparsing the document. Ranges use UTF-8 byte offsets into the text produced by plain copy, so a search for hello world finds hello **world**, and repeated text is identified by position. Highlights do not change layout, selection, or copy behavior.

Highlights remain on unchanged text when content updates, including appended text during streaming. After an edit inside a table, cells in and after the edited row lose their highlights because cells are identified by position. The Markdown example now has a “Find in preview” field and match count to demonstrate the API.

Public API

Added in gpui-base and re-exported from gpui_component::text:

  • TextViewState::rendered_text(&self) -> RenderedText gives applications the visible text to search when computing highlight ranges. The setter itself reads the current text internally.
  • TextViewState::set_range_highlights(&mut self, highlights: impl IntoIterator<Item = RangeHighlight>, cx: &mut Context<Self>) -> Result<(), RangeHighlightError> replaces highlights for the current rendered text; compute the ranges and set them in the same state update.
  • TextViewState::clear_range_highlights(&mut self, cx: &mut Context<Self>) removes all highlights.
  • RenderedText provides as_str(&self) -> &str, len(&self) -> usize, and is_empty(&self) -> bool. Its view/revision identity is used only for equality, allowing observers such as the Markdown example to skip searching unchanged text again.
  • RangeHighlight::new(range: Range<usize>, background: impl Into<Hsla>) -> Self creates a highlight with a required background color; range(&self) -> Range<usize> and background(&self) -> Hsla expose its values.
  • RangeHighlightError reports Unsupported (HTML views) or InvalidRange(usize); rejected updates leave existing highlights intact.

How to Test

  • Run cargo test -p gpui-base --lib text:: for rendered-text, range validation, update retention, and paint behavior.
  • Run cargo run -p example-markdown, then type into “Find in preview” at the bottom. Matches should be highlighted in the preview and the count should update.

Checklist

  • I have read the CONTRIBUTING document and followed the guidelines.
  • Reviewed the changes in this PR and confirmed AI generated code (If any) is accurate.
  • Passed cargo run for story tests related to the changes.
  • Tested macOS, Windows and Linux platforms performance (if the change is platform-specific)

Let applications highlight ranges of the rendered text, e.g. search
results, without reparsing the document.

Refs longbridge#3214
huacnlee
huacnlee previously approved these changes Sep 24, 2026
@huacnlee

Copy link
Copy Markdown
Member

@white07S I simplified the range highlight API in 1c2458b: removed set_range_highlights_for_snapshot and its stale/foreign errors, kept rendered_text() for computing offsets in the rendered text, and updated the docs and PR description. The gpui-base text tests and Markdown example check pass. Could you review the updates and let me know if the API matches what you intended?

@white07S

Copy link
Copy Markdown
Contributor Author

@huacnlee Thanks, that matches what I intended, and dropping the snapshot argument makes it simpler to use. Two small things: the RangeHighlight doc comment now ends mid-sentence ("…paints over the earlier. A"), and it might be worth one line in the docs that an app searching outside the update can compare rendered_text() before setting. I'll update #3216 so reveal_range follows the same shape (reveal_range(range, cx)).

@huacnlee
huacnlee merged commit aa2c3f7 into longbridge:main Sep 24, 2026
12 checks passed
@white07S
white07S deleted the text-view-range-highlights branch September 25, 2026 12:06
huacnlee added a commit that referenced this pull request Sep 25, 2026
Closes #3214

Follows #3215, which added the range highlights this builds on.

## Description

Adds `reveal_range`, which scrolls the line where a range starts into
view, for example the current search result, or a line deep inside a
long paragraph.

It works in a scrollable `TextView` and inside an app's `gpui::list`,
such as a chat. Other scroll containers, like a `div` with
`overflow_y_scroll`, can use the new `TextView::on_reveal` callback. It
gets the line's position, so the app can scroll its own container.

It doesn't scroll when the line is already visible, and it follows the
content the same way highlights do. It also gives up after a second if
the line can't be shown, so it never jumps late.

The markdown example gets previous and next buttons. Enter and
Shift+Enter in the find field do the same.

Not covered: text scrolled sideways inside a table, and a scrollable
`TextView` inside an app list, which only scrolls itself.

Also fixes a `RangeHighlight` doc comment that was cut off mid-sentence
in #3215.

## Public API

### gpui-base

Also available on the `gpui_component::text::TextView` wrapper.

- `TextViewState::reveal_range(&mut self, range: Range<usize>, cx: &mut
Context<Self>) -> Result<(), RangeHighlightError>`: scrolls the line
where `range` starts into view. Like `set_range_highlights`, `range`
points into the current `rendered_text()`. It's best effort: `Ok(())`
means the request was taken, not that the view has scrolled.
- `TextView::on_reveal(self, handler: impl Fn(Bounds<Pixels>, &mut
Window, &mut App) + 'static) -> Self`: lets a container that ignores
scroll requests follow a reveal, using the line's bounds in window
coordinates.

## How to Test

- `cargo test -p gpui-base --lib text::`
- `cargo run -p example-markdown`, search in the "Find in preview"
field, then step through the matches with the arrows or Enter /
Shift+Enter.

## Checklist

- [x] I have read the [CONTRIBUTING](../CONTRIBUTING.md) document and
followed the guidelines.
- [x] Reviewed the changes in this PR and confirmed AI generated code
(If any) is accurate.
- [ ] 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>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants