import type { TUI } from "@earendil-works/pi-tui"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { writeRenderers } from "../src/core/tools/renderers/write.ts"; import { codemodeRenderers } from "../src/extensions/codemode/renderer.ts"; import { ToolExecutionComponent } from "../src/modes/interactive/components/tool-execution.ts"; import { CodeHighlighter, highlightCode, initTheme } from "../src/modes/interactive/theme/theme.ts"; import * as syntaxHighlight from "../src/utils/syntax-highlight.ts"; const tui = { requestRender: () => {} } as unknown as TUI; describe("CodeHighlighter", () => { beforeEach(() => initTheme("dark")); afterEach(() => { vi.restoreAllMocks(); vi.useRealTimers(); }); it("reuses its last result until the code, the theme or the grammars change", async () => { const code = 'const page = html`
ok
`;'; const highlighter = new CodeHighlighter(); const first = highlighter.highlight(code, "javascript"); expect(highlighter.highlight(code, "javascript")).toBe(first); expect(highlighter.highlight("const a = 1;", "javascript")).not.toBe(first); initTheme("light"); expect(highlighter.highlight(code, "javascript")).toEqual(highlightCode(code, "javascript")); const light = highlighter.highlight(code, "javascript"); await syntaxHighlight.loadAllHighlightLanguages(); expect(highlighter.highlight(code, "javascript")).not.toEqual(light); }); it("codemode highlights the script once across progress, timer ticks, expanding and completion", () => { vi.useFakeTimers(); const highlight = vi.spyOn(syntaxHighlight, "highlight"); const row = new ToolExecutionComponent( "codemode", "c", { code: "await tools.read({});" }, {}, codemodeRenderers, tui, "/", ); row.setArgsComplete(); row.markExecutionStarted(); row.updateResult({ content: [], details: { calls: [] }, isError: false }, true); vi.advanceTimersByTime(2_000); row.setExpanded(true); row.updateResult({ content: [{ type: "text", text: "done" }], details: { calls: [] }, isError: false }, false); row.render(80); expect(highlight).toHaveBeenCalledTimes(1); }); it("write highlights unchanged first lines and a complete file only once", () => { const content = Array.from({ length: 60 }, (_, i) => `const v${i} = ${i};`).join("\n"); const row = new ToolExecutionComponent("write", "w", { path: "a.ts", content }, {}, writeRenderers, tui, "/"); row.updateArgs({ path: "a.ts", content: `${content} //` }); row.render(80); const highlight = vi.spyOn(syntaxHighlight, "highlight"); // Only the changed last line is highlighted again. row.updateArgs({ path: "a.ts", content: `${content} // more` }); row.render(80); expect(highlight).toHaveBeenCalledTimes(1); row.setArgsComplete(); row.setExpanded(true); row.setExpanded(false); row.render(80); expect(highlight).toHaveBeenCalledTimes(2); }); });