From ae6c938519d45ed6ba05e00bb873a2cf6b1f9801 Mon Sep 17 00:00:00 2001 From: Sam Cheng Date: Wed, 19 Jun 2024 16:52:10 +0800 Subject: [PATCH] Upgrade codemirror 5 to 6 (breaking change) https://github.com/uiwjs/react-codemirror/issues/216 --- frontend/package.json | 12 ++- frontend/src/pages/runjs/formatting.js | 110 ------------------------- frontend/src/pages/runjs/index.js | 100 +++------------------- frontend/src/pages/runjs/init.js | 34 -------- frontend/src/pages/runjs/lib.js | 77 ----------------- frontend/src/template.html | 5 +- frontend/webpack.config.js | 1 + 7 files changed, 24 insertions(+), 315 deletions(-) delete mode 100644 frontend/src/pages/runjs/formatting.js delete mode 100644 frontend/src/pages/runjs/init.js delete mode 100644 frontend/src/pages/runjs/lib.js diff --git a/frontend/package.json b/frontend/package.json index ce35c00..23d31b0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -38,20 +38,26 @@ "webpack-dev-server": "^5.0.4" }, "dependencies": { + "@codemirror/autocomplete": "^6.16.3", + "@codemirror/commands": "^6.6.0", + "@codemirror/lang-javascript": "^6.2.2", + "@codemirror/language": "^6.10.2", + "@codemirror/lint": "^6.8.1", + "@codemirror/search": "^6.5.6", + "@codemirror/state": "^6.4.1", + "@codemirror/view": "^6.28.1", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", "@fontsource/roboto": "^5.0.13", "@loadable/component": "^5.12.0", "@mui/icons-material": "^5.15.19", "@mui/material": "^5.15.19", - "codemirror": "^5.65.16", + "@uiw/react-codemirror": "^4.22.2", "core-js": "^3.1.4", "file-saver": "^2.0.5", "jszip": "^3.6.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-github-btn": "^1.2.0", - "react-github-button": "^0.1.11", "react-router-dom": "^6.23.1", "styled-components": "^5.2.3" }, diff --git a/frontend/src/pages/runjs/formatting.js b/frontend/src/pages/runjs/formatting.js deleted file mode 100644 index 49bf3c8..0000000 --- a/frontend/src/pages/runjs/formatting.js +++ /dev/null @@ -1,110 +0,0 @@ -import * as CodeMirror from "codemirror/lib/codemirror"; - -(function() { - - CodeMirror.extendMode("css", { - commentStart: "/*", - commentEnd: "*/", - newlineAfterToken: function(type, content) { - return /^[;{}]$/.test(content); - } - }); - - CodeMirror.extendMode("javascript", { - commentStart: "/*", - commentEnd: "*/", - // FIXME semicolons inside of for - newlineAfterToken: function(type, content, textAfter, state) { - if (this.jsonMode) { - return /^[\[,{]$/.test(content) || /^}/.test(textAfter); - } else { - if (content == ";" && state.lexical && state.lexical.type == ")") return false; - return /^[;{}]$/.test(content) && !/^;/.test(textAfter); - } - } - }); - - CodeMirror.extendMode("xml", { - commentStart: "", - newlineAfterToken: function(type, content, textAfter) { - return type == "tag" && />$/.test(content) || /^ -1 && endIndex > -1 && endIndex > startIndex) { - // Take string till comment start - selText = selText.substr(0, startIndex) - // From comment start till comment end - + selText.substring(startIndex + curMode.commentStart.length, endIndex) - // From comment end till string end - + selText.substr(endIndex + curMode.commentEnd.length); - } - cm.replaceRange(selText, from, to); - } - }); - }); - - // Applies automatic mode-aware indentation to the specified range - CodeMirror.defineExtension("autoIndentRange", function (from, to) { - var cmInstance = this; - this.operation(function () { - for (var i = from.line; i <= to.line; i++) { - cmInstance.indentLine(i, "smart"); - } - }); - }); - - // Applies automatic formatting to the specified range - CodeMirror.defineExtension("autoFormatRange", function (from, to) { - var cm = this; - var outer = cm.getMode(), text = cm.getRange(from, to).split("\n"); - var state = CodeMirror.copyState(outer, cm.getTokenAt(from).state); - var tabSize = cm.getOption("tabSize"); - - var out = "", lines = 0, atSol = from.ch == 0; - function newline() { - out += "\n"; - atSol = true; - ++lines; - } - - for (var i = 0; i < text.length; ++i) { - var stream = new CodeMirror.StringStream(text[i], tabSize); - while (!stream.eol()) { - var inner = CodeMirror.innerMode(outer, state); - var style = outer.token(stream, state), cur = stream.current(); - stream.start = stream.pos; - if (!atSol || /\S/.test(cur)) { - out += cur; - atSol = false; - } - if (!atSol && inner.mode.newlineAfterToken && - inner.mode.newlineAfterToken(style, cur, stream.string.slice(stream.pos) || text[i+1] || "", inner.state)) - newline(); - } - if (!stream.pos && outer.blankLine) outer.blankLine(state); - if (!atSol) newline(); - } - - cm.operation(function () { - cm.replaceRange(out, from, to); - for (var cur = from.line + 1, end = from.line + lines; cur <= end; ++cur) - cm.indentLine(cur, "smart"); - cm.setSelection(from, cm.getCursor(false)); - }); - }); -})(); diff --git a/frontend/src/pages/runjs/index.js b/frontend/src/pages/runjs/index.js index 650d7df..b65ddc7 100644 --- a/frontend/src/pages/runjs/index.js +++ b/frontend/src/pages/runjs/index.js @@ -1,19 +1,27 @@ -import React, { useEffect, useCallback, useRef, useState } from "react"; -import { initCodeEditor, createNode } from "./lib"; +import React, { useRef, useState } from "react"; +import CodeMirror from '@uiw/react-codemirror'; +import { javascript } from '@codemirror/lang-javascript'; -import init from "./init"; import "./bulma.min.css"; import "./index.less"; import AppBar from './components/AppBar' +// TODO: // material UI Layout // import Grid from '@mui/material/Unstable_Grid2'; +// https://stackoverflow.com/questions/5379120/get-the-highlighted-selected-text export default () => { const [editorMode, setEditorMode] = useState("js"); const [autoRun, setAutoRun] = useState(false); + const [value, setValue] = React.useState("console.log('hello world!');"); + const onChange = React.useCallback((val, viewUpdate) => { + console.log('val:', val); + setValue(val); + }, []); + let staticRef = useRef({ isAuto: false, js: null, @@ -22,74 +30,6 @@ export default () => { lib: ["https://unpkg.com/babel-standalone/babel.min.js", "https://unpkg.com/react/umd/react.development.js", "https://unpkg.com/react-dom/umd/react-dom.development.js"], }); - const onFormat = useCallback((type) => { - let editor = staticRef.current[type]; - editor.execCommand("selectAll"); - editor.autoFormatRange(editor.getCursor(true), editor.getCursor(false)); - editor.execCommand("goDocEnd"); - }, []); - - const onLoad = useCallback(() => { - let iframe = document.getElementById("preview"), - html = staticRef.current.html.getValue(), - css = staticRef.current.css.getValue(), - js = staticRef.current.js.getValue(); - var preview; - if (iframe.contentDocument) { - preview = iframe.contentDocument; - } else if (iframe.contentWindow) { - preview = iframe.contentWindow.document; - } else { - preview = iframe.document; - } - let lib = ``; - staticRef.current.lib.map((item) => { - lib += ``; - }); - preview.open(); - preview.write(`${lib}${html}`); - preview.close(); - preview.head.innerHTML = ` - - - `; - }, []); - - const onRun = useCallback(() => { - let iframe = document.getElementById("preview"); - iframe.contentWindow.location.reload(true); - }, []); - - const onAutoRun = useCallback(() => { - if (staticRef.current.isAuto) onRun(); - }, [autoRun]); - - useEffect(() => { - if (staticRef.current.js == null && staticRef.current.html == null && staticRef.current.css == null) { - staticRef.current.js = initCodeEditor(document.getElementById("js"), "javascript", init.javascript, onAutoRun); - staticRef.current.html = initCodeEditor(document.getElementById("html"), "htmlmixed", init.html, onAutoRun); - staticRef.current.css = initCodeEditor(document.getElementById("css"), "css", init.css, onAutoRun); - onFormat("js"); - onFormat("css"); - onFormat("html"); - onRun(); - } - - function showMessage(data) { - if (data.data && ["log", "error", "info", 'warn'].includes(data.data.type)) { - let console = document.getElementById("console"); - console.appendChild(createNode(data.data.data)); - console.scrollTop = console.scrollHeight; - } - } - window.addEventListener("message", showMessage); - - // https://stackoverflow.com/questions/71795406/react-useeffect-cleanup-for-event-listener - return () => { - window.removeEventListener("message", showMessage); - } - }, []); - return (
{ autoRun={autoRun} /> -
-
- -
-
- -
-
- -
-
- -
- -
- -
+
); }; diff --git a/frontend/src/pages/runjs/init.js b/frontend/src/pages/runjs/init.js deleted file mode 100644 index 9971ec7..0000000 --- a/frontend/src/pages/runjs/init.js +++ /dev/null @@ -1,34 +0,0 @@ -export default { - css: `body{ - background:#36404a; - width:100%; - height:100%; - } - .blend{ - position:absolute; - left:50%; - top:50%; - transform:translate(-50%,-50%); - } - h3{ - color:#fff; - } - p{ - color:rgba(255,255,255,0.75); - }`, - html: `
`, - javascript: `function App() { - console.log('Pen Editor'); - console.info('a simple code editor'); - return ( -
-

Pen Editor

-

a simple code editor

-
- ); - } - const container = document.getElementById('app'); - const root = ReactDOM.createRoot(container); - root.render(); -`, -}; diff --git a/frontend/src/pages/runjs/lib.js b/frontend/src/pages/runjs/lib.js deleted file mode 100644 index 058a41a..0000000 --- a/frontend/src/pages/runjs/lib.js +++ /dev/null @@ -1,77 +0,0 @@ -//code mirror 核心 -import * as CodeMirror from "codemirror/lib/codemirror"; - -import "./formatting"; - -import "codemirror/lib/codemirror.css"; - -//主题 -import "codemirror/theme/material.css"; - -//语法支持 -import "codemirror/mode/javascript/javascript"; -import "codemirror/mode/htmlmixed/htmlmixed"; -import "codemirror/mode/css/css"; - -//折叠 -import "codemirror/addon/fold/foldcode"; -import "codemirror/addon/fold/foldgutter"; -import "codemirror/addon/fold/brace-fold"; -import "codemirror/addon/fold/comment-fold"; -import "codemirror/addon/fold/foldgutter.css"; - -//括号匹配 -import "codemirror/addon/edit/matchbrackets"; - -//代码补全 -import "codemirror/addon/hint/show-hint"; -import "codemirror/addon/hint/javascript-hint"; -import "codemirror/addon/hint/html-hint"; -import "codemirror/addon/hint/css-hint"; -import "codemirror/addon/hint/show-hint.css"; - -//快捷键方案 -import "codemirror/keymap/sublime.js"; - - -function debounce(fn, wait) { - var timer = null; - return function () { - if (timer !== null) { - clearTimeout(timer); - } - timer = setTimeout(fn, wait); - }; -} - -export function initCodeEditor(dom, mode, initValue, fn) { - let editor = CodeMirror.fromTextArea(dom, { - mode: mode, //编辑器语言 - lineWrapping: true, - foldGutter: true, - gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"], - matchBrackets: true, - smartIndent: true, - indentUnit: 4, - theme: "material", //编辑器主题 - keymap: "sublime", - extraKeys: { - Tab: "emmetExpandAbbreviation", - Esc: "emmetResetAbbreviation", - Enter: "emmetInsertLineBreak", - Ctrl: "autocomplete", - }, - lineNumbers: true, - }); - editor.setOption("value", initValue); - - editor.on("changes", debounce(fn, 800)); - - return editor; -} - -export function createNode(htmlStr) { - var div = document.createElement("div"); - div.innerHTML = htmlStr; - return div.childNodes[0]; -} diff --git a/frontend/src/template.html b/frontend/src/template.html index 2851abc..c54a5bd 100644 --- a/frontend/src/template.html +++ b/frontend/src/template.html @@ -4,14 +4,13 @@ PenEditor -- A simple editor - - + + diff --git a/frontend/webpack.config.js b/frontend/webpack.config.js index e99a621..bce928e 100644 --- a/frontend/webpack.config.js +++ b/frontend/webpack.config.js @@ -72,6 +72,7 @@ module.exports = { }, resolve: { extensions: [".js", ".json", ".jsx", ".less", ".css"], + alias: { '@codemirror/state': __dirname + '/node_modules/@codemirror/state/dist/index.cjs', }, }, module: { rules: [