Repository navigation
Fix right sidebar Find typing lag - #3739
Conversation
The Find search field had no injectable boundary, so the regression test could not count backend searches without launching ripgrep. This adds the smallest controller seam and a test that types a burst through controlTextDidChange and expects one backend search after the debounce window. Constraint: Regression test must exercise observable input behavior instead of grepping source text Confidence: high Scope-risk: narrow Directive: Keep the spy-backed input test on the AppKit delegate path so future changes preserve typing debounce semantics Tested: Baseline UI repro and sample captured at /tmp/cmux-3738-baseline-sample.txt Not-tested: Local XCTest run intentionally skipped per instruction
Typing in the right-sidebar Find field used to synchronously restart rg and process every JSON result through the main actor. The fix gives the text field a single debounced search trigger, moves rg output parsing and path normalization behind a background actor, batches result snapshots, and only applies targeted table updates for append/same-size changes. Constraint: Local test runs are disabled for this repo; verification used the required tagged reload build and manual reproduction sampling. Rejected: Only adding debounce | leaves JSON parsing and path normalization able to starve the main actor once rg starts streaming matches Rejected: Sprinkling detached work at each parse site | keeps lifecycle ownership fragmented instead of one output pipeline Confidence: high Scope-risk: moderate Directive: Keep Find search input, rg lifecycle, output parsing, and table updates on one cancellable pipeline; do not reintroduce per-keystroke synchronous search starts. Tested: ./scripts/reload.sh --tag issue-3738-find-sidebar-typing-lag --launch; manual fast typing of private in /Users/austinwang/manaflow/term/cmux1; post-fix sample at /tmp/cmux-3738-fixed-sample.txt Not-tested: Local XCTest/XCUITest suites per repository policy
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Merged the latest origin/main into the PR branch before CI iteration so this fix is evaluated against current workflow and auxiliary-window close-shortcut checks. Constraint: iterate-pr workflow requires syncing the PR branch with the latest base branch before acting on CI feedback. Confidence: high Scope-risk: narrow Tested: Merge completed cleanly Not-tested: Local tests per repository policy
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a debounce to find input, moves ripgrep JSON parsing and path normalization into a background FileSearchOutputPipeline actor, makes search types Sendable, integrates the pipeline with FileSearchController for generation-aware updates and safer termination, applies incremental table updates, and adds debounce tests. ChangesSearch Debouncing and Async Pipeline
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
Caution Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional.
❌ Failed checks (3 errors, 1 warning)
✅ Passed checks (11 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
The initial main-actor isolation fix made the whole file explorer coordinator actor-isolated, which compiled locally but added new Swift isolation warnings that the macOS debug-build warning budget rejects. Scope the actor hop to the container visibility update instead, preserving synchronous outline refresh behavior without expanding coordinator isolation. Constraint: CI enforces scripts/swift_warning_budget.py on debug build output. Rejected: Refresh the warning budget | this was avoidable new debt from the PR, not accepted project debt Confidence: high Scope-risk: narrow Tested: ./scripts/reload.sh --tag issue-3738-find-sidebar-typing-lag --launch; python3 scripts/swift_warning_budget.py --log /tmp/cmux-reload-issue-3738-find-sidebar-typing-lag.log Not-tested: Local XCTest/XCUITest suites per repository policy
Greptile SummaryFixes right-sidebar Find typing lag by debouncing keystrokes (200ms via Combine
Confidence Score: 5/5Safe to merge; the concurrency design is correct and the generation-based guards handle all search restart and early-exit orderings. The actor isolation and task cancellation paths are correct: searchTask self-cancels cleanly via terminationSignal returning nil, finish() guards against stale generations in both the normal and shouldStopProcess paths, and no applyPipelineUpdate can race past pipeline.finish() because stdoutDone resolution drains all pending await applyUpdate calls. The only finding is a file-length and responsibility-mixing note on FileExplorerView.swift, which does not affect correctness. Sources/FileExplorerView.swift — now at 2124 lines with debounce orchestration state woven into the view class. Important Files Changed
Sequence DiagramsequenceDiagram
participant NSTextField
participant ContainerView as FileExplorerContainerView (@MainActor)
participant Combine as Combine Debounce (RunLoop.main)
participant Controller as FileSearchController (@MainActor)
participant Task as searchTask (@MainActor)
participant Pipeline as FileSearchOutputPipeline (actor)
participant TermSig as FileSearchTerminationSignal (actor)
participant RG as rg process
NSTextField->>ContainerView: controlTextDidChange
ContainerView->>Combine: searchDebounceSubject.send(generation)
Note over Combine: 200ms debounce
Combine->>ContainerView: generation matches - refreshSearchIfNeeded()
ContainerView->>Controller: search(query:rootPath:isLocal:)
Controller->>Controller: stopAndAdvanceGeneration()
Controller->>RG: process.run()
Controller->>Task: "Task { streamStdout + streamStderr + wait }"
par Background streams
Task->>Pipeline: "consumeStdoutLine (off main actor via @concurrent)"
Pipeline-->>Task: FileSearchPipelineUpdate (every 50ms or at limit)
Task->>Controller: applyPipelineUpdate - emit snapshot
Controller->>ContainerView: onSnapshotChanged - applySearchSnapshot
ContainerView->>ContainerView: applySearchResultsUpdate (insertRows / reloadData)
and stderr drain
Task->>Pipeline: consumeStderrLine
end
RG->>TermSig: terminationHandler - complete(status)
Task->>TermSig: await wait() - status
Task->>Pipeline: finish(status) - final update
Task->>Controller: finish(generation:update:) - emit final snapshot
Controller->>ContainerView: onSnapshotChanged - applySearchSnapshot
Reviews (10): Last reviewed commit: "Clarify Find search task ownership" | Re-trigger Greptile |
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 22185ca9-6f43-4281-98fb-e84022cc2020
📒 Files selected for processing (3)
Sources/FileExplorerSearchController.swiftSources/FileExplorerView.swiftcmuxTests/FileExplorerStoreTests.swift
The right-sidebar Find status label changes text as ripgrep streams matches. Its intrinsic width was still participating in the search-bar layout, so status updates could move the search field while the user was typing. Give the status area a stable width and add DEBUG-only evidence logs for input cadence, debounce firing, snapshot apply cost, and search-bar width changes. Constraint: Use the project reload script only; never invoke xcodebuild directly for this branch. Constraint: User needs debug-log evidence for a reproducible non-crash latency issue. Rejected: Hide or remove the status label | loses progress feedback and does not prove whether status text was driving field geometry churn. Confidence: medium Scope-risk: narrow Directive: Revisit the DEBUG file.search.* probes after the active repro is resolved; keep only logs that remain useful for future typing-lag diagnostics. Tested: ./scripts/reload.sh --tag issue-3738-find-sidebar-typing-lag --launch Tested: Typed private into the right-sidebar Find input on /Users/austinwang/manaflow/term/cmux1; file.search.* logs showed one debounced search and stable field/status widths while status intrinsic width changed. Tested: python3 scripts/swift_warning_budget.py --log /tmp/cmux-reload-issue-3738-find-sidebar-typing-lag.log Not-tested: XCTest and XCUITest, per local testing policy.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Sources/FileExplorerView.swift`:
- Around line 1051-1056: Replace the expensive allocation
Array(nextResults.prefix(previousResults.count)) used in applySearchSnapshot
with the non-allocating prefix check nextResults.starts(with: previousResults);
specifically, in the block that checks if nextResults.count >
previousResults.count and currently compares the prefix, change that comparison
to nextResults.starts(with: previousResults) so searchResultsView.insertRows(at:
IndexSet(integersIn: insertedRange), withAnimation: []) still runs when
appropriate but without allocating a new array each snapshot.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 849b3bff-b8b4-4f5d-82d7-80a129a21838
📒 Files selected for processing (1)
Sources/FileExplorerView.swift
The fixed inline status slot stopped width churn but took too much space from the search field, especially for long queries. Move status text below the full-width search field and make the field cell scrollable so typing follows the insertion point. Constraint: Preserve the no-resize property for streaming status updates without sacrificing input visibility. Rejected: Keep shrinking the inline status slot | still trades query visibility against result status and repeats the same layout coupling. Confidence: high Scope-risk: narrow Tested: ./scripts/reload.sh --tag issue-3738-find-sidebar-typing-lag --launch Tested: Typed abcdefghijklmnopqrstuvwxyz-private-function-search-long-query into the launched Find field; visible text followed the suffix instead of remaining stuck at the prefix. Tested: python3 scripts/swift_warning_budget.py --log /tmp/cmux-reload-issue-3738-find-sidebar-typing-lag.log Not-tested: XCTest/XCUITest locally, per repository policy.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Sources/FileExplorerView.swift`:
- Around line 1033-1058: The debounce Task stored in searchDebounceTask is only
cancelled on teardown, allowing a pending debounce to fire after the Find panel
is hidden; fix this by cancelling and clearing any pending debounce when Find is
hidden: call cancelPendingSearchRefresh() from the code path that changes
isSearchVisible to false (e.g., the isSearchVisible setter or the method that
hides the Find UI), and ensure cancelPendingSearchRefresh() keeps cancelling and
nil-ing searchDebounceTask; this prevents scheduleSearchRefresh /
refreshSearchIfNeeded from running after the Find UI is no longer visible.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: ef7c04ac-2901-48b7-9177-caf092cdcb06
📒 Files selected for processing (1)
Sources/FileExplorerView.swift
Review feedback exposed two remaining latency-path risks: the debounce still used a Swift sleep timer, and process finalization could race pipe consumption. This changes the Find debounce to a cancellable main-queue work item, cancels pending work when Find is hidden, avoids prefix array allocation during incremental table updates, and makes the search controller finalize only after process termination plus stdout/stderr stream completion. Constraint: Build must use scripts/reload.sh only; no direct xcodebuild and no local tests. Rejected: Keep readabilityHandler callbacks and guard late results individually | still allows final status to race tail output. Confidence: high Scope-risk: moderate Directive: Keep search parsing/finalization ordered off the text input path; do not reintroduce per-keystroke sleeps or main-thread pipe parsing. Tested: ./scripts/reload.sh --tag issue-3738-find-sidebar-typing-lag --launch Tested: python3 scripts/swift_warning_budget.py --log /tmp/cmux-reload-issue-3738-find-sidebar-typing-lag.log Not-tested: Local XCTest per repository policy.
Greptile's summary correctly pointed out that Coordinator.reloadIfNeeded relied on MainActor.assumeIsolated. The method is UI work, so it now carries @mainactor directly and the Combine store observer performs an explicit main-actor hop before reloading. Constraint: Build must use scripts/reload.sh only; no direct xcodebuild and no local tests. Rejected: Leave assumeIsolated because the sink uses RunLoop.main | keeps a runtime trap where compile-time isolation is available. Confidence: high Scope-risk: narrow Directive: Prefer @mainactor annotations for FileExplorer UI reload paths when the caller can hop explicitly. Tested: ./scripts/reload.sh --tag issue-3738-find-sidebar-typing-lag --launch Tested: python3 scripts/swift_warning_budget.py --log /tmp/cmux-reload-issue-3738-find-sidebar-typing-lag.log Not-tested: Local XCTest per repository policy.
The structured async stream path consumes ripgrep output line-by-line through consumeStdoutLine. Keeping the old Data-buffered consumeStdout method and stdoutBuffer field created dead code after the review-response refactor, so this removes the unused path and leaves one output ingestion model. Constraint: Build must use scripts/reload.sh only; no direct xcodebuild and no local tests. Rejected: Keep both buffered and line-based ingestion paths | invites future edits to the wrong path and weakens the pipeline ownership model. Confidence: high Scope-risk: narrow Directive: FileSearchOutputPipeline stdout ingestion is line-based through AsyncBytes.lines; add buffering only if a future reader no longer provides complete lines. Tested: ./scripts/reload.sh --tag issue-3738-find-sidebar-typing-lag --launch Tested: python3 scripts/swift_warning_budget.py --log /tmp/cmux-reload-issue-3738-find-sidebar-typing-lag.log Not-tested: Local XCTest per repository policy.
The debounce path now uses a generation token to invalidate stale callbacks instead of letting a DispatchWorkItem closure retain the work item it is assigned to. The deferred field-editor scroll also uses an explicit MainActor task so the handoff is visible to Swift concurrency checking. Constraint: Build must use ./scripts/reload.sh only; no direct xcodebuild invocation Rejected: Capture DispatchWorkItem inside its own closure | canceled items can retain themselves Confidence: high Scope-risk: narrow Tested: ./scripts/reload.sh --tag issue-3738-find-sidebar-typing-lag --launch Tested: python3 scripts/swift_warning_budget.py --log /tmp/cmux-reload-issue-3738-find-sidebar-typing-lag.log Not-tested: Local XCTest per repo policy
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Sources/FileExplorerView.swift (1)
579-1366: 🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy liftExtract the new Find plumbing out of this already over-budget view file.
This block adds debounce orchestration, snapshot diffing, and DEBUG diagnostics to a file that is already well past the repo’s large-file threshold. Please move the new search-specific controller/state helpers into dedicated Swift file(s) and keep
Sources/FileExplorerView.swiftfocused on AppKit view composition.As per coding guidelines: “do not accept more than 250 lines added to an existing production Swift file that is already over 800 lines, unless an extraction exception is met by removing/moving mixed responsibilities and decreasing total line count by more than 200 lines.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Sources/FileExplorerView.swift` around lines 579 - 1366, The file is doing too much — extract all search-specific state, debounce logic, snapshot diffing, and debug helpers into a new helper type and move related NSTableView/NSSearchField delegate plumbing into separate file(s). Create a new FileExplorerSearchManager (or similar) that owns: properties searchController, searchSnapshot, searchDebounceGeneration, searchDebounceWorkItem, searchDebounceDelaySeconds, searchBarVisibleHeight; methods refreshSearchIfNeeded(search:rootPath:isLocal:contentRevision:), scheduleSearchRefresh(), cancelPendingSearchRefresh(), applySearchSnapshot(_:), applySearchResultsUpdate(previous:next:), statusText(for:), and all DEBUG helper funcs; expose a closure onSnapshotChanged and simple API to start/cancel searches and query status. Remove those fields/methods from FileExplorerContainerView and replace them with an instance of the manager, calling manager.scheduleSearchRefresh() from controlTextDidChange, manager.refreshSearchIfNeeded() when presentation/root changes, and wiring manager.onSnapshotChanged to update UI (searchStatusLabel, searchResultsView reload/selection). Move NSTableView/NSSearchField delegate implementations that are search-specific into a new FileExplorerSearchUI.swift extension that uses the manager. Keep only view composition, presentation toggles, and keyboard focus logic in FileExplorerContainerView.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@Sources/FileExplorerView.swift`:
- Around line 579-1366: The file is doing too much — extract all search-specific
state, debounce logic, snapshot diffing, and debug helpers into a new helper
type and move related NSTableView/NSSearchField delegate plumbing into separate
file(s). Create a new FileExplorerSearchManager (or similar) that owns:
properties searchController, searchSnapshot, searchDebounceGeneration,
searchDebounceWorkItem, searchDebounceDelaySeconds, searchBarVisibleHeight;
methods refreshSearchIfNeeded(search:rootPath:isLocal:contentRevision:),
scheduleSearchRefresh(), cancelPendingSearchRefresh(), applySearchSnapshot(_:),
applySearchResultsUpdate(previous:next:), statusText(for:), and all DEBUG helper
funcs; expose a closure onSnapshotChanged and simple API to start/cancel
searches and query status. Remove those fields/methods from
FileExplorerContainerView and replace them with an instance of the manager,
calling manager.scheduleSearchRefresh() from controlTextDidChange,
manager.refreshSearchIfNeeded() when presentation/root changes, and wiring
manager.onSnapshotChanged to update UI (searchStatusLabel, searchResultsView
reload/selection). Move NSTableView/NSSearchField delegate implementations that
are search-specific into a new FileExplorerSearchUI.swift extension that uses
the manager. Keep only view composition, presentation toggles, and keyboard
focus logic in FileExplorerContainerView.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 8d472256-a61c-4d56-85aa-2f7131bf51d0
📒 Files selected for processing (2)
Sources/FileExplorerSearchController.swiftSources/FileExplorerView.swift
The Find field debounce now uses a Combine subject and generation guard instead of queueing Dispatch work items. Search process cancellation sends SIGTERM directly, and the stdout/stderr readers are marked @Concurrent so their I/O and parsing path cannot inherit main-actor execution. Constraint: Build must use ./scripts/reload.sh only; no direct xcodebuild invocation Rejected: DispatchQueue.asyncAfter debounce | reviewer rule treats queue timers in typing paths as blocking-risk code Rejected: Task.detached process termination | detached tasks obscure lifecycle and were flagged by review Confidence: high Scope-risk: moderate Tested: ./scripts/reload.sh --tag issue-3738-find-sidebar-typing-lag --launch Tested: python3 scripts/swift_warning_budget.py --log /tmp/cmux-reload-issue-3738-find-sidebar-typing-lag.log Not-tested: Local XCTest per repo policy
CircleCI still runs Xcode 16.4, so the search stream readers keep the @Concurrent annotation only for Swift 6.2+ compilers. Newer local toolchains still force the I/O-heavy readers off the caller actor, while older CI compilers can build the same source. Constraint: CircleCI macOS executor uses Xcode 16.4 Constraint: Build must use ./scripts/reload.sh only; no direct xcodebuild invocation Rejected: Ungated @Concurrent | Swift 6.1 CI toolchains cannot parse the attribute Confidence: high Scope-risk: narrow Tested: ./scripts/reload.sh --tag issue-3738-find-sidebar-typing-lag --launch Tested: python3 scripts/swift_warning_budget.py --log /tmp/cmux-reload-issue-3738-find-sidebar-typing-lag.log Not-tested: Local XCTest per repo policy
FileSearchTerminationSignal now tracks cancellable wait continuations by id, so canceling a search task resumes the waiter with nil instead of holding the task until the process termination handler runs. Normal process completion still resumes every pending waiter with the exit status. Constraint: Build must use ./scripts/reload.sh only; no direct xcodebuild invocation Rejected: Plain CheckedContinuation wait | cancellation cannot release the search task promptly Confidence: high Scope-risk: narrow Tested: ./scripts/reload.sh --tag issue-3738-find-sidebar-typing-lag --launch Tested: python3 scripts/swift_warning_budget.py --log /tmp/cmux-reload-issue-3738-find-sidebar-typing-lag.log Not-tested: Local XCTest per repo policy
Superseded by later commits; the referenced inline CodeRabbit findings are resolved and the current CodeRabbit check is passing.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit e9fbd9e. Configure here.
The search stream task used a weak capture but immediately promoted it to a task-long strong controller reference. Keep the task weak with an async update handler so streaming updates and final completion only retain the controller at the call site. Constraint: Cursor flagged the misleading weak-to-strong promotion after CI completed. Rejected: Capture self strongly for the whole task | preserves the temporary retain cycle the review identified. Confidence: high Scope-risk: narrow Tested: ./scripts/reload.sh --tag issue-3738-find-sidebar-typing-lag --launch Tested: python3 scripts/swift_warning_budget.py --log /tmp/cmux-reload-issue-3738-find-sidebar-typing-lag.log Not-tested: Local tests per repository policy; CI will run after push.

Closes #3738
Bottleneck analysis
Baseline confirmed the static-analysis findings in the right-sidebar Find path:
FileExplorerContainerView.controlTextDidChangesynchronously calledrefreshSearchIfNeeded()on each keystroke, so fast typing restartedrgper character.FileSearchController.consumeStdoutparsedrg --jsonoutput on the main actor, includingJSONSerializationandFileExplorerTerminalPathInsertion.relativePathpath normalization for each result.FileExplorerContainerView.applySearchSnapshotdid full table reloads for streaming result updates.Reproduction and baseline sample
Baseline build/run:
Repro workspace:
/Users/austinwang/manaflow/term/cmux1Steps: focused Find with
Cmd+Shift+F, typedprivateat about 8 chars/sec, and sampled the baseline app process for 5 seconds while typing.Baseline sample saved locally at
/tmp/cmux-3738-baseline-sample.txt.Observed baseline behavior: visible cursor/result stalls under fast input, no dropped characters in this run, and results stabilized roughly 1 second after the last keystroke.
Relevant baseline sample frames:
Fix
FileSearchControllingseam plus a regression test showing a typing burst should not invoke one search per character.rg.rgstdout/stderr buffering, JSON parsing, and per-result path normalization into a single background actor (FileSearchOutputPipeline).reloadData()only when needed.file.search.*diagnostics for input cadence, debounce firing, search request count, snapshot apply time, and search-bar width changes.Post-fix verification
Fixed build/run:
Repeated the same repro in
/Users/austinwang/manaflow/term/cmux1withprivateat the same cadence.Post-fix sample saved locally at
/tmp/cmux-3738-fixed-sample.txt.Observed fixed behavior: no visible input lag, no dropped characters,
privateremained focused in the Find field, and results stabilized within the debounce window plus the first streamed batch, about 250 ms after the last keystroke.Relevant fixed sample frames:
Follow-up layout instrumentation from the non-crash repro (
/tmp/cmux-debug-issue-3738-find-sidebar-typing-lag.log, around13:16:46):Long-input follow-up:
Test plan
./scripts/reload.sh --tag issue-3738-find-sidebar-typing-lag-baseline --launch../scripts/reload.sh --tag issue-3738-find-sidebar-typing-lag --launch../scripts/reload.sh --tag issue-3738-find-sidebar-typing-lag --launch.python3 scripts/swift_warning_budget.py --log /tmp/cmux-reload-issue-3738-find-sidebar-typing-lag.log.Summary by CodeRabbit
Improvements
New Features
Tests
Note
Medium Risk
Moderate risk: refactors file search process lifecycle and introduces new async/actor pipeline and debouncing, which could affect cancellation/termination behavior and snapshot timing.
Overview
Reduces right-sidebar Find typing lag by debouncing keystroke-driven searches (200ms) and updating UI snapshots at a capped rate while results stream.
Refactors
FileSearchControllerto use a background actor pipeline for stdout/stderr processing and result parsing, with async streaming, generation-based cancellation, and SIGTERM termination; also introduces aFileSearchControllingprotocol for injection.Improves Find UI responsiveness by avoiding full table reloads on every snapshot (incremental row inserts/targeted reloads), reworking the search bar layout so status text no longer shrinks the field, and ensuring long queries keep the insertion point visible. Adds a regression test verifying typing bursts coalesce into a single debounced search.
Reviewed by Cursor Bugbot for commit 899d0e7. Bugbot is set up for automated code reviews on this repo. Configure here.