Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"@codemirror/lang-yaml": "^6.1.2",
"@codemirror/language": "^6.12.2",
"@codemirror/legacy-modes": "^6.5.2",
"@codemirror/merge": "^6.12.1",
"@codemirror/search": "^6.6.0",
"@codemirror/state": "^6.5.4",
"@codemirror/theme-one-dark": "^6.1.3",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { defaultKeymap, indentWithTab } from "@codemirror/commands";
import { MergeView } from "@codemirror/merge";
import { highlightSelectionMatches, searchKeymap } from "@codemirror/search";
import { Compartment, EditorState } from "@codemirror/state";
import {
drawSelection,
EditorView,
highlightSpecialChars,
keymap,
lineNumbers,
} from "@codemirror/view";
import { useEffect, useRef } from "react";
import { electronTrpc } from "renderer/lib/electron-trpc";
import { createCodeMirrorTheme } from "renderer/screens/main/components/WorkspaceView/components/CodeEditor/createCodeMirrorTheme";
import { loadLanguageSupport } from "renderer/screens/main/components/WorkspaceView/components/CodeEditor/loadLanguageSupport";
import { getCodeSyntaxHighlighting } from "renderer/screens/main/components/WorkspaceView/utils/code-theme";
import { useResolvedTheme } from "renderer/stores/theme";
import type { DiffViewMode } from "shared/changes-types";

interface CodeMirrorDiffViewerProps {
original: string;
modified: string;
language: string;
viewMode: DiffViewMode;
}

export function CodeMirrorDiffViewer({
original,
modified,
language,
viewMode,
}: CodeMirrorDiffViewerProps) {
const containerRef = useRef<HTMLDivElement>(null);
const mergeViewRef = useRef<MergeView | null>(null);
const langCompartmentA = useRef(new Compartment()).current;
const langCompartmentB = useRef(new Compartment()).current;
const themeCompartmentA = useRef(new Compartment()).current;
const themeCompartmentB = useRef(new Compartment()).current;
const activeTheme = useResolvedTheme();
const { data: fontSettings } = electronTrpc.settings.getFontSettings.useQuery(
undefined,
{ staleTime: 30_000 },
);
const editorFontFamily = fontSettings?.editorFontFamily ?? undefined;
const editorFontSize = fontSettings?.editorFontSize ?? undefined;

// biome-ignore lint/correctness/useExhaustiveDependencies: MergeView is created once and destroyed on unmount
useEffect(() => {
if (!containerRef.current) return;

const baseExtensions = [
lineNumbers(),
highlightSpecialChars(),
drawSelection(),
highlightSelectionMatches(),
EditorState.readOnly.of(true),
EditorView.editable.of(false),
EditorView.lineWrapping,
keymap.of([indentWithTab, ...defaultKeymap, ...searchKeymap]),
];

const themeExts = [
getCodeSyntaxHighlighting(activeTheme),
createCodeMirrorTheme(
activeTheme,
{ fontFamily: editorFontFamily, fontSize: editorFontSize },
true,
),
];

const mergeView = new MergeView({
parent: containerRef.current,
collapseUnchanged: { margin: 3, minSize: 4 },
a: {
doc: original,
extensions: [
...baseExtensions,
themeCompartmentA.of(themeExts),
langCompartmentA.of([]),
],
},
b: {
doc: modified,
extensions: [
...baseExtensions,
themeCompartmentB.of(themeExts),
langCompartmentB.of([]),
],
},
});

mergeViewRef.current = mergeView;

void loadLanguageSupport(language).then((ext) => {
if (!ext || !mergeViewRef.current) return;
const mv = mergeViewRef.current;
mv.a.dispatch({ effects: langCompartmentA.reconfigure(ext) });
mv.b.dispatch({ effects: langCompartmentB.reconfigure(ext) });
});
Comment thread
MocA-Love marked this conversation as resolved.

return () => {
mergeView.destroy();
mergeViewRef.current = null;
};
}, [original, modified, language, viewMode]);

useEffect(() => {
const mv = mergeViewRef.current;
if (!mv) return;

const themeExts = [
getCodeSyntaxHighlighting(activeTheme),
createCodeMirrorTheme(
activeTheme,
{ fontFamily: editorFontFamily, fontSize: editorFontSize },
true,
),
];

mv.a.dispatch({ effects: themeCompartmentA.reconfigure(themeExts) });
mv.b.dispatch({ effects: themeCompartmentB.reconfigure(themeExts) });
}, [
activeTheme,
editorFontFamily,
editorFontSize,
themeCompartmentA,
themeCompartmentB,
]);

return <div ref={containerRef} className="h-full w-full overflow-auto" />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { CodeMirrorDiffViewer } from "./CodeMirrorDiffViewer";
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { detectLanguage } from "shared/detect-language";
import { isImageFile, isSpreadsheetFile } from "shared/file-types";
import type { FileViewerMode } from "shared/tabs-types";
import { useScrollToFirstDiffChange } from "../../hooks/useScrollToFirstDiffChange";
import { CodeMirrorDiffViewer } from "../CodeMirrorDiffViewer";
import { DiffScrollbarDecorations } from "../DiffScrollbarDecorations";
import { DiffViewerContextMenu } from "../DiffViewerContextMenu";
import { FileEditorContextMenu } from "../FileEditorContextMenu";
Expand Down Expand Up @@ -319,6 +320,20 @@ export function FileViewerContent({
);
}

const totalLines =
diffData.original.split("\n").length +
diffData.modified.split("\n").length;
if (totalLines > 2000) {
return (
<CodeMirrorDiffViewer
original={diffData.original}
modified={diffData.modified}
language={diffData.language}
viewMode={diffViewMode}
/>
);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

return (
<DiffViewerContextMenu
containerRef={diffContainerRef}
Expand Down
3 changes: 3 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.