fix(zalouser): preserve quoted markdown blocks

This commit is contained in:
Tuyen
2026-03-12 11:03:49 +07:00
parent 0ad01d6c0c
commit 9273b6a38b
2 changed files with 73 additions and 10 deletions

View File

@@ -97,6 +97,13 @@ describe("parseZalouserTextStyles", () => {
});
});
it("treats spaced nested blockquotes as deeper quoted lines", () => {
expect(parseZalouserTextStyles("> > quoted")).toEqual({
text: "quoted",
styles: [{ start: 0, len: 6, st: TextStyle.Indent, indentSize: 2 }],
});
});
it("treats indented quoted fences as literal code blocks", () => {
expect(parseZalouserTextStyles(" > ```\n > *cmd*\n > ```")).toEqual({
text: "*cmd*",
@@ -104,6 +111,13 @@ describe("parseZalouserTextStyles", () => {
});
});
it("treats spaced nested quoted fences as literal code blocks", () => {
expect(parseZalouserTextStyles("> > ```\n> > code\n> > ```")).toEqual({
text: "code",
styles: [],
});
});
it("preserves inner quote markers inside quoted fenced code blocks", () => {
expect(parseZalouserTextStyles("> ```\n>> prompt\n> ```")).toEqual({
text: "> prompt",
@@ -111,6 +125,17 @@ describe("parseZalouserTextStyles", () => {
});
});
it("keeps quote indentation on heading lines", () => {
expect(parseZalouserTextStyles("> # Title")).toEqual({
text: "Title",
styles: [
{ start: 0, len: 5, st: TextStyle.Bold },
{ start: 0, len: 5, st: TextStyle.Big },
{ start: 0, len: 5, st: TextStyle.Indent, indentSize: 1 },
],
});
});
it("keeps unmatched fences literal", () => {
expect(parseZalouserTextStyles("```python")).toEqual({
text: "```python",
@@ -150,4 +175,11 @@ describe("parseZalouserTextStyles", () => {
styles: [],
});
});
it("keeps indented code blocks literal", () => {
expect(parseZalouserTextStyles(" *cmd*")).toEqual({
text: "\u00A0\u00A0\u00A0\u00A0*cmd*",
styles: [],
});
});
});

View File

@@ -143,14 +143,33 @@ export function parseZalouserTextStyles(input: string): { text: string; styles:
continue;
}
const outputLineIndex = processedLines.length;
if (isIndentedCodeBlockLine(line)) {
if (baseIndent > 0) {
lineStyles.push({
lineIndex: outputLineIndex,
style: TextStyle.Indent,
indentSize: baseIndent,
});
}
processedLines.push(escapeLiteralText(normalizeCodeBlockLeadingWhitespace(line), escapeMap));
continue;
}
const headingMatch = line.match(/^(#{1,4})\s(.*)$/);
if (headingMatch) {
const outputLineIndex = processedLines.length;
const depth = headingMatch[1].length;
lineStyles.push({ lineIndex: outputLineIndex, style: TextStyle.Bold });
if (depth === 1) {
lineStyles.push({ lineIndex: outputLineIndex, style: TextStyle.Big });
}
if (baseIndent > 0) {
lineStyles.push({
lineIndex: outputLineIndex,
style: TextStyle.Indent,
indentSize: baseIndent,
});
}
processedLines.push(headingMatch[2]);
continue;
}
@@ -163,7 +182,6 @@ export function parseZalouserTextStyles(input: string): { text: string; styles:
content = indentMatch[2];
}
const totalIndent = Math.min(5, baseIndent + indentLevel);
const outputLineIndex = processedLines.length;
if (/^[-*+]\s\[[ xX]\]\s/.test(content)) {
if (totalIndent > 0) {
@@ -331,18 +349,27 @@ function stripQuotePrefix(
line: string,
maxDepth = Number.POSITIVE_INFINITY,
): { text: string; indent: number } {
const match = line.match(/^([ ]{0,3})(>+)( ?)(.*)$/);
if (!match) {
let cursor = 0;
while (cursor < line.length && cursor < 3 && line[cursor] === " ") {
cursor += 1;
}
let removedDepth = 0;
let consumedCursor = cursor;
while (removedDepth < maxDepth && consumedCursor < line.length && line[consumedCursor] === ">") {
removedDepth += 1;
consumedCursor += 1;
if (line[consumedCursor] === " ") {
consumedCursor += 1;
}
}
if (removedDepth === 0) {
return { text: line, indent: 0 };
}
const removedDepth = Math.min(match[2].length, maxDepth);
const remainingMarkers = match[2].slice(removedDepth);
const remainingText =
remainingMarkers.length > 0 ? `${remainingMarkers}${match[3]}${match[4]}` : match[4];
return {
text: remainingText,
text: line.slice(consumedCursor),
indent: Math.min(5, removedDepth),
};
}
@@ -468,6 +495,10 @@ function normalizeCodeBlockLeadingWhitespace(line: string): string {
);
}
function isIndentedCodeBlockLine(line: string): boolean {
return /^(?: {4,}|\t)/.test(line);
}
function stripCodeFenceIndent(line: string, indent: number): string {
let consumed = 0;
let cursor = 0;