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 (
a simple code editor
-