mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(site): include diff length in cache key to prevent stale highlight reuse (#22942)
When a PR diff update arrives via SSE, the diff content query is
invalidated and re-fetched. `parsePatchFiles` was called with the same
cache key prefix (`chat-{chatId}`) regardless of content, so the
`@pierre/diffs` worker pool's LRU cache returned the old highlighted
AST. The stale `code.additionLines`/`code.deletionLines` arrays no
longer matched the new diff's line structure, causing
`DiffHunksRenderer.processDiffResult` to throw:
```
DiffHunksRenderer.processDiffResult: deletionLine and additionLine are null, something is wrong
```
**Root cause:** The rendering pipeline has two phases that both call
`iterateOverDiff` but with different `diffStyle` parameters. Phase 1
(highlighting) uses `diffStyle: "both"` to populate
`code.deletionLines[]` and `code.additionLines[]`. Phase 2 (DOM
construction in `processDiffResult`) uses `diffStyle: "unified"` or
`"split"` to consume those arrays. When the cache returned stale phase 1
output for new diff content, the line indices from phase 2 pointed to
entries that didn't exist in the stale arrays.
**Fix:** Append `diff.length` to the cache key prefix so that content
changes produce a cache miss and trigger fresh highlighting. While not
collision-proof, it's vanishingly unlikely that two sequential PR diff
updates have the exact same byte length.
This commit is contained in:
@@ -559,13 +559,20 @@ export const FilesChangedPanel: FC<FilesChangedPanelProps> = ({
|
||||
try {
|
||||
// The cacheKeyPrefix enables the worker pool's LRU cache
|
||||
// so highlighted ASTs are reused across re-renders instead
|
||||
// of being re-computed on every render cycle.
|
||||
const patches = parsePatchFiles(diff, `chat-${chatId}`);
|
||||
// of being re-computed on every render cycle. We include
|
||||
// dataUpdatedAt so that when the diff content changes
|
||||
// (e.g. new commits pushed) the old cached highlight AST
|
||||
// is not reused with mismatched line indices, which would
|
||||
// cause DiffHunksRenderer.processDiffResult to throw.
|
||||
const patches = parsePatchFiles(
|
||||
diff,
|
||||
`chat-${chatId}-${diffContentsQuery.dataUpdatedAt}`,
|
||||
);
|
||||
return patches.flatMap((p) => p.files);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}, [diffContentsQuery.data?.diff, chatId]);
|
||||
}, [diffContentsQuery.data?.diff, diffContentsQuery.dataUpdatedAt, chatId]);
|
||||
|
||||
const handleSubmitComment = useCallback(
|
||||
(text: string) => {
|
||||
|
||||
Reference in New Issue
Block a user