diff --git a/AGENTS.md b/AGENTS.md index 5744d0b..0f0a775 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -307,14 +307,15 @@ someone spells it or pins it. opener search stay whole: broken into named steps they drift from the algorithm being faithful is the whole point of. - A readable spelling tried ahead of a general one — a CommonMark block, the image, the pipe - table, a pipe cell — gives way with `undefined` for every shape it cannot spell, and fails only - where the general form fails on the same node; it takes the `try` prefix, so both arms read at - the call site (20). Refusing there refuses a document the general form spells, so a refusal the - general form does not share belongs in the general form or nowhere — save the nested list a - tight spelling would swallow, whose refusal the tight-versus-blank answer owns (`todo.md` 2b). A readable spelling that must spell its subtree - before it can give way — the list, whose thematic-break first line and blank lines exist only - spelled — hands that one walk to the general form instead: giving way after the walk walks - again at every level, doubling per level (4b). + table, a pipe cell, the CommonMark link — gives way with `undefined` for every shape it cannot + spell, and fails only where the general form fails on the same node; it takes the `try` prefix, + so both arms read at the call site (20). Refusing there refuses a document the general form + spells, so a refusal the general form does not share belongs in the general form or nowhere — + save the nested list a tight spelling would swallow, whose refusal the tight-versus-blank answer + owns (`todo.md` 2b). A readable spelling that must spell its subtree before it can give way — + the list, whose thematic-break first line and blank lines exist only spelled — hands that one + walk to the general form instead: giving way after the walk walks again at every level, doubling + per level (4b). - Nothing recurses unbounded: the guards walk iteratively, and blocks, marks and JSON values — an attribute's and a carried node's alike — are all held to 500 levels, so a deep document is a `Result` rather than the stack overflow that waits near 2000. A level is one block-list diff --git a/src/markdown/emit/inline-line.ts b/src/markdown/emit/inline-line.ts index 5872a9a..1c2034c 100644 --- a/src/markdown/emit/inline-line.ts +++ b/src/markdown/emit/inline-line.ts @@ -258,7 +258,7 @@ function emitMarkedRun(nodes: readonly AdfNode[], mark: AdfMark, depth: number, if (attributes === undefined) return success({ carry: range }) if (spelling.kind === 'code') return emitCodeSpan(nodes, depth, range, path) if (spelling.kind === 'emphasis') return emitEmphasis(nodes, spelling.spelling, depth, range, context) - const link = spelling.kind === 'link' ? emitLink(nodes, mark, depth, range, context) : undefined + const link = spelling.kind === 'link' ? tryLink(nodes, mark, depth, range, context) : undefined if (link !== undefined) return link const inner = emitRun(nodes, depth + 1, index, { ...context, bracketed: true, spansLines: false }) if (!inner.ok) return inner @@ -300,8 +300,7 @@ function needsPadding(text: string): boolean { return text.startsWith(' ') && text.endsWith(' ') && /[^ ]/.test(text) } -// `undefined` where the link takes the directive form the caller spells. -function emitLink(nodes: readonly AdfNode[], mark: AdfMark, depth: number, range: NodeRange, context: InlineContext): Result | undefined { +function tryLink(nodes: readonly AdfNode[], mark: AdfMark, depth: number, range: NodeRange, context: InlineContext): Result | undefined { const href = linkHref(nodeAttrs(mark)) if (href === undefined) return success({ carry: range }) const opening = depth === 0 && range.first === 0 && context.openingLinkAsDirective diff --git a/todo-history.md b/todo-history.md index ea3d371..d06e268 100644 --- a/todo-history.md +++ b/todo-history.md @@ -933,8 +933,11 @@ The done `todo.md` items in full, as they were written. `todo.md` keeps a one-li directive form shares — a walk, `codeBlockText`, `emitInlineLine` — so moving it out is the second walk 4b removed, and they take the prefix alone. `tryRule` refuses nothing, so it takes the narrower type as well, `string | undefined` through `readableText`, which is what - `tryImage` and `tryPipeTable` already return. §11's readable-spelling rule carries the - prefix, so the next spelling is named by the rule rather than by the six beside it. + `tryImage` and `tryPipeTable` already return. `emitLink` is the same channel outside the + six — the CommonMark link tried ahead of the inline directive form — so it takes the prefix + with them, and the comment naming its `undefined` arm goes with the rename. §11's + readable-spelling rule carries the prefix, so the next spelling is named by the rule rather + than by the seven beside it. - [x] **28 — `emitLine`'s retry loop cannot spin (`0.2.0`).** `emit/inline-line.ts:67` is a `for (;;)` that re-emits the line until every unspellable node has been carried, and its termination rests on a comment: each pass carries at least one more node, or flips diff --git a/todo.md b/todo.md index 199145f..d0cc238 100644 --- a/todo.md +++ b/todo.md @@ -8,7 +8,9 @@ milestone. A done item shrinks to its title here; its full text moves to `todo-h Start a session with: `Read AGENTS.md and todo.md, then do what todo.md's "Next session" says.` 1. `git fetch origin` first and read this file at `origin/main`, then branch off it, not the - worktree left behind: a checkout behind the remote reads a merged item as unchecked. + worktree left behind: a checkout behind the remote reads a merged item as unchecked. `tea pr + list` in the same breath — an open PR is a chunk already in flight whatever line 3 says, and + finishing it is the session. 2. The first unchecked item in shipping order, per AGENTS.md §15 — the order the Milestones line states, which wins over where an item's bullet sits: a newly filed item is written beside the one it came in with, not at its own place in the order. Where that item has no release, the