feat(editor): Shiki syntax highlighting in the file editor (CodeMirror)
Bring the CodeMirror file editor up to the same rich highlighting as the Shiki
file view, so toggling edit <-> view is visually consistent. lezer collapses far
more tokens than TextMate (import/from/const are all "keyword"), so a theme
remap can't reach parity — instead, project real Shiki tokens onto decorations.
- Worker: add highlightTokens — tokenize with an arbitrary registered TextMate
theme and return per-line styled runs with offsets. The theme object ships to
the worker once per name; later calls send only the name.
- New shikiHighlight CodeMirror extension: a StateField of mark decorations
built from worker tokens. Re-tokenizes on a short idle (off the keystroke
path) and maps decorations through edits so colors persist while typing.
- flexokiTheme: add { syntaxColors: false } to keep only the editor UI theme,
so the lezer highlighter doesn't compete with the Shiki decorations.
- FilesView: enable Shiki highlighting (same language resolver as the file view
→ identical language) and drop lezer token colors when it's active. lezer
language stays on for indentation/folding/brackets.
This commit is contained in:
@@ -29,6 +29,8 @@ import { JsonTreeView } from '@/components/ui/JsonTreeView';
|
||||
import { SimpleMarkdownRenderer } from '@/components/chat/MarkdownRenderer';
|
||||
import { languageByExtension, loadLanguageByExtension } from '@/lib/codemirror/languageByExtension';
|
||||
import { createFlexokiCodeMirrorTheme } from '@/lib/codemirror/flexokiTheme';
|
||||
import { shikiHighlightExtension } from '@/lib/codemirror/shikiHighlight';
|
||||
import { getResolvedShikiTheme } from '@/lib/shiki/appThemeRegistry';
|
||||
import { File as PierreFile } from '@pierre/diffs/react';
|
||||
import {
|
||||
Dialog,
|
||||
@@ -2824,11 +2826,23 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
return [createFlexokiCodeMirrorTheme(currentTheme)];
|
||||
}
|
||||
|
||||
const extensions = [createFlexokiCodeMirrorTheme(currentTheme)];
|
||||
// Shiki token colors (worker-backed) match the Shiki file view exactly.
|
||||
// Same language resolver as the view, so both agree on the language. When
|
||||
// Shiki is the color source, drop the lezer token colors to avoid a
|
||||
// competing highlighter (keep the lezer language for indentation/folding).
|
||||
const shikiLanguage = getLanguageFromExtension(selectedFile.path);
|
||||
const extensions = [createFlexokiCodeMirrorTheme(currentTheme, shikiLanguage ? { syntaxColors: false } : undefined)];
|
||||
const language = staticLanguageExtension ?? dynamicLanguageExtension;
|
||||
if (language) {
|
||||
extensions.push(language);
|
||||
}
|
||||
if (shikiLanguage) {
|
||||
extensions.push(shikiHighlightExtension({
|
||||
language: shikiLanguage,
|
||||
themeName: currentTheme.metadata.id,
|
||||
theme: getResolvedShikiTheme(currentTheme),
|
||||
}));
|
||||
}
|
||||
if (wrapLines) {
|
||||
extensions.push(EditorView.lineWrapping);
|
||||
}
|
||||
@@ -2939,7 +2953,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
}, [pdfAssetAuthKey, t]);
|
||||
|
||||
const isPdfAssetAuthLoading = Boolean(pdfAssetAuthKey && pdfAssetAuthReadyKey !== pdfAssetAuthKey);
|
||||
|
||||
|
||||
const imageSrc = selectedFile?.path && isSelectedImage
|
||||
? (runtime.isDesktop
|
||||
? (isSelectedSvg
|
||||
|
||||
Reference in New Issue
Block a user