diff:<lang> glues hunks and files together, so one unterminated construct contaminates the rest
Introduced by #4110. diff selects 'unchanged, deleted, diff' and 'unchanged, inserted, diff'. Everything not named by a selector is dropped from the text handed to the inner language — coord lines (@@ …, --- a/file, ***, and normal-diff 1c1), and also the unmatched text, which is where diff --git a/x b/x lands since it matches none of the coord patterns.
Dropping them makes non-adjacent regions contiguous, so an unterminated construct — block comment, string, template literal, fenced block — runs on into the next hunk and the next file.
Repro
import { createInstance } from './tests/helper/prism-loader.js';
const prism = await createInstance(['diff', 'javascript', 'clike', 'markup']);
const code = [
'diff --git a/a.js b/a.js', '@@ -1,2 +1,2 @@', ' /* note', '-const s = 1;',
'diff --git a/b.js b/b.js', '@@ -1,1 +1,1 @@', '-const t = 2;', '',
].join('\n');
console.log(prism.highlight(code, 'diff:javascript'));v2 [deleted [prefix -][keyword const] t [operator =] [number 2][punctuation ;]
new [deleted [prefix -][comment const t = 2;b.js is a different file and renders entirely as a comment. Highlighting a real git log -p or a multi-hunk patch hits this routinely: git diff 28d76273 2b5ee8bd as diff:javascript differs from v2 on 29 of its 1304 lines for this reason.
Why v2 was fine
$inner used to sit inside each line-block token, so every block was tokenized alone. #4110 moved it to the top level so constructs can span blocks — which is the feature, and it is what makes a comment opened on an unchanged line and closed on a changed one work. The concatenation now also spans things that are not contiguous.
Why it needs a new concept
The obvious rule — "a container the selector does not name breaks the document" — is wrong. Building the before document must skip inserted blocks without breaking, because the old file really is contiguous across them, but must break at coord. Both are merely unnamed, so the selector language cannot tell them apart.
One option, prototyped and confirmed working (~40 lines, not submitted because it is new public API rather than a bug fix): a separator selector on InnerSpec naming containers that end a document, with diff declaring separator: 'coord'. For the cross-file case the unmatched text would have to break as well, or the bleed survives wherever two regions abut without a @@ between them.
Is that concept one you want in the model, or would you rather solve it another way?
Source: PrismJS/prism