diff --git a/docs/reading-cost.md b/docs/reading-cost.md index 901d90e..84f6a86 100644 --- a/docs/reading-cost.md +++ b/docs/reading-cost.md @@ -792,3 +792,16 @@ Read most (opens and searches): - `package.json` 1 - `src/markdown/commonmark/backtick-runs.ts` 1 - `src/markdown/commonmark/grammar.ts` 1 + +## Reading cost, chunk ca685f64bce7, finished 2026-11-09T00:27:56Z + +85473 new input tokens before the first commit. + +Read most (opens and searches): + +- `src/markdown/inline-content.ts` 8 +- `todo.md` 5 +- `README.md` 3 +- `AGENTS.md` 1 +- `ci.sh` 1 +- `docs/decisions.md` 1 diff --git a/src/markdown/inline-content.ts b/src/markdown/inline-content.ts index 782f761..e505b3a 100644 --- a/src/markdown/inline-content.ts +++ b/src/markdown/inline-content.ts @@ -177,7 +177,8 @@ function readToken(scan: Scan, inline: InlineSource, index: number, token: Inlin scan.pieces.push({ image: token.image, kind: 'open' }) return success(end) case 'bracket-close': - return closeBracket(scan, index) + flush(scan, false) + return applyClosing(scan, closeBracket(scan, index), index) case 'code-span': flush(scan, false) record(scan.shared, { at: scan.offset + index, kind: 'codeSpans' }) @@ -416,42 +417,40 @@ function linkedText(text: string, scheme: string): AdfNode { return { marks: [{ attrs: { href: `${scheme}${text}` }, type: 'link' }], text, type: 'text' } } -function closeBracket(scan: Scan, index: number): Result { - flush(scan, false) - const open = popBracket(scan) - if (open === undefined) return success(literalClose(scan, index)) - const target = open.active ? resolveTarget(scan, open.start, index) : undefined - if (target === undefined) return success(unopened(scan, open, index)) +// What a `]` makes of the innermost open bracket, decided without writing to the scan. `closesLink` is whether a link closes here, or one inside these brackets did, which leaves every link opener still open literal. +type Closing = + | { closesLink: boolean; kind: 'literal'; open: OpenBracket | undefined } + | { closesLink: boolean; end: number; kind: 'built'; open: OpenBracket; pieces: Result } + +function closeBracket(scan: Scan, index: number): Closing { + const open = scan.brackets.at(-1) + if (open === undefined) return { closesLink: false, kind: 'literal', open } + const target = open.image || scan.brackets.length > scan.linkOpenersFrom ? resolveTarget(scan, open.start, index) : undefined + if (target === undefined) return { closesLink: false, kind: 'literal', open } const inner = scan.pieces.slice(open.index + 1) - // A link closing, or one inside these brackets, leaves every link opener still open literal. - if (!open.image) scan.linkOpenersFrom = scan.brackets.length // Before `linkPieces`' own-marks guard: brackets that go literal mark nothing, so a carry inside them is no error. - if (!open.image && holdsLink(inner)) return success(unopened(scan, open, index)) - record(scan.shared, { at: scan.offset + open.start - 1, kind: 'links' }) - const closed = open.image ? imagePieces(scan, inner, target.definition()) : linkPieces(scan, inner, target.definition()) - if (!closed.ok) return closed - scan.pieces.length = open.index - for (const piece of closed.value) scan.pieces.push(piece) - return success(index + 1 + target.length) + if (!open.image && holdsLink(inner)) return { closesLink: true, kind: 'literal', open } + const pieces = open.image ? imagePieces(scan, inner, target.definition()) : linkPieces(scan, inner, target.definition()) + return { closesLink: !open.image, end: index + 1 + target.length, kind: 'built', open, pieces } } // A close that builds nothing still pops its bracket, so no later close walks past it. -function popBracket(scan: Scan): (OpenBracket & { active: boolean }) | undefined { - const open = scan.brackets.pop() - if (open === undefined) return undefined - const active = open.image || scan.brackets.length >= scan.linkOpenersFrom - scan.linkOpenersFrom = Math.min(scan.linkOpenersFrom, scan.brackets.length) - return { ...open, active } -} - -function literalClose(scan: Scan, index: number): number { - scan.pending += ']' - return index + 1 -} - -function unopened(scan: Scan, open: OpenBracket, index: number): number { - scan.pieces[open.index] = { kind: 'nodes', nodes: bracketNodes(open) } - return literalClose(scan, index) +function applyClosing(scan: Scan, closing: Closing, index: number): Result { + const { closesLink, open } = closing + if (open !== undefined) { + scan.brackets.pop() + scan.linkOpenersFrom = closesLink ? scan.brackets.length : Math.min(scan.linkOpenersFrom, scan.brackets.length) + } + if (closing.kind === 'literal') { + if (open !== undefined) scan.pieces[open.index] = { kind: 'nodes', nodes: bracketNodes(open) } + scan.pending += ']' + return success(index + 1) + } + record(scan.shared, { at: scan.offset + closing.open.start - 1, kind: 'links' }) + if (!closing.pieces.ok) return closing.pieces + scan.pieces.length = closing.open.index + for (const piece of closing.pieces.value) scan.pieces.push(piece) + return success(closing.end) } function bracketNodes(bracket: Bracket | OpenBracket): AdfNode[] { diff --git a/todo.md b/todo.md index 4e3fe2b..2e80d70 100644 --- a/todo.md +++ b/todo.md @@ -13,7 +13,6 @@ | ID | Release | Blocked by | Exempt | Item | L | E | I | R | S | Moves | Score | |---|---|---|---|---|---|---|---|---|---|---|---| | 153 | 0.4.0 | | question | **Decide whether `src/markdown/` root may import `emit/`, `parse/` or `portable/`, and hold the answer in `.oxlintrc.json` and §The source parts by ADF and format.** | 1 | 2 | 2 | 3 | 2 | code health by 3 | 26.2 | -| 155 | 0.4.0 | | | **Have `closeBracket` return the link, image or literal bracket it builds, and apply it to the scan in one caller.** | 2 | 1 | 2 | 3 | 4 | code health by 4 | 17.4 | | 162 | 0.4.0 | | | **Pin each rule of the emphasis sweep with a hand-worked line, so every mutant that lets a pair fall where the one-run-per-pass reference keeps it fails a test.** | 2 | 1 | 1 | 2 | 4 | code health by 4 | 17.4 | | 175 | 0.4.0 | | | **Delete the guards in `tryTaskList` that `reduceToPortable` makes unreachable, once a test shows `adfToPortableMarkdown` reaches none of them.** | 1 | 1 | 1 | 1 | 2 | code health by 2 | 17.4 | | 45 | 0.4.0 | | defect | **Replace `isAdfDocument` with a reader returning `Result`.** | 2 | 1 | 3 | 4 | 3 | 1 by 2 | 13.1 | @@ -73,13 +72,6 @@ a paragraph opening with a spellable `!adf:link` (§Nothing recurses unbounded). `src/markdown/**` bans only `html/`; banning `emit/`, `parse/` and `portable/` for the root's own files, with its `ci.sh` probe, extends the decision. -### 155. Have `closeBracket` return the link, image or literal bracket it builds, and apply it to the scan in one caller. - -A comprehension seat found `src/markdown/inline-content.ts` `closeBracket` hard: it writes -`scan.pieces`, the link openers and the reading, and calls `unopened`, `literalClose` and -`popBracket`, which write `scan.pieces`, `scan.pending` and the link openers. Make that place -easier to read. - ### 162. Pin each rule of the emphasis sweep with a hand-worked line, so every mutant that lets a pair fall where the one-run-per-pass reference keeps it fails a test. About 20 mutants of `src/markdown/emit/unspellable-runs.ts` survive the gate. Each lets a pair fall