From 6ff48b9517ae09e05bf7bca34f1fd4a19c0cbfb3 Mon Sep 17 00:00:00 2001 From: Silvano Cerza Date: Thu, 20 Feb 2025 10:30:22 +0100 Subject: [PATCH] Go with react-codemirror lib --- package.json | 1 + pnpm-lock.yaml | 75 +++++++++++++ src/views/conflicts-resolution/component.tsx | 8 +- .../diff-highlight-plugin.ts | 8 +- .../conflicts-resolution/editor-pane.tsx | 104 +++++++++--------- 5 files changed, 132 insertions(+), 64 deletions(-) diff --git a/package.json b/package.json index 28b6e4a..361e2cf 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "@codemirror/lang-markdown": "^6.3.2", "@codemirror/state": "^6.5.2", "@codemirror/view": "^6.36.2", + "@uiw/react-codemirror": "^4.23.8", "codemirror": "^6.0.1", "react": "^19.0.0", "react-dom": "^19.0.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 59038fd..2099953 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: '@codemirror/view': specifier: ^6.36.2 version: 6.36.2 + '@uiw/react-codemirror': + specifier: ^4.23.8 + version: 4.23.8(@babel/runtime@7.26.9)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.9)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.2)(codemirror@6.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) codemirror: specifier: ^6.0.1 version: 6.0.1 @@ -68,6 +71,10 @@ packages: resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} engines: {node: '>=6.9.0'} + '@babel/runtime@7.26.9': + resolution: {integrity: sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==} + engines: {node: '>=6.9.0'} + '@codemirror/autocomplete@6.18.6': resolution: {integrity: sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==} @@ -98,6 +105,9 @@ packages: '@codemirror/state@6.5.2': resolution: {integrity: sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==} + '@codemirror/theme-one-dark@6.1.2': + resolution: {integrity: sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==} + '@codemirror/view@6.36.2': resolution: {integrity: sha512-DZ6ONbs8qdJK0fdN7AB82CgI6tYXf4HWk1wSVa0+9bhVznCuuvhQtX8bFBoy3dv8rZSQqUd8GvhVAcielcidrA==} @@ -420,6 +430,28 @@ packages: resolution: {integrity: sha512-mCFtBbFBJDCNCWUl5y6sZSCHXw1DEFEk3c/M3nRK2a4XUB8StGFtmcEMizdjKuBzB6e/smJAAWYug3VrdLMr1w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@uiw/codemirror-extensions-basic-setup@4.23.8': + resolution: {integrity: sha512-XJR/8AEVcE7ufy1BhW2nCN9qSVDYEdCtYLfvhaMwl6Q3qcaYYCGE2K5QbFCy7LsdP/3uZKvc1OskuqatoOPdhQ==} + peerDependencies: + '@codemirror/autocomplete': '>=6.0.0' + '@codemirror/commands': '>=6.0.0' + '@codemirror/language': '>=6.0.0' + '@codemirror/lint': '>=6.0.0' + '@codemirror/search': '>=6.0.0' + '@codemirror/state': '>=6.0.0' + '@codemirror/view': '>=6.0.0' + + '@uiw/react-codemirror@4.23.8': + resolution: {integrity: sha512-/NA5Pj4MmXkLSlmlUm4yfEmRLntrNq5TkQKBSINn7TukXQ4fc+C6Bk0U60Qa4rkvCSgwzZdQ2exyP0t0+2GtqA==} + peerDependencies: + '@babel/runtime': '>=7.11.0' + '@codemirror/state': '>=6.0.0' + '@codemirror/theme-one-dark': '>=6.0.0' + '@codemirror/view': '>=6.0.0' + codemirror: '>=6.0.0' + react: '>=16.8.0' + react-dom: '>=16.8.0' + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -755,6 +787,9 @@ packages: resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} engines: {node: '>=0.10.0'} + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -861,6 +896,10 @@ snapshots: '@babel/helper-validator-identifier@7.25.9': optional: true + '@babel/runtime@7.26.9': + dependencies: + regenerator-runtime: 0.14.1 + '@codemirror/autocomplete@6.18.6': dependencies: '@codemirror/language': 6.10.8 @@ -940,6 +979,13 @@ snapshots: dependencies: '@marijn/find-cluster-break': 1.0.2 + '@codemirror/theme-one-dark@6.1.2': + dependencies: + '@codemirror/language': 6.10.8 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.36.2 + '@lezer/highlight': 1.2.1 + '@codemirror/view@6.36.2': dependencies: '@codemirror/state': 6.5.2 @@ -1237,6 +1283,33 @@ snapshots: '@typescript-eslint/types': 8.19.0 eslint-visitor-keys: 4.2.0 + '@uiw/codemirror-extensions-basic-setup@4.23.8(@codemirror/autocomplete@6.18.6)(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.9)(@codemirror/state@6.5.2)(@codemirror/view@6.36.2)': + dependencies: + '@codemirror/autocomplete': 6.18.6 + '@codemirror/commands': 6.8.0 + '@codemirror/language': 6.10.8 + '@codemirror/lint': 6.8.4 + '@codemirror/search': 6.5.9 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.36.2 + + '@uiw/react-codemirror@4.23.8(@babel/runtime@7.26.9)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.9)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.2)(codemirror@6.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.9 + '@codemirror/commands': 6.8.0 + '@codemirror/state': 6.5.2 + '@codemirror/theme-one-dark': 6.1.2 + '@codemirror/view': 6.36.2 + '@uiw/codemirror-extensions-basic-setup': 4.23.8(@codemirror/autocomplete@6.18.6)(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.9)(@codemirror/state@6.5.2)(@codemirror/view@6.36.2) + codemirror: 6.0.1 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + transitivePeerDependencies: + - '@codemirror/autocomplete' + - '@codemirror/language' + - '@codemirror/lint' + - '@codemirror/search' + acorn-jsx@5.3.2(acorn@8.14.0): dependencies: acorn: 8.14.0 @@ -1591,6 +1664,8 @@ snapshots: react@19.0.0: {} + regenerator-runtime@0.14.1: {} + resolve-from@4.0.0: {} reusify@1.0.4: {} diff --git a/src/views/conflicts-resolution/component.tsx b/src/views/conflicts-resolution/component.tsx index 20479fe..6b3b238 100644 --- a/src/views/conflicts-resolution/component.tsx +++ b/src/views/conflicts-resolution/component.tsx @@ -61,10 +61,10 @@ const DiffView: React.FC = ({
@@ -79,10 +79,10 @@ const DiffView: React.FC = ({
diff --git a/src/views/conflicts-resolution/diff-highlight-plugin.ts b/src/views/conflicts-resolution/diff-highlight-plugin.ts index c21e832..e22fab4 100644 --- a/src/views/conflicts-resolution/diff-highlight-plugin.ts +++ b/src/views/conflicts-resolution/diff-highlight-plugin.ts @@ -7,7 +7,7 @@ import { } from "@codemirror/view"; import { DiffChunk } from "./diff"; -interface DiffHighlightPluginSpec { +export interface DiffHighlightPluginSpec { diff: DiffChunk[]; isOriginal: boolean; } @@ -21,12 +21,6 @@ export function createDiffHighlightPlugin(spec: DiffHighlightPluginSpec) { this.decorations = this.buildDecorations(view); } - update(update: ViewUpdate) { - if (update.docChanged) { - this.decorations = this.buildDecorations(update.view); - } - } - buildDecorations(view: EditorView): DecorationSet { const decorations = []; const doc = view.state.doc; diff --git a/src/views/conflicts-resolution/editor-pane.tsx b/src/views/conflicts-resolution/editor-pane.tsx index 6873520..ebea7d5 100644 --- a/src/views/conflicts-resolution/editor-pane.tsx +++ b/src/views/conflicts-resolution/editor-pane.tsx @@ -1,71 +1,69 @@ import { markdown } from "@codemirror/lang-markdown"; -import { EditorState, StateEffect } from "@codemirror/state"; -import { EditorView, ViewPlugin } from "@codemirror/view"; +import { EditorView, ViewUpdate } from "@codemirror/view"; import * as React from "react"; +import { + DiffHighlightPluginSpec, + createDiffHighlightPlugin, +} from "./diff-highlight-plugin"; +import CodeMirror from "@uiw/react-codemirror"; interface EditorPaneProps { content: string; - highlightPlugin: ViewPlugin; + highlightPluginSpec: DiffHighlightPluginSpec; onEditorUpdate?: (editor: EditorView) => void; onContentChange: (content: string) => void; } const EditorPane: React.FC = ({ content, - highlightPlugin, + highlightPluginSpec, onEditorUpdate, onContentChange, }) => { - const editorRef = React.useRef(null); + const extensions = [ + // basicSetup minus line numbers + // EditorView.lineWrapping, + EditorView.editable.of(true), + createDiffHighlightPlugin(highlightPluginSpec), + EditorView.theme({ + "&": { + backgroundColor: "var(--background-primary)", + color: "var(--text-normal)", + }, + ".cm-content": { + padding: 0, + caretColor: "var(--caret-color)", + fontSize: "var(--font-text-size)", + fontFamily: "var(--font-text)", + }, + "&.cm-focused .cm-selectionBackground, .cm-selectionBackground": { + background: "var(--text-selection)", + }, + "&.cm-focused": { + outline: 0, + }, + "&.cm-focused .cm-cursor": { + borderLeftColor: "var(--text-normal)", + }, + }), + markdown(), + ]; - React.useEffect(() => { - if (editorRef.current) { - const editorState = EditorState.create({ - doc: content, - extensions: [ - // basicSetup minus line numbers - // EditorView.lineWrapping, - EditorView.editable.of(true), - highlightPlugin, - EditorView.theme({ - "&": { - backgroundColor: "var(--background-primary)", - color: "var(--text-normal)", - }, - ".cm-content": { - padding: 0, - }, - "&.cm-focused .cm-selectionBackground, .cm-selectionBackground": { - background: "var(--text-selection)", - }, - "&.cm-focused .cm-cursor": { - borderLeftColor: "var(--text-normal)", - }, - }), - markdown(), - EditorView.updateListener.of((update) => { - if (update.docChanged) { - onContentChange(update.state.doc.toString()); - } - // We want to know when it updates in case the line height changes - onEditorUpdate?.(update.view); - }), - ], - }); - - const editor = new EditorView({ - parent: editorRef.current, - state: editorState, - }); - - return () => { - // Cleanup - editor.destroy(); - }; - } - }, []); - - return
; + return ( + { + if (viewUpdate.docChanged) { + onContentChange(viewUpdate.state.doc.toString()); + } + // We want to know when it updates in case the line height changes + onEditorUpdate?.(viewUpdate.view); + }} + /> + ); }; export default EditorPane;