Read the block nodes back, and stop a marker change splitting a list #40
+2
-1
@@ -6,7 +6,8 @@ One directory per contract kind, each landing with its milestone:
|
||||
document, byte for byte, and that `markdownToAdf` must read back to it (AGENTS.md §2). Grouped
|
||||
by what the fixture exercises.
|
||||
- `normalization/` — `<name>.md` + `<name>.json`: markdown input, and the document
|
||||
`markdownToAdf` must build from it. One-way; the markdown is not canonical.
|
||||
`markdownToAdf` must build from it, which must in turn emit and read back to itself. The
|
||||
markdown is not canonical.
|
||||
- `errors/` — `<name>.md`: markdown input that must not convert. A `<name>.error` beside it
|
||||
pins which error.
|
||||
- `real-payloads/` — `<name>.json`: sanitized live ADF, round-tripped ADF→markdown→ADF. No
|
||||
|
||||
@@ -223,6 +223,18 @@ export function setextHeadingLevel(line: string): number | undefined {
|
||||
return underline.startsWith('=') ? 1 : 2
|
||||
}
|
||||
|
||||
export function trimSpace(text: string): string {
|
||||
return text.replace(/^[ \t]+|[ \t]+$/g, '')
|
||||
function spaceOrTab(character: string): boolean {
|
||||
return character === ' ' || character === '\t'
|
||||
}
|
||||
|
||||
export function trimSpace(text: string): string {
|
||||
let start = 0
|
||||
while (start < text.length && spaceOrTab(text.charAt(start))) start += 1
|
||||
return trimTrailingSpace(text.slice(start))
|
||||
}
|
||||
|
||||
export function trimTrailingSpace(text: string): string {
|
||||
let end = text.length
|
||||
while (end > 0 && spaceOrTab(text.charAt(end - 1))) end -= 1
|
||||
return text.slice(0, end)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { AdfMark, AdfNode } from '../../adf/document.ts'
|
||||
import type { EmphasisPairing } from '../emphasis-matching.ts'
|
||||
import type { LinkDefinition } from '../link-syntax.ts'
|
||||
import { backslashEscape, decodeTextEscapes, inlineHtmlConstruct, readBracketedAutolink, readEmailAutolink } from '../commonmark-grammar.ts'
|
||||
import { backslashEscape, decodeTextEscapes, inlineHtmlConstruct, readBracketedAutolink, readEmailAutolink, trimTrailingSpace } from '../commonmark-grammar.ts'
|
||||
import { backtickRun, closingBacktickRun } from '../backtick-runs.ts'
|
||||
import { delimiterFlags, matchEmphasis, runLength } from '../emphasis-matching.ts'
|
||||
import { failure, faulted, success, type ConvertErrorPath, type Result } from '../../result.ts'
|
||||
@@ -28,9 +28,7 @@ type Run = { canClose: boolean; canOpen: boolean; character: string; index: numb
|
||||
|
||||
type Scan = { definitions: LinkDefinitions; path: ConvertErrorPath; pending: string; pieces: Piece[]; source: string }
|
||||
|
||||
const hardBreakSpaces = / {2,}$/
|
||||
const imageAlone = 'an image fits only as a paragraph of its own'
|
||||
const trailingSpace = /[ \t]+$/
|
||||
|
||||
export function parseInlineContent(source: string, definitions: LinkDefinitions, path: ConvertErrorPath): Result<InlineContent> {
|
||||
const scan: Scan = { definitions, path, pending: '', pieces: [], source }
|
||||
@@ -95,7 +93,7 @@ function readBackslash(scan: Scan, index: number): number {
|
||||
}
|
||||
|
||||
function readLineEnding(scan: Scan, index: number): number {
|
||||
const hard = hardBreakSpaces.test(scan.pending)
|
||||
const hard = scan.pending.endsWith(' ')
|
||||
flush(scan, true)
|
||||
if (hard) pushNode(scan, { type: 'hardBreak' })
|
||||
else scan.pending = ' '
|
||||
@@ -154,7 +152,7 @@ function readDirective(scan: Scan, index: number): Result<number> {
|
||||
}
|
||||
|
||||
function flush(scan: Scan, strip: boolean): void {
|
||||
const raw = strip ? scan.pending.replace(trailingSpace, '') : scan.pending
|
||||
const raw = strip ? trimTrailingSpace(scan.pending) : scan.pending
|
||||
scan.pending = ''
|
||||
if (raw !== '') scan.pieces.push({ kind: 'nodes', nodes: [{ text: decodeTextEscapes(raw), type: 'text' }] })
|
||||
}
|
||||
|
||||
+25
-25
@@ -281,31 +281,6 @@ Under **3 — `markdownToAdf` (`0.1.0`)**:
|
||||
`spec/flavour.md`'s closing-fence sentence now says: a run reaching past the innermost
|
||||
leaves the fence it did not close a named error, which §2 prefers to closing more than the
|
||||
author wrote.
|
||||
- [x] **3h — The block nodes.** `block-nodes/` reads back: the `codeBlock` directive's fenced body and the
|
||||
`language` attribute a bare fence leaves it; the media family's composition; and both
|
||||
table forms, the pipe table's cell split and its named errors. `fenceInfo` is a rule both
|
||||
directions answer alike and moves to the `markdown/` root with the language attribute.
|
||||
**Settled** (the maintainer, 2026-08-27): 1d's last pick, the one
|
||||
`container-block-separation` holds — a CommonMark block and a directive block sit adjacent
|
||||
in a container body with no blank line between them. That reduces the three cases to one
|
||||
rule, separation only where its absence would merge the blocks: the `:::` fence is
|
||||
separation already, and 3c's claim ends the lazy continuation that would otherwise swallow
|
||||
it. The fixture becomes a round-trip pair, and with `nested-list-separation` and 3e's pair
|
||||
that empties `corpus/unspellable/`: this chunk settles the directory's own guard in
|
||||
`corpus.test.ts` too, and `unspelled-block-separation`, which loses its only cause here.
|
||||
The emitter's other refusals survive on causes no fixture in that directory covers, so
|
||||
3k's one-list pass is where they get fixtures or the directory goes.
|
||||
**Settled** (the maintainer, 2026-09-01): losing that cause closed one of the shapes input
|
||||
accepted and emit refused, not the last. Two adjacent lists of a kind are what
|
||||
`adfToMarkdown` refuses and one `- ` spelling cannot hold apart, and the walk reached them
|
||||
two ways — a marker change, which CommonMark opens a second list on, and an empty last item,
|
||||
whose blank line pops the container the list's identity hung from. The parser opens no list
|
||||
beside one of its own kind instead, the way it already drops the blank lines between items;
|
||||
3k owes the CommonMark suite an exception where the reference HTML holds two `<ul>`. The
|
||||
`normalization/` arm emits each document and reads it back from here, so the population that
|
||||
class lives in is checked rather than read. The README's canonical-fixpoint sentence still
|
||||
claims more than the parser keeps — 3e's three shapes — which stays milestone 5's to
|
||||
narrow.
|
||||
- [x] **3g — The node tables read backwards.** `commonmark-subset/` reads back, the first
|
||||
directory to. A parsed directive becomes its node: the name to the type and an unknown one
|
||||
to a named error, the arg to the attribute it names, each value to the type its section
|
||||
@@ -334,3 +309,28 @@ Under **3 — `markdownToAdf` (`0.1.0`)**:
|
||||
path and returns `Result`, so no second reader took it. The drift guard earned itself on
|
||||
the way in: the spec's `text` attribute was missing from three inline table entries, which
|
||||
the content slot spells and the vocabulary walk already passes over.
|
||||
- [x] **3h — The block nodes.** `block-nodes/` reads back: the `codeBlock` directive's fenced body and the
|
||||
`language` attribute a bare fence leaves it; the media family's composition; and both
|
||||
table forms, the pipe table's cell split and its named errors. `fenceInfo` is a rule both
|
||||
directions answer alike and moves to the `markdown/` root with the language attribute.
|
||||
**Settled** (the maintainer, 2026-08-27): 1d's last pick, the one
|
||||
`container-block-separation` holds — a CommonMark block and a directive block sit adjacent
|
||||
in a container body with no blank line between them. That reduces the three cases to one
|
||||
rule, separation only where its absence would merge the blocks: the `:::` fence is
|
||||
separation already, and 3c's claim ends the lazy continuation that would otherwise swallow
|
||||
it. The fixture becomes a round-trip pair, and with `nested-list-separation` and 3e's pair
|
||||
that empties `corpus/unspellable/`: this chunk settles the directory's own guard in
|
||||
`corpus.test.ts` too, and `unspelled-block-separation`, which loses its only cause here.
|
||||
The emitter's other refusals survive on causes no fixture in that directory covers, so
|
||||
3k's one-list pass is where they get fixtures or the directory goes.
|
||||
**Settled** (the maintainer, 2026-09-01): losing that cause closed one of the shapes input
|
||||
accepted and emit refused, not the last. Two adjacent lists of a kind are what
|
||||
`adfToMarkdown` refuses and one `- ` spelling cannot hold apart, and the walk reached them
|
||||
two ways — a marker change, which CommonMark opens a second list on, and an empty last item,
|
||||
whose blank line pops the container the list's identity hung from. The parser opens no list
|
||||
beside one of its own kind instead, the way it already drops the blank lines between items;
|
||||
3k owes the CommonMark suite an exception where the reference HTML holds two `<ul>`. The
|
||||
`normalization/` arm emits each document and reads it back from here, so the population that
|
||||
class lives in is checked rather than read. The README's canonical-fixpoint sentence still
|
||||
claims more than the parser keeps — 3e's three shapes — which stays milestone 5's to
|
||||
narrow.
|
||||
|
||||
Reference in New Issue
Block a user