12c: a leaf given a body faults at its opener
This commit is contained in:
@@ -115,5 +115,6 @@ test('closes the containers a closer names past as unclosed, and crosses no list
|
||||
assert.deepEqual(faults('> !adf:panel info\n> Part.\n!adf:/panel\n'), [unclosed('panel'), unopened('panel')])
|
||||
assert.deepEqual(faults('!adf:panel info\n> !adf:/panel\n!adf:/panel\n'), [unopened('panel')])
|
||||
assert.deepEqual(faults('Part.\n\n!adf:/panel\n'), [unopened('panel')])
|
||||
assert.deepEqual(faults('!adf:rule {localId=a-1}\nPart.\n!adf:/rule\n'), ['rule takes no body, so no !adf:/rule closes it'])
|
||||
assert.deepEqual(kinds('!adf:rule {localId=a-1}\nPart.\n!adf:/rule\n'), ['fault', 'paragraph'])
|
||||
assert.deepEqual(kinds('!adf:rule {localId=a-1}\n!adf:/rule\n!adf:/rule\n'), ['fault', 'fault'])
|
||||
})
|
||||
|
||||
@@ -60,14 +60,16 @@ type ContainerStart = { kind: 'blockquote'; rest: Line } | { fresh: boolean; ind
|
||||
// The line from an absolute column on: a tab a cut splits keeps the stop it is measured against.
|
||||
type Line = { column: number; text: string }
|
||||
|
||||
type Walk = ParsedBlocks & { leaf: OpenLeaf | undefined; position: SourcePosition; stack: OpenContainer[] }
|
||||
type LeafOpener = { index: number; position: SourcePosition }
|
||||
|
||||
type Walk = ParsedBlocks & { leaf: OpenLeaf | undefined; leafOpeners: Map<Block[], Map<string, LeafOpener>>; position: SourcePosition; stack: OpenContainer[] }
|
||||
|
||||
const indentedCodeColumns = 4
|
||||
const largestOpenerIndentation = 3
|
||||
const tabStop = 4
|
||||
|
||||
export function parseBlocks(markdown: string): ParsedBlocks {
|
||||
const walk: Walk = { blocks: [], definitions: new Map(), leaf: undefined, position: { line: 1, offset: 0 }, stack: [] }
|
||||
const walk: Walk = { blocks: [], definitions: new Map(), leaf: undefined, leafOpeners: new Map(), position: { line: 1, offset: 0 }, stack: [] }
|
||||
for (const line of sourceLines(markdown)) {
|
||||
walk.position = line.position
|
||||
readLine(walk, { column: 0, text: line.text })
|
||||
@@ -229,15 +231,16 @@ function openDirective(walk: Walk, directive: Extract<DirectiveLine, { kind: 'op
|
||||
position: walk.position,
|
||||
}
|
||||
const parent = currentBlocks(walk)
|
||||
parent.push(block)
|
||||
const index = parent.push(block) - 1
|
||||
const { position } = block
|
||||
if (block.blocks !== undefined) walk.stack.push({ blocks: block.blocks, index: parent.length - 1, kind: 'directive', name, parent, position })
|
||||
if (block.blocks !== undefined) walk.stack.push({ blocks: block.blocks, index, kind: 'directive', name, parent, position })
|
||||
else leafOpenersIn(walk, parent).set(name, { index, position })
|
||||
}
|
||||
|
||||
function closeDirective(walk: Walk, name: string): void {
|
||||
const closer = spellDirectiveCloser(name)
|
||||
if (blockDirectiveForm(name) === 'leaf') {
|
||||
pushFault(walk, malformedDirective(`${name} takes no body, so no ${closer} closes it`))
|
||||
faultLeafOpener(walk, name, malformedDirective(`${name} takes no body, so no ${closer} closes it`))
|
||||
return
|
||||
}
|
||||
const depth = openDirectiveDepth(walk, name)
|
||||
@@ -259,6 +262,27 @@ function openDirectiveDepth(walk: Walk, name: string): number | undefined {
|
||||
return undefined
|
||||
}
|
||||
|
||||
// A leaf given a body faults at its opener, as a container missing its closer does.
|
||||
function faultLeafOpener(walk: Walk, name: string, fault: ConvertFault): void {
|
||||
const blocks = currentBlocks(walk)
|
||||
const openers = walk.leafOpeners.get(blocks)
|
||||
const opener = openers?.get(name)
|
||||
if (openers === undefined || opener === undefined) {
|
||||
pushFault(walk, fault)
|
||||
return
|
||||
}
|
||||
openers.delete(name)
|
||||
blocks[opener.index] = { fault, kind: 'fault', position: opener.position }
|
||||
}
|
||||
|
||||
function leafOpenersIn(walk: Walk, blocks: Block[]): Map<string, LeafOpener> {
|
||||
const known = walk.leafOpeners.get(blocks)
|
||||
if (known !== undefined) return known
|
||||
const openers = new Map<string, LeafOpener>()
|
||||
walk.leafOpeners.set(blocks, openers)
|
||||
return openers
|
||||
}
|
||||
|
||||
function pushFault(walk: Walk, fault: ConvertFault): void {
|
||||
currentBlocks(walk).push({ fault, kind: 'fault', position: walk.position })
|
||||
}
|
||||
|
||||
@@ -315,7 +315,18 @@ test('names the closer that finds no container open where it stands', () => {
|
||||
assert.equal(content(markdownToAdf('!adf:panel info\n> !adf:/panel\n!adf:/panel\n')), unopened('panel'))
|
||||
assert.equal(content(markdownToAdf('!adf:panel info\n- !adf:/panel\n!adf:/panel\n')), unopened('panel'))
|
||||
assert.equal(content(markdownToAdf('!adf:panel info\nPart.\n!adf:/expand\n!adf:/panel\n')), unopened('expand'))
|
||||
assert.equal(content(markdownToAdf('!adf:rule {localId=a-1}\nPart.\n!adf:/rule\n')), 'malformed-directive: rule takes no body, so no !adf:/rule closes it')
|
||||
})
|
||||
|
||||
test('names the leaf given a body at its opener, ahead of any refusal the leaf holds itself', () => {
|
||||
const body = (name: string): string => `malformed-directive: ${name} takes no body, so no !adf:/${name} closes it`
|
||||
assert.equal(content(markdownToAdf('!adf:rule\nPart.\n!adf:/rule\n')), body('rule'))
|
||||
assert.deepEqual(position(markdownToAdf('Part.\n\n!adf:rule\nPart.\n!adf:/rule\n')), { line: 3, offset: 7 })
|
||||
assert.equal(content(markdownToAdf('- a\n\n!adf:listBreak\n!adf:/listBreak\n\n- b\n')), body('listBreak'))
|
||||
assert.equal(content(markdownToAdf('- a\n\n!adf:listBreak\nPart.\n!adf:/listBreak\n\n- b\n')), body('listBreak'))
|
||||
assert.deepEqual(path(markdownToAdf('!adf:rule {localId=a-1}\n!adf:rule {localId=a-2}\nPart.\n!adf:/rule\n')), ['content', 1])
|
||||
assert.deepEqual(path(markdownToAdf('!adf:rule {localId=a-1}\n!adf:/rule\n!adf:/rule\n')), ['content', 0])
|
||||
assert.deepEqual(path(markdownToAdf('Part.\n\n!adf:/rule\n')), ['content', 1])
|
||||
assert.deepEqual(path(markdownToAdf('!adf:panel info\n!adf:rule {localId=a-1}\n!adf:/panel\n!adf:/rule\n')), ['content', 1])
|
||||
})
|
||||
|
||||
test('names the position a directive name the other one spells belongs to', () => {
|
||||
|
||||
Reference in New Issue
Block a user