Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/quadratic-code-block-scan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"remend": patch
---

Fix quadratic code-block scanning. `isInsideCodeBlock` now builds a linear-time position lookup (cached per text) instead of rescanning the whole prefix on every call, so handlers that probe many positions no longer degrade quadratically. Repairing an unclosed, bracket-heavy 58k-character code block drops from ~915ms to ~0.4ms per call.
17 changes: 17 additions & 0 deletions packages/remend/__benchmarks__/remend.bench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,3 +279,20 @@ ${"Regular paragraph text with some [links](https://example.com) and more conten
{ iterations: 1000 }
);
});

describe("Streamed Code Blocks", () => {
// An unclosed fence full of brackets is the pathological case for the
// code-block scan: every "[" probes isInsideCodeBlock, which previously
// rescanned the whole prefix per probe (quadratic overall).
const bracketHeavyLine =
"const x = arr[i]; if (map[key]) { list[j] = grid[a][b]; }\n";
const streamingCodeBlock = `\`\`\`ts\n${bracketHeavyLine.repeat(1000)}`;

bench(
"unclosed bracket-heavy code block (58k chars)",
() => {
remend(streamingCodeBlock);
},
{ iterations: 10 }
);
});
88 changes: 88 additions & 0 deletions packages/remend/__tests__/code-block-utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { describe, expect, it } from "vitest";
import { isInsideCodeBlock } from "../src/code-block-utils";

// Reference implementation: the previous per-call scan, kept verbatim so the
// lookup-based rewrite can be checked against it position by position.
const referenceIsInsideCodeBlock = (
text: string,
position: number
): boolean => {
let inInlineCode = false;
let inMultilineCode = false;

for (let i = 0; i < position; i += 1) {
if (text[i] === "\\" && i + 1 < text.length && text[i + 1] === "`") {
i += 1;
continue;
}
if (text.substring(i, i + 3) === "```") {
inMultilineCode = !inMultilineCode;
i += 2;
continue;
}
if (!inMultilineCode && text[i] === "`") {
inInlineCode = !inInlineCode;
}
}

return inInlineCode || inMultilineCode;
};

// Returns positions where the rewrite disagrees with the reference scan.
const parityMismatches = (text: string): number[] => {
const mismatches: number[] = [];
for (let p = 0; p <= text.length + 1; p += 1) {
if (isInsideCodeBlock(text, p) !== referenceIsInsideCodeBlock(text, p)) {
mismatches.push(p);
}
}
return mismatches;
};

describe("isInsideCodeBlock", () => {
it("reports positions inside a fenced code block", () => {
const text = "before ```js\nconst x = arr[0];\n``` after";
expect(isInsideCodeBlock(text, text.indexOf("arr"))).toBe(true);
expect(isInsideCodeBlock(text, text.indexOf("before"))).toBe(false);
expect(isInsideCodeBlock(text, text.indexOf("after"))).toBe(false);
});

it("reports positions inside inline code", () => {
const text = "use `map[key]` here";
expect(isInsideCodeBlock(text, text.indexOf("key"))).toBe(true);
expect(isInsideCodeBlock(text, text.indexOf("here"))).toBe(false);
});

it("ignores escaped backticks", () => {
const text = "not code \\` still [not] code";
expect(isInsideCodeBlock(text, text.indexOf("[not]"))).toBe(false);
});

it("treats an unclosed fence as extending to the end", () => {
const text = "```python\nvalues[0] = 1";
expect(isInsideCodeBlock(text, text.indexOf("values"))).toBe(true);
expect(isInsideCodeBlock(text, text.length)).toBe(true);
});

it("matches the per-call scan at every position on mixed input", () => {
const cases = [
"a `b` c ```\nd [e] `f`\n``` g \\` h ``` i",
"``````",
"\\`",
"`unclosed inline [x]",
"text \\``real` code",
];
for (const text of cases) {
expect(parityMismatches(text)).toEqual([]);
}
});

it("stays correct when queried texts alternate", () => {
const inCode = "```\n[x]";
const inProse = "plain [x]";
for (let round = 0; round < 3; round += 1) {
expect(isInsideCodeBlock(inCode, inCode.indexOf("[x]"))).toBe(true);
expect(isInsideCodeBlock(inProse, inProse.indexOf("[x]"))).toBe(false);
}
});
});
47 changes: 40 additions & 7 deletions packages/remend/src/code-block-utils.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,63 @@
// Check if a position is inside a code block (between ``` or `)
export const isInsideCodeBlock = (text: string, position: number): boolean => {
// Check for inline code (backticks)
// Builds the isInsideCodeBlock answer for every position in one linear pass.
// lookup[p] === 1 means scanning chars [0, p) ends inside inline or fenced
// code. Scanning per call is O(position), which makes callers that probe many
// positions (e.g. the link handler walking every "[" of a long streamed code
// block) quadratic overall.
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: "Mirrors the original scan's control flow exactly so the lookup is provably equivalent"
const buildCodeBlockLookup = (text: string): Uint8Array => {
const lookup = new Uint8Array(text.length + 1);
let inInlineCode = false;
let inMultilineCode = false;
let i = 0;

for (let i = 0; i < position; i += 1) {
while (i < text.length) {
// Skip escaped backticks
if (text[i] === "\\" && i + 1 < text.length && text[i + 1] === "`") {
i += 1;
const state = inInlineCode || inMultilineCode ? 1 : 0;
lookup[i + 1] = state;
lookup[i + 2] = state;
i += 2;
continue;
}

// Check for triple backticks (multiline code blocks)
if (text.substring(i, i + 3) === "```") {
inMultilineCode = !inMultilineCode;
i += 2; // Skip the next 2 backticks
const state = inInlineCode || inMultilineCode ? 1 : 0;
const next = Math.min(i + 3, text.length);
for (let p = i + 1; p <= next; p += 1) {
lookup[p] = state;
}
i = next;
continue;
}

// Only check for inline code if not in multiline code
if (!inMultilineCode && text[i] === "`") {
inInlineCode = !inInlineCode;
}
lookup[i + 1] = inInlineCode || inMultilineCode ? 1 : 0;
i += 1;
}

return inInlineCode || inMultilineCode;
return lookup;
};

// Handlers repeatedly probe positions of the same text within one remend()
// call, so a single-entry cache converts each probe to O(1) after one O(n)
// build per distinct text.
let cache: { text: string; lookup: Uint8Array } | null = null;

// Check if a position is inside a code block (between ``` or `)
export const isInsideCodeBlock = (text: string, position: number): boolean => {
let current = cache;
if (current === null || current.text !== text) {
current = { text, lookup: buildCodeBlockLookup(text) };
cache = current;
}
// Positions past the end resolve to the state after scanning the full text,
// matching the previous per-call scan.
return current.lookup[Math.min(position, text.length)] === 1;
};

// Checks if a backtick at position i is part of a triple backtick sequence
Expand Down
Loading