An unchanged diff line takes only the last selector reading, so a construct spanning into a deleted line renders half-highlighted
Introduced by #4110. unchanged and ! blocks are named in both of diff's selectors, and documents.reverse() plus the done set make the last selector claim them — so an unchanged line always takes the inserted document's reading.
A construct opening on an unchanged line and closing on a deleted one then gets its two halves from different documents.
Repro
import { createInstance } from './tests/helper/prism-loader.js';
const prism = await createInstance(['diff', 'javascript', 'clike', 'markup']);
console.log(prism.highlight(' const t = `hi\n- there`;\n const x = 1;', 'diff:javascript'));v2 [deleted [prefix -] there`[punctuation ;] ← nothing highlighted
new [deleted [prefix -][template-string [string there][template-punctuation `]][punctuation ;]The closing half is a template-string; the opening backtick one line up is plain text. A closing token with no opener.
This one looks inherent
An unchanged line is genuinely in both versions of the file, and the two versions can tokenize it differently. Last-wins, first-wins, longest-wins — every assignment rule picks one document and leaves the other half disagreeing. There is no choice that makes both readings coherent, because the line has two truths.
It is milder than #4117: the damage stops at that container and does not run on. The trade is "no information" (v2) against "wrong-looking information" (now).
If it is worth addressing
When two documents produce different tokens for the same shared container, leave that container untokenized instead of picking a winner. That restores the v2 rendering exactly where the ambiguity exists, keeps the new behaviour everywhere else, and needs no new public API — cheaper than the separator concept #4117 would want.
Worth doing, or acceptable as-is?
Source: PrismJS/prism