From fda02e9acbbf69b64076ad24e71a1a3197991673 Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Wed, 28 Oct 2026 13:08:01 +0100 Subject: [PATCH] =?UTF-8?q?Answer=20principles=20round=201:=200=20?= =?UTF-8?q?=F0=9F=94=B4,=202=20=F0=9F=9F=A1=20(0=20misread);=20the=20end?= =?UTF-8?q?=20of=20the=20text=20is=20destinationEnds'=20last=20stop,=20and?= =?UTF-8?q?=20a=20nested=20unclosed=20paren=20after=20a=20space=20is=20pin?= =?UTF-8?q?ned?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/markdown/commonmark/link-syntax.test.ts | 1 + src/markdown/commonmark/link-syntax.ts | 21 +++++++-------------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/markdown/commonmark/link-syntax.test.ts b/src/markdown/commonmark/link-syntax.test.ts index 4d0b60c..10fc996 100644 --- a/src/markdown/commonmark/link-syntax.test.ts +++ b/src/markdown/commonmark/link-syntax.test.ts @@ -33,6 +33,7 @@ test('a destination holding an unbalanced unescaped `(` at a space or the end re assert.deepEqual(targets('[a](b(c "t") [d](e)'), [undefined, { destination: 'e', length: 3 }]) assert.deepEqual(targets('[a](b(c) "t")'), [{ destination: 'b(c)', length: 10, title: 't' }]) assert.deepEqual(targets('[a](b((c) "t")'), [undefined]) + assert.deepEqual(targets('[a](b(c d))'), [undefined]) assert.equal(readDestination('b(c', 0), undefined) assert.equal(readDestination('b(c d', 0), undefined) assert.deepEqual(readDestination('b(c) d', 0), { length: 4, value: 'b(c)' }) diff --git a/src/markdown/commonmark/link-syntax.ts b/src/markdown/commonmark/link-syntax.ts index 25a8e29..bcaba70 100644 --- a/src/markdown/commonmark/link-syntax.ts +++ b/src/markdown/commonmark/link-syntax.ts @@ -72,27 +72,20 @@ export function targetMemo(text: string): TargetMemo { return { destinationEnds: undefined, tails: new Map(), text } } -// Of the starts still open at a stop, only the innermost has its parentheses balanced; the others hold a `(` nothing closed and keep no end. -function stopAt(ends: Int32Array, open: number[], index: number): void { - const innermost = open.pop() - if (innermost !== undefined) ends[innermost] = index - open.length = 0 -} - -// `ends[start]` is where an unbracketed destination from `start` stops, filled in one pass for every start a target can have: just after a `(`, a space or a control character, and 0, where `readDestination`'s run begins. +// `ends[start]` is where an unbracketed destination from `start` stops, filled in one pass for every start a target can have: just after a `(`, a space or a control character, and 0, where `readDestination`'s run begins. At a stop only the innermost open start has its parentheses balanced, so the starts below it keep no end. function destinationEnds(text: string): Int32Array { const ends = new Int32Array(text.length + 1) - const open = [0] - for (let index = 0; index < text.length; index += backslashEscape(text, index) === undefined ? 1 : 2) { + let open = [0] + for (let index = 0; index <= text.length; index += backslashEscape(text, index) === undefined ? 1 : 2) { const character = text.charAt(index) if (character === '(') open.push(index + 1) const start = character === ')' ? open.pop() : undefined if (start !== undefined) ends[start] = index - if (character !== ' ' && !holdsControlCharacter(character)) continue - stopAt(ends, open, index) - open.push(index + 1) + if (index < text.length && character !== ' ' && !holdsControlCharacter(character)) continue + const innermost = open.pop() + if (innermost !== undefined) ends[innermost] = index + open = [index + 1] } - stopAt(ends, open, text.length) return ends }