Answer the architecture review: one interrupt rule both directions read
CI / gate (push) Successful in 8s
CI / gate (push) Successful in 8s
This commit is contained in:
@@ -1,5 +1,14 @@
|
||||
import type { LinkDefinition } from './link-reference-definitions.ts'
|
||||
import { atxHeading, claimsDirectiveLine, claimsPipeLine, closingCodeFence, isThematicBreak, openingCodeFence, setextHeadingLevel } from '../commonmark-grammar.ts'
|
||||
import {
|
||||
atxHeading,
|
||||
claimsDirectiveLine,
|
||||
claimsPipeLine,
|
||||
closingCodeFence,
|
||||
isThematicBreak,
|
||||
markerInterruptsParagraph,
|
||||
openingCodeFence,
|
||||
setextHeadingLevel,
|
||||
} from '../commonmark-grammar.ts'
|
||||
import { openingHtmlBlock } from './html-blocks.ts'
|
||||
import { readLinkDefinitions } from './link-reference-definitions.ts'
|
||||
|
||||
@@ -85,10 +94,11 @@ function continuesContainer(walk: Walk, container: OpenContainer, line: string):
|
||||
}
|
||||
|
||||
function openContainers(walk: Walk, line: string, paragraphOpen: boolean, depth: number): { opened: boolean; rest: string } {
|
||||
const unmatched = walk.stack[depth]
|
||||
let opened = false
|
||||
let rest = line
|
||||
while (leadingColumns(rest) < indentedCodeColumns) {
|
||||
const start = containerStart(rest, opened ? false : paragraphOpen, opened ? undefined : walk.stack[depth])
|
||||
const start = containerStart(rest, opened ? false : paragraphOpen, opened ? undefined : unmatched)
|
||||
if (start === undefined) break
|
||||
if (!opened) closeContainers(walk, depth)
|
||||
opened = true
|
||||
@@ -102,11 +112,15 @@ function containerStart(line: string, paragraphOpen: boolean, enclosing: OpenCon
|
||||
const opener = removeColumns(line, largestOpenerIndentation)
|
||||
if (opener.startsWith('>')) return { kind: 'blockquote', rest: removeColumns(opener.slice(1), 1) }
|
||||
if (isThematicBreak(opener) || (paragraphOpen && setextHeadingLevel(opener) !== undefined)) return undefined
|
||||
return itemStart(line, opener, paragraphOpen, enclosing)
|
||||
}
|
||||
|
||||
function itemStart(line: string, opener: string, paragraphOpen: boolean, enclosing: OpenContainer | undefined): ContainerStart | undefined {
|
||||
const marker = itemMarker(opener)
|
||||
if (marker === undefined) return undefined
|
||||
const after = opener.slice(marker.width)
|
||||
const blank = blankLine.test(after)
|
||||
if (paragraphOpen && (blank || (marker.list.kind === 'orderedList' && marker.list.start !== 1))) return undefined
|
||||
if (paragraphOpen && !markerInterruptsParagraph(marker.list.kind === 'orderedList' ? marker.list.start : undefined, blank)) return undefined
|
||||
const spaces = leadingColumns(after)
|
||||
const padding = blank || spaces > indentedCodeColumns ? 1 : spaces
|
||||
const continued = enclosing?.kind === 'item' && enclosing.list.kind === marker.list.kind && enclosing.marker === marker.marker
|
||||
|
||||
@@ -197,6 +197,7 @@ test('opens a list beside a paragraph only where the marker interrupts it', () =
|
||||
assert.deepEqual(content(markdownToAdf('Part.\n- - -\n')), [paragraph('Part.'), { type: 'rule' }])
|
||||
assert.deepEqual(content(markdownToAdf('Part.\n-\n')), [{ attrs: { level: 2 }, content: [text('Part.')], type: 'heading' }])
|
||||
assert.deepEqual(content(markdownToAdf('- a\n 2. b\n')), [bulletList(item(paragraph('a 2. b')))])
|
||||
assert.deepEqual(content(markdownToAdf('- a\n 1. b\n')), [bulletList(item(paragraph('a'), orderedList(1, item(paragraph('b')))))])
|
||||
})
|
||||
|
||||
test('folds a lazy continuation into the paragraph the container holds', () => {
|
||||
|
||||
@@ -22,16 +22,28 @@ function blockNodes(blocks: readonly Block[], path: ConvertErrorPath, depth: num
|
||||
return success(content)
|
||||
}
|
||||
|
||||
// Switched, not chained: `noImplicitReturns` then refuses the kind a later milestone adds and forgets.
|
||||
function blockNode(block: Block, path: ConvertErrorPath, depth: number): Result<AdfNode> {
|
||||
if (block.kind === 'blockquote') return containerNode({ type: 'blockquote' }, block.blocks, path, depth)
|
||||
if (block.kind === 'bulletList') return listNode({ type: 'bulletList' }, block.items, path, depth)
|
||||
if (block.kind === 'claim') return claimFailure(block.construct, path)
|
||||
if (block.kind === 'code') return success(codeBlockNode(block.language, block.text))
|
||||
if (block.kind === 'heading') return success(withContent({ attrs: { level: block.level }, type: 'heading' }, block.text))
|
||||
if (block.kind === 'html') return failure('unmappable-html', `no ADF node carries ${block.construct}`, path)
|
||||
if (block.kind === 'orderedList') return listNode({ attrs: { order: block.start }, type: 'orderedList' }, block.items, path, depth)
|
||||
if (block.kind === 'paragraph') return success(withContent({ type: 'paragraph' }, block.text))
|
||||
return success({ type: 'rule' })
|
||||
switch (block.kind) {
|
||||
case 'blockquote':
|
||||
return containerNode({ type: 'blockquote' }, block.blocks, path, depth)
|
||||
case 'bulletList':
|
||||
return listNode({ type: 'bulletList' }, block.items, path, depth)
|
||||
case 'claim':
|
||||
return claimFailure(block.construct, path)
|
||||
case 'code':
|
||||
return success(codeBlockNode(block.language, block.text))
|
||||
case 'heading':
|
||||
return success(withContent({ attrs: { level: block.level }, type: 'heading' }, block.text))
|
||||
case 'html':
|
||||
return failure('unmappable-html', `no ADF node carries ${block.construct}`, path)
|
||||
case 'orderedList':
|
||||
return listNode({ attrs: { order: block.start }, type: 'orderedList' }, block.items, path, depth)
|
||||
case 'paragraph':
|
||||
return success(withContent({ type: 'paragraph' }, block.text))
|
||||
case 'rule':
|
||||
return success({ type: 'rule' })
|
||||
}
|
||||
}
|
||||
|
||||
function containerNode(node: AdfNode, blocks: readonly Block[], path: ConvertErrorPath, depth: number): Result<AdfNode> {
|
||||
@@ -51,8 +63,12 @@ function listNode(node: AdfNode, items: readonly Block[][], path: ConvertErrorPa
|
||||
}
|
||||
|
||||
function claimFailure(construct: ClaimedConstruct, path: ConvertErrorPath): Result<AdfNode> {
|
||||
if (construct === 'directive') return failure('malformed-directive', 'the line claims a directive and parses as none', path)
|
||||
return failure('malformed-pipe-table', 'the line claims a pipe table and parses as none', path)
|
||||
switch (construct) {
|
||||
case 'directive':
|
||||
return failure('malformed-directive', 'the line claims a directive and parses as none', path)
|
||||
case 'pipe-table':
|
||||
return failure('malformed-pipe-table', 'the line claims a pipe table and parses as none', path)
|
||||
}
|
||||
}
|
||||
|
||||
function codeBlockNode(language: string, text: string): AdfNode {
|
||||
|
||||
Reference in New Issue
Block a user