mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 16:02:32 +00:00
Optimize nested toggle validation by sharing fence ranges across sibling
When validating nested details elements, computing fence ranges once and reusing across siblings eliminates redundant body rescans. Export MarkdownFenceRanges type and add precomputedFenceRanges parameter to matchDetailsHtmlBlock.
This commit is contained in:
@@ -91,6 +91,22 @@ describe('details markdown html', () => {
|
||||
expect(isEditableHtml(nestedToggles(16))).toBe(true)
|
||||
})
|
||||
|
||||
it('bounds each of many sibling nested toggles independently', () => {
|
||||
const siblings = (count: number, extra = ''): string =>
|
||||
Array.from(
|
||||
{ length: count },
|
||||
(_, index) =>
|
||||
`<details class="orca-details"${extra}>\n<summary>sibling ${index}</summary>\n\nBody\n\n</details>`
|
||||
).join('\n\n')
|
||||
const wrap = (body: string): string =>
|
||||
`<details class="orca-details">\n<summary>Outer</summary>\n\n${body}\n\n</details>`
|
||||
|
||||
expect(isEditableHtml(wrap(siblings(40)))).toBe(true)
|
||||
// A single non-editable sibling must still reject, so sharing fence ranges
|
||||
// cannot make later siblings inherit an earlier sibling's boundaries.
|
||||
expect(isEditableHtml(wrap(`${siblings(20)}\n\n${siblings(1, ' id="x"')}`))).toBe(false)
|
||||
})
|
||||
|
||||
it('rejects a toggle whose nested toggle is not itself editable', () => {
|
||||
const block: DetailsHtmlBlock = {
|
||||
raw: '',
|
||||
|
||||
@@ -36,6 +36,10 @@ export type DetailsHtmlBlock = {
|
||||
inner: string
|
||||
}
|
||||
|
||||
// Fence ranges depend only on the scanned string, so callers scanning one body
|
||||
// repeatedly compute them once and share them across sibling matches.
|
||||
export type MarkdownFenceRanges = readonly (readonly [number, number])[]
|
||||
|
||||
export type DetailsSummaryHtml = {
|
||||
attributes: string
|
||||
content: string
|
||||
@@ -87,7 +91,7 @@ export function renderDetailsAttributes(attrs: Record<string, unknown> | undefin
|
||||
return attributes.join(' ')
|
||||
}
|
||||
|
||||
function markdownFenceRanges(content: string): [number, number][] {
|
||||
function markdownFenceRanges(content: string): MarkdownFenceRanges {
|
||||
const ranges: [number, number][] = []
|
||||
let offset = 0
|
||||
let openFence: { marker: '`' | '~'; length: number; start: number } | null = null
|
||||
@@ -129,11 +133,15 @@ function markdownFenceRanges(content: string): [number, number][] {
|
||||
return ranges
|
||||
}
|
||||
|
||||
function isInsideRange(index: number, ranges: [number, number][]): boolean {
|
||||
function isInsideRange(index: number, ranges: MarkdownFenceRanges): boolean {
|
||||
return ranges.some(([start, end]) => index >= start && index < end)
|
||||
}
|
||||
|
||||
export function matchDetailsHtmlBlock(content: string, start: number): DetailsHtmlBlock | null {
|
||||
export function matchDetailsHtmlBlock(
|
||||
content: string,
|
||||
start: number,
|
||||
precomputedFenceRanges?: MarkdownFenceRanges
|
||||
): DetailsHtmlBlock | null {
|
||||
const openingMatch = content.slice(start).match(/^<details\b[^>]*>/i)
|
||||
if (!openingMatch) {
|
||||
return null
|
||||
@@ -141,7 +149,7 @@ export function matchDetailsHtmlBlock(content: string, start: number): DetailsHt
|
||||
|
||||
const detailsTagPattern = /<\/?details\b[^>]*>/gi
|
||||
detailsTagPattern.lastIndex = start
|
||||
const fenceRanges = markdownFenceRanges(content)
|
||||
const fenceRanges = precomputedFenceRanges ?? markdownFenceRanges(content)
|
||||
|
||||
let depth = 0
|
||||
|
||||
@@ -270,6 +278,8 @@ const MAX_DETAILS_NESTING_LEVELS = 16
|
||||
function stripEditableNestedDetails(bodyHtml: string, nestingLevel: number): string | null {
|
||||
let result = ''
|
||||
let index = 0
|
||||
// Why: without sharing this, N sibling toggles rescan the whole body N times.
|
||||
let fenceRanges: MarkdownFenceRanges | null = null
|
||||
|
||||
for (;;) {
|
||||
const nestedStart = indexOfAsciiIgnoreCase(bodyHtml, '<details', index)
|
||||
@@ -281,7 +291,8 @@ function stripEditableNestedDetails(bodyHtml: string, nestingLevel: number): str
|
||||
return null
|
||||
}
|
||||
|
||||
const nested = matchDetailsHtmlBlock(bodyHtml, nestedStart)
|
||||
fenceRanges ??= markdownFenceRanges(bodyHtml)
|
||||
const nested = matchDetailsHtmlBlock(bodyHtml, nestedStart, fenceRanges)
|
||||
if (!nested || !isEditableDetailsHtmlBlock(nested, nestingLevel + 1)) {
|
||||
return null
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user