mirror of
https://github.com/larvit/adf-codec.git
synced 2026-11-12 19:58:28 +01:00
Answer stability round 1: a task list nested beside a task item of paragraphs stays nested when written, the property checks every block task item holds paragraphs alone, item 49 names the checkbox loss it closes, and item 93 files the stale refusal path
This commit is contained in:
@@ -25,6 +25,16 @@ function shownText(nodes: readonly AdfNode[]): string[] {
|
||||
return shown.filter((text) => text !== '')
|
||||
}
|
||||
|
||||
function heldByBlockTaskItems(nodes: readonly AdfNode[]): string[] {
|
||||
const held: string[] = []
|
||||
for (const node of nodes) {
|
||||
const content = node.content ?? []
|
||||
if (node.type === 'blockTaskItem') for (const child of content) held.push(child.type)
|
||||
for (const type of heldByBlockTaskItems(content)) held.push(type)
|
||||
}
|
||||
return held
|
||||
}
|
||||
|
||||
test('a generated document emits markdown that reads back to it', { timeout: propertyTimeout }, () => {
|
||||
fc.assert(
|
||||
fc.property(adfDocument, (document) => {
|
||||
@@ -38,13 +48,14 @@ test('a generated document emits markdown that reads back to it', { timeout: pro
|
||||
)
|
||||
})
|
||||
|
||||
test('a generated document writes portable markdown refusing only what the guard refuses, and that markdown reads back to its text and to itself', { timeout: propertyTimeout }, () => {
|
||||
test('a generated document writes portable markdown refusing only what the guard refuses, and that markdown reads back to its text and to itself, a block task item holding paragraphs alone', { timeout: propertyTimeout }, () => {
|
||||
fc.assert(
|
||||
fc.property(adfDocument, (document) => {
|
||||
const written = adfToPortableMarkdown(document)
|
||||
assert.ok(written.ok, written.ok ? '' : `${written.error.code}: ${written.error.message}`)
|
||||
const read = portableMarkdownToAdf(written.value)
|
||||
assert.ok(read.ok, read.ok ? '' : `${read.error.code}: ${read.error.message} — reading ${JSON.stringify(written.value)}`)
|
||||
assert.deepEqual(heldByBlockTaskItems(read.value.content ?? []).filter((type) => type !== 'paragraph'), [], `reading ${JSON.stringify(written.value)}`)
|
||||
const reduced = reduceToPortable(document)
|
||||
assert.deepEqual(shownText(read.value.content ?? []), reduced.ok ? shownText(reduced.value.content ?? []) : reduced, `reading ${JSON.stringify(written.value)}`)
|
||||
assert.deepEqual(adfToPortableMarkdown(read.value), written, `reading ${JSON.stringify(written.value)}`)
|
||||
|
||||
@@ -148,6 +148,9 @@ test('reads a bullet list whose every item leads with a task marker to a task li
|
||||
test('moves a nested task list beside its item and makes an item holding more than one paragraph a block task item', () => {
|
||||
assert.deepEqual(read('- [x] Parent\n - [ ] Child\n- [ ] Next\n'), [bare('taskList', task('DONE', text('Parent')), bare('taskList', task('TODO', text('Child'))), task('TODO', text('Next')))])
|
||||
assert.deepEqual(read('- [x] First.\n\n Second.\n- [ ] Next\n'), [bare('taskList', node('blockTaskItem', { state: 'DONE' }, said('First.'), said('Second.')), task('TODO', text('Next')))])
|
||||
const nested = bare('taskList', node('blockTaskItem', { state: 'DONE' }, said('p1'), said('p2')), bare('taskList', task('TODO', text('n'))), task('TODO', text('B')))
|
||||
assert.deepEqual(read('- [x] p1\n\n p2\n - [ ] n\n- [ ] B\n'), [nested])
|
||||
assert.deepEqual(roundTripped(nested), [nested])
|
||||
})
|
||||
|
||||
test('stands a block a task item cannot hold, and what follows it in the item, after the task list, which resumes at the next task', () => {
|
||||
|
||||
@@ -132,6 +132,8 @@ test('spells a task list as a bullet list whose items lead with their state', ()
|
||||
const blockTask = node('blockTaskItem', { state: 'DONE' }, said('First.'), said('Second.'))
|
||||
const codeTask = node('blockTaskItem', { state: 'TODO' }, { content: [text('x')], type: 'codeBlock' })
|
||||
assert.equal(portable(node('taskList', {}, blockTask, codeTask)), '- [x] First.\n\n Second.\n- [ ]\n\n```\nx\n```\n')
|
||||
const parent = node('blockTaskItem', { state: 'TODO' }, said('q'), said('q2'))
|
||||
assert.equal(portable(node('taskList', {}, parent, node('taskList', {}, task('DONE', 'n')), task('DONE', 'B'))), '- [ ] q\n\n q2\n - [x] n\n- [x] B\n')
|
||||
const listTask = node('blockTaskItem', { state: 'DONE' }, bulletList(item(said('a'))))
|
||||
assert.equal(portable(node('taskList', {}, task('TODO', ''), listTask, node('taskList', {}, task('TODO', 'b')))), '- \\[ ]\n- \\[x]\n- a\n- \\[ ] b\n')
|
||||
assert.equal(portable(node('taskList', {}, task('DONE', 'a'), node('taskList', {}, task('TODO', '')))), '- [x] a\n - [ ]\n')
|
||||
|
||||
@@ -314,11 +314,8 @@ function reduceTaskList(node: AdfNode, reduction: Reduction): Result<AdfNode[]>
|
||||
return success(listOf(nodeContent(tasksAsText({ content: tasks, type: 'taskList' })), 'bulletList'))
|
||||
}
|
||||
|
||||
// The writer nests a list in the task before it, so one closing a block task item's blocks merges with it.
|
||||
function nestIn(tasks: AdfNode[], nested: readonly AdfNode[]): void {
|
||||
const previous = tasks.at(-1)
|
||||
if (previous?.type === 'blockTaskItem') tasks[tasks.length - 1] = { ...previous, content: mergedLists([...nodeContent(previous), ...nested]) }
|
||||
else for (const block of mergedLists(nested)) tasks.push(block)
|
||||
for (const block of mergedLists(nested)) tasks.push(block)
|
||||
}
|
||||
|
||||
function isTask(node: AdfNode | undefined): boolean {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
`Bar = 9`
|
||||
|
||||
`Next ID = 93`
|
||||
`Next ID = 94`
|
||||
|
||||
| Goal | W |
|
||||
|---|---|
|
||||
@@ -47,6 +47,7 @@
|
||||
| 75 | 0.3.0 | | **Make the generated-markdown property write block carries as `adf:` fences, canonical and hostile.** | 2 | 2 | 3 | 7 | 1 | 17.5 |
|
||||
| 81 | 0.3.0 | | **Fail the gate unless the README's error tables list exactly the codes `ConvertErrorCode` holds.** | 1 | 2 | 5 | 5 | 3 | 16.3 |
|
||||
| 46 | 0.3.0 | | **Publish the bundle size in the README, failing the release pipeline when it drifts.** | 2 | 4 | 5 | 5 | 3, 8 | 14.8 |
|
||||
| 93 | 0.3.0 | | **Report a refusal's `path` where the node would stand after `portableMarkdownToAdf` moves it out of a task item.** | 2 | 3 | 3 | 5 | 1 | 13.2 |
|
||||
| 72 | 0.3.0 | decision | **Re-create `gitea.larvit.se/larvit/adf-codec` as a read-only pull mirror of GitHub, its issues and Actions off and its description pointing at GitHub.** | 2 | 1 | 3 | 6 | 3 | 13.1 |
|
||||
| 52 | 0.3.0 | | **Spell `colwidth` as a comma list, `colwidth="340,420"`.** | 3 | 3 | 5 | 6 | 6 | 11.5 |
|
||||
| 51 | 0.3.0 | | **Match a reference label to its definition under Unicode case folding.** | 2 | 2 | 2 | 7 | 4, 5 | 10.9 |
|
||||
@@ -74,7 +75,8 @@ Today `- a` then `+ b`, or `1.` then `1)`, reads as one list; CommonMark reads t
|
||||
301 and 302). Goal 5 settles it for the lossless flavour too: CommonMark spells adjacent lists by
|
||||
changing the marker, so `adfToLosslessMarkdown` and `adfToPortableMarkdown` alternate `-` and `*`
|
||||
between adjacent bullet lists, a task list among them, and `.` and `)` between adjacent ordered
|
||||
lists, and `!adf:listBreak` retires. `*` is the maintainer's pick (2026-10-04). Breaking: `MIGRATION.md`'s Readings and
|
||||
lists, and `!adf:listBreak` retires. Until then, a bullet list standing after a task list joins
|
||||
it when written, so `adfToPortableMarkdown` writes every task's marker as text. `*` is the maintainer's pick (2026-10-04). Breaking: `MIGRATION.md`'s Readings and
|
||||
Spellings tables gain their rows. Examples 301 and 302 lose their `pending` exceptions, and the
|
||||
spelling leaves the README's "Seven CommonMark spellings" bullet, which counts one fewer.
|
||||
|
||||
@@ -249,6 +251,13 @@ or report the unminified gzip). The figure lands in README §The package beside
|
||||
dependencies" claim. Measured today, unminified: tarball 60.4 kB, unpacked 221.5 kB, JS gzipped 45.6
|
||||
kB.
|
||||
|
||||
### 93. Report a refusal's `path` where the node would stand after `portableMarkdownToAdf` moves it out of a task item.
|
||||
|
||||
`readBlocks` fixes a block's path before `splitTaskList` and `bulletNode` move blocks beside or
|
||||
after their task, so `- [x] A\n\n ```\n x\n ```\n\n <div>x</div>\n` reports
|
||||
`/content/0/content/0/content/2` for a block that would stand at `/content/2`. The README says
|
||||
`path` is the node's place from the document root.
|
||||
|
||||
### 72. Re-create `gitea.larvit.se/larvit/adf-codec` as a read-only pull mirror of GitHub, its issues and Actions off and its description pointing at GitHub.
|
||||
|
||||
Lands after every other `0.3.0` item (the maintainer, 2026-10-05). The maintainer does this. Gitea
|
||||
|
||||
Reference in New Issue
Block a user