mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
fix(zalouser): preserve literal quote lines in code fences
This commit is contained in:
@@ -76,6 +76,20 @@ describe("parseZalouserTextStyles", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("preserves quote-prefixed lines inside normal fenced code blocks", () => {
|
||||||
|
expect(parseZalouserTextStyles("```\n> prompt\n```")).toEqual({
|
||||||
|
text: "> prompt",
|
||||||
|
styles: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not treat quote-prefixed fence text inside code as a closing fence", () => {
|
||||||
|
expect(parseZalouserTextStyles("```\n> ```\n*still code*\n```")).toEqual({
|
||||||
|
text: "> ```\n*still code*",
|
||||||
|
styles: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("keeps unmatched fences literal", () => {
|
it("keeps unmatched fences literal", () => {
|
||||||
expect(parseZalouserTextStyles("```python")).toEqual({
|
expect(parseZalouserTextStyles("```python")).toEqual({
|
||||||
text: "```python",
|
text: "```python",
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ type FenceMarker = {
|
|||||||
indent: number;
|
indent: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type ActiveFence = FenceMarker & {
|
||||||
|
quoteIndent: number;
|
||||||
|
};
|
||||||
|
|
||||||
const TAG_STYLE_MAP: Record<string, InlineStyle | null> = {
|
const TAG_STYLE_MAP: Record<string, InlineStyle | null> = {
|
||||||
red: TextStyle.Red,
|
red: TextStyle.Red,
|
||||||
orange: TextStyle.Orange,
|
orange: TextStyle.Orange,
|
||||||
@@ -102,41 +106,40 @@ export function parseZalouserTextStyles(input: string): { text: string; styles:
|
|||||||
const lines = input.split("\n");
|
const lines = input.split("\n");
|
||||||
const lineStyles: LineStyle[] = [];
|
const lineStyles: LineStyle[] = [];
|
||||||
const processedLines: string[] = [];
|
const processedLines: string[] = [];
|
||||||
let activeFence: FenceMarker | null = null;
|
let activeFence: ActiveFence | null = null;
|
||||||
|
|
||||||
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
||||||
let line = lines[lineIndex];
|
const rawLine = lines[lineIndex];
|
||||||
let { text: unquotedLine, indent: baseIndent } = stripQuotePrefix(line);
|
const { text: unquotedLine, indent: baseIndent } = stripQuotePrefix(rawLine);
|
||||||
line = unquotedLine;
|
|
||||||
|
|
||||||
const fence = parseFenceMarker(line);
|
if (activeFence) {
|
||||||
if (fence) {
|
const codeLine = activeFence.quoteIndent > 0 ? unquotedLine : rawLine;
|
||||||
if (!activeFence) {
|
if (isClosingFence(codeLine, activeFence)) {
|
||||||
if (!hasClosingFence(lines, lineIndex + 1, fence)) {
|
|
||||||
processedLines.push(escapeLiteralText(line, escapeMap));
|
|
||||||
activeFence = fence;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
activeFence = fence;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isClosingFence(line, activeFence)) {
|
|
||||||
activeFence = null;
|
activeFence = null;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (activeFence) {
|
|
||||||
processedLines.push(
|
processedLines.push(
|
||||||
escapeLiteralText(
|
escapeLiteralText(
|
||||||
normalizeCodeBlockLeadingWhitespace(stripCodeFenceIndent(line, activeFence.indent)),
|
normalizeCodeBlockLeadingWhitespace(stripCodeFenceIndent(codeLine, activeFence.indent)),
|
||||||
escapeMap,
|
escapeMap,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let line = unquotedLine;
|
||||||
|
const openingFence = resolveOpeningFence(rawLine);
|
||||||
|
if (openingFence) {
|
||||||
|
const fenceLine = openingFence.quoteIndent > 0 ? unquotedLine : rawLine;
|
||||||
|
if (!hasClosingFence(lines, lineIndex + 1, openingFence)) {
|
||||||
|
processedLines.push(escapeLiteralText(fenceLine, escapeMap));
|
||||||
|
activeFence = openingFence;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
activeFence = openingFence;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const headingMatch = line.match(/^(#{1,4})\s(.*)$/);
|
const headingMatch = line.match(/^(#{1,4})\s(.*)$/);
|
||||||
if (headingMatch) {
|
if (headingMatch) {
|
||||||
const outputLineIndex = processedLines.length;
|
const outputLineIndex = processedLines.length;
|
||||||
@@ -288,15 +291,38 @@ function clampIndent(spaceCount: number): number {
|
|||||||
return Math.min(5, Math.max(1, Math.floor(spaceCount / 2)));
|
return Math.min(5, Math.max(1, Math.floor(spaceCount / 2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasClosingFence(lines: string[], startIndex: number, fence: FenceMarker): boolean {
|
function hasClosingFence(lines: string[], startIndex: number, fence: ActiveFence): boolean {
|
||||||
for (let index = startIndex; index < lines.length; index += 1) {
|
for (let index = startIndex; index < lines.length; index += 1) {
|
||||||
if (isClosingFence(stripQuotePrefix(lines[index]).text, fence)) {
|
const candidate = fence.quoteIndent > 0 ? stripQuotePrefix(lines[index]).text : lines[index];
|
||||||
|
if (isClosingFence(candidate, fence)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolveOpeningFence(line: string): ActiveFence | null {
|
||||||
|
const directFence = parseFenceMarker(line);
|
||||||
|
if (directFence) {
|
||||||
|
return { ...directFence, quoteIndent: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
const quoted = stripQuotePrefix(line);
|
||||||
|
if (quoted.indent === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const quotedFence = parseFenceMarker(quoted.text);
|
||||||
|
if (!quotedFence) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...quotedFence,
|
||||||
|
quoteIndent: quoted.indent,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function stripQuotePrefix(line: string): { text: string; indent: number } {
|
function stripQuotePrefix(line: string): { text: string; indent: number } {
|
||||||
const match = line.match(/^(>+)\s?(.*)$/);
|
const match = line.match(/^(>+)\s?(.*)$/);
|
||||||
if (!match) {
|
if (!match) {
|
||||||
|
|||||||
Reference in New Issue
Block a user