Skip to content

Cap content-search previews so huge single-line files don't freeze the UI#56

Merged
GordonBeeming merged 3 commits into
mainfrom
gb/search-preview-cap
Jul 11, 2026
Merged

Cap content-search previews so huge single-line files don't freeze the UI#56
GordonBeeming merged 3 commits into
mainfrom
gb/search-preview-cap

Conversation

@GordonBeeming

@GordonBeeming GordonBeeming commented Jul 11, 2026

Copy link
Copy Markdown
Owner

Summary

  • Searching a workspace with a big single-line minified file (a 935KB highlight.js in the dotfiles repo, just under the 1MB search cap) froze the whole app for minutes: every match carried the full line as its preview, so up to 200 matches × 935KB got serialized over IPC and rendered into the DOM. The tree view shares that webview, so everything locked up, and reopening the search pane re-ran the query and froze it again.
  • Previews are now capped at 500 chars around the match with an ellipsis on each cut end, in both workspace search (src-tauri/src/workspace.rs, which also covers the browser HTTP endpoint) and single-file find (src/currentFileSearch.ts).
  • matchStart/matchEnd are untouched: still UTF-16 offsets into the full line, so EditorPane match selection works exactly as before. A line's offsets are also computed in one scan instead of one scan per match.

Test plan

  • cargo test (235 passed) and cargo clippy --all-targets (clean), including 3 new tests: preview cap on huge lines, full-line UTF-16 offsets preserved (astral emoji), multi-byte chars at window edges
  • npm test (406 passed) and tsc --noEmit clean, including 4 new currentFileSearch tests (cap, offset integrity, short-line passthrough, start/end-of-line ellipsis)
  • End to end on a seeded 900KB single-line file: results in ~50ms with ~1ms max main-thread stall (previously minutes of freeze), all previews at 502 chars, find-in-file instant, match selection renders, pane switch away/back stalls 3ms
  • Normal short lines render byte-identical, no ellipsis

Summary by CodeRabbit

  • New Features
    • Search results and current-file search now display capped, character-safe line previews centered on matches, adding ellipses for very long lines.
  • Bug Fixes
    • Improved match start/end positioning for huge and multibyte/Unicode content, preserving correct selection even when previews are truncated.
    • Ensures preview windows don’t break surrogate pairs (e.g., emoji), keeping highlights accurate.
  • Tests
    • Added coverage for preview truncation (including ellipses) and Unicode boundary correctness for large and tricky inputs.

…ze the UI

Searching a workspace containing a big single-line minified file (a 935KB
highlight.js in the dotfiles repo) shipped the full line as the preview text
for every match: up to 200 matches times 935KB serialized over IPC and
rendered into the DOM, freezing the whole webview for minutes. Reopening the
search pane re-ran the same query and froze it again.

Previews are now capped at 500 chars around the match with an ellipsis on
each cut end, in both workspace search (Rust) and single-file search (TS).
matchStart/matchEnd stay UTF-16 offsets into the full line, so editor match
selection is unchanged. A line's match offsets are also computed in one scan
of the line instead of one scan per match.

Claude-Session: https://claude.ai/code/session_018TAt3cnJRcK25UqH2YKwNz
@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Search results now use bounded previews for long lines. Workspace search batches UTF-16 offset mapping and preserves match positions across Unicode content, while current-file search applies equivalent preview-window behavior with boundary-safe ellipses.

Changes

Search preview updates

Layer / File(s) Summary
Backend preview and UTF-16 mapping
src-tauri/src/workspace.rs
Workspace search batches UTF-16 offset conversion, constructs character-boundary-safe previews around matches, and adds coverage for long and multibyte lines.
Frontend preview windows
src/currentFileSearch.ts, src/currentFileSearch.test.ts
Current-file search limits lineText to a match-centered window and tests truncation, short lines, boundary matches, preserved offsets, and surrogate-pair safety.

Estimated code review effort: 3 (Moderate) | ~30 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: capping search previews for huge single-line files to avoid UI freezes.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch gb/search-preview-cap

Comment @coderabbitai help to get the list of available commands.

