feat(ui): syntax-highlight the read-only source previews (#1284)

The read-only previews beside Problems, Show Usages and TODO results are syntax-coloured from one whole-file parse (capped for very large files), so a line inside a block comment or a multi-line string is coloured as such; the pane renders plain first and recolours when the parse lands. SourcePreview takes the colouring as a colorize prop (the editor's previewColors), keeping src/ui free of editor imports, and the tok-* colours live once in global.css instead of a copy per view, with the theme's comment style.

Co-authored-by: qtvbwfn <[email protected]>
Цей коміт міститься в:
Qingтаqtvbwfn authored and GitHub committed 2026-11-05 00:55:03 +08:00
1 джерело f4fa2b42f6
коміт 78a3768ec1
15 files changed
+297 -293

No files matched your search

+6 -31
Переглянути файл
@@ -3,12 +3,13 @@
* `ChunkExtractor`: each file is parsed whole once, so a line inside a block
* comment or a multi-line string is coloured as such.
*/
import { useEffect, useState, type ReactNode } from "react";
import { useEffect, useState } from "react";
import { Text } from "@codemirror/state";
import { editorsFor } from "@/editor/api";
import { codeHighlighter, highlightTokens, languageForPath, type CodeToken } from "@/editor/lsp/markdown";
import { call, commands } from "@/platform/ipc";
import { isVirtualUri, virtualDocument } from "@/platform/virtualDocs";
import type { SourceColorizer } from "@/ui/SourcePreview";
export type { CodeToken };
@@ -135,33 +136,7 @@ export function useLineTokens(source: LineSource | undefined, text: string): Cod
return loaded?.key === key ? loaded.tokens : peekLineTokens({ path, line, col }, text);
}
/**
* `text` between `from` and `to` as spans with the tokens' `tok-*` classes
* (styled by the caller); `key` prefixes the React keys.
*/
export function tokenSpans(
text: string,
from: number,
to: number,
tokens: readonly CodeToken[] | null,
key = "",
): ReactNode[] {
if (!tokens?.length) return [text.slice(from, to)];
const out: ReactNode[] = [];
let pos = from;
for (const t of tokens) {
if (t.to <= pos) continue;
if (t.from >= to) break;
const s = Math.max(t.from, pos);
const e = Math.min(t.to, to);
if (s > pos) out.push(text.slice(pos, s));
out.push(
<span key={`${key}${s}`} className={t.cls}>
{text.slice(s, e)}
</span>,
);
pos = e;
}
if (pos < to) out.push(text.slice(pos, to));
return out;
}
export { tokenSpans } from "@/ui/tokenSpans";
/** Whole-file syntax colours for `SourcePreview` (its `colorize` prop). */
export const previewColors: SourceColorizer = (text, path) => codeHighlighter(text, languageForPath(path));