diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index c00833334..217ff31b2 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed syntax highlighting losing colors after the first line of multiline strings and comments in fenced code blocks ([#10143](https://github.com/earendil-works/pi/issues/10143)) + ## [1.0.3] - 2026-10-05 ### New Features diff --git a/packages/coding-agent/src/modes/interactive/theme/theme.ts b/packages/coding-agent/src/modes/interactive/theme/theme.ts index 26d408f51..5fee354c9 100644 --- a/packages/coding-agent/src/modes/interactive/theme/theme.ts +++ b/packages/coding-agent/src/modes/interactive/theme/theme.ts @@ -966,6 +966,7 @@ function buildCliHighlightTheme(t: Theme): CliHighlightTheme { number: (s: string) => t.fg("syntaxNumber", s), regexp: (s: string) => t.fg("syntaxString", s), string: (s: string) => t.fg("syntaxString", s), + subst: (s: string) => t.fg("text", s), comment: (s: string) => t.fg("syntaxComment", s), doctag: (s: string) => t.fg("syntaxComment", s), meta: (s: string) => t.fg("muted", s), diff --git a/packages/coding-agent/src/utils/syntax-highlight.ts b/packages/coding-agent/src/utils/syntax-highlight.ts index 626f94ab2..1ee1986b9 100644 --- a/packages/coding-agent/src/utils/syntax-highlight.ts +++ b/packages/coding-agent/src/utils/syntax-highlight.ts @@ -155,7 +155,12 @@ export function renderHighlightedHtml(html: string, theme: HighlightTheme = {}): return; } const formatter = getActiveFormatter(scopes, theme); - output += formatter ? formatter(textBuffer) : textBuffer; + output += formatter + ? textBuffer + .split("\n") + .map((line) => (line ? formatter(line) : line)) + .join("\n") + : textBuffer; textBuffer = ""; }; diff --git a/packages/coding-agent/test/syntax-highlight.test.ts b/packages/coding-agent/test/syntax-highlight.test.ts index b8e93b56c..17fe3b2e4 100644 --- a/packages/coding-agent/test/syntax-highlight.test.ts +++ b/packages/coding-agent/test/syntax-highlight.test.ts @@ -1,6 +1,6 @@ import { resetCapabilitiesCache, setCapabilities } from "@earendil-works/pi-tui"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; -import { highlightCode, initTheme, theme } from "../src/modes/interactive/theme/theme.ts"; +import { getMarkdownTheme, highlightCode, initTheme, theme } from "../src/modes/interactive/theme/theme.ts"; import { highlight, loadAllHighlightLanguages, @@ -96,6 +96,23 @@ describe("theme syntax highlighting", () => { resetCapabilitiesCache(); }); + // #10143 + it("colors each line of Python docstrings independently", () => { + const code = '"""\nline one\n\nline two\n"""\nafter'; + const ansi = theme.getFgAnsi("syntaxString"); + const expected = [ + `${ansi}"""\x1b[39m`, + `${ansi}line one\x1b[39m`, + "", + `${ansi}line two\x1b[39m`, + `${ansi}"""\x1b[39m`, + "after", + ]; + + expect(highlightCode(code, "python")).toEqual(expected); + expect(getMarkdownTheme().highlightCode?.(code, "python")).toEqual(expected); + }); + it("colors diff additions and deletions in fenced diff blocks", () => { const lines = highlightCode("-old\n+new\n", "diff");