@GordonBeeming GordonBeeming marked this pull request as ready for review July 11, 2026 13:26
Copilot AI review requested due to automatic review settings July 11, 2026 13:26

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces preview capping for search matches on extremely long or minified lines in both the Rust backend and TypeScript frontend to prevent performance issues and webview freezes. It also optimizes UTF-16 offset mapping by scanning lines once instead of per-match. The review feedback suggests simplifying the Rust character boundary logic using idiomatic iterators and adjusting the TypeScript string slicing boundaries to prevent splitting surrogate pairs (e.g., emojis).

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src-tauri/src/workspace.rs Outdated
Comment thread src/currentFileSearch.ts

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR prevents UI freezes when searching files that contain extremely large single-line (minified) content by capping the per-match preview text to a small window around the match, while keeping matchStart/matchEnd as full-line UTF-16 offsets so EditorPane selection behavior remains unchanged.

Changes:

  • Cap current-file search match previews to a 500-char window around the match (src/currentFileSearch.ts).
  • Cap workspace search previews similarly and compute UTF-16 offsets for all matches on a line in a single scan (src-tauri/src/workspace.rs).
  • Add/extend tests to validate preview caps and offset integrity (src/currentFileSearch.test.ts, src-tauri/src/workspace.rs tests).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/currentFileSearch.ts Adds preview windowing for current-file search results to avoid huge IPC/DOM payloads.
src/currentFileSearch.test.ts Adds tests for preview capping and ellipsis behavior in current-file search.
src-tauri/src/workspace.rs Caps workspace-search preview text and optimizes UTF-16 offset computation across matches.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/currentFileSearch.ts
Comment thread src/currentFileSearch.test.ts Outdated
Comment thread src-tauri/src/workspace.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 `@src/currentFileSearch.ts`:
- Around line 13-27: Update previewWindow to adjust windowStart and windowEnd to
Unicode code-point boundaries before slicing, avoiding surrogate-pair splits
while preserving the existing maximum preview length and ellipsis behavior. Add
a regression test covering a mixed ASCII/emoji line where the 160-character
window boundary falls inside an emoji.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b9133186-6de9-440b-b687-9a8bd1942a95

📥 Commits

Reviewing files that changed from the base of the PR and between fa7e988 and 4077817.

📒 Files selected for processing (3)
  • src-tauri/src/workspace.rs
  • src/currentFileSearch.test.ts
  • src/currentFileSearch.ts

Comment thread src/currentFileSearch.ts
- Avoid splitting surrogate pairs at preview window edges in
  currentFileSearch.ts (Gemini, Copilot, CodeRabbit) — boundaries nudge off a
  low surrogate so no lone surrogate renders in the preview; new test covers
  both edges.
- Truncate match ranges to the remaining result capacity before building the
  UTF-16 offset table in workspace.rs (Copilot) — a one-letter query on a huge
  minified line no longer allocates offsets for matches past the page.
- Rewrite build_search_preview boundary walks with char_indices iterators
  (Gemini) and apply cargo fmt (fixes the red Build & Test check).
- Correct a misleading test comment about match counts (Copilot).

Claude-Session: https://claude.ai/code/session_018TAt3cnJRcK25UqH2YKwNz
Copilot AI review requested due to automatic review settings July 11, 2026 14:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comment thread src-tauri/src/workspace.rs Outdated
Copilot follow-up: line_text was an owned copy of the whole (possibly
hundreds-of-KB) line even though only the preview window ships, and the char
count scanned every line fully. The line is borrowed now, and the short-line
check is a byte-length test plus a probe one char past the cap.

Claude-Session: https://claude.ai/code/session_018TAt3cnJRcK25UqH2YKwNz
Copilot AI review requested due to automatic review settings July 11, 2026 14:23
@GordonBeeming GordonBeeming merged commit b526c62 into main Jul 11, 2026
4 of 6 checks passed
@GordonBeeming GordonBeeming deleted the gb/search-preview-cap branch July 11, 2026 14:27

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comment on lines +564 to +574
let mut ranges = case_insensitive_match_byte_ranges(line, &normalized_query);
if ranges.is_empty() {
continue;
}
// Each range becomes exactly one SearchMatch and the loop below stops at
// collection_limit, so ranges past the remaining capacity would only feed
// offset bookkeeping for matches that never ship. Drop them up front —
// on a huge minified line a one-letter query can produce hundreds of
// thousands of ranges for a 200-result page.
ranges.truncate(collection_limit - matches.len());

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