mirror of
https://github.com/coder/coder.git
synced 2026-10-16 14:48:56 +08:00
fix(site): keep scanning past long lines that are not headings
The heading scan stopped at any setext pair too long to parse, so a long list item or quote followed by a thematic break hid a later heading. The pair is now classified from the start of its first line, cut to fit the parse bound, and the scan stops only when the pair is a heading.
This commit is contained in:
@@ -287,6 +287,16 @@ describe("getThinkingDisclosureDisplay", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it.each(["- ", "> "])(
|
||||
"scans past a long %j line that is not a heading",
|
||||
(marker) => {
|
||||
const body = `${marker}${"step ".repeat(100)}\n---\n\n`;
|
||||
expect(
|
||||
getThinkingDisclosureDisplay(`${body}## Next`, { isStreaming: false }),
|
||||
).toEqual({ title: "Thinking about next", body });
|
||||
},
|
||||
);
|
||||
|
||||
it("ignores headings inside fenced code blocks", () => {
|
||||
expect(
|
||||
getThinkingDisclosureDisplay(
|
||||
|
||||
@@ -190,6 +190,8 @@ const getParagraphHeadingText = (
|
||||
return heading;
|
||||
};
|
||||
|
||||
// A first heading too long to parse ends the scan, so a later heading cannot
|
||||
// stand in for it.
|
||||
const getFirstHeading = (text: string): HeadingMatch | undefined => {
|
||||
let activeFence: { character: "`" | "~"; length: number } | undefined;
|
||||
let setextCandidate: LineRange | undefined;
|
||||
@@ -213,8 +215,6 @@ const getFirstHeading = (text: string): HeadingMatch | undefined => {
|
||||
|
||||
if (/^ {0,3}#{1,6}(?:[ \t]|$)/.test(line)) {
|
||||
const root = parseHeadingCandidate(line);
|
||||
// A first heading too long to parse ends the scan, so a later
|
||||
// heading cannot stand in for it.
|
||||
if (!root) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -238,18 +238,26 @@ const getFirstHeading = (text: string): HeadingMatch | undefined => {
|
||||
}
|
||||
|
||||
if (setextCandidate && /^ {0,3}(=+|-+)[ \t]*$/.test(line)) {
|
||||
const root = parseHeadingCandidate(`${setextCandidate.line}\n${line}`);
|
||||
if (!root) {
|
||||
return undefined;
|
||||
}
|
||||
const [block] = root.children;
|
||||
const heading = block?.type === "heading" ? getPlainText(block) : "";
|
||||
if (heading) {
|
||||
return {
|
||||
text: heading,
|
||||
start: setextCandidate.start,
|
||||
end: lineRange.nextStart,
|
||||
};
|
||||
// A line's start decides whether an underline makes it a heading, so
|
||||
// a line too long to parse is cut to classify the pair.
|
||||
const candidate = sliceAtGraphemeBoundary(
|
||||
setextCandidate.line,
|
||||
PARSED_SOURCE_MAX_LENGTH - line.length - 1,
|
||||
);
|
||||
const [block] =
|
||||
parseHeadingCandidate(`${candidate}\n${line}`)?.children ?? [];
|
||||
if (block?.type === "heading") {
|
||||
if (candidate !== setextCandidate.line) {
|
||||
return undefined;
|
||||
}
|
||||
const heading = getPlainText(block);
|
||||
if (heading) {
|
||||
return {
|
||||
text: heading,
|
||||
start: setextCandidate.start,
|
||||
end: lineRange.nextStart,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user