Skip to content

fix(ui): preserve syntax state across folded hunks - #669

Merged
benvinegar merged 4 commits into
mainfrom
fix/source-backed-highlight-state
Aug 5, 2026
Merged

fix(ui): preserve syntax state across folded hunks#669
benvinegar merged 4 commits into
mainfrom
fix/source-backed-highlight-state

Conversation

@benvinegar

@benvinegar benvinegar commented Aug 2, 2026

Copy link
Copy Markdown
Member

Summary

  • highlight partial VCS diffs against validated old/new source prefixes so hidden multiline openers preserve Shiki grammar state
  • remap source-backed tokens onto the existing Pierre rows while retaining per-side syntax and word-diff styling
  • fall back to patch-fragment highlighting when source is unavailable, stale, or rejected
  • invalidate highlight caches when the source provider changes
  • add unit and PTY regression coverage for the Elixir heredoc case in Highlighting mis-tokenization when a hunk starts inside an Elixir heredoc #664

Screenshots

Before

Patch-only highlighting incorrectly keeps def hello do in the hidden heredoc's string state.

Before: def hello is incorrectly highlighted as a string

After

Source-backed highlighting restores def to the Elixir keyword state.

After: def hello is correctly highlighted as Elixir code

Testing

  • bun run typecheck
  • bun run lint
  • bun run format:check
  • focused unit tests: 26 passed
  • bun run test:integration: 87 passed
  • bun run test:tty-smoke: 9 passed
  • real Git/PTY Elixir reproduction

Notes

This corrects source-backed reviews such as diff, show, and stash show. Arbitrary source-less hunk patch input remains best-effort because omitted lexical context cannot be reconstructed reliably.

bun run test has one unrelated existing failure in src/extensions/hostRuntimeModules.test.ts, reproduced on untouched origin/main.

Addresses #664.

This PR description was generated by Pi using OpenAI GPT-5.6

@vercel

vercel Bot commented Aug 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hunk-web Ready Ready Preview Aug 5, 2026 4:34pm

Request Review

@greptile-apps

greptile-apps Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR preserves lexical state for partial source-backed diffs by validating full-source prefixes, highlighting them through Pierre, and remapping tokens onto the visible patch rows.

  • Adds source-prefix validation and old/new line-index remapping with patch-fragment fallback
  • Extends highlight-cache invalidation to account for source providers
  • Adds unit and PTY regression coverage for folded multiline syntax

Confidence Score: 4/5

The PR appears safe to merge, with a non-blocking watch-mode cache-efficiency issue for unchanged partial files.

Source-prefix validation and token remapping have guarded fallbacks and focused regression coverage, but cache invalidation follows newly allocated fetcher objects rather than stable source revisions, forcing unnecessary source loading and highlighting after reloads.

Files Needing Attention: src/ui/diff/useHighlightedDiff.ts

Important Files Changed

Filename Overview
src/ui/diff/sourceBackedHighlight.ts Adds validation, prefix-metadata construction, and deterministic remapping of full-source highlight lines to partial-diff indexes.
src/ui/diff/pierre.ts Integrates source-backed highlighting with guarded fallback to the existing patch-fragment and plain-text paths.
src/ui/diff/useHighlightedDiff.ts Invalidates highlights by source-fetcher identity, which prevents stale reuse but also defeats cache reuse across equivalent reload-created providers.
src/ui/diff/sourceBackedHighlight.test.ts Covers source-prefix grafting, stale-source rejection, remapping, and absent sides for added or deleted files.
test/pty/highlighting.test.ts Adds an end-to-end terminal regression test for preserving Elixir heredoc grammar state.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Partial diff metadata] --> B{Source fetcher available?}
  B -- No --> F[Highlight patch fragment]
  B -- Yes --> C[Fetch old and new source]
  C --> D{Visible lines and gaps validate?}
  D -- No --> F
  D -- Yes --> E[Highlight validated source prefixes]
  E --> G[Remap tokens to partial line indexes]
  F --> H[Build split or stack rows]
  G --> H
Loading
Prompt To Fix All With AI
### Issue 1
src/ui/diff/useHighlightedDiff.ts:89-94
**Fetcher identity defeats cache reuse**

Each session reload rebuilds `DiffFile` and its source fetcher, so assigning cache IDs by fetcher object identity gives an unchanged partial file a new highlight key. This discards reusable highlights and prefetched work, forcing another source fetch and highlight pass and causing an avoidable plain-text-to-highlighted repaint in watch mode.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "fix(ui): preserve syntax state across fo..." | Re-trigger Greptile

Comment on lines +89 to +94
let id = sourceFetcherIds.get(file.sourceFetcher);
if (id === undefined) {
id = nextSourceFetcherId;
nextSourceFetcherId += 1;
sourceFetcherIds.set(file.sourceFetcher, id);
}

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.

P2 Fetcher identity defeats cache reuse

Each session reload rebuilds DiffFile and its source fetcher, so assigning cache IDs by fetcher object identity gives an unchanged partial file a new highlight key. This discards reusable highlights and prefetched work, forcing another source fetch and highlight pass and causing an avoidable plain-text-to-highlighted repaint in watch mode.

Knowledge Base Used: Diff Rendering Pipeline

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/ui/diff/useHighlightedDiff.ts
Line: 89-94

Comment:
**Fetcher identity defeats cache reuse**

Each session reload rebuilds `DiffFile` and its source fetcher, so assigning cache IDs by fetcher object identity gives an unchanged partial file a new highlight key. This discards reusable highlights and prefetched work, forcing another source fetch and highlight pass and causing an avoidable plain-text-to-highlighted repaint in watch mode.

**Knowledge Base Used:** [Diff Rendering Pipeline](https://app.greptile.com/modem/-/custom-context/knowledge-base/modem-dev/hunk/-/docs/ui-diff-rendering.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@benvinegar benvinegar Aug 5, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 42c76fe. Source readers can now expose a stable sourceCacheKey, which is combined with the per-file patch fingerprint. The Git adapter derives it from resolved refs and semantic index entries, so equivalent reload-created fetchers reuse highlighted output while changed source bases still invalidate safely. Added cache, adapter-boundary, and Git regression coverage plus extension documentation.

Responded by Pi using OpenAI GPT-5.6.

This comment was generated by Pi using OpenAI GPT-5.6

@benvinegar
benvinegar force-pushed the fix/source-backed-highlight-state branch from d6043a2 to 959ab4d Compare August 5, 2026 16:34
@benvinegar
benvinegar merged commit ab570a0 into main Aug 5, 2026
12 checks passed
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.

1 participant