Corpus 2e1: the carve-out escapes and the claimed line #19

Merged
lilleman merged 2 commits from carve-outs into main 2026-08-26 22:29:41 +02:00
5 changed files with 43 additions and 37 deletions
Showing only changes of commit 8d1d96a2ee - Show all commits
+1 -1
View File
@@ -4,7 +4,7 @@ One directory per contract kind, each landing with its milestone:
- `round-trip/``<name>.json` + `<name>.md`: the markdown `adfToMarkdown` must emit for that
document, byte for byte, and that `markdownToAdf` must read back to it (AGENTS.md §2). Grouped
by node family, `combinations/` holding what crosses them.
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.
- `errors/``<name>.md`: markdown input that must not convert. A `<name>.error` beside it
@@ -5,33 +5,6 @@
"panelType": "info"
},
"content": [
{
"content": [
{
"text": ":::",
"type": "text"
}
],
"type": "paragraph"
},
{
"content": [
{
"text": "::: x",
"type": "text"
}
],
"type": "paragraph"
},
{
"content": [
{
"text": "::",
"type": "text"
}
],
"type": "paragraph"
},
{
"content": [
{
@@ -64,6 +37,33 @@
}
],
"type": "blockquote"
},
{
"content": [
{
"text": ":::",
"type": "text"
}
],
"type": "paragraph"
},
{
"content": [
{
"text": "::: x",
"type": "text"
}
],
"type": "paragraph"
},
{
"content": [
{
"text": "::",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "panel"
@@ -1,13 +1,13 @@
:::panel info
- \:::
> \:::
\:::
\::: x
\::
- \:::
> \:::
:::
\:::
+3 -1
View File
@@ -86,7 +86,8 @@ function fenceNestingFault(markdown: string): string | undefined {
const run = colons[1]?.length ?? 0
const enclosing = open[open.length - 1]
if (colons[2] === '') {
if (enclosing !== undefined && run < enclosing) return `${JSON.stringify(line)} is shorter than the ${enclosing} colons it would close`
if (enclosing === undefined) return `${JSON.stringify(line)} closes no open container`
if (run < enclosing) return `${JSON.stringify(line)} is shorter than the ${enclosing} colons it would close`
open.pop()
continue
}
@@ -101,6 +102,7 @@ test('the fence nesting check catches a fence a container cannot hold', () => {
assert.equal(fenceNestingFault(':::panel info\n- - :::panel warning\n B\n :::\n:::'), '"- - :::panel warning" sits in a container fenced with 3 colons')
assert.equal(fenceNestingFault(':::panel info\n10. :::panel warning\n B\n :::\n:::'), '"10. :::panel warning" sits in a container fenced with 3 colons')
assert.equal(fenceNestingFault('::::panel info\n:::\n::::'), '":::" is shorter than the 4 colons it would close')
assert.equal(fenceNestingFault('Text\n:::\n'), '":::" closes no open container')
assert.equal(fenceNestingFault('::::panel info\n- - :::panel warning\n B\n :::\n::::'), undefined)
assert.equal(fenceNestingFault(':::tableCell\n```text\n:::::::panel warning\n:::\n```\n:::'), undefined)
})
+8 -4
View File
@@ -84,8 +84,8 @@ detail is settled at its own milestone.
attribute fallback 2e owes, since the reservation leaves that node no other spelling
whatever 1d decides for its `localId`.
- [ ] **2e — Carve-outs and combinations.** Fixtures and emitter together, into
`corpus/round-trip/combinations/` — the documents that cross what 2a2d isolated.
- [ ] **2e1 — The carve-outs and the claimed line.** The directory opens: the three carve-outs
`corpus/round-trip/combinations/`.
- [ ] **2e1 — The carve-outs and the claimed line.** The three carve-outs
and their escapes, and a paragraph line inside a container body shaped like a closing
fence (`:::`, `::: x`). Guard `fenceNestingFault`'s bare-run pop here too — a run shorter
than the open fence is a fault, not a close — which today's emitter cannot reach.
@@ -117,7 +117,8 @@ detail is settled at its own milestone.
and it is provable without one. It also settles the emitter's one known approximation:
delimiter flanking is exact, but CommonMark's *matching* — the multiple-of-3 rule and the
way a run splits across several openers — is not modelled. No reachable violation has been
found by hand; the property test is what decides it.
found by hand; the property test is what decides it, and 2e1's `carve-out-strike` pins a
second backslash only flanking-without-matching emits.
- [ ] **2f — The attributes CommonMark cannot hold.** 1d's settled answer: the block nodes
CommonMark spells — `blockquote`, `bulletList`, `codeBlock`, `heading`, `listItem`,
`orderedList`, `paragraph`, `rule` — get directive sections in `spec/flavour.md` carrying
@@ -140,7 +141,10 @@ detail is settled at its own milestone.
assumes — CommonMark flanking, as for `*` — which `spec/flavour.md` does not yet pin, and
the precedence between a directive and the constructs a raw attribute value opens inside it —
a code span, an entity, raw HTML — which one directive alone already reaches until 2e's
escape lands.
escape lands. Whether a claimed line interrupts a paragraph is pinned for the plain case and
unstated for the lazy one: a closing fence on the line after a blockquote's open paragraph is
lazy continuation in CommonMark, which would swallow the fence and leave the container
unclosed. 2e1's `closing-fence-line` orders its blockquote away from that edge meanwhile.
`src/` gets its hierarchy at the same split — `adf/`,
`markdown/`, `html/`, the grammar module shared inside `markdown/` — while the rename is
still mechanical. Three files do not move whole: `block-directives.ts` and