From 91c7faa0f0c146b3aa5fb57952d9d266d7c9c562 Mon Sep 17 00:00:00 2001 From: Tuyen Date: Thu, 12 Mar 2026 00:05:15 +0700 Subject: [PATCH] fix(zalouser): support tilde fenced code blocks --- extensions/zalouser/src/text-styles.test.ts | 14 ++++ extensions/zalouser/src/text-styles.ts | 80 ++++++++++++++++++--- 2 files changed, 83 insertions(+), 11 deletions(-) diff --git a/extensions/zalouser/src/text-styles.test.ts b/extensions/zalouser/src/text-styles.test.ts index 851f602168b..1e17a433541 100644 --- a/extensions/zalouser/src/text-styles.test.ts +++ b/extensions/zalouser/src/text-styles.test.ts @@ -48,6 +48,20 @@ describe("parseZalouserTextStyles", () => { }); }); + it("treats tilde fences as literal code blocks", () => { + expect(parseZalouserTextStyles("~~~bash\n*cmd*\n~~~")).toEqual({ + text: "*cmd*", + styles: [], + }); + }); + + it("treats fences indented under list items as literal code blocks", () => { + expect(parseZalouserTextStyles(" ```\n*cmd*\n ```")).toEqual({ + text: "*cmd*", + styles: [], + }); + }); + it("keeps unmatched fences literal", () => { expect(parseZalouserTextStyles("```python")).toEqual({ text: "```python", diff --git a/extensions/zalouser/src/text-styles.ts b/extensions/zalouser/src/text-styles.ts index b37421814ca..82f4d96b278 100644 --- a/extensions/zalouser/src/text-styles.ts +++ b/extensions/zalouser/src/text-styles.ts @@ -28,6 +28,12 @@ type ResolvedInlineMatch = { priority: number; }; +type FenceMarker = { + char: "`" | "~"; + length: number; + indent: number; +}; + const TAG_STYLE_MAP: Record = { red: TextStyle.Red, orange: TextStyle.Orange, @@ -96,24 +102,37 @@ export function parseZalouserTextStyles(input: string): { text: string; styles: const lines = input.split("\n"); const lineStyles: LineStyle[] = []; const processedLines: string[] = []; - let inCodeBlock = false; + let activeFence: FenceMarker | null = null; for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) { let line = lines[lineIndex]; let baseIndent = 0; - if (/^```/.test(line)) { - if (!inCodeBlock && !hasClosingFence(lines, lineIndex + 1)) { - processedLines.push(escapeLiteralText(line, escapeMap)); - inCodeBlock = true; + const fence = parseFenceMarker(line); + if (fence) { + if (!activeFence) { + if (!hasClosingFence(lines, lineIndex + 1, fence)) { + processedLines.push(escapeLiteralText(line, escapeMap)); + activeFence = fence; + continue; + } + activeFence = fence; + continue; + } + + if (isClosingFence(line, activeFence)) { + activeFence = null; continue; } - inCodeBlock = !inCodeBlock; - continue; } - if (inCodeBlock) { - processedLines.push(escapeLiteralText(normalizeCodeBlockLeadingWhitespace(line), escapeMap)); + if (activeFence) { + processedLines.push( + escapeLiteralText( + normalizeCodeBlockLeadingWhitespace(stripCodeFenceIndent(line, activeFence.indent)), + escapeMap, + ), + ); continue; } @@ -274,15 +293,42 @@ function clampIndent(spaceCount: number): number { return Math.min(5, Math.max(1, Math.floor(spaceCount / 2))); } -function hasClosingFence(lines: string[], startIndex: number): boolean { +function hasClosingFence(lines: string[], startIndex: number, fence: FenceMarker): boolean { for (let index = startIndex; index < lines.length; index += 1) { - if (/^```/.test(lines[index])) { + if (isClosingFence(lines[index], fence)) { return true; } } return false; } +function parseFenceMarker(line: string): FenceMarker | null { + const match = line.match(/^([ ]{0,3})(`{3,}|~{3,})(.*)$/); + if (!match) { + return null; + } + + const marker = match[2]; + const char = marker[0]; + if (char !== "`" && char !== "~") { + return null; + } + + return { + char, + length: marker.length, + indent: match[1].length, + }; +} + +function isClosingFence(line: string, fence: FenceMarker): boolean { + const match = line.match(/^([ ]{0,3})(`{3,}|~{3,})[ \t]*$/); + if (!match) { + return false; + } + return match[2][0] === fence.char && match[2].length >= fence.length; +} + function escapeLiteralText(input: string, escapeMap: string[]): string { return input.replace(/[\\*_~{}`]/g, (ch) => { const index = escapeMap.length; @@ -376,3 +422,15 @@ function normalizeCodeBlockLeadingWhitespace(line: string): string { leadingWhitespace.replace(/\t/g, "\u00A0\u00A0\u00A0\u00A0").replace(/ /g, "\u00A0"), ); } + +function stripCodeFenceIndent(line: string, indent: number): string { + let consumed = 0; + let cursor = 0; + + while (cursor < line.length && consumed < indent && line[cursor] === " ") { + cursor += 1; + consumed += 1; + } + + return line.slice(cursor); +}