diff --git a/src/markdown/emit/line-escaping.ts b/src/markdown/emit/line-escaping.ts index 78fbbd8..69b73af 100644 --- a/src/markdown/emit/line-escaping.ts +++ b/src/markdown/emit/line-escaping.ts @@ -91,14 +91,15 @@ function escapeClaims(scan: string, escapings: readonly InlineEscaping[], contai if (index > line.start + line.text.length) line = scanLine(scan, line.start + line.text.length + 1) const escaping = escapings[index] const escapable = escaping === 'backslash' || escaping === 'bracketed' - afterEscape = + const claimed: boolean = (escapable && (claimsLineStart(line, index, container) || mergesWithSyntax(scan, escapings, index) || opensConstruct(scan, linkClose, index, escaping === 'bracketed', container, afterEscape))) || (escaping === 'bracketed-link-target' && ((scan.charAt(index) === '`' && opensCodeSpan(scan, index, afterEscape)) || claimsDirectivePrefix(scan, index))) - if (afterEscape) escaped.add(index) + if (claimed) escaped.add(index) + afterEscape = claimed } return escaped } diff --git a/src/markdown/parse/blocks.ts b/src/markdown/parse/blocks.ts index e6af3e6..5054b07 100644 --- a/src/markdown/parse/blocks.ts +++ b/src/markdown/parse/blocks.ts @@ -43,7 +43,7 @@ export type DirectiveBlock = Extract type ListBlock = Extract -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 | { 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 leaf: OpenLeaf | undefined leafOpeners: Map> 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() + 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 new Map()).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 } diff --git a/todo-history.md b/todo-history.md index bc87b97..71a80c4 100644 --- a/todo-history.md +++ b/todo-history.md @@ -1064,7 +1064,7 @@ The done `todo.md` items in full, as they were written. `todo.md` keeps a one-li panel's first and second hardest places. `escapeClaims` hands each predicate the one fact it reads — whether the character before was escaped — and `escapeClosedRuns` takes that pass's set read-only and returns the whole. `containerStack` is the one writer of the stack, - `edges` and each directive's `depths`, all read-only elsewhere; deriving `edges` from the + `edges` and the open directives' depths by name, all read-only elsewhere; deriving `edges` from the stack instead makes every line walk the open directives, quadratic in their nesting. ## 5 — Ship `0.1.0`