3
0
mirror of https://github.com/larvit/adf-codec.git synced 2026-11-12 11:48:29 +01:00

Answer principles round 1: 0 🔴, 2 🟡; drop the unreachable guards and pass the list index in

This commit is contained in:
2026-10-22 18:48:08 +02:00
committed by Lillem4n
parent f3b0576023
commit 3928fcddde
2 changed files with 13 additions and 10 deletions
+8 -8
View File
@@ -17,10 +17,10 @@ import { leadingMarker, readAlertMarker, readTaskMarker } from '../portable/conv
import { mintTaskIds } from '../portable/task-ids.ts'
import { parseBlocks } from './blocks.ts'
import { parseInlineContent } from './inline-content.ts'
import { pathTo, splitTaskList } from '../portable/task-list.ts'
import { readBlockDirectiveNode } from '../directive/node-reading.ts'
import { spellStringAttribute, unsupportedNodeShape } from '../directive/syntax.ts'
import { spellsEmpty } from '../directive/empty-keys.ts'
import { pathTo, splitTaskList } from '../portable/task-list.ts'
import { withinEngineLimits } from '../length-bound.ts'
type Paragraph = Extract<Block, { kind: 'paragraph' }>
@@ -86,7 +86,7 @@ function readBlocks(blocks: readonly ReadBlock[], reading: Reading, path: Conver
return positioned(faulted(fault, nodePath), block.position)
}
if (block.kind === 'bulletList' && flavourClaims[reading.flavour].taskMarkers) {
const listed = positioned(bulletBlocks(block.items, reading, nodePath, depth), block.position)
const listed = positioned(bulletBlocks(block.items, reading, nodePath, depth, content.length), block.position)
if (!listed.ok) return listed
for (const node of listed.value) content.push(node)
continue
@@ -209,7 +209,7 @@ function taskItems(items: readonly Block[][]) {
return led
}
function bulletBlocks(items: readonly Block[][], reading: Reading, path: ConvertErrorPath, depth: number): Result<AdfNode[]> {
function bulletBlocks(items: readonly Block[][], reading: Reading, path: ConvertErrorPath, depth: number, listIndex: number): Result<AdfNode[]> {
const tasks = taskBlocks(items, reading, path, depth)
if (tasks === undefined) {
const list = listNode({ type: 'bulletList' }, items, reading, path, depth)
@@ -218,14 +218,14 @@ function bulletBlocks(items: readonly Block[][], reading: Reading, path: Convert
if (!tasks.ok) return tasks
const { nodes: blocks, refusal } = tasks.value
if (refusal === undefined) return success(blocks)
const listIndex = path.at(-1)
const place = pathTo(blocks, refusal.marker)
if (place === undefined || typeof listIndex !== 'number') return { error: refusal.error, ok: false }
// Failures are values, so a marker the split ever copied out of sight leaves the error unplaced rather than asserting; task-list.test.ts pins that the split keeps it.
if (place === undefined) return { error: refusal.error, ok: false }
const [blockIndex, ...steps] = place
return { error: { ...refusal.error, path: [...path.slice(0, -1), listIndex + blockIndex, ...steps.slice(0, -2), ...refusal.within] }, ok: false }
}
// A block a task list refused. A placeholder holding `marker` stands where the block's node would, so the split that builds the list places the refusal too, and `within` is the error's path below that node.
// A block a task list refused. A placeholder holding `marker` as its one child stands where the block's node would, so the split that builds the list places the refusal too, and `within` is the error's path below that node.
type Refusal = { error: ConvertError; marker: AdfNode; within: ConvertErrorPath }
// Nodes read from task blocks and the first refusal among them; a list is read up to the task holding its refusal.
@@ -255,12 +255,12 @@ function taskBlocks(items: readonly Block[][], reading: Reading, path: ConvertEr
// The nodes a block of a task reads to. A refused one is a placeholder, typed as the split tells paragraphs and task lists from other blocks, so a paragraph that failed to read stays a paragraph.
function readTaskBlock(block: ReadBlock, reading: Reading, path: ConvertErrorPath, depth: number): TaskNodes {
const nodePath = [...path, 'content', 0]
const nested = block.kind === 'bulletList' && depth <= largestNesting ? taskBlocks(block.items, reading, nodePath, depth) : undefined
const nested = block.kind === 'bulletList' ? taskBlocks(block.items, reading, nodePath, depth) : undefined
if (nested?.ok) return nested.value
const read = positioned(nested ?? readBlocks([block], reading, path, depth), block.position)
if (read.ok) return { nodes: read.value, refusal: undefined }
const marker: AdfNode = { text: '', type: 'text' }
const type = block.kind === 'taskParagraph' ? 'paragraph' : block.kind === 'bulletList' && taskItems(block.items) !== undefined ? 'taskList' : block.kind
const type = block.kind === 'taskParagraph' ? 'paragraph' : nested === undefined ? block.kind : 'taskList'
return { nodes: [{ content: [marker], type }], refusal: { error: read.error, marker, within: read.error.path.slice(nodePath.length) } }
}
+5 -2
View File
@@ -4,12 +4,15 @@ import test from 'node:test'
import type { AdfNode } from '../../adf/document.ts'
import { pathTo, splitTaskList } from './task-list.ts'
test('names the steps from a block to a node deep in it, and none for a node it does not hold', () => {
test('names the steps from a block to a node deep in it', () => {
const target: AdfNode = { text: 'x', type: 'text' }
const blocks: AdfNode[] = [{ type: 'rule' }, { content: [{ content: [{ type: 'paragraph' }, { content: [target], type: 'paragraph' }], type: 'blockquote' }], type: 'panel' }]
assert.deepEqual(pathTo(blocks, target), [1, 'content', 0, 'content', 1, 'content', 0])
assert.deepEqual(pathTo(blocks, blocks[0] ?? target), [0])
assert.equal(pathTo(blocks, { text: 'x', type: 'text' }), undefined)
})
test('names no steps to a node the blocks do not hold', () => {
assert.equal(pathTo([{ content: [{ text: 'x', type: 'text' }], type: 'paragraph' }], { text: 'x', type: 'text' }), undefined)
})
test('keeps the inline nodes of a lone paragraph in the taskItem it becomes, so pathTo still finds them', () => {