Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 95ff9ee910 | |||
| 6681bec391 | |||
| 6c8ed03d36 |
@@ -38,7 +38,7 @@ type LineAttempt = { fallback: NodeRange | 'opening-link'; line?: undefined } |
|
||||
|
||||
type LineFallbacks = { carried: Set<number>; flavour: Flavour; openingLinkAsDirective: boolean }
|
||||
|
||||
export type PlainLineFallback = { kind: 'claimed-line'; line: number; text: string } | { kind: 'opening-link' } | { kind: 'unspellable-run'; run: MarkRun; runs: MarkRun[] }
|
||||
export type PlainLineFallback = { kind: 'claimed-line'; line: number; text: string } | { kind: 'opening-link' } | { kind: 'unspellable-run'; runs: [MarkRun, ...MarkRun[]] }
|
||||
|
||||
export function emitInlineLine(nodes: readonly AdfNode[], container: LineContainer, path: ConvertErrorPath, flavour: Flavour): Result<string> {
|
||||
const emitted = emitLine(nodes, container, path, flavour)
|
||||
@@ -120,7 +120,7 @@ function lineSegments(nodes: readonly AdfNode[], container: LineContainer, path:
|
||||
function attemptLine(segments: readonly InlineSegment[], container: LineContainer, path: ConvertErrorPath, flavour: Flavour): Result<LineAttempt> {
|
||||
const verdict = lineVerdict(segments, container, flavour)
|
||||
if (verdict.kind === 'opening-link') return success({ fallback: 'opening-link' })
|
||||
if (verdict.kind === 'unspellable-run') return success({ fallback: verdict.run })
|
||||
if (verdict.kind === 'unspellable-run') return success({ fallback: verdict.runs[0] })
|
||||
if (verdict.kind === 'claimed-line') return failure('unspellable-line-start', `block parsing would claim the emitted line ${JSON.stringify(verdict.text)}`, path)
|
||||
return success({ line: verdict.text })
|
||||
}
|
||||
@@ -129,8 +129,8 @@ function attemptLine(segments: readonly InlineSegment[], container: LineContaine
|
||||
function lineVerdict(segments: readonly InlineSegment[], container: LineContainer, flavour: Flavour): PlainLineFallback | { kind: 'line'; text: string } {
|
||||
const assembled = assembleInlineLine(segments, container, flavour)
|
||||
if (assembled.openingLinkAsDirective) return { kind: 'opening-link' }
|
||||
const [run] = assembled.unspellableRuns
|
||||
if (run !== undefined) return { kind: 'unspellable-run', run, runs: assembled.unspellableRuns }
|
||||
const [run, ...others] = assembled.unspellableRuns
|
||||
if (run !== undefined) return { kind: 'unspellable-run', runs: [run, ...others] }
|
||||
const lines = assembled.line.split('\n')
|
||||
const claimed = container === 'paragraph' ? lines.findIndex((single, index) => claimsLine(single, index === 0 ? 'first' : 'later')) : -1
|
||||
return claimed === -1 ? { kind: 'line', text: assembled.line } : { kind: 'claimed-line', line: claimed, text: lines[claimed] ?? '' }
|
||||
|
||||
@@ -125,6 +125,7 @@ test('spells a task list as a bullet list whose items lead with their state', ()
|
||||
assert.equal(plain(node('taskList', {}, task('DONE', 'Write the spec'), nested, task('TODO', 'Ship it'))), '- [x] Write the spec\n - [ ] Review\n- [ ] Ship it\n')
|
||||
assert.equal(plain(node('taskList', {}, nested, task('DONE', ''), said('Stray'))), '- - [ ] Review\n- [x]\n- Stray\n')
|
||||
assert.equal(plain(node('taskList', {}, node('taskList', {}), task('DONE', 'a'), said('b'), node('taskList', {}, task('TODO', 'c')))), '- [x] a\n- b\n - [ ] c\n')
|
||||
assert.equal(plain(node('taskList', {}, paragraph(), task('DONE', 'a'))), '- [x] a\n')
|
||||
assert.equal(plain(bulletList(item(said('x'))), node('taskList', {}, task('DONE', 'a'), node('taskList', {}, task('TODO', 'c')))), '- x\n- [x] a\n - [ ] c\n')
|
||||
assert.equal(plain(node('taskList', {}), task('TODO', 'Loose')), 'Loose\n')
|
||||
const blockTask = node('blockTaskItem', { state: 'DONE' }, said('First.'), said('Second.'))
|
||||
|
||||
@@ -274,26 +274,29 @@ function blankedLines(code: readonly AdfNode[]): AdfNode[] {
|
||||
return blanked === '' ? [] : [text(blanked)]
|
||||
}
|
||||
|
||||
// A task list opening with a task and holding tasks and task lists alone keeps its spelling, a list nesting in the task before it; any other keeps its markers as text.
|
||||
// A task list opening with a task and holding tasks and task lists alone keeps its spelling, a list nesting in the task before it; any other keeps its markers as text. A child reducing to nothing counts for neither.
|
||||
function reduceTaskList(node: AdfNode, reduction: Reduction): Result<AdfNode[]> {
|
||||
const children = nodeContent(node)
|
||||
const regular = isTask(children[0]) && children.every((child) => isTask(child) || child.type === 'taskList')
|
||||
const tasks: AdfNode[] = []
|
||||
let nested: AdfNode[] = []
|
||||
for (const [index, child] of children.entries()) {
|
||||
const kept: { blocks: AdfNode[]; child: AdfNode }[] = []
|
||||
for (const [index, child] of nodeContent(node).entries()) {
|
||||
const at = childReduction(reduction, index)
|
||||
const reduced = isTask(child) ? reduceTask(child, at) : reduceStanding(child, at)
|
||||
if (!reduced.ok) return reduced
|
||||
if (reduced.value.length > 0) kept.push({ blocks: reduced.value, child })
|
||||
}
|
||||
const regular = isTask(kept[0]?.child) && kept.every(({ child }) => isTask(child) || child.type === 'taskList')
|
||||
const tasks: AdfNode[] = []
|
||||
let nested: AdfNode[] = []
|
||||
for (const { blocks, child } of kept) {
|
||||
if (!regular) {
|
||||
const standsAlone = !isTask(child) && child.type !== 'taskList'
|
||||
for (const block of standsAlone ? [{ content: reduced.value, type: 'listItem' }] : reduced.value) tasks.push(block)
|
||||
for (const block of standsAlone ? [{ content: blocks, type: 'listItem' }] : blocks) tasks.push(block)
|
||||
continue
|
||||
}
|
||||
if (isTask(child)) {
|
||||
nestIn(tasks, nested)
|
||||
nested = []
|
||||
}
|
||||
for (const block of reduced.value) (isTask(child) ? tasks : nested).push(block)
|
||||
for (const block of blocks) (isTask(child) ? tasks : nested).push(block)
|
||||
}
|
||||
nestIn(tasks, nested)
|
||||
if (regular) return success([{ content: tasks, type: 'taskList' }])
|
||||
|
||||
@@ -34,6 +34,10 @@
|
||||
paragraph of 2000 × `un` plus `**-r**`: each run its flanking cannot spell re-emits the whole line
|
||||
before riding the carry, quadratic in the runs (Goal 9), and the plain reduction's
|
||||
`spellableLine` drops one mark per re-emit the same way. Make both linear.
|
||||
- **42 — Trim a text leaf's trailing blanks in linear time.** `plain-inline.ts`'s `leafEdges` finds
|
||||
the trail with an unanchored `/[ \t]*$/`, quadratic in a run of blanks inside one leaf: a
|
||||
paragraph of `a`, 80 000 spaces, `b` takes 6.5 s in `adfToPlainMarkdown` (Goal 9). Scan backward,
|
||||
as the expand title's trim does.
|
||||
- **34 — Read emphasis flanking by the whole character beside an astral symbol.** Check whether
|
||||
`line-escaping.ts`'s `charAt` and the parser's flanking read one UTF-16 unit beside an astral
|
||||
symbol — a lone surrogate is neither punctuation nor symbol, where CommonMark reads `😀` as
|
||||
|
||||
Reference in New Issue
Block a user