4c - the guard's spread was a class: the code block's held lines and a mark run's segments too
This commit is contained in:
@@ -174,7 +174,6 @@ export function isThematicBreak(line: string): boolean {
|
||||
return holdsThematicBreak(thematicBreakTail(line), line)
|
||||
}
|
||||
|
||||
// A break runs to the line's end, so one scan of that run answers every level the list walk opens.
|
||||
export function thematicBreakTail(text: string): ThematicBreakTail | undefined {
|
||||
let marker: string | undefined
|
||||
let markers = 0
|
||||
@@ -195,6 +194,8 @@ export function thematicBreakTail(text: string): ThematicBreakTail | undefined {
|
||||
return { longest: text.length - first, marker, shortest: text.length - third }
|
||||
}
|
||||
|
||||
// `text` is a suffix of what `tail` was read from, or opens with a space: a length alone names a
|
||||
// suffix, and the marker check is what refuses one starting mid-run — `--- ---` holds ` ---`.
|
||||
export function holdsThematicBreak(tail: ThematicBreakTail | undefined, text: string): boolean {
|
||||
if (tail === undefined) return false
|
||||
return text.length >= tail.shortest && text.length <= tail.longest && text.charAt(0) === tail.marker
|
||||
|
||||
@@ -17,7 +17,7 @@ export type DirectiveLine =
|
||||
| { argument: string | undefined; attributes: DirectiveAttributes; kind: 'opener'; name: string }
|
||||
| { kind: 'closer'; name: string }
|
||||
|
||||
// spans: the directives the content holds, at their offset in it, so reading the slot back never scans them again.
|
||||
// spans keys index content, so the two travel together: separate them and every offset is wrong.
|
||||
export type DirectiveSpan = { attributes: DirectiveAttributes; content: string | undefined; length: number; name: string; spans: NestedSpans }
|
||||
|
||||
export type NestedSpans = ReadonlyMap<number, DirectiveSpan>
|
||||
@@ -254,10 +254,11 @@ function readDirectiveContent(text: string, start: number, depth: number): Read<
|
||||
cursor = span
|
||||
continue
|
||||
}
|
||||
const nested = keepNestedSpan(text, cursor, depth, start, spans)
|
||||
const nested = readNestedDirective(text, cursor, depth + 1)
|
||||
if (nested?.fault !== undefined) return { fault: nested.fault }
|
||||
if (nested !== undefined) {
|
||||
cursor = nested.value
|
||||
spans.set(cursor - start, nested.value)
|
||||
cursor += nested.value.length
|
||||
continue
|
||||
}
|
||||
if (character === ']' && brackets === 0) return { value: { end: cursor, spans } }
|
||||
@@ -268,14 +269,6 @@ function readDirectiveContent(text: string, start: number, depth: number): Read<
|
||||
return { fault: malformedDirective(`an inline directive [content] is unclosed; ${directiveEscape}`) }
|
||||
}
|
||||
|
||||
function keepNestedSpan(text: string, cursor: number, depth: number, start: number, spans: Map<number, DirectiveSpan>): Read<number> | undefined {
|
||||
const nested = readNestedDirective(text, cursor, depth + 1)
|
||||
if (nested === undefined) return undefined
|
||||
if (nested.fault !== undefined) return { fault: nested.fault }
|
||||
spans.set(cursor - start, nested.value)
|
||||
return { value: cursor + nested.value.length }
|
||||
}
|
||||
|
||||
// `undefined` where the span crosses the line ending an inline directive may not cross.
|
||||
function readCodeSpanEnd(text: string, index: number): number | undefined {
|
||||
const opener = backtickRun(text, index)
|
||||
|
||||
@@ -736,3 +736,8 @@ test('carries whitespace CommonMark strips in the reserved text directive', () =
|
||||
// CommonMark strips spaces and tabs alone, so the whitespace beside them is plain text.
|
||||
assert.equal(emitted({ text: '\va\f', type: 'text' }), '\va\f\n')
|
||||
})
|
||||
|
||||
test("joins a mark run's segments as a walk rather than as one call's arguments", () => {
|
||||
const run = Array.from({ length: 200000 }, (): AdfNode => ({ marks: [{ type: 'strong' }], text: 'a', type: 'text' }))
|
||||
assert.equal(markdown(adfToMarkdown(document({ content: run, type: 'paragraph' }))), `**${'a'.repeat(200000)}**\n`)
|
||||
})
|
||||
|
||||
@@ -166,7 +166,7 @@ function emitRun(nodes: readonly AdfNode[], depth: number, firstIndex: number, c
|
||||
const emitted = run.kind === 'plain' ? emitLeaf(run.node, runContext, run.index) : emitMarkedRun(run.nodes, run.mark, depth, run.index, runContext)
|
||||
if (!emitted.ok) return emitted
|
||||
if (emitted.value.carry !== undefined) return emitted
|
||||
segments.push(...emitted.value.segments)
|
||||
for (const segment of emitted.value.segments) segments.push(segment)
|
||||
}
|
||||
return success({ segments })
|
||||
}
|
||||
|
||||
@@ -118,3 +118,7 @@ test('closes the containers a closer names past as unclosed, and crosses no list
|
||||
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'])
|
||||
})
|
||||
|
||||
test("releases an indented code block's held blank lines as a walk rather than as one call's arguments", () => {
|
||||
assert.deepEqual(kinds(` a\n${'\n'.repeat(200000)} b\n`), ['code'])
|
||||
})
|
||||
|
||||
@@ -365,7 +365,8 @@ function readIndentedCodeLine(leaf: Extract<OpenLeaf, { kind: 'indented-code' }>
|
||||
return true
|
||||
}
|
||||
if (leadingColumns(line) < indentedCodeColumns) return false
|
||||
leaf.lines.push(...leaf.held, removeColumns(line, indentedCodeColumns).text)
|
||||
for (const held of leaf.held) leaf.lines.push(held)
|
||||
leaf.lines.push(removeColumns(line, indentedCodeColumns).text)
|
||||
leaf.held.length = 0
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user