UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

89 lines (88 loc) 2.63 kB
//#region packages/markdown-core/src/fences.ts const FENCE_LINE_RE = /(?:^|\n)( {0,3})(`{3,}|~{3,})([^\r\n\u2028\u2029]*)\r?(?=\n|$)/g; const SINGLE_LINE_FENCE_RE = new RegExp(FENCE_LINE_RE, "gy"); /** Scans fenced-code spans incrementally so chunking can carry an open fence forward. */ function scanFenceSpans(buffer, state) { const spans = []; const startsAtLineStart = state?.atLineStart ?? true; let open = state?.open ? { ...state.open, start: 0 } : void 0; const pattern = buffer.includes("\n") ? FENCE_LINE_RE : SINGLE_LINE_FENCE_RE; for (const match of buffer.matchAll(pattern)) { const [, indent, marker, trailing] = match; if (indent === void 0 || marker === void 0 || trailing === void 0) continue; const start = match.index + (match[0].startsWith("\n") ? 1 : 0); if (start === 0 && !startsAtLineStart) continue; const markerChar = marker.charAt(0); const markerLen = marker.length; if (!open) open = { start, markerChar, markerLen, openLine: `${indent}${marker}${trailing}`, marker, indent }; else if (open.markerChar === markerChar && markerLen >= open.markerLen && /^[ \t]*$/.test(trailing)) { spans.push({ start: open.start, end: match.index + match[0].length, openLine: open.openLine, marker: open.marker, indent: open.indent }); open = void 0; } } if (open) spans.push({ start: open.start, end: buffer.length, openLine: open.openLine, marker: open.marker, indent: open.indent }); return { spans, state: { atLineStart: buffer.length === 0 ? startsAtLineStart : buffer.endsWith("\n"), ...open ? { open: { markerChar: open.markerChar, markerLen: open.markerLen, openLine: open.openLine, marker: open.marker, indent: open.indent } } : {} } }; } /** Parses all fenced-code spans in a complete markdown buffer. */ function parseFenceSpans(buffer) { return scanFenceSpans(buffer).spans; } /** Looks up the fence containing an offset; spans must be sorted by start offset. */ function findFenceSpanAt(spans, index) { let low = 0; let high = spans.length - 1; while (low <= high) { const mid = Math.floor((low + high) / 2); const span = spans[mid]; if (!span) break; if (index <= span.start) { high = mid - 1; continue; } if (index >= span.end) { low = mid + 1; continue; } return span; } } /** True when a chunk boundary would not split a fenced-code block. */ function isSafeFenceBreak(spans, index) { return !findFenceSpanAt(spans, index); } //#endregion export { scanFenceSpans as i, isSafeFenceBreak as n, parseFenceSpans as r, findFenceSpanAt as t };