26 - the container stack owns the directive depths, and the escape claim hands over after it records
CI / gate (push) Successful in 30s
CI / publish (push) Successful in 5s

This commit was merged in pull request #125.
This commit is contained in:
2026-09-24 00:02:58 +02:00
parent 15fb7fca81
commit d5370b126d
3 changed files with 13 additions and 11 deletions
+9 -8
View File
@@ -43,7 +43,7 @@ export type DirectiveBlock = Extract<Block, { kind: 'directive' }>
type ListBlock = Extract<Block, { items: Block[][] }>
type OpenDirective = { blocks: Block[]; depths: number[]; index: number; kind: 'directive'; name: string; parent: Block[]; position: SourcePosition }
type OpenDirective = { blocks: Block[]; index: number; kind: 'directive'; name: string; parent: Block[]; position: SourcePosition }
type EdgeContainer = Extract<Block, { kind: 'blockquote' }> | { blocks: Block[]; indentation: number; kind: 'item'; list: ListBlock }
@@ -65,6 +65,7 @@ type Line = { column: number; text: string }
type LeafOpener = { index: number; position: SourcePosition }
type ContainerStack = {
directiveDepth: (name: string) => number | undefined
drop: (depth: number) => OpenContainer[]
edges: readonly { container: EdgeContainer; depth: number }[]
open: readonly OpenContainer[]
@@ -72,7 +73,6 @@ type ContainerStack = {
}
type Walk = ParsedBlocks & {
directiveDepths: Map<string, number[]>
leaf: OpenLeaf | undefined
leafOpeners: Map<Block[], Map<string, LeafOpener>>
position: SourcePosition
@@ -87,7 +87,6 @@ export function parseBlocks(markdown: string): ParsedBlocks {
const walk: Walk = {
blocks: [],
definitions: new Map(),
directiveDepths: new Map(),
leaf: undefined,
leafOpeners: new Map(),
position: { line: 1, offset: 0 },
@@ -102,13 +101,16 @@ export function parseBlocks(markdown: string): ParsedBlocks {
}
function containerStack(): ContainerStack {
const directiveDepths = new Map<string, number[]>()
const depthsOf = (name: string): number[] => entryOf(directiveDepths, name, () => [])
const edges: { container: EdgeContainer; depth: number }[] = []
const open: OpenContainer[] = []
return {
directiveDepth: (name) => directiveDepths.get(name)?.at(-1),
drop: (depth) => {
const dropped = open.splice(depth)
for (const container of dropped) {
if (container.kind === 'directive') container.depths.pop()
if (container.kind === 'directive') depthsOf(container.name).pop()
else edges.pop()
}
return dropped
@@ -117,7 +119,7 @@ function containerStack(): ContainerStack {
open,
push: (container) => {
const depth = open.push(container) - 1
if (container.kind === 'directive') container.depths.push(depth)
if (container.kind === 'directive') depthsOf(container.name).push(depth)
else edges.push({ container, depth })
},
}
@@ -281,8 +283,7 @@ function openDirective(walk: Walk, directive: Extract<DirectiveLine, { kind: 'op
entryOf(walk.leafOpeners, parent, () => new Map<string, LeafOpener>()).set(name, { index, position })
return
}
const depths = entryOf(walk.directiveDepths, name, (): number[] => [])
walk.stack.push({ blocks: block.blocks, depths, index, kind: 'directive', name, parent, position })
walk.stack.push({ blocks: block.blocks, index, kind: 'directive', name, parent, position })
}
function closeDirective(walk: Walk, name: string): void {
@@ -302,7 +303,7 @@ function closeDirective(walk: Walk, name: string): void {
// A closer crosses no list item or blockquote edge.
function openDirectiveDepth(walk: Walk, name: string): number | undefined {
const depth = walk.directiveDepths.get(name)?.at(-1)
const depth = walk.stack.directiveDepth(name)
return depth === undefined || depth < (walk.stack.edges.at(-1)?.depth ?? -1) ? undefined : depth
}