Skip to content

fix: Highlight focused AI block matches under async find#11205

Open
vkodithala wants to merge 2 commits into
masterfrom
varoon/async-find-ai-highlights
Open

fix: Highlight focused AI block matches under async find#11205
vkodithala wants to merge 2 commits into
masterfrom
varoon/async-find-ai-highlights

Conversation

@vkodithala
Copy link
Copy Markdown
Contributor

@vkodithala vkodithala commented May 18, 2026

Description

Highlights focused AI block matches under async find. With the AsyncFind feature flag enabled (default-off for dogfood), pressing up/down on the find bar advances the current/total counter through AI matches but never (1) applies the focused-match highlight color to the AI match, (2) scrolls to the AI block containing the focused match, or (3) auto-expands a collapsed reasoning block containing it. Sync find handles all three correctly.

Root cause: AsyncFindController's focused-match cache was terminal-only, so the AI-rendering call site and the reasoning auto-expand handler — both of which look up the focused RichContentMatchId via the sync BlockListFindRun — see None whenever async find is active.

Fix:

  • Refactor the controller's focused-match cache to a single Option<FocusedMatchResolution> that resolves to either a terminal grid match or an AI match, replacing the previous terminal-only Option<AsyncBlockGridMatch>. focused_terminal_match() and focused_ai_match() are thin pattern-matching accessors over the unified cache.
  • Plumb the AI variant through TerminalFindModel::focused_block_list_match() so TerminalView::scroll_to_match routes to FindMatchScrollLocation::RichContent for AI matches.
  • Add a path-agnostic TerminalFindModel::focused_rich_content_match_id() helper used by both get_highlight_ranges_for_find_matches (highlight rendering) and AIBlock::handle_find_match_focus_change (reasoning auto-expand) in place of their previous sync-only lookups.
  • AI matches inside one block are walked bottom-to-top for MostRecentLast so per-AI-block focus traversal matches sync find.

Linked Issue

N/A; found while dogfooding.

Testing

  • I have manually tested my changes locally with ./script/run

Added three unit tests in async_find_tests.rs:

  • test_focused_ai_match_resolves_only_ai_block — seeds a single AI block with two matches and verifies focused_ai_match() resolves correctly under MostRecentLast (per-block iteration reversal).
  • test_focused_ai_match_most_recent_first_preserves_storage_order — same fixture under MostRecentFirst to exercise un-reversed iteration.
  • test_focused_match_index_walks_across_terminal_and_ai_blocks — mixed terminal + AI fixture asserting the unified cache holds at most one variant at a time and the focused index walks across both block types in TotalIndex-descending order.

Existing parity tests (test_async_focused_order_matches_sync_*) and all 18 prior async-find tests continue to pass (21 total).

Verified:

  • cargo check -p warp --tests → clean
  • cargo clippy -p warp --tests -- -D warnings → clean
  • cargo fmt -p warp → clean
  • cargo test -p warp --lib async_find → 21 passed

Screenshots / Videos

Manually tested; demo here:

Screen.Recording.2026-05-18.at.12.21.36.PM.mov

First half is the existing implementation; notice how matches in AI blocks are highlighted, but not properly focused (aka given orange highlights and scrolled to) when the match index changes. Second half (new implementation) fixes this.

There's a KI (that's around for the sync implementation, too) around match highlights for AI blocks not being dismissed when the find bar is. i.e., if I type a find query with matches in AI blocks, then dismiss that find bar with esc, matches in AI blocks aren't dismissed until I refocus the pane being searched. Matches in terminal blocks are handled fine. I plan on fixing this in a stacked PR.

Agent Mode

  • Warp Agent Mode - This PR was created via Warp's AI Agent Mode

When the `AsyncFind` feature flag is enabled, pressing up/down on the find
bar advances the current/total counter through AI block matches but never
highlights one, never scrolls to it, and never auto-expands a collapsed
reasoning block containing it. The cached focused match in
`AsyncFindController` was terminal-only, so AI rendering and reasoning
auto-expand (which both look up the focused `RichContentMatchId` via
`BlockListFindRun`) saw nothing.

Refactor the controller's focused-match cache to a single
`Option<FocusedMatchResolution>` that resolves to either a terminal or
an AI match. Plumb the AI variant through
`TerminalFindModel::focused_block_list_match()` so `scroll_to_match`
routes to `FindMatchScrollLocation::RichContent`, and add a path-agnostic
`focused_rich_content_match_id()` helper used by both AI highlight
rendering and the reasoning-block auto-expand handler. AI matches inside
one block are walked bottom-to-top for `MostRecentLast` to match sync
find's per-AI-block traversal.

Co-Authored-By: Oz <oz-agent@warp.dev>
@cla-bot cla-bot Bot added the cla-signed label May 18, 2026
@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 18, 2026

@vkodithala

I'm starting a first review of this pull request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

This PR updates async find so focused AI block matches resolve through the same rich-content match ID path used by sync find, enabling focused-match highlighting, scrolling, and collapsed reasoning auto-expand behavior. The code/security review did not find a diff-line correctness or security issue.

Concerns

  • For this user-facing change, please include screenshots or a screen recording demonstrating the focused AI match highlight, scroll-to-match, and reasoning auto-expand flow working end to end. The PR currently marks screenshots/videos as N/A, but this behavior is visually observable even if it is gated by the AsyncFind dogfood flag.

Verdict

Found: 0 critical, 1 important, 0 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Per review feedback: the only caller of `compute_focused_match` was the
single-line wrapper `update_cached_focused_match`. Inline the body so the\nresolution writes directly into `self.cached_focused_match` instead of\nbouncing through an intermediate `Option<FocusedMatchResolution>` return.\n\nCo-Authored-By: Oz <oz-agent@warp.dev>
@vkodithala vkodithala changed the title Highlight focused AI block matches under async find fix: Highlight focused AI block matches under async find May 18, 2026
@vkodithala vkodithala requested a review from vorporeal May 18, 2026 16:23
/// The id of the focused match within the rich content block.
pub match_id: RichContentMatchId,
/// The total index of the rich content block in the blocklist sumtree.
pub total_index: TotalIndex,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we might want to clarify in the doc comment for the struct what the expected lifetime is, as these two values can get stale as new output is streamed in/blocks get created.

as-is, nothing prevents an AsyncFocusedAiMatch from being stored somewhere and getting stale. if it's intended to be a short-lived/transient state holder, we may want to consider removing Clone, so that we can return a reference to it, but nothing outside of this module can ever hold onto one.

}

/// Returns the focused match as an AsyncBlockGridMatch if it's a terminal match.
/// Returns the focused match as an `AsyncBlockGridMatch` if it's a terminal match.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit/tip: further wrapping this in brackets (e.g.: "[`" with a pair at the end) will linkify it, if the item within the brackets is a symbol in the current scope.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants