Skip to content

Commit

Permalink
add diff editor
Browse files Browse the repository at this point in the history
  • Loading branch information
ysmood committed Nov 1, 2024
1 parent f5a5dd0 commit a45beca
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 10 deletions.
8 changes: 7 additions & 1 deletion devtools-ui/src/Components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,25 @@ export function Button({
onClick,
className,
title,
selected = false,
disabled = false,
}: {
icon?: React.ReactElement;
text?: string;
onClick?: () => void;
className?: string;
title: string;
selected?: boolean;
disabled?: boolean;
}) {
return (
<div
onClick={disabled ? undefined : onClick}
className={cx(buttonStyle, className, { disabled, active: !disabled })}
className={cx(buttonStyle, className, {
disabled,
active: !disabled,
selected,
})}
title={title}
>
{icon} {text}
Expand Down
89 changes: 89 additions & 0 deletions devtools-ui/src/DiffEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { useRef, useEffect } from "react";
import { setStaging, usePrevStateContent, useStaging } from "./store/staging";
import monaco, { theme } from "./store/editor";
import { useThrottle } from "./store/utils";

export function DiffEditor({
className,
width,
height,
}: {
className?: string;
width: number;
height: number;
}) {
const container = useRef<HTMLDivElement>(null);
const editorRef = useRef<monaco.editor.IStandaloneDiffEditor | null>();
const prevValue = usePrevStateContent();
const currValue = useStaging();

const setEditorContent = useThrottle(
(prev: string, curr: string) => {
editorRef.current?.getModel()?.original.setValue(prev);
editorRef.current?.getModel()?.modified.setValue(curr);
},
100,
[]
);

useEffect(() => {
if (!container.current) return;

const editor = monaco.editor.createDiffEditor(container.current, {
theme,
});

editor.setModel({
original: monaco.editor.createModel("", "json"),
modified: monaco.editor.createModel("", "json"),
});

editorRef.current = editor;

const contentLn = editor.getModel()?.modified.onDidChangeContent(() => {
setStaging(editorRef.current?.getModel()?.modified.getValue() || "");
});

// TODO: Patch to prevent calling setValue on disposed editor
// Remove it once the issue is fixed in monaco
const patchLn = function (e: PromiseRejectionEvent) {
if (
e.reason.stack.includes("Delayer.cancel") &&
e.reason.stack.includes("Delayer.dispose")
) {
e.preventDefault();
} else {
throw e;
}
};
window.addEventListener("unhandledrejection", patchLn);
const patchLn2 = function (e: ErrorEvent) {
if (
e.error.message.includes("AbstractContextKeyService has been disposed")
) {
e.preventDefault();
} else {
throw e.error;
}
};
window.addEventListener("error", patchLn2);

return () => {
window.removeEventListener("unhandledrejection", patchLn);
window.removeEventListener("error", patchLn2);

contentLn?.dispose();
editor.dispose();
};
}, []);

useEffect(() => {
setEditorContent(prevValue, currValue);
}, [setEditorContent, prevValue, currValue]);

useEffect(() => {
editorRef.current?.layout({ width, height });
}, [width, height]);

return <div ref={container} className={className}></div>;
}
4 changes: 2 additions & 2 deletions devtools-ui/src/Editor.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useRef, useEffect } from "react";
import { setStaging, useStaging } from "./store/staging";
import monaco from "./store/editor";
import monaco, { theme } from "./store/editor";
import { useThrottle } from "./store/utils";

export function Editor({
Expand All @@ -27,7 +27,7 @@ export function Editor({

const editor = monaco.editor.create(container.current, {
language: "json",
theme: "vs-dark",
theme,
wordBasedSuggestions: "currentDocument",
quickSuggestions: true,
});
Expand Down
50 changes: 43 additions & 7 deletions devtools-ui/src/Staging.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { commit, format, useSameLast } from "./store/staging";
import {
commit,
format,
toggleDiffMode,
useDiffMode,
useSameLast,
} from "./store/staging";
import { selectRecord, travelTo, useSelected } from "./store/history";
import { css } from "@emotion/css";
import { Button, Title } from "./Components";
Expand All @@ -9,23 +15,39 @@ import { useFiltered } from "./store/filter";
import Slider from "./Slider";
import AutoSizer from "react-virtualized-auto-sizer";
import { TbClockCode } from "react-icons/tb";
import { DiffEditor } from "./DiffEditor";
import { VscDiffMultiple } from "react-icons/vsc";

export default function Staging() {
return (
<div className={style}>
<Toolbar />
<div>
<AutoSizer>
{({ width, height }) => (
<Editors />
</div>
);
}

function Editors() {
const diffMode = useDiffMode();

return (
<div>
<AutoSizer>
{({ width, height }) =>
diffMode ? (
<DiffEditor width={width} height={height} />
) : (
<Editor className="editor" width={width} height={height} />
)}
</AutoSizer>
</div>
)
}
</AutoSizer>
</div>
);
}

function Toolbar() {
const diffMode = useDiffMode();

return (
<div className="toolbar">
<Title text="Staging" />
Expand All @@ -48,6 +70,13 @@ function Toolbar() {
title="Format json"
/>

<Button
onClick={() => toggleDiffMode()}
icon={<VscDiffMultiple size={14} />}
className="diff"
title="Diff mode"
selected={diffMode}
/>
<Travel />
</div>
);
Expand Down Expand Up @@ -94,6 +123,13 @@ const style = css({
fontFamily: "monospace",
},

".diff.selected": {
background: "#ad0c46",
"&:hover": {
background: " #e6427d",
},
},

".travel": {
display: "grid",
gridTemplateColumns: "auto 1fr",
Expand Down
1 change: 1 addition & 0 deletions devtools-ui/src/store/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const emptySession = {
name: "none", // connection name

staging: "",
diffMode: false,

selected: -1,
history: new History(),
Expand Down
2 changes: 2 additions & 0 deletions devtools-ui/src/store/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ window.MonacoEnvironment = {
};

export * as default from "monaco-editor/esm/vs/editor/editor.api";

export const theme = "vs-dark";
17 changes: 17 additions & 0 deletions devtools-ui/src/store/staging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ export function useStaging() {
return useSession((s) => s.staging);
}

export function usePrevStateContent(): string {
return useSession((s) => {
if (s.selected === 0) return s.staging;
return s.history.list.get(s.selected - 1)!.state;
});
}

export function setStaging(content: string) {
setSession((s) => {
if (s.staging === content) return;
Expand Down Expand Up @@ -42,3 +49,13 @@ export function format() {
s.staging = JSON.stringify(JSON.parse(s.staging), null, 2);
});
}

export function useDiffMode() {
return useSession((s) => s.diffMode);
}

export function toggleDiffMode() {
setSession((s) => {
s.diffMode = !s.diffMode;
});
}

0 comments on commit a45beca

Please sign in to comment.