mirror of
https://github.com/larvit/adf-codec.git
synced 2026-11-12 11:48:29 +01:00
Answer architecture round 1: a * marker before a first line of * alone takes a backslash, the marker choice is a decision, the spec names which lists alternate, and the stale listBreak test goes
This commit is contained in:
@@ -13,6 +13,7 @@ In `docs/decisions.md`:
|
||||
- Markdown in is a canonical fixpoint
|
||||
- Equality is deep
|
||||
- `!adf:textBreak{}` parts text CommonMark would join
|
||||
- A list beside one of its kind takes the other marker
|
||||
- An empty key spells `empty`
|
||||
- `-0` is spelled `-0`
|
||||
- Empty markdown is a document of no blocks
|
||||
|
||||
@@ -175,6 +175,25 @@
|
||||
}
|
||||
],
|
||||
"type": "bulletList"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"text": "**",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
}
|
||||
],
|
||||
"type": "bulletList"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
|
||||
@@ -14,3 +14,5 @@
|
||||
|
||||
- - Head
|
||||
* Tail
|
||||
|
||||
* \**
|
||||
|
||||
+15
-3
@@ -71,6 +71,16 @@ Two adjacent text nodes CommonMark would read back as one are parted by the rese
|
||||
`!adf:textBreak{}`: a leaf building no node keeps both nodes and asks nothing of the text around
|
||||
it. A code span holds no directive, so the spans close and reopen around the leaf. The grammar: `spec/flavour.md` §Inline nodes, **Adjacent text nodes**.
|
||||
|
||||
## A list beside one of its kind takes the other marker
|
||||
|
||||
2026-10-04, the maintainer. Goals 4 and 5. Valid while CommonMark starts a new list where the
|
||||
bullet character or the ordered delimiter changes.
|
||||
|
||||
Every reader parts lists where the marker changes, as CommonMark does. Both writers give a bullet or
|
||||
task list right after a bullet or task list `* `, and an ordered list right after an ordered one
|
||||
`1)`; the list after that takes `- ` or `1.` again. CommonMark already spells two adjacent lists, so
|
||||
no directive parts them. The grammar: `spec/flavour.md` §Canonical form.
|
||||
|
||||
## An empty key spells `empty`
|
||||
|
||||
2026-10-03, a writer panel and the maintainer. Goals 1 and 6. Valid while no attribute value
|
||||
@@ -278,7 +288,9 @@ renderer confirming each shape. Reader panels settled `error` as an error panel,
|
||||
(3 of 3) and a Han, Hangul, kana, Thai, Lao, Khmer or Myanmar character on either side bounding a
|
||||
delimiter, so `は==日本語==で` (3 of 3), `==한국어==에서만` and `iPhone==専用==` (6 of 7) highlight,
|
||||
the external image's two forms (6 of 7), a rule opening a list item dropping and the omission
|
||||
notes (3 of 3), and a list's numbering overflowing into bullets (3 of 3, 5 of 7).
|
||||
notes (3 of 3), and a list's numbering overflowing into bullets (3 of 3). A 5 of 7 verdict joining
|
||||
adjacent ordered lists whose numbering breaks into one such list lapsed on 2026-10-09, when a
|
||||
delimiter change began parting them.
|
||||
An omission note reads as the converter's, never as the author's.
|
||||
Reading takes other tools' spellings, since it reads their output and writes none of them.
|
||||
Rejected: `~sub~` and `^sup^` (`~2~` is a strike on GitHub, so `subsup` drops), underline and colour
|
||||
@@ -689,8 +701,8 @@ two cannot disagree — which is what makes the kept value a memo rather than a
|
||||
per level above it otherwise.
|
||||
|
||||
The parse and the portable reduction keep each node's readable spelling in a memo, so the
|
||||
`commonMarkSpelling` ask stops spelling a node once per level above it. `text` and `spelling` carry
|
||||
no depth and `headroom` is affine in it, so a read at or above the depth that filled the entry
|
||||
`commonMarkSpelling` ask stops spelling a node once per level above it. The spelled texts and
|
||||
`spelling` carry no depth and `headroom` is affine in it, so a read at or above the depth that filled the entry
|
||||
rebases; a read below re-spells, because a hit skips the depth guards the walk it replaces runs and
|
||||
an ordered list past the marker cap gives way, spending two emitter levels where the parser spent
|
||||
one. Only what succeeded is kept, so no path minted at another position is ever read.
|
||||
|
||||
+4
-3
@@ -19,9 +19,10 @@ normalizes to it through the round-trip.
|
||||
single tilde or a run of three or more is literal text — and block structure resolves before
|
||||
inline, so a `~~~` line opens a CommonMark tilde code fence.
|
||||
- Bullet lists `- `; ordered lists incrementing `1.` `2.` `3.`, the first number the node's
|
||||
`order` attribute. A list right after a list of its kind takes the other marker, `* ` or `1)`,
|
||||
since CommonMark starts a new list where the bullet character or the delimiter changes; the
|
||||
list after that takes `- ` or `1.` again. Continuation lines align with the first character
|
||||
`order` attribute. A bullet or task list right after a bullet or task list takes `* `, and an
|
||||
ordered list right after an ordered one `1)`, since CommonMark starts a new list where the
|
||||
bullet character or the delimiter changes; the list after that takes `- ` or `1.` again, and a
|
||||
first line of `*` alone after `* ` opens with a backslash, `* \**`. Continuation lines align with the first character
|
||||
after the marker (two spaces for `- `, three for `1. `); blank lines inside an item are empty
|
||||
lines, none between a nested list and a CommonMark block above it — one wherever the nested
|
||||
list's own marker cannot interrupt a paragraph (an ordered list whose first number is not 1, or
|
||||
|
||||
@@ -177,6 +177,9 @@ test('alternates the marker between adjacent lists of a kind, the marker change
|
||||
assert.equal(markdown(adfToLosslessMarkdown(document(nested))), '- - x\n * x\n')
|
||||
const stars: AdfNode = { content: [{ content: [paragraph({ text: '* *', type: 'text' })], type: 'listItem' }], type: 'bulletList' }
|
||||
assert.equal(markdown(adfToLosslessMarkdown(document(list, stars))), '- x\n\n* \\* *\n')
|
||||
const strong: AdfNode = { content: [{ content: [paragraph({ text: '**', type: 'text' }), paragraph({ text: 'y', type: 'text' })], type: 'listItem' }], type: 'bulletList' }
|
||||
assert.equal(markdown(adfToLosslessMarkdown(document(strong))), '- **\n\n y\n')
|
||||
assert.equal(markdown(adfToLosslessMarkdown(document(list, strong))), '- x\n\n* \\**\n\n y\n')
|
||||
const carried: AdfNode = { ...list, attrs: { unknown: 'x' } }
|
||||
assert.ok(markdown(adfToLosslessMarkdown(document(carried, carried))).includes('```\n\n```adf:bulletList\n'))
|
||||
assert.ok(markdown(adfToLosslessMarkdown(document(carried, list))).endsWith('```\n\n- x\n'))
|
||||
|
||||
@@ -364,8 +364,10 @@ function tryListItemLines(inner: string, marker: string): string | undefined {
|
||||
if (body.some((line) => line !== '' && isBlankLine(line))) return undefined
|
||||
const indent = ' '.repeat(marker.length)
|
||||
const lines = body.map((line, index) => (index === 0 ? `${marker}${line}` : line === '' ? '' : `${indent}${line}`))
|
||||
if (isThematicBreak(lines[0] ?? '')) return undefined
|
||||
return lines.join('\n')
|
||||
const first = lines[0] ?? ''
|
||||
if (!isThematicBreak(first)) return lines.join('\n')
|
||||
// A first line of `*` alone beside the `* ` marker is text, since a rule is spelled `---`: a backslash keeps it from reading as one.
|
||||
return inner.startsWith('*') ? [`${marker}\\${first.slice(marker.length)}`, ...lines.slice(1)].join('\n') : undefined
|
||||
}
|
||||
|
||||
function tryParagraph(node: AdfNode, path: ConvertErrorPath, flavour: WrittenFlavour): Result<EmittedBlock> | undefined {
|
||||
|
||||
@@ -284,10 +284,6 @@ test('reads a change of bullet character or ordered delimiter as a second list,
|
||||
assert.deepEqual(path(losslessMarkdownToAdf('- a\n\n* b\n\n| x |\n')), ['content', 2])
|
||||
})
|
||||
|
||||
test('reads the retired list separator as a directive no node spells', () => {
|
||||
assert.equal(content(losslessMarkdownToAdf('- a\n\n!adf:listBreak\n\n- b\n')), 'unknown-directive-name: the directive name listBreak reads back to no node; \\!adf: keeps the prefix literal')
|
||||
})
|
||||
|
||||
test('reads the image a pipe cell holds as its linked alt text', () => {
|
||||
assert.deepEqual(content(losslessMarkdownToAdf('| a |\n| --- |\n|  |\n')), [
|
||||
{ content: [{ content: [cell('tableHeader', text('a'))], type: 'tableRow' }, { content: [cell('tableCell', marked('x', link('/u')))], type: 'tableRow' }], type: 'table' },
|
||||
|
||||
Reference in New Issue
Block a user