3
0
mirror of https://github.com/larvit/adf-codec.git synced 2026-11-13 12:18:28 +01:00

Answer prose round 1: 0 🔴, 2 🟡 (0 misread); the memo's comments say what they guard, two test names say what they check, item 151 names the one walk

This commit is contained in:
2026-10-28 12:09:38 +01:00
committed by Lillem4n
parent 167d59836c
commit 273c08ac08
4 changed files with 13 additions and 13 deletions
+2 -2
View File
@@ -18,7 +18,7 @@ test('a target nothing closes reads as none, at every `](`', () => {
assert.deepEqual(targets('[a](b'.repeat(3)), [undefined, undefined, undefined])
})
test('targets sharing a destination end share its title and its failure', () => {
test('targets sharing a destination end share its failure', () => {
assert.deepEqual(targets('[a](b[c](d "t" x)'), [undefined, undefined])
assert.deepEqual(targets('[a](b[c](d "t" x'), [undefined, undefined])
})
@@ -28,7 +28,7 @@ test('an unbracketed destination ends at its first unmatched `)`', () => {
assert.deepEqual(targets('[a](b\\)c)'), [{ destination: 'b)c', length: 6 }])
})
test('a target reads its whitespace, a bracketed destination and a title', () => {
test('a target reads whitespace and a title around its destination, bracketed, unbracketed or empty', () => {
assert.deepEqual(targets('[a]( b\n "t" ) [c](<d e> \'f\') [g]()'), [
{ destination: 'b', length: 10, title: 't' },
{ destination: 'd e', length: 11, title: 'f' },
+6 -6
View File
@@ -7,17 +7,17 @@ export type LinkDefinition = { destination: string; title?: string }
export type LinkPart = { length: number; value: string }
// What follows a destination ending at an index, the same for every destination ending there: the index after the `)`, and the title.
// Every destination ending at one index shares what follows it: the title and the closing `)`.
type Tail = { end: number; title: string | undefined }
// `destinationEnds[start]` is where an unbracketed destination starting there stops, for every start a target reads from: one after a `(`, a space or a control character.
// `destinationEnds[start]` is where an unbracketed destination starting there stops, for every start a target can have: the index just after a `(`, a space or a control character.
export type TargetMemo = { destinationEnds: Int32Array; tails: Map<number, Tail | undefined> }
// The lossless flavour escapes a quote alone, writing a title holding a backslash or an entity reference as a directive (spec/flavour.md, Marks); the portable flavour writes no directive, so it escapes those too, and a table cell's `|` (README, Portable markdown).
export type TitleEscapes = 'lossless' | 'portable' | 'portable-table-cell'
const bracketedDestination = /<((?:[^\n<>\\]|\\[^\n])*)>/y
// The longest text an unbracketed destination can take: it stops at a space or a control character.
// The longest text an unbracketed destination can take.
const destinationRun = new RegExp(`[^ ${controlCharacterRange}]*`, 'y')
// CommonMark: a link label holds at most this many characters between its brackets, an escape counting as two.
export const labelLimit = 999
@@ -51,7 +51,7 @@ export function readDestination(text: string, offset: number): LinkPart | undefi
return length === 0 ? undefined : { length, value: decodeTextEscapes(run.slice(0, length)) }
}
// `offset` sits on the `(` a link text closes into. The definition is read on demand, so a target its link refuses costs no copy.
// `offset` sits on the `(` a link text closes into. The definition is built only when called, so a link the reader then refuses never decodes its destination.
export function readInlineTarget(text: string, offset: number, memo: TargetMemo): InlineTarget | undefined {
const start = skipLinkWhitespace(text, offset + 1)
const end = destinationEnd(text, start, memo)
@@ -67,7 +67,7 @@ export function readInlineTarget(text: string, offset: number, memo: TargetMemo)
return { definition, length: tail.end - offset }
}
// Every unbracketed destination's end in one pass, with a stack of the starts still open. The text's own start is one, which `readDestination` reads.
// Every unbracketed destination's end, in one pass. Index 0 is a start too, for `readDestination`, which reads from its text's first character.
export function targetMemo(text: string): TargetMemo {
const destinationEnds = new Int32Array(text.length + 1)
const open = [0]
@@ -123,7 +123,7 @@ export function readTitle(text: string, offset: number): LinkPart | undefined {
// The label, the destination and the title each take at most one line ending with them.
export function skipLinkWhitespace(text: string, offset: number): number {
linkWhitespace.lastIndex = offset
// The pattern matches empty, so the test always moves `lastIndex` past the whitespace.
// The pattern matches empty, so the test never fails, which would reset `lastIndex` to 0.
linkWhitespace.test(text)
return linkWhitespace.lastIndex
}
-1
View File
@@ -87,7 +87,6 @@ type Scan = {
pieces: Piece[]
source: string
spans: NestedSpans
// Built at the first inline target the scan reads.
targets: TargetMemo | undefined
}
+5 -4
View File
@@ -72,10 +72,11 @@
### 151. Read a link destination holding an unbalanced unescaped parenthesis as no destination, in every markdown reader.
CommonMark lets an unbracketed destination hold a parenthesis only escaped or in a balanced pair,
but `[a](b(c "t")` reads as a link to `b(c` titled `t`, where cmark and commonmark.js read text.
`readDestination` and `targetMemo` (`src/markdown/commonmark/link-syntax.ts`) stop at a space with
parentheses still open and keep the destination. The writers escape an unbalanced parenthesis, so
no written markdown changes. Found while shipping item 140, 2026-10-28.
but `[a](b(c "t")` reads as a link to `b(c` titled `t`; cmark and commonmark.js read it as text.
`targetMemo` (`src/markdown/commonmark/link-syntax.ts`), which `readDestination` reads through,
stops at a space with parentheses still open and keeps the destination. The writers escape an
unbalanced parenthesis, so markdown they wrote reads the same after the fix. Found while shipping
item 140, 2026-10-28.
### 95. Find the code-span closers an escape forms, the mark runs a line cannot spell and the text `[` that opens link text by reading the line back, in place of `escapeClosedRuns`, `unspellableRuns` and `lastLinkClose` predicting them.