Bug: Word diff marks disappear on the new side while the editor is attached
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 55/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- electron, typescript
Research direction
Start at onDeferTokenize and FileDiff.updateRenderCache, where the report says editor token batches rebuild cached rows on the new side. Trace how diff span marks are represented when editor tokens update the render cache, then reproduce with the supplied VirtualizedFileDiff example using Highlights and Shiki. Done when the new side keeps its word marks through editor attachment and theme changes, with existing marks preserved on both sides.
Written by the indexing model from the issue text.
Description
Prerequisites
- I have searched for duplicate or closed issues
- I have read the contributing guidelines
Describe the issue
Hi! We're using the diff editor in an Electron app, and we keep losing the word diff marks (data-diff-span) on the new side while the editor is attached. The old side always keeps them, the syntax colors stay, and ending the edit session brings them back.
You can see it on https://diffs.com/edit: click "diff", then "split". The new side loses its marks, and switching back to unified doesn't bring them back.
We tried a lot of combinations, and it comes down to these triggers:
- Highlights, attaching the editor. The marks go right away with a
Virtualizer, or at the next re-render without one (adiffStyleoroverflowchange, for example). That's the diffs.com case. - Shiki or Highlights, a theme change while editing. Either the page's color scheme flips (
themeType: 'system') orthemeis changed (with a single theme name this one didn't happen every time).setThemeType()doesn't trigger it.
With no editor attached, nothing is lost in any of the cases we tried.
I think it's one cause. In both cases the editor's tokenizer runs a background pass over the whole document (after attach for Highlights, after setTheme for both), and each batch goes through onDeferTokenize to FileDiff.updateRenderCache. That rebuilds the new side's cached rows from the editor's tokens, which have no data-diff-span. Later renders reuse that cache, so the marks stay gone. With a Virtualizer, the editor also repaints the visible rows straight away, which is why it shows at once there.
Expected: the word marks stay while the editor is attached, on both sides.
Reproduction
<style>body { color-scheme: dark } body.light { color-scheme: light }</style>
<div id="scroller" style="height: 2000px; overflow: auto"><div id="diff"></div></div>
import { Virtualizer, VirtualizedFileDiff } from '@pierre/diffs';
import { Editor } from '@pierre/diffs/edit';
const PREFERRED_HIGHLIGHTER = 'highlights'; // or 'shiki-js'
const code = (changed: boolean) =>
Array.from({ length: 30 }, (_, i) => `export function f${i}(x: number): number {\n return ${changed && i === 15 ? 'y' : 'x'} + ${i};\n}`).join('\n') +
`\nconst s = 'foo again${changed ? '170' : ''}';\n`;
const host = document.getElementById('diff')!;
const virtualizer = new Virtualizer();
virtualizer.setup(document.getElementById('scroller')!);
const view = new VirtualizedFileDiff(
{ theme: { dark: 'pierre-dark', light: 'pierre-light' }, preferredHighlighter: PREFERRED_HIGHLIGHTER, lineDiffType: 'word' },
virtualizer,
);
view.render({ fileContainer: host, oldFile: { name: 'example.ts', contents: code(false) }, newFile: { name: 'example.ts', contents: code(true) } });
const marks = () => {
const rows = [...host.shadowRoot!.querySelectorAll('[data-diff-span]')].map((mark) => mark.closest('[data-line-type]')?.getAttribute('data-line-type'));
return `old side ${rows.filter((t) => t === 'change-deletion').length}, new side ${rows.filter((t) => t === 'change-addition').length}`;
};
const settle = () => new Promise((resolve) => setTimeout(resolve, 1000));
await settle();
console.log('1. rendered, read-only:', marks());
const editor = new Editor('file-diff');
const endEditing = editor.edit(view);
await settle();
console.log('2. editor attached:', marks());
document.body.classList.add('light'); // the page's color scheme turns light
await settle();
console.log('3. color scheme flipped while editing:', marks());
endEditing();
await settle();
console.log('4. editing ended:', marks());
What it logs:
highlights:
1. rendered, read-only: old side 2, new side 2
2. editor attached: old side 2, new side 0
3. color scheme flipped while editing: old side 2, new side 0
4. editing ended: old side 2, new side 2
shiki-js:
1. rendered, read-only: old side 2, new side 2
2. editor attached: old side 2, new side 2
3. color scheme flipped while editing: old side 2, new side 0
4. editing ended: old side 2, new side 2
To load themes at all, I had to patch @pierre/theming locally (#1207).
Tested in headless Chromium 156, with @pierre/diffs 1.6.0, @pierre/highlights 0.1.3 and shiki 4.5.0. The theme change case also happens on 1.5.1. We haven't tried the React components, CodeView, worker pools, or Firefox and Safari.
What browser(s) are you seeing the problem on?
Chrome
What version of @pierre/diffs are you using?
1.6.0
- Dominant language
- TypeScript
- Stars
- 6.2k
- Forks
- 226
- Avg merge
- 1d 10h
- Merged PRs (30d)
- 36
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from pierrecomputer/pierre
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
pierrecomputer/pierre#1187 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
pierrecomputer/pierre#1150 · 3 comments ·
Maintainers usually reply within 1 day
-
Bug: Dragging a selection past the edge of an editable CodeView collapses it, then stops it growingOpenbug
Difficulty 4/5 3-5 days Newbie friendliness 35/100
pierrecomputer/pierre#1189 ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 25/100
pierrecomputer/pierre#1184 · 4 comments ·
Maintainers usually reply within 1 day
-
bug
Difficulty 3/5 1-2 days Newbie friendliness 63/100
pierrecomputer/pierre#1172 ·
Maintainers usually reply within 1 day
All issues in pierrecomputer/pierre
Similar issues
-
bug skills
Difficulty 2/5 1-3 hours Newbie friendliness 77/100
equinor/fusion-framework#5846 ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 74/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
Eyevinn/open-videocore#1256 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
[Bug]: Theme preset geist points at undefined --font-geistPossibly taken @claude claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 73/100
Maintainers usually reply within 1 day