Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/web/src/components/DiffPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
resolveDiffThemeName,
resolveFileDiffPath,
} from "../lib/diffRendering";
import { PREFERRED_HIGHLIGHTER } from "../lib/syntaxHighlighting";
import { areAllDiffFilesCollapsed, toggleAllDiffFiles } from "../lib/diffCollapse";
import { useTurnDiffSummaries } from "../hooks/useTurnDiffSummaries";
import { useProject, useThread } from "../state/entities";
Expand Down Expand Up @@ -960,6 +961,7 @@ export default function DiffPanel({
lineDiffType: "none",
overflow: wordWrap ? "wrap" : "scroll",
theme: resolveDiffThemeName(resolvedTheme),
preferredHighlighter: PREFERRED_HIGHLIGHTER,
themeType: resolvedTheme as DiffThemeType,
stickyHeaders: true,
...(loadDiffFiles ? { loadDiffFiles } : {}),
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/components/DiffWorkerPoolProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as Schema from "effect/Schema";
import { useEffect, useMemo, type ReactNode } from "react";
import { useTheme } from "../hooks/useTheme";
import { resolveDiffThemeName, type DiffThemeName } from "../lib/diffRendering";
import { PREFERRED_HIGHLIGHTER } from "../lib/syntaxHighlighting";

export class DiffWorkerError extends Schema.TaggedErrorClass<DiffWorkerError>()("DiffWorkerError", {
operation: Schema.Literals(["create-worker", "get-render-options", "set-render-options"]),
Expand Down Expand Up @@ -73,6 +74,7 @@ export function DiffWorkerPoolProvider({ children }: { children?: ReactNode }) {
}}
highlighterOptions={{
theme: diffThemeName,
preferredHighlighter: PREFERRED_HIGHLIGHTER,
tokenizeMaxLineLength: 1_000,
useTokenTransformer: true,
}}
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/components/chat/MessagesTimeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {
resolveDiffThemeName,
resolveFileDiffPath,
} from "../../lib/diffRendering";
import { PREFERRED_HIGHLIGHTER } from "../../lib/syntaxHighlighting";
import ChatMarkdown from "../ChatMarkdown";
import {
BotIcon,
Expand Down Expand Up @@ -2110,6 +2111,7 @@ function UserMessageReviewCommentCard({ comment }: { comment: ReviewCommentConte
collapsed: false,
diffStyle: "unified",
theme: resolveDiffThemeName(ctx.resolvedTheme),
preferredHighlighter: PREFERRED_HIGHLIGHTER,
}}
/>
))}
Expand Down
3 changes: 3 additions & 0 deletions apps/web/src/components/files/FilePreviewPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { useClientSettings } from "~/hooks/useSettings";
import { useTheme } from "~/hooks/useTheme";
import { getLocalStorageItem, setLocalStorageItem, useLocalStorage } from "~/hooks/useLocalStorage";
import { DIFF_SURFACE_THEME_UNSAFE_CSS, resolveDiffThemeName } from "~/lib/diffRendering";
import { PREFERRED_HIGHLIGHTER } from "~/lib/syntaxHighlighting";
import { cn } from "~/lib/utils";
import { isPreviewSupportedInRuntime } from "~/previewStateStore";
import { resolvePathLinkTarget } from "~/terminal-links";
Expand Down Expand Up @@ -670,6 +671,7 @@ function EditableFileSurface({
onLineSelectionEnd: handleLineSelectionEnd,
overflow: wordWrap ? "wrap" : "scroll",
theme: resolveDiffThemeName(resolvedTheme),
preferredHighlighter: PREFERRED_HIGHLIGHTER,
themeType: resolvedTheme,
unsafeCSS: FILE_LINK_REVEAL_UNSAFE_CSS,
onPostRender: handlePostRender,
Expand Down Expand Up @@ -1039,6 +1041,7 @@ export default function FilePreviewPanel({
disableFileHeader: true,
overflow: wordWrap ? "wrap" : "scroll",
theme: resolveDiffThemeName(resolvedTheme),
preferredHighlighter: PREFERRED_HIGHLIGHTER,
themeType: resolvedTheme,
unsafeCSS: FILE_LINK_REVEAL_UNSAFE_CSS,
onPostRender: onFilePostRender,
Expand Down
3 changes: 2 additions & 1 deletion apps/web/src/components/settings/SettingsFontPreviews.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { terminalThemeFromApp } from "../ThreadTerminalDrawer";
import { useTheme } from "../../hooks/useTheme";
import { DISCONNECTED_COMPOSER_PLACEHOLDER } from "../../composerPlaceholder";
import { resolveDiffThemeName, type DiffThemeName } from "../../lib/diffRendering";
import { PREFERRED_HIGHLIGHTER } from "../../lib/syntaxHighlighting";
import { GhosttyTerminalSurface } from "~/terminal/ghostty/surface";

// The font previews are the real surfaces, not lookalikes: the composer's
Expand Down Expand Up @@ -79,7 +80,7 @@ function loadDiffPreviewHtml(theme: DiffThemeName): Promise<readonly string[]> {
if (promise === undefined) {
promise = preloadPatchFile({
patch: DIFF_PREVIEW_PATCH,
options: { diffStyle: "unified", theme },
options: { diffStyle: "unified", theme, preferredHighlighter: PREFERRED_HIGHLIGHTER },
}).then((results) => results.map((result) => result.prerenderedHTML));
diffPreviewHtmlByTheme.set(theme, promise);
}
Expand Down
9 changes: 8 additions & 1 deletion apps/web/src/lib/syntaxHighlighting.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ vi.mock("@pierre/diffs", () => ({
getSharedHighlighter,
}));

import { getSyntaxHighlighterPromise } from "./syntaxHighlighting";
import { getSyntaxHighlighterPromise, PREFERRED_HIGHLIGHTER } from "./syntaxHighlighting";

it("prefers the Oniguruma WASM highlighter", () => {
expect(PREFERRED_HIGHLIGHTER).toBe("shiki-wasm");
});

it("caches the recovered text highlighter for unsupported languages", async () => {
const textHighlighter = {} as DiffsHighlighter;
Expand All @@ -25,4 +29,7 @@ it("caches the recovered text highlighter for unsupported languages", async () =

expect(second).toBe(first);
expect(getSharedHighlighter).toHaveBeenCalledTimes(2);
expect(getSharedHighlighter).toHaveBeenCalledWith(
expect.objectContaining({ preferredHighlighter: "shiki-wasm" }),
);
});
11 changes: 10 additions & 1 deletion apps/web/src/lib/syntaxHighlighting.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
import {
getSharedHighlighter,
type DiffsHighlighter,
type HighlighterTypes,
type SupportedLanguages,
} from "@pierre/diffs";

import { resolveDiffThemeName } from "./diffRendering";

/**
* Always highlight with the Oniguruma WASM engine. The JS regex engine can
* backtrack catastrophically and hang the tokenizing thread. The shared
* highlighter is a first-caller-wins singleton, so every creation site must
* pass this value.
*/
export const PREFERRED_HIGHLIGHTER: HighlighterTypes = "shiki-wasm";

const highlighterPromiseCache = new Map<string, Promise<DiffsHighlighter>>();

export function getSyntaxHighlighterPromise(language: string): Promise<DiffsHighlighter> {
Expand All @@ -15,7 +24,7 @@ export function getSyntaxHighlighterPromise(language: string): Promise<DiffsHigh
const promise = getSharedHighlighter({
themes: [resolveDiffThemeName("dark"), resolveDiffThemeName("light")],
langs: [language as SupportedLanguages],
preferredHighlighter: "shiki-js",
preferredHighlighter: PREFERRED_HIGHLIGHTER,
}).catch((error) => {
if (language === "text") {
highlighterPromiseCache.delete(language);
Expand Down
Loading