From 981bea7a15490d1d429d3a1e99b7183dcb122e9d Mon Sep 17 00:00:00 2001 From: Connie Ye Date: Fri, 23 May 2025 00:00:49 -0700 Subject: [PATCH 1/7] codemirror v5 -> v6 --- .../IDE/components/Editor/codemirror.js | 313 +++----- .../modules/IDE/components/Editor/hinter.js | 118 --- .../modules/IDE/components/Editor/index.jsx | 64 +- .../IDE/components/Editor/stateUtils.js | 391 ++++++++++ .../modules/IDE/components/Editor/tidier.js | 47 +- client/modules/IDE/components/Editor/utils.js | 20 - client/styles/main.scss | 6 +- client/utils/htmlmixed.js | 153 ---- package-lock.json | 695 +++++++++++++++++- package.json | 18 +- 10 files changed, 1259 insertions(+), 566 deletions(-) delete mode 100644 client/modules/IDE/components/Editor/hinter.js create mode 100644 client/modules/IDE/components/Editor/stateUtils.js delete mode 100644 client/modules/IDE/components/Editor/utils.js delete mode 100644 client/utils/htmlmixed.js diff --git a/client/modules/IDE/components/Editor/codemirror.js b/client/modules/IDE/components/Editor/codemirror.js index 08e787724e..367857673b 100644 --- a/client/modules/IDE/components/Editor/codemirror.js +++ b/client/modules/IDE/components/Editor/codemirror.js @@ -1,46 +1,39 @@ import { useRef, useEffect } from 'react'; -import CodeMirror from 'codemirror'; -import 'codemirror/mode/css/css'; -import 'codemirror/mode/clike/clike'; -import 'codemirror/addon/selection/active-line'; -import 'codemirror/addon/lint/lint'; -import 'codemirror/addon/lint/javascript-lint'; -import 'codemirror/addon/lint/css-lint'; -import 'codemirror/addon/lint/html-lint'; -import 'codemirror/addon/fold/brace-fold'; -import 'codemirror/addon/fold/comment-fold'; -import 'codemirror/addon/fold/foldcode'; -import 'codemirror/addon/fold/foldgutter'; -import 'codemirror/addon/fold/indent-fold'; -import 'codemirror/addon/fold/xml-fold'; -import 'codemirror/addon/comment/comment'; -import 'codemirror/keymap/sublime'; -import 'codemirror/addon/search/searchcursor'; -import 'codemirror/addon/search/matchesonscrollbar'; -import 'codemirror/addon/search/match-highlighter'; -import 'codemirror/addon/search/jump-to-line'; -import 'codemirror/addon/edit/matchbrackets'; -import 'codemirror/addon/edit/closebrackets'; -import 'codemirror/addon/selection/mark-selection'; -import 'codemirror-colorpicker'; +import { EditorView, lineNumbers as lineNumbersExt } from '@codemirror/view'; +import { closeBrackets } from '@codemirror/autocomplete'; + +// TODO: Check what the v6 variants of these addons are. +// import 'codemirror/addon/search/searchcursor'; +// import 'codemirror/addon/search/matchesonscrollbar'; +// import 'codemirror/addon/search/match-highlighter'; +// import 'codemirror/addon/search/jump-to-line'; import { debounce } from 'lodash'; -import emmet from '@emmetio/codemirror-plugin'; +import { + getFileMode, + createNewFileState, + updateFileStates +} from './stateUtils'; import { useEffectWithComparison } from '../../hooks/custom-hooks'; -import { metaKey } from '../../../../utils/metaKey'; -import { showHint } from './hinter'; -import tidyCode from './tidier'; -import getFileMode from './utils'; - -const INDENTATION_AMOUNT = 2; - -emmet(CodeMirror); - -/** - * This is a custom React hook that manages CodeMirror state. - * TODO(Connie Ye): Revisit the linting on file switch. - */ +import tidyCodeWithPrettier from './tidier'; + +// ----- GENERAL TODOS (in order of priority) ----- +// - autocomplete (hinter) +// - p5-javascript +// - search, find & replace +// - color themes +// - javascript color picker (extension works for css but needs to be forked for js) +// - revisit keymap differences, esp around sublime +// - emmet doesn't trigger if text is copy pasted in +// - need to re-implement emmet auto rename tag +// - color picker should be triggered by metakey cmd k +// - clike addon +// ----- QUESTIONS ----- +// do we want shift tab to indent less? existing behavior is explicitly turned off but i think its nice to have +// do we want any extra emmet functionality? https://www.npmjs.com/package/@emmetio/codemirror6-plugin + +/** This is a custom React hook that manages CodeMirror state. */ export default function useCodeMirror({ theme, lineNumbers, @@ -61,34 +54,9 @@ export default function useCodeMirror({ onUpdateLinting }) { // The codemirror instance. - const cmInstance = useRef(); + const cmView = useRef(); // The current codemirror files. - const docs = useRef(); - - function onKeyUp() { - const lineNumber = parseInt(cmInstance.current.getCursor().line + 1, 10); - setCurrentLine(lineNumber); - } - - function onKeyDown(_cm, e) { - // Show hint - const mode = cmInstance.current.getOption('mode'); - if (/^[a-z]$/i.test(e.key) && (mode === 'css' || mode === 'javascript')) { - showHint(_cm, autocompleteHinter, fontSize); - } - if (e.key === 'Escape') { - e.preventDefault(); - const selections = cmInstance.current.listSelections(); - - if (selections.length > 1) { - const firstPos = selections[0].head || selections[0].anchor; - cmInstance.current.setSelection(firstPos); - cmInstance.current.scrollIntoView(firstPos); - } else { - cmInstance.current.getInputField().blur(); - } - } - } + const fileStates = useRef(); // We have to create a ref for the file ID, or else the debouncer // will old onto an old version of the fileId and just overrwrite the initial file. @@ -99,118 +67,96 @@ export default function useCodeMirror({ function onChange() { setUnsavedChanges(true); hideRuntimeErrorWarning(); - updateFileContent(fileId.current, cmInstance.current.getValue()); + updateFileContent(fileId.current, cmView.current.state.doc.toString()); if (autorefresh && isPlaying) { clearConsole(); startSketch(); } } + // Call onChange at most once every second. const debouncedOnChange = debounce(onChange, 1000); + // This is called when the CM view updates. + function onViewUpdate(updateView) { + const { state } = updateView; + + // TODO - check if need to subtract one + setCurrentLine(state.doc.lineAt(state.selection.main.head).number); + + if (updateView.docChanged) { + debouncedOnChange(); + } + } + // When the container component enters the DOM, we want this function // to be called so we can setup the CodeMirror instance with the container. function setupCodeMirrorOnContainerMounted(container) { - cmInstance.current = CodeMirror(container, { - theme: `p5-${theme}`, - lineNumbers, - styleActiveLine: true, - inputStyle: 'contenteditable', - lineWrapping: linewrap, - fixedGutter: false, - foldGutter: true, - foldOptions: { widget: '\u2026' }, - gutters: ['CodeMirror-foldgutter', 'CodeMirror-lint-markers'], - keyMap: 'sublime', - highlightSelectionMatches: true, // highlight current search match - matchBrackets: true, - emmet: { - preview: ['html'], - markTagPairs: true, - autoRenameTags: true - }, - autoCloseBrackets: autocloseBracketsQuotes, - styleSelectedText: true, - lint: { - onUpdateLinting, - options: { - asi: true, - eqeqeq: false, - '-W041': false, - esversion: 11 - } - }, - colorpicker: { - type: 'sketch', - mode: 'edit' - } + cmView.current = new EditorView({ + parent: container }); - - delete cmInstance.current.options.lint.options.errors; - - const replaceCommand = - metaKey === 'Ctrl' ? `${metaKey}-H` : `${metaKey}-Option-F`; - cmInstance.current.setOption('extraKeys', { - Tab: (tabCm) => { - if (!tabCm.execCommand('emmetExpandAbbreviation')) return; - // might need to specify and indent more? - const selection = tabCm.doc.getSelection(); - if (selection.length > 0) { - tabCm.execCommand('indentMore'); - } else { - tabCm.replaceSelection(' '.repeat(INDENTATION_AMOUNT)); - } - }, - Enter: 'emmetInsertLineBreak', - Esc: 'emmetResetAbbreviation', - [`Shift-Tab`]: false, - [`${metaKey}-Enter`]: () => null, - [`Shift-${metaKey}-Enter`]: () => null, - [`${metaKey}-F`]: 'findPersistent', - [`Shift-${metaKey}-F`]: () => tidyCode(cmInstance.current), - [`${metaKey}-G`]: 'findPersistentNext', - [`Shift-${metaKey}-G`]: 'findPersistentPrev', - [replaceCommand]: 'replace', - // Cassie Tarakajian: If you don't set a default color, then when you - // choose a color, it deletes characters inline. This is a - // hack to prevent that. - [`${metaKey}-K`]: (metaCm, event) => - metaCm.state.colorpicker.popup_color_picker({ length: 0 }), - [`${metaKey}-.`]: 'toggleComment' // Note: most adblockers use the shortcut ctrl+. - }); - - // Setup the event listeners on the CodeMirror instance. - cmInstance.current.on('change', debouncedOnChange); - cmInstance.current.on('keyup', onKeyUp); - cmInstance.current.on('keydown', onKeyDown); - - cmInstance.current.getWrapperElement().style['font-size'] = `${fontSize}px`; } // When settings change, we pass those changes into CodeMirror. + // TODO: There should be a useEffect hook for when the theme changes. useEffect(() => { - cmInstance.current.getWrapperElement().style['font-size'] = `${fontSize}px`; + cmView.current.dom.style['font-size'] = `${fontSize}px`; }, [fontSize]); useEffect(() => { - cmInstance.current.setOption('lineWrapping', linewrap); + const reconfigureEffect = (fileState) => + fileState.lineWrappingCpt.reconfigure( + linewrap ? EditorView.lineWrapping : [] + ); + updateFileStates({ + fileStates: fileStates.current, + cmView: cmView.current, + file, + reconfigureEffect + }); }, [linewrap]); useEffect(() => { - cmInstance.current.setOption('theme', `p5-${theme}`); - }, [theme]); - useEffect(() => { - cmInstance.current.setOption('lineNumbers', lineNumbers); + const reconfigureEffect = (fileState) => + fileState.lineNumbersCpt.reconfigure(lineNumbers ? lineNumbersExt() : []); + updateFileStates({ + fileStates: fileStates.current, + cmView: cmView.current, + file, + reconfigureEffect + }); }, [lineNumbers]); useEffect(() => { - cmInstance.current.setOption('autoCloseBrackets', autocloseBracketsQuotes); + const reconfigureEffect = (fileState) => + fileState.closeBracketsCpt.reconfigure( + autocloseBracketsQuotes ? closeBrackets() : [] + ); + updateFileStates({ + fileStates: fileStates.current, + cmView: cmView.current, + file, + reconfigureEffect + }); }, [autocloseBracketsQuotes]); - // Initializes the files as CodeMirror documents. + // Initializes the files as CodeMirror states. function initializeDocuments() { - docs.current = {}; + if (!fileStates.current) { + fileStates.current = {}; + } + files.forEach((currentFile) => { - if (currentFile.name !== 'root') { - docs.current[currentFile.id] = CodeMirror.Doc( + if ( + currentFile.name !== 'root' && + !(currentFile.id in fileStates.current) + ) { + fileStates.current[currentFile.id] = createNewFileState( + currentFile.name, currentFile.content, - getFileMode(currentFile.name) + { + linewrap, + lineNumbers, + autocloseBracketsQuotes, + onUpdateLinting, + onViewUpdate + } ); } }); @@ -219,64 +165,47 @@ export default function useCodeMirror({ // When the files change, reinitialize the documents. useEffect(initializeDocuments, [files]); - // When the file changes, we change the file mode and - // make the CodeMirror call to swap out the document. + // When the file changes, make the CodeMirror call to swap out the document. useEffectWithComparison( (_, prevProps) => { - const fileMode = getFileMode(file.name); - if (fileMode === 'javascript') { - // Define the new Emmet configuration based on the file mode - const emmetConfig = { - preview: ['html'], - markTagPairs: false, - autoRenameTags: true - }; - cmInstance.current.setOption('emmet', emmetConfig); - } - const oldDoc = cmInstance.current.swapDoc(docs.current[file.id]); - if (prevProps?.file) { - docs.current[prevProps.file.id] = oldDoc; + // We need to save the previous CodeMirror state so we can restore it + // when we switch back to it. + const previousState = cmView.current.state; + if (Array.isArray(prevProps) && prevProps.length > 0 && previousState) { + const prevId = prevProps[0]; + fileStates.current[prevId].cmState = previousState; } - cmInstance.current.focus(); - for (let i = 0; i < cmInstance.current.lineCount(); i += 1) { - cmInstance.current.removeLineClass( - i, - 'background', - 'line-runtime-error' - ); - } + const { cmState } = fileStates.current[file.id]; + cmView.current.setState(cmState); }, [file.id] ); - // Remove the CM listeners on component teardown. - function teardownCodeMirror() { - cmInstance.current.off('keyup', onKeyUp); - cmInstance.current.off('change', debouncedOnChange); - cmInstance.current.off('keydown', onKeyDown); - } - const getContent = () => { - const content = cmInstance.current.getValue(); + const content = cmView.current.state.doc.toString(); const updatedFile = Object.assign({}, file, { content }); return updatedFile; }; - const showFind = () => { - cmInstance.current.execCommand('findPersistent'); - }; - - const showReplace = () => { - cmInstance.current.execCommand('replace'); + // TODO: Add find and replace functionality. + // const showFind = () => { + // cmInstance.current.execCommand('findPersistent'); + // }; + // const showReplace = () => { + // cmInstance.current.execCommand('replace'); + // }; + + const tidyCode = () => { + const fileMode = getFileMode(file.name); + tidyCodeWithPrettier(cmView.current, fileMode); }; return { setupCodeMirrorOnContainerMounted, - teardownCodeMirror, - cmInstance, getContent, - showFind, - showReplace + tidyCode + // showFind, + // showReplace }; } diff --git a/client/modules/IDE/components/Editor/hinter.js b/client/modules/IDE/components/Editor/hinter.js deleted file mode 100644 index 81bb73d353..0000000000 --- a/client/modules/IDE/components/Editor/hinter.js +++ /dev/null @@ -1,118 +0,0 @@ -import Fuse from 'fuse.js'; -import CodeMirror from 'codemirror'; -import { JSHINT } from 'jshint'; -import { HTMLHint } from 'htmlhint'; -import { CSSLint } from 'csslint'; - -import 'codemirror/addon/hint/css-hint'; -import * as hinterDefinition from '../../../../utils/p5-hinter'; -import '../show-hint'; // TODO: Remove for codemirror v6? - -window.JSHINT = JSHINT; -window.CSSLint = CSSLint; -window.HTMLHint = HTMLHint; - -const hinter = new Fuse(hinterDefinition.p5Hinter, { - threshold: 0.05, - keys: ['text'] -}); - -/** Hides the hinter. */ -export function hideHinter(cmInstance) { - CodeMirror.showHint(cmInstance, () => {}, {}); -} - -/** - * Shows a hint popup in the codemirror instance. - * It will only be visible if the user has autocomplete on in the settings. - */ -export function showHint(cmInstance, autocompleteHinter, fontSize) { - if (!autocompleteHinter) { - CodeMirror.showHint(cmInstance, () => {}, {}); - return; - } - - let focusedLinkElement = null; - const setFocusedLinkElement = (set) => { - if (set && !focusedLinkElement) { - const activeItemLink = document.querySelector( - `.CodeMirror-hint-active a` - ); - if (activeItemLink) { - focusedLinkElement = activeItemLink; - focusedLinkElement.classList.add('focused-hint-link'); - focusedLinkElement.parentElement.parentElement.classList.add( - 'unfocused' - ); - } - } - }; - const removeFocusedLinkElement = () => { - if (focusedLinkElement) { - focusedLinkElement.classList.remove('focused-hint-link'); - focusedLinkElement.parentElement.parentElement.classList.remove( - 'unfocused' - ); - focusedLinkElement = null; - return true; - } - return false; - }; - - const hintOptions = { - _fontSize: fontSize, - completeSingle: false, - extraKeys: { - 'Shift-Right': (cm, e) => { - const activeItemLink = document.querySelector( - `.CodeMirror-hint-active a` - ); - if (activeItemLink) activeItemLink.click(); - }, - Right: (cm, e) => { - setFocusedLinkElement(true); - }, - Left: (cm, e) => { - removeFocusedLinkElement(); - }, - Up: (cm, e) => { - const onLink = removeFocusedLinkElement(); - e.moveFocus(-1); - setFocusedLinkElement(onLink); - }, - Down: (cm, e) => { - const onLink = removeFocusedLinkElement(); - e.moveFocus(1); - setFocusedLinkElement(onLink); - }, - Enter: (cm, e) => { - if (focusedLinkElement) focusedLinkElement.click(); - else e.pick(); - } - }, - closeOnUnfocus: false - }; - - if (cmInstance.options.mode === 'javascript') { - CodeMirror.showHint( - cmInstance, - () => { - const cursor = cmInstance.getCursor(); - const token = cmInstance.getTokenAt(cursor); - - const hints = hinter - .search(token.string) - .filter((h) => h.item.text[0] === token.string[0]); - - return { - list: hints, - from: CodeMirror.Pos(cursor.line, token.start), - to: CodeMirror.Pos(cursor.line, cursor.ch) - }; - }, - hintOptions - ); - } else if (cmInstance.options.mode === 'css') { - CodeMirror.showHint(cmInstance, CodeMirror.hint.css, hintOptions); - } -} diff --git a/client/modules/IDE/components/Editor/index.jsx b/client/modules/IDE/components/Editor/index.jsx index bf1eca9882..dd05bc7292 100644 --- a/client/modules/IDE/components/Editor/index.jsx +++ b/client/modules/IDE/components/Editor/index.jsx @@ -8,9 +8,6 @@ import { debounce } from 'lodash'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import MediaQuery from 'react-responsive'; -import '../../../../utils/htmlmixed'; -import '../../../../utils/p5-javascript'; -import '../../../../utils/codemirror-search'; import beepUrl from '../../../../sounds/audioAlert.mp3'; import RightArrowIcon from '../../../../images/right-arrow.svg'; @@ -34,9 +31,7 @@ import { EditorContainer, EditorHolder } from './MobileEditor'; import { FolderIcon } from '../../../../common/icons'; import IconButton from '../../../../common/IconButton'; -import { hideHinter } from './hinter'; -import tidyCode from './tidier'; -import useCodeMirror from './codemirror'; +import useCodeMirror from './codeMirror'; import { useEffectWithComparison } from '../../hooks/custom-hooks'; function Editor({ @@ -89,11 +84,11 @@ function Editor({ // a reference to the actual CM instance. const { setupCodeMirrorOnContainerMounted, - teardownCodeMirror, - cmInstance, + // cmInstance, getContent, - showFind, - showReplace + tidyCode + // showFind, + // showReplace } = useCodeMirror({ theme, lineNumbers, @@ -117,12 +112,13 @@ function Editor({ // Lets the parent component access file content-specific functionality... useEffect(() => { provideController({ - tidyCode: () => tidyCode(cmInstance.current), - showFind, - showReplace, - getContent + tidyCode, + getContent, + // TODO: Reimplement these. + showFind: () => {}, + showReplace: () => {} }); - }, [showFind, showReplace, getContent]); + }, [getContent]); // When the CM container div mounts, we set up CodeMirror. const onContainerMounted = useCallback(setupCodeMirrorOnContainerMounted, []); @@ -132,25 +128,13 @@ function Editor({ useEffect(() => { beep.current = new Audio(beepUrl); - provideController({ - tidyCode: () => tidyCode(cmInstance.current), - showFind, - showReplace, - getContent - }); - return () => { provideController(null); - teardownCodeMirror(); }; }, []); - useEffect(() => { - // Close the hinter window once the preference is turned off - if (!autocompleteHinter) hideHinter(cmInstance.current); - }, [autocompleteHinter]); - // Updates the error console. + // TODO: Need to revisit this functionality for v6. useEffectWithComparison( (_, prevProps) => { if (runtimeErrorWarningVisible) { @@ -176,22 +160,22 @@ function Editor({ (f) => f.name === fileName && f.filePath === filePath ); setSelectedFile(fileWithError.id); - cmInstance.current.addLineClass( - line.lineNumber - 1, - 'background', - 'line-runtime-error' - ); + // cmInstance.current.addLineClass( + // line.lineNumber - 1, + // 'background', + // 'line-runtime-error' + // ); }); } }); } else { - for (let i = 0; i < cmInstance.current.lineCount(); i += 1) { - cmInstance.current.removeLineClass( - i, - 'background', - 'line-runtime-error' - ); - } + // for (let i = 0; i < cmInstance.current.lineCount(); i += 1) { + // cmInstance.current.removeLineClass( + // i, + // 'background', + // 'line-runtime-error' + // ); + // } } } }, diff --git a/client/modules/IDE/components/Editor/stateUtils.js b/client/modules/IDE/components/Editor/stateUtils.js new file mode 100644 index 0000000000..b5599455f9 --- /dev/null +++ b/client/modules/IDE/components/Editor/stateUtils.js @@ -0,0 +1,391 @@ +import { EditorState, Compartment } from '@codemirror/state'; +import { + EditorView, + lineNumbers as lineNumbersExt, + highlightActiveLine, + highlightActiveLineGutter, + gutters, + keymap, + highlightSpecialChars, + drawSelection, + dropCursor, + rectangularSelection, + crosshairCursor +} from '@codemirror/view'; +import { + foldGutter, + foldKeymap, + bracketMatching, + indentOnInput, + syntaxHighlighting, + defaultHighlightStyle +} from '@codemirror/language'; +import { highlightSelectionMatches } from '@codemirror/search'; +import { closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete'; +import { + defaultKeymap, + history, + historyKeymap, + insertTab, + indentLess +} from '@codemirror/commands'; +import { lintGutter } from '@codemirror/lint'; +import { color as colorPicker } from '@uiw/codemirror-extensions-color'; +import { + expandAbbreviation, + abbreviationTracker +} from '@emmetio/codemirror6-plugin'; + +import { javascript } from '@codemirror/lang-javascript'; +import { css } from '@codemirror/lang-css'; +import { html } from '@codemirror/lang-html'; +import { json } from '@codemirror/lang-json'; +import { xml } from '@codemirror/lang-xml'; +import { linter } from '@codemirror/lint'; +import { JSHINT } from 'jshint'; +import { HTMLHint } from 'htmlhint'; +import { CSSLint } from 'csslint'; +import { emmetConfig } from '@emmetio/codemirror6-plugin'; + +import tidyCodeWithPrettier from './tidier'; + +// ----- TODOS ----- +// - JSON linter +// - shader syntax highlighting +// - should we add xml specific language support? +// - add docstrings for all exported functions + +/** Detects what mode the file is based on the name. */ +function getFileMode(fileName) { + let mode; + if (fileName.match(/.+\.js$/i)) { + mode = 'javascript'; + } else if (fileName.match(/.+\.css$/i)) { + mode = 'css'; + } else if (fileName.match(/.+\.(html)$/i)) { + mode = 'html'; + } else if (fileName.match(/.+\.(xml)$/i)) { + mode = 'xml'; + } else if (fileName.match(/.+\.json$/i)) { + mode = 'application/json'; + } else if (fileName.match(/.+\.(frag|glsl)$/i)) { + mode = 'x-shader/x-fragment'; + } else if (fileName.match(/.+\.(vert|stl|mtl)$/i)) { + mode = 'x-shader/x-vertex'; + } else { + mode = 'text/plain'; + } + return mode; +} + +function getFileLanguage(fileName) { + const fileMode = getFileMode(fileName); + + switch (fileMode) { + case 'javascript': + return javascript; + case 'css': + return css; + case 'html': + return html; + case 'xml': + return xml; + case 'application/json': + return json; + default: + return null; + } +} + +function makeCssLinter(callback) { + return (view) => { + const documentContent = view.state.doc.toString(); + const { messages } = CSSLint.verify(documentContent, {}); + const diagnostics = []; + messages.forEach((message) => { + if (!message) return; + + const { + line: messageLine, + col: messageCharacter, + type: messageType, + message: messageText + } = message; + const cmLine = view.state.doc.line(messageLine); + + // TODO: Can we to do the to/from smarter? + diagnostics.push({ + from: cmLine.from + messageCharacter - 1, + to: cmLine.from + messageCharacter, + severity: messageType, + message: messageText + }); + }); + + if (callback) callback(diagnostics); + + return diagnostics; + }; +} + +// https://github.com/codemirror/codemirror5/blob/master/addon/lint/html-lint.js +const HTMLHINT_OPTIONS = { + 'tagname-lowercase': true, + 'attr-lowercase': true, + 'attr-value-double-quotes': true, + 'doctype-first': false, + 'tag-pair': true, + 'spec-char-escape': true, + 'id-unique': true, + 'src-not-empty': true, + 'attr-no-duplication': true +}; + +function makeHtmlLinter(callback) { + return (view) => { + const documentContent = view.state.doc.toString(); + + const messages = HTMLHint.verify(documentContent, HTMLHINT_OPTIONS) || []; + + const diagnostics = []; + messages.forEach((message) => { + if (!message) return; + + const { + line: messageLine, + col: messageCharacter, + type: messageType, + message: messageText + } = message; + const cmLine = view.state.doc.line(messageLine); + + // TODO: Can we to do the to/from smarter? + diagnostics.push({ + from: cmLine.from + messageCharacter - 1, + to: cmLine.from + messageCharacter, + severity: messageType, + message: messageText + }); + }); + + if (callback) callback(diagnostics); + + return diagnostics; + }; +} + +const JSHINT_OPTIONS = { + asi: true, + eqeqeq: false, + '-W041': false, + esversion: 11 +}; + +// TODO: Consider using ESLINT instead +function makeJsLinter(callback) { + return (view) => { + const documentContent = view.state.doc.toString(); + + // Run JSHINT + JSHINT(documentContent, JSHINT_OPTIONS); + const { errors } = JSHINT; + + // Return errors + const diagnostics = []; + errors.forEach((error) => { + if (!error) return; + + const { line: errorLine, character: errorCharacter, evidence } = error; + const cmLine = view.state.doc.line(errorLine); + + // https://github.com/codemirror/codemirror5/blob/master/addon/lint/javascript-lint.js + const start = errorCharacter - 1; + let end = start + 1; + if (evidence) { + const index = evidence.substring(start).search(/.\b/); + if (index > -1) { + end += index; + } + } + + diagnostics.push({ + from: cmLine.from + start, + to: cmLine.from + end, + severity: error.code.startsWith('W') ? 'warning' : 'error', + message: error.reason + }); + }); + + if (callback) callback(diagnostics); + + return diagnostics; + }; +} + +function getFileLinter(fileName, callback) { + const fileMode = getFileMode(fileName); + + switch (fileMode) { + case 'javascript': + return linter(makeJsLinter(callback)); + case 'html': + return linter(makeHtmlLinter(callback)); + case 'css': + return linter(makeCssLinter(callback)); + default: + return null; + } +} + +function getFileEmmetConfig(fileName) { + const fileMode = getFileMode(fileName); + + switch (fileMode) { + case 'html': + return emmetConfig.of({ syntax: 'html' }); + case 'css': + return emmetConfig.of({ syntax: 'css' }); + default: + return null; + } +} + +// Extra custom keymaps. +// TODO: We need to add sublime mappings + other missing extra mappings here. +const extraKeymaps = [{ key: 'Tab', run: insertTab, shift: indentLess }]; +const emmetKeymaps = [{ key: 'Tab', run: expandAbbreviation }]; + +/** + * Creates a new CodeMirror editor state with configurations, + * extensions, and keymaps tailored to the file type and settings. + * + * Returns a "file state" object containing the CodeMirror state and compartments. + */ +export function createNewFileState(filename, document, settings) { + const { + linewrap, + lineNumbers, + autocloseBracketsQuotes, + onUpdateLinting, + onViewUpdate + } = settings; + const lineNumbersCpt = new Compartment(); + const lineWrappingCpt = new Compartment(); + const closeBracketsCpt = new Compartment(); + + // Depending on the file mode, we have a different tidier function. + const mode = getFileMode(filename); + extraKeymaps.push({ + key: `Shift-Mod-F`, + run: (cmView) => tidyCodeWithPrettier(cmView, mode) + }); + + const keymaps = [ + extraKeymaps, + closeBracketsKeymap, + defaultKeymap, + historyKeymap, + foldKeymap + ]; + + // https://github.com/codemirror/basic-setup/blob/main/src/codemirror.ts + const extensions = [ + // The first few extensions can be toggled on or off. + lineNumbersCpt.of(lineNumbers ? lineNumbersExt() : []), + lineWrappingCpt.of(linewrap ? EditorView.lineWrapping : []), + closeBracketsCpt.of(autocloseBracketsQuotes ? closeBrackets() : []), + + // Everything below here should always be on. + history(), + // Highlight extensions + highlightActiveLine(), + highlightActiveLineGutter(), + highlightSpecialChars(), + highlightSelectionMatches(), + syntaxHighlighting(defaultHighlightStyle, { fallback: true }), // Might need to replace highlight style + // Selection extensions + drawSelection(), + rectangularSelection(), + dropCursor(), + crosshairCursor(), + EditorState.allowMultipleSelections.of(true), + // Gutter extensions + gutters({ fixed: false }), + foldGutter(), + // Misc extensions + indentOnInput(), + bracketMatching(), + colorPicker, + + // Setup the event listeners on the CodeMirror instance. + EditorView.updateListener.of(onViewUpdate) + ]; + + const fileLanguage = getFileLanguage(filename); + const fileLinter = getFileLinter(filename, onUpdateLinting); + const fileEmmetConfig = getFileEmmetConfig(filename); + + if (fileLanguage) { + extensions.push(fileLanguage()); + } + if (fileLinter) { + extensions.push(fileLinter); + extensions.push(lintGutter()); + } + + // If it's HTML or CSS, we add some emmet-specific configs. + if (fileEmmetConfig) { + extensions.push(fileEmmetConfig); + extensions.push(abbreviationTracker()); + keymaps.push(emmetKeymaps); + } + + // Now add the keymaps... + extensions.push(keymap.of(keymaps.flat())); + + // Create the state with document content if we have it. + const stateOptions = { + extensions + }; + if (document) { + stateOptions.doc = document; + } + + const cmState = EditorState.create(stateOptions); + return { cmState, lineNumbersCpt, lineWrappingCpt, closeBracketsCpt }; +} + +/** + * Given a reconfigure effect, this function will update all + * of the file states. + * + * We need to do this whenever the settings like line numbers + * change, so it will get called in the useEffect hooks. + */ +export function updateFileStates({ + fileStates, + cmView, + file: currentFile, + reconfigureEffect +}) { + if (!fileStates) return; + + Object.entries(fileStates).forEach(([fileId, fileState]) => { + // Either grab the current state from the view or saved in the fileStates. + let { cmState } = fileState; + if (fileId === currentFile.id) { + cmState = cmView.state; + } + + // Apply the new effects and grab the new state. + const { state: newCmState } = cmState.update({ + effects: reconfigureEffect(fileState) + }); + + // Save the new states and update the view for the currently open file. + fileStates[fileId].cmState = newCmState; + if (fileId === currentFile.id) { + cmView.setState(newCmState); + } + }); +} diff --git a/client/modules/IDE/components/Editor/tidier.js b/client/modules/IDE/components/Editor/tidier.js index cadd601c15..0b21aed1f0 100644 --- a/client/modules/IDE/components/Editor/tidier.js +++ b/client/modules/IDE/components/Editor/tidier.js @@ -3,34 +3,33 @@ import babelParser from 'prettier/parser-babel'; import htmlParser from 'prettier/parser-html'; import cssParser from 'prettier/parser-postcss'; -function prettierFormatWithCursor(parser, plugins, cmInstance) { - try { - const { formatted, cursorOffset } = prettier.formatWithCursor( - cmInstance.doc.getValue(), - { - cursorOffset: cmInstance.doc.indexFromPos(cmInstance.doc.getCursor()), - parser, - plugins - } - ); - const { left, top } = cmInstance.getScrollInfo(); - cmInstance.doc.setValue(formatted); - cmInstance.focus(); - cmInstance.doc.setCursor(cmInstance.doc.posFromIndex(cursorOffset)); - cmInstance.scrollTo(left, top); - } catch (error) { - console.error(error); - } +function prettierFormatWithCursor(parser, plugins, cmView) { + const { doc } = cmView.state; + const cursorOffset = cmView.state.selection.main.head; + const { + formatted, + cursorOffset: newCursorOffset + } = prettier.formatWithCursor(doc.toString(), { + cursorOffset, + parser, + plugins + }); + + cmView.dispatch({ + changes: { from: 0, to: doc.length, insert: formatted }, + selection: { anchor: newCursorOffset } + }); + + cmView.focus(); } /** Runs prettier on the codemirror instance, depending on the mode. */ -export default function tidyCode(cmInstance) { - const mode = cmInstance.getOption('mode'); +export default function tidyCodeWithPrettier(cmView, mode) { if (mode === 'javascript') { - prettierFormatWithCursor('babel', [babelParser], cmInstance); + prettierFormatWithCursor('babel', [babelParser], cmView); } else if (mode === 'css') { - prettierFormatWithCursor('css', [cssParser], cmInstance); - } else if (mode === 'htmlmixed') { - prettierFormatWithCursor('html', [htmlParser], cmInstance); + prettierFormatWithCursor('css', [cssParser], cmView); + } else if (mode === 'html') { + prettierFormatWithCursor('html', [htmlParser], cmView); } } diff --git a/client/modules/IDE/components/Editor/utils.js b/client/modules/IDE/components/Editor/utils.js deleted file mode 100644 index fb89d57a68..0000000000 --- a/client/modules/IDE/components/Editor/utils.js +++ /dev/null @@ -1,20 +0,0 @@ -/** Detects what mode the file is based on the name. */ -export default function getFileMode(fileName) { - let mode; - if (fileName.match(/.+\.js$/i)) { - mode = 'javascript'; - } else if (fileName.match(/.+\.css$/i)) { - mode = 'css'; - } else if (fileName.match(/.+\.(html|xml)$/i)) { - mode = 'htmlmixed'; - } else if (fileName.match(/.+\.json$/i)) { - mode = 'application/json'; - } else if (fileName.match(/.+\.(frag|glsl)$/i)) { - mode = 'x-shader/x-fragment'; - } else if (fileName.match(/.+\.(vert|stl|mtl)$/i)) { - mode = 'x-shader/x-vertex'; - } else { - mode = 'text/plain'; - } - return mode; -} diff --git a/client/styles/main.scss b/client/styles/main.scss index 91e2d93483..e332a66690 100644 --- a/client/styles/main.scss +++ b/client/styles/main.scss @@ -6,9 +6,9 @@ @import 'base/reset'; @import 'base/base'; -@import '../../node_modules/codemirror/lib/codemirror'; -@import '../../node_modules/codemirror/addon/lint/lint'; -@import '../../node_modules/codemirror-colorpicker/addon/codemirror-colorpicker'; +// @import '../../node_modules/codemirror/lib/codemirror'; +// @import '../../node_modules/codemirror/addon/lint/lint'; +// @import '../../node_modules/codemirror-colorpicker/addon/codemirror-colorpicker'; @import '../../node_modules/dropzone/dist/dropzone'; @import '../../node_modules/primer-tooltips/build/build'; diff --git a/client/utils/htmlmixed.js b/client/utils/htmlmixed.js deleted file mode 100644 index 23a0eacf05..0000000000 --- a/client/utils/htmlmixed.js +++ /dev/null @@ -1,153 +0,0 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: http://codemirror.net/LICENSE -/* eslint-disable */ - -(function(mod) { - if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("codemirror"), require("codemirror/mode/xml/xml"), require("./p5-javascript"), require("codemirror/mode/css/css")); - else if (typeof define == "function" && define.amd) // AMD - define(["codemirror", "codemirror/mode/xml/xml", "./p5-javascript", "codemirror/mode/css/css"], mod); - else // Plain browser env - mod(CodeMirror); -})(function(CodeMirror) { - "use strict"; - - var defaultTags = { - script: [ - ["lang", /(javascript|babel)/i, "javascript"], - ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^$/i, "javascript"], - ["type", /./, "text/plain"], - [null, null, "javascript"] - ], - style: [ - ["lang", /^css$/i, "css"], - ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"], - ["type", /./, "text/plain"], - [null, null, "css"] - ] - }; - - function maybeBackup(stream, pat, style) { - var cur = stream.current(), close = cur.search(pat); - if (close > -1) { - stream.backUp(cur.length - close); - } else if (cur.match(/<\/?$/)) { - stream.backUp(cur.length); - if (!stream.match(pat, false)) stream.match(cur); - } - return style; - } - - var attrRegexpCache = {}; - function getAttrRegexp(attr) { - var regexp = attrRegexpCache[attr]; - if (regexp) return regexp; - return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*"); - } - - function getAttrValue(text, attr) { - var match = text.match(getAttrRegexp(attr)) - return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : "" - } - - function getTagRegexp(tagName, anchored) { - return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i"); - } - - function addTags(from, to) { - for (var tag in from) { - var dest = to[tag] || (to[tag] = []); - var source = from[tag]; - for (var i = source.length - 1; i >= 0; i--) - dest.unshift(source[i]) - } - } - - function findMatchingMode(tagInfo, tagText) { - for (var i = 0; i < tagInfo.length; i++) { - var spec = tagInfo[i]; - if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2]; - } - } - - CodeMirror.defineMode("htmlmixed", function (config, parserConfig) { - var htmlMode = CodeMirror.getMode(config, { - name: "xml", - htmlMode: true, - multilineTagIndentFactor: parserConfig.multilineTagIndentFactor, - multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag - }); - - var tags = {}; - var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes; - addTags(defaultTags, tags); - if (configTags) addTags(configTags, tags); - if (configScript) for (var i = configScript.length - 1; i >= 0; i--) - tags.script.unshift(["type", configScript[i].matches, configScript[i].mode]) - - function html(stream, state) { - var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName - if (tag && !/[<>\s\/]/.test(stream.current()) && - (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) && - tags.hasOwnProperty(tagName)) { - state.inTag = tagName + " " - } else if (state.inTag && tag && />$/.test(stream.current())) { - var inTag = /^([\S]+) (.*)/.exec(state.inTag) - state.inTag = null - var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2]) - var mode = CodeMirror.getMode(config, modeSpec) - var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false); - state.token = function (stream, state) { - if (stream.match(endTagA, false)) { - state.token = html; - state.localState = state.localMode = null; - return null; - } - return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState)); - }; - state.localMode = mode; - state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, "")); - } else if (state.inTag) { - state.inTag += stream.current() - if (stream.eol()) state.inTag += " " - } - return style; - }; - - return { - startState: function () { - var state = CodeMirror.startState(htmlMode); - return {token: html, inTag: null, localMode: null, localState: null, htmlState: state}; - }, - - copyState: function (state) { - var local; - if (state.localState) { - local = CodeMirror.copyState(state.localMode, state.localState); - } - return {token: state.token, inTag: state.inTag, - localMode: state.localMode, localState: local, - htmlState: CodeMirror.copyState(htmlMode, state.htmlState)}; - }, - - token: function (stream, state) { - return state.token(stream, state); - }, - - indent: function (state, textAfter) { - if (!state.localMode || /^\s*<\//.test(textAfter)) - return htmlMode.indent(state.htmlState, textAfter); - else if (state.localMode.indent) - return state.localMode.indent(state.localState, textAfter); - else - return CodeMirror.Pass; - }, - - innerMode: function (state) { - return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode}; - } - }; - }, "xml", "javascript", "css"); - - CodeMirror.defineMIME("text/html", "htmlmixed"); -}); diff --git a/package-lock.json b/package-lock.json index 84964fb5f1..dde744bcfc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,13 +13,27 @@ "@aws-sdk/client-s3": "^3.412.0", "@babel/core": "^7.14.6", "@babel/register": "^7.14.5", + "@codemirror/autocomplete": "^6.18.6", + "@codemirror/commands": "^6.8.1", + "@codemirror/lang-css": "^6.3.1", + "@codemirror/lang-html": "^6.4.9", + "@codemirror/lang-javascript": "^6.2.3", + "@codemirror/lang-json": "^6.0.1", + "@codemirror/lang-xml": "^6.1.0", + "@codemirror/language": "^6.11.0", + "@codemirror/lint": "^6.8.5", + "@codemirror/search": "^6.5.10", + "@codemirror/state": "^6.5.2", + "@codemirror/view": "^6.36.5", "@emmetio/codemirror-plugin": "^1.2.4", + "@emmetio/codemirror6-plugin": "^0.4.0", "@gatsbyjs/webpack-hot-middleware": "^2.25.3", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11", "@redux-devtools/core": "^3.11.0", "@redux-devtools/dock-monitor": "^3.0.1", "@redux-devtools/log-monitor": "^4.0.2", "@reduxjs/toolkit": "^1.9.3", + "@uiw/codemirror-extensions-color": "^4.23.12", "async": "^3.2.3", "axios": "^1.8.2", "babel-plugin-styled-components": "^1.13.2", @@ -29,7 +43,7 @@ "bson-objectid": "^2.0.3", "classnames": "^2.3.1", "clipboard": "^1.7.1", - "codemirror": "^5.62.0", + "codemirror": "^6.0.1", "codemirror-colorpicker": "^1.9.72", "connect-mongo": "^3.2.0", "console-feed": "^3.2.0", @@ -51,7 +65,7 @@ "friendly-words": "^1.2.1", "fuse.js": "^6.6.2", "history": "^4.10.1", - "htmlhint": "^0.15.1", + "htmlhint": "^0.15.2", "i18next": "^19.9.2", "i18next-http-backend": "^1.2.6", "is-url": "^1.2.4", @@ -6110,6 +6124,143 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@codemirror/autocomplete": { + "version": "6.18.6", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz", + "integrity": "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.8.1.tgz", + "integrity": "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/lang-css": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz", + "integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.2", + "@lezer/css": "^1.1.7" + } + }, + "node_modules/@codemirror/lang-html": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.9.tgz", + "integrity": "sha512-aQv37pIMSlueybId/2PVSP6NPnmurFDVmZwzc7jszd2KAF8qd4VBbvNYPXWQq90WIARjsdVkPbw29pszmHws3Q==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/lang-css": "^6.0.0", + "@codemirror/lang-javascript": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/css": "^1.1.0", + "@lezer/html": "^1.3.0" + } + }, + "node_modules/@codemirror/lang-javascript": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.3.tgz", + "integrity": "sha512-8PR3vIWg7pSu7ur8A07pGiYHgy3hHj+mRYRCSG8q+mPIrl0F02rgpGv+DsQTHRTc30rydOsf5PZ7yjKFg2Ackw==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/javascript": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-json": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz", + "integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/json": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-xml": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.1.0.tgz", + "integrity": "sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/xml": "^1.0.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.0.tgz", + "integrity": "sha512-A7+f++LodNNc1wGgoRDTt78cOwWm9KVezApgjOMp1W4hM0898nsqBXwF+sbePE7ZRcjN7Sa1Z5m2oN27XkmEjQ==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.1.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.8.5", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.5.tgz", + "integrity": "sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.35.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/search": { + "version": "6.5.10", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.10.tgz", + "integrity": "sha512-RMdPdmsrUf53pb2VwflKGHEe1XVM07hI7vV2ntgw1dmqhimpatSJKva4VA9h4TLUDOD4EIF02201oZurpnEFsg==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz", + "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==", + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "node_modules/@codemirror/view": { + "version": "6.36.5", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.5.tgz", + "integrity": "sha512-cd+FZEUlu3GQCYnguYm3EkhJ8KJVisqqUsCOKedBoAt/d9c76JUUap6U0UrpElln5k6VyrEOYliMuDAKIeDQLg==", + "dependencies": { + "@codemirror/state": "^6.5.0", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -6138,11 +6289,58 @@ "node": ">=10.0.0" } }, + "node_modules/@emmetio/abbreviation": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@emmetio/abbreviation/-/abbreviation-2.3.3.tgz", + "integrity": "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA==", + "dependencies": { + "@emmetio/scanner": "^1.0.4" + } + }, "node_modules/@emmetio/codemirror-plugin": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@emmetio/codemirror-plugin/-/codemirror-plugin-1.2.4.tgz", "integrity": "sha512-wVw2gqI6X+uVWYVRtTVymzTgbo4hEZIcPCNj4xrXw4l/+L3Qa+tAC/yf+Xy9nenRPCqRq0RLqGiQL+Qf/wYE9Q==" }, + "node_modules/@emmetio/codemirror6-plugin": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emmetio/codemirror6-plugin/-/codemirror6-plugin-0.4.0.tgz", + "integrity": "sha512-ZP3W8JvN0cEFTdsrcKPIBg/K9sadE15g//TfubPXfM28ZxUp3SwNASbRfRLRQmPyP73+pAZzLqeOwACUUz/oAw==", + "dependencies": { + "@emmetio/math-expression": "^1.0.5", + "emmet": "^2.4.11" + }, + "peerDependencies": { + "@codemirror/autocomplete": "^6.17.0", + "@codemirror/commands": "^6.6.0", + "@codemirror/lang-css": "^6.2.1", + "@codemirror/lang-html": "^6.4.9", + "@codemirror/language": "^6.10.2", + "@codemirror/state": "^6.4.1", + "@codemirror/view": "^6.29.1" + } + }, + "node_modules/@emmetio/css-abbreviation": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@emmetio/css-abbreviation/-/css-abbreviation-2.1.8.tgz", + "integrity": "sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw==", + "dependencies": { + "@emmetio/scanner": "^1.0.4" + } + }, + "node_modules/@emmetio/math-expression": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@emmetio/math-expression/-/math-expression-1.0.5.tgz", + "integrity": "sha512-qf5SXD/ViS04rXSeDg9CRGM10xLC9dVaKIbMHrrwxYr5LNB/C0rOfokhGSBwnVQKcidLmdRJeNWH1V1tppZ84Q==", + "dependencies": { + "@emmetio/scanner": "^1.0.4" + } + }, + "node_modules/@emmetio/scanner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@emmetio/scanner/-/scanner-1.0.4.tgz", + "integrity": "sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA==" + }, "node_modules/@emotion/cache": { "version": "10.0.29", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz", @@ -7899,6 +8097,82 @@ "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==", "dev": true }, + "node_modules/@lezer/common": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", + "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==" + }, + "node_modules/@lezer/css": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.11.tgz", + "integrity": "sha512-FuAnusbLBl1SEAtfN8NdShxYJiESKw9LAFysfea1T96jD3ydBn12oYjaSG1a04BQRIUd93/0D8e5CV1cUMkmQg==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/highlight": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz", + "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/html": { + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.10.tgz", + "integrity": "sha512-dqpT8nISx/p9Do3AchvYGV3qYc4/rKr3IBZxlHmpIKam56P47RSHkSF5f13Vu9hebS1jM0HmtJIwLbWz1VIY6w==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/javascript": { + "version": "1.4.21", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.21.tgz", + "integrity": "sha512-lL+1fcuxWYPURMM/oFZLEDm0XuLN128QPV+VuGtKpeaOGdcl9F2LYC3nh1S9LkPqx9M0mndZFdXCipNAZpzIkQ==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.1.3", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/json": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.3.tgz", + "integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz", + "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/xml": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.6.tgz", + "integrity": "sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==" + }, "node_modules/@mdx-js/react": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-2.3.0.tgz", @@ -14923,6 +15197,24 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@uiw/codemirror-extensions-color": { + "version": "4.23.12", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-color/-/codemirror-extensions-color-4.23.12.tgz", + "integrity": "sha512-jpcSGG4yNkh+wM1/ImRIK5n22d6ubwCKvLQ29a6B1IFB5ojZUCXWt/hJN0LW02SrGrZtuUuH2p3eUVIKQ9MDPQ==", + "dependencies": { + "colors-named": "^1.0.0", + "colors-named-hex": "^1.0.0", + "hsl-matcher": "^1.2.3" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@codemirror/language": ">=6.0.0", + "@codemirror/state": ">=6.0.0", + "@codemirror/view": ">=6.0.0" + } + }, "node_modules/@webassemblyjs/ast": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", @@ -17646,9 +17938,18 @@ } }, "node_modules/codemirror": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.62.0.tgz", - "integrity": "sha512-Xnl3304iCc8nyVZuRkzDVVwc794uc9QNX0UcPGeNic1fbzkSrO4l4GVXho9tRNKBgPYZXgocUqXyfIv3BILhCQ==" + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", + "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", + "dependencies": { + "@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" + } }, "node_modules/codemirror-colorpicker": { "version": "1.9.80", @@ -17718,6 +18019,28 @@ "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", "dev": true }, + "node_modules/colors-named": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/colors-named/-/colors-named-1.0.2.tgz", + "integrity": "sha512-2ANq2r393PV9njYUD66UdfBcxR1slMqRA3QRTWgCx49JoCJ+kOhyfbQYxKJbPZQIhZUcNjVOs5AlyY1WwXec3w==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } + }, + "node_modules/colors-named-hex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/colors-named-hex/-/colors-named-hex-1.0.2.tgz", + "integrity": "sha512-k6kq1e1pUCQvSVwIaGFq2l0LrkAPQZWyeuZn1Z8nOiYSEZiKoFj4qx690h2Kd34DFl9Me0gKS6MUwAMBJj8nuA==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -18374,6 +18697,11 @@ "node": ">=8" } }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" + }, "node_modules/cross-env": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.2.1.tgz", @@ -20480,6 +20808,15 @@ "url": "https://github.com/sindresorhus/emittery?sponsor=1" } }, + "node_modules/emmet": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/emmet/-/emmet-2.4.11.tgz", + "integrity": "sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==", + "dependencies": { + "@emmetio/abbreviation": "^2.3.3", + "@emmetio/css-abbreviation": "^2.1.8" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -23733,6 +24070,17 @@ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, + "node_modules/hsl-matcher": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/hsl-matcher/-/hsl-matcher-1.2.4.tgz", + "integrity": "sha512-tS7XnJS33Egirm+6cI+Z/kH/aVZt94uxGlJxOZlGql2/yqbAzPg3zHHnTnVN4cVpoJnEYEGq+LE3iXbuUIe8BA==", + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } + }, "node_modules/html-encoding-sniffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", @@ -28288,9 +28636,9 @@ } }, "node_modules/jshint": { - "version": "2.13.4", - "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.13.4.tgz", - "integrity": "sha512-HO3bosL84b2qWqI0q+kpT/OpRJwo0R4ivgmxaO848+bo10rc50SkPnrtwSFXttW0ym4np8jbJvLwk5NziB7jIw==", + "version": "2.13.6", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.13.6.tgz", + "integrity": "sha512-IVdB4G0NTTeQZrBoM8C5JFVLjV2KtZ9APgybDA1MK73xb09qFs0jCXyQLnCOp1cSZZZbvhq/6mfXHUTaDkffuQ==", "dependencies": { "cli": "~1.0.0", "console-browserify": "1.1.x", @@ -38232,6 +38580,11 @@ "webpack": "^5.0.0" } }, + "node_modules/style-mod": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", + "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==" + }, "node_modules/style-to-object": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz", @@ -39495,6 +39848,11 @@ "browser-process-hrtime": "^1.0.0" } }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -45068,6 +45426,143 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "@codemirror/autocomplete": { + "version": "6.18.6", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz", + "integrity": "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==", + "requires": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + } + }, + "@codemirror/commands": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.8.1.tgz", + "integrity": "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==", + "requires": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" + } + }, + "@codemirror/lang-css": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz", + "integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==", + "requires": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.2", + "@lezer/css": "^1.1.7" + } + }, + "@codemirror/lang-html": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.9.tgz", + "integrity": "sha512-aQv37pIMSlueybId/2PVSP6NPnmurFDVmZwzc7jszd2KAF8qd4VBbvNYPXWQq90WIARjsdVkPbw29pszmHws3Q==", + "requires": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/lang-css": "^6.0.0", + "@codemirror/lang-javascript": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/css": "^1.1.0", + "@lezer/html": "^1.3.0" + } + }, + "@codemirror/lang-javascript": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.3.tgz", + "integrity": "sha512-8PR3vIWg7pSu7ur8A07pGiYHgy3hHj+mRYRCSG8q+mPIrl0F02rgpGv+DsQTHRTc30rydOsf5PZ7yjKFg2Ackw==", + "requires": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/javascript": "^1.0.0" + } + }, + "@codemirror/lang-json": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz", + "integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==", + "requires": { + "@codemirror/language": "^6.0.0", + "@lezer/json": "^1.0.0" + } + }, + "@codemirror/lang-xml": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.1.0.tgz", + "integrity": "sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==", + "requires": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/xml": "^1.0.0" + } + }, + "@codemirror/language": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.0.tgz", + "integrity": "sha512-A7+f++LodNNc1wGgoRDTt78cOwWm9KVezApgjOMp1W4hM0898nsqBXwF+sbePE7ZRcjN7Sa1Z5m2oN27XkmEjQ==", + "requires": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.1.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "@codemirror/lint": { + "version": "6.8.5", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.5.tgz", + "integrity": "sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==", + "requires": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.35.0", + "crelt": "^1.0.5" + } + }, + "@codemirror/search": { + "version": "6.5.10", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.10.tgz", + "integrity": "sha512-RMdPdmsrUf53pb2VwflKGHEe1XVM07hI7vV2ntgw1dmqhimpatSJKva4VA9h4TLUDOD4EIF02201oZurpnEFsg==", + "requires": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "@codemirror/state": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz", + "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==", + "requires": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "@codemirror/view": { + "version": "6.36.5", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.5.tgz", + "integrity": "sha512-cd+FZEUlu3GQCYnguYm3EkhJ8KJVisqqUsCOKedBoAt/d9c76JUUap6U0UrpElln5k6VyrEOYliMuDAKIeDQLg==", + "requires": { + "@codemirror/state": "^6.5.0", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, "@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -45087,11 +45582,49 @@ "integrity": "sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==", "dev": true }, + "@emmetio/abbreviation": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@emmetio/abbreviation/-/abbreviation-2.3.3.tgz", + "integrity": "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA==", + "requires": { + "@emmetio/scanner": "^1.0.4" + } + }, "@emmetio/codemirror-plugin": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@emmetio/codemirror-plugin/-/codemirror-plugin-1.2.4.tgz", "integrity": "sha512-wVw2gqI6X+uVWYVRtTVymzTgbo4hEZIcPCNj4xrXw4l/+L3Qa+tAC/yf+Xy9nenRPCqRq0RLqGiQL+Qf/wYE9Q==" }, + "@emmetio/codemirror6-plugin": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emmetio/codemirror6-plugin/-/codemirror6-plugin-0.4.0.tgz", + "integrity": "sha512-ZP3W8JvN0cEFTdsrcKPIBg/K9sadE15g//TfubPXfM28ZxUp3SwNASbRfRLRQmPyP73+pAZzLqeOwACUUz/oAw==", + "requires": { + "@emmetio/math-expression": "^1.0.5", + "emmet": "^2.4.11" + } + }, + "@emmetio/css-abbreviation": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@emmetio/css-abbreviation/-/css-abbreviation-2.1.8.tgz", + "integrity": "sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw==", + "requires": { + "@emmetio/scanner": "^1.0.4" + } + }, + "@emmetio/math-expression": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@emmetio/math-expression/-/math-expression-1.0.5.tgz", + "integrity": "sha512-qf5SXD/ViS04rXSeDg9CRGM10xLC9dVaKIbMHrrwxYr5LNB/C0rOfokhGSBwnVQKcidLmdRJeNWH1V1tppZ84Q==", + "requires": { + "@emmetio/scanner": "^1.0.4" + } + }, + "@emmetio/scanner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@emmetio/scanner/-/scanner-1.0.4.tgz", + "integrity": "sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA==" + }, "@emotion/cache": { "version": "10.0.29", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz", @@ -46376,6 +46909,82 @@ "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==", "dev": true }, + "@lezer/common": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", + "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==" + }, + "@lezer/css": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.11.tgz", + "integrity": "sha512-FuAnusbLBl1SEAtfN8NdShxYJiESKw9LAFysfea1T96jD3ydBn12oYjaSG1a04BQRIUd93/0D8e5CV1cUMkmQg==", + "requires": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "@lezer/highlight": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz", + "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==", + "requires": { + "@lezer/common": "^1.0.0" + } + }, + "@lezer/html": { + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.10.tgz", + "integrity": "sha512-dqpT8nISx/p9Do3AchvYGV3qYc4/rKr3IBZxlHmpIKam56P47RSHkSF5f13Vu9hebS1jM0HmtJIwLbWz1VIY6w==", + "requires": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "@lezer/javascript": { + "version": "1.4.21", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.21.tgz", + "integrity": "sha512-lL+1fcuxWYPURMM/oFZLEDm0XuLN128QPV+VuGtKpeaOGdcl9F2LYC3nh1S9LkPqx9M0mndZFdXCipNAZpzIkQ==", + "requires": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.1.3", + "@lezer/lr": "^1.3.0" + } + }, + "@lezer/json": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.3.tgz", + "integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==", + "requires": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "@lezer/lr": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz", + "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==", + "requires": { + "@lezer/common": "^1.0.0" + } + }, + "@lezer/xml": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.6.tgz", + "integrity": "sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==", + "requires": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==" + }, "@mdx-js/react": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-2.3.0.tgz", @@ -51446,6 +52055,16 @@ "eslint-visitor-keys": "^3.3.0" } }, + "@uiw/codemirror-extensions-color": { + "version": "4.23.12", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-color/-/codemirror-extensions-color-4.23.12.tgz", + "integrity": "sha512-jpcSGG4yNkh+wM1/ImRIK5n22d6ubwCKvLQ29a6B1IFB5ojZUCXWt/hJN0LW02SrGrZtuUuH2p3eUVIKQ9MDPQ==", + "requires": { + "colors-named": "^1.0.0", + "colors-named-hex": "^1.0.0", + "hsl-matcher": "^1.2.3" + } + }, "@webassemblyjs/ast": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", @@ -53520,9 +54139,18 @@ "dev": true }, "codemirror": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.62.0.tgz", - "integrity": "sha512-Xnl3304iCc8nyVZuRkzDVVwc794uc9QNX0UcPGeNic1fbzkSrO4l4GVXho9tRNKBgPYZXgocUqXyfIv3BILhCQ==" + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", + "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", + "requires": { + "@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" + } }, "codemirror-colorpicker": { "version": "1.9.80", @@ -53587,6 +54215,16 @@ "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", "dev": true }, + "colors-named": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/colors-named/-/colors-named-1.0.2.tgz", + "integrity": "sha512-2ANq2r393PV9njYUD66UdfBcxR1slMqRA3QRTWgCx49JoCJ+kOhyfbQYxKJbPZQIhZUcNjVOs5AlyY1WwXec3w==" + }, + "colors-named-hex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/colors-named-hex/-/colors-named-hex-1.0.2.tgz", + "integrity": "sha512-k6kq1e1pUCQvSVwIaGFq2l0LrkAPQZWyeuZn1Z8nOiYSEZiKoFj4qx690h2Kd34DFl9Me0gKS6MUwAMBJj8nuA==" + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -54066,6 +54704,11 @@ } } }, + "crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" + }, "cross-env": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.2.1.tgz", @@ -55592,6 +56235,15 @@ "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", "dev": true }, + "emmet": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/emmet/-/emmet-2.4.11.tgz", + "integrity": "sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==", + "requires": { + "@emmetio/abbreviation": "^2.3.3", + "@emmetio/css-abbreviation": "^2.1.8" + } + }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -58006,6 +58658,11 @@ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, + "hsl-matcher": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/hsl-matcher/-/hsl-matcher-1.2.4.tgz", + "integrity": "sha512-tS7XnJS33Egirm+6cI+Z/kH/aVZt94uxGlJxOZlGql2/yqbAzPg3zHHnTnVN4cVpoJnEYEGq+LE3iXbuUIe8BA==" + }, "html-encoding-sniffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", @@ -61304,9 +61961,9 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" }, "jshint": { - "version": "2.13.4", - "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.13.4.tgz", - "integrity": "sha512-HO3bosL84b2qWqI0q+kpT/OpRJwo0R4ivgmxaO848+bo10rc50SkPnrtwSFXttW0ym4np8jbJvLwk5NziB7jIw==", + "version": "2.13.6", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.13.6.tgz", + "integrity": "sha512-IVdB4G0NTTeQZrBoM8C5JFVLjV2KtZ9APgybDA1MK73xb09qFs0jCXyQLnCOp1cSZZZbvhq/6mfXHUTaDkffuQ==", "requires": { "cli": "~1.0.0", "console-browserify": "1.1.x", @@ -68855,6 +69512,11 @@ "dev": true, "requires": {} }, + "style-mod": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", + "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==" + }, "style-to-object": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz", @@ -69810,6 +70472,11 @@ "browser-process-hrtime": "^1.0.0" } }, + "w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" + }, "walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", diff --git a/package.json b/package.json index 98256b4c4c..6723d1269f 100644 --- a/package.json +++ b/package.json @@ -161,13 +161,27 @@ "@aws-sdk/client-s3": "^3.412.0", "@babel/core": "^7.14.6", "@babel/register": "^7.14.5", + "@codemirror/autocomplete": "^6.18.6", + "@codemirror/commands": "^6.8.1", + "@codemirror/lang-css": "^6.3.1", + "@codemirror/lang-html": "^6.4.9", + "@codemirror/lang-javascript": "^6.2.3", + "@codemirror/lang-json": "^6.0.1", + "@codemirror/lang-xml": "^6.1.0", + "@codemirror/language": "^6.11.0", + "@codemirror/lint": "^6.8.5", + "@codemirror/search": "^6.5.10", + "@codemirror/state": "^6.5.2", + "@codemirror/view": "^6.36.5", "@emmetio/codemirror-plugin": "^1.2.4", + "@emmetio/codemirror6-plugin": "^0.4.0", "@gatsbyjs/webpack-hot-middleware": "^2.25.3", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11", "@redux-devtools/core": "^3.11.0", "@redux-devtools/dock-monitor": "^3.0.1", "@redux-devtools/log-monitor": "^4.0.2", "@reduxjs/toolkit": "^1.9.3", + "@uiw/codemirror-extensions-color": "^4.23.12", "async": "^3.2.3", "axios": "^1.8.2", "babel-plugin-styled-components": "^1.13.2", @@ -177,7 +191,7 @@ "bson-objectid": "^2.0.3", "classnames": "^2.3.1", "clipboard": "^1.7.1", - "codemirror": "^5.62.0", + "codemirror": "^6.0.1", "codemirror-colorpicker": "^1.9.72", "connect-mongo": "^3.2.0", "console-feed": "^3.2.0", @@ -199,7 +213,7 @@ "friendly-words": "^1.2.1", "fuse.js": "^6.6.2", "history": "^4.10.1", - "htmlhint": "^0.15.1", + "htmlhint": "^0.15.2", "i18next": "^19.9.2", "i18next-http-backend": "^1.2.6", "is-url": "^1.2.4", From 51a01c5747104feb3d9f0bebbf0448c2247ef7c6 Mon Sep 17 00:00:00 2001 From: Connie Ye Date: Thu, 29 May 2025 23:47:12 -0700 Subject: [PATCH 2/7] Update codemirror.js --- client/modules/IDE/components/Editor/codemirror.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/modules/IDE/components/Editor/codemirror.js b/client/modules/IDE/components/Editor/codemirror.js index 367857673b..3ec86cf54c 100644 --- a/client/modules/IDE/components/Editor/codemirror.js +++ b/client/modules/IDE/components/Editor/codemirror.js @@ -19,6 +19,8 @@ import { useEffectWithComparison } from '../../hooks/custom-hooks'; import tidyCodeWithPrettier from './tidier'; // ----- GENERAL TODOS (in order of priority) ----- +// - CAN'T SCROLL???? +// - ConsoleInput // - autocomplete (hinter) // - p5-javascript // - search, find & replace From f50c5c5646c80e9ef8be3442e9724c2b489d8c61 Mon Sep 17 00:00:00 2001 From: Connie Ye Date: Fri, 30 May 2025 00:03:26 -0700 Subject: [PATCH 3/7] remove unused cm5 styles --- client/styles/main.scss | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/styles/main.scss b/client/styles/main.scss index e332a66690..70c2f5cb9c 100644 --- a/client/styles/main.scss +++ b/client/styles/main.scss @@ -6,9 +6,6 @@ @import 'base/reset'; @import 'base/base'; -// @import '../../node_modules/codemirror/lib/codemirror'; -// @import '../../node_modules/codemirror/addon/lint/lint'; -// @import '../../node_modules/codemirror-colorpicker/addon/codemirror-colorpicker'; @import '../../node_modules/dropzone/dist/dropzone'; @import '../../node_modules/primer-tooltips/build/build'; From d6cf64edceaa023fce4454b82bd87412e736156a Mon Sep 17 00:00:00 2001 From: Connie Ye Date: Fri, 30 May 2025 00:06:14 -0700 Subject: [PATCH 4/7] remove cm5 plugins --- package-lock.json | 26 -------------------------- package.json | 2 -- 2 files changed, 28 deletions(-) diff --git a/package-lock.json b/package-lock.json index dde744bcfc..ea90e86d12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,6 @@ "@codemirror/search": "^6.5.10", "@codemirror/state": "^6.5.2", "@codemirror/view": "^6.36.5", - "@emmetio/codemirror-plugin": "^1.2.4", "@emmetio/codemirror6-plugin": "^0.4.0", "@gatsbyjs/webpack-hot-middleware": "^2.25.3", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11", @@ -44,7 +43,6 @@ "classnames": "^2.3.1", "clipboard": "^1.7.1", "codemirror": "^6.0.1", - "codemirror-colorpicker": "^1.9.72", "connect-mongo": "^3.2.0", "console-feed": "^3.2.0", "cookie-parser": "^1.4.5", @@ -6297,11 +6295,6 @@ "@emmetio/scanner": "^1.0.4" } }, - "node_modules/@emmetio/codemirror-plugin": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@emmetio/codemirror-plugin/-/codemirror-plugin-1.2.4.tgz", - "integrity": "sha512-wVw2gqI6X+uVWYVRtTVymzTgbo4hEZIcPCNj4xrXw4l/+L3Qa+tAC/yf+Xy9nenRPCqRq0RLqGiQL+Qf/wYE9Q==" - }, "node_modules/@emmetio/codemirror6-plugin": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/@emmetio/codemirror6-plugin/-/codemirror6-plugin-0.4.0.tgz", @@ -17951,14 +17944,6 @@ "@codemirror/view": "^6.0.0" } }, - "node_modules/codemirror-colorpicker": { - "version": "1.9.80", - "resolved": "https://registry.npmjs.org/codemirror-colorpicker/-/codemirror-colorpicker-1.9.80.tgz", - "integrity": "sha512-7lGqNxf5haBJXLnVR1ynPiPkN2d1Whm0jdy8Z9QsSOhRWVyK2C2ihgm1dX4DCks57ht/jKMdpL9lYv+zAphxWQ==", - "peerDependencies": { - "codemirror": "^5.48.0" - } - }, "node_modules/collect-v8-coverage": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", @@ -45590,11 +45575,6 @@ "@emmetio/scanner": "^1.0.4" } }, - "@emmetio/codemirror-plugin": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@emmetio/codemirror-plugin/-/codemirror-plugin-1.2.4.tgz", - "integrity": "sha512-wVw2gqI6X+uVWYVRtTVymzTgbo4hEZIcPCNj4xrXw4l/+L3Qa+tAC/yf+Xy9nenRPCqRq0RLqGiQL+Qf/wYE9Q==" - }, "@emmetio/codemirror6-plugin": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/@emmetio/codemirror6-plugin/-/codemirror6-plugin-0.4.0.tgz", @@ -54152,12 +54132,6 @@ "@codemirror/view": "^6.0.0" } }, - "codemirror-colorpicker": { - "version": "1.9.80", - "resolved": "https://registry.npmjs.org/codemirror-colorpicker/-/codemirror-colorpicker-1.9.80.tgz", - "integrity": "sha512-7lGqNxf5haBJXLnVR1ynPiPkN2d1Whm0jdy8Z9QsSOhRWVyK2C2ihgm1dX4DCks57ht/jKMdpL9lYv+zAphxWQ==", - "requires": {} - }, "collect-v8-coverage": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", diff --git a/package.json b/package.json index 6723d1269f..a03f13f4ce 100644 --- a/package.json +++ b/package.json @@ -173,7 +173,6 @@ "@codemirror/search": "^6.5.10", "@codemirror/state": "^6.5.2", "@codemirror/view": "^6.36.5", - "@emmetio/codemirror-plugin": "^1.2.4", "@emmetio/codemirror6-plugin": "^0.4.0", "@gatsbyjs/webpack-hot-middleware": "^2.25.3", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11", @@ -192,7 +191,6 @@ "classnames": "^2.3.1", "clipboard": "^1.7.1", "codemirror": "^6.0.1", - "codemirror-colorpicker": "^1.9.72", "connect-mongo": "^3.2.0", "console-feed": "^3.2.0", "cookie-parser": "^1.4.5", From 9c75ea2a73f20bc29b7c826ad3589c3cd74847b3 Mon Sep 17 00:00:00 2001 From: Connie Ye Date: Fri, 30 May 2025 08:26:50 -0700 Subject: [PATCH 5/7] git didnt track the file case rename so i renamed it back --- client/modules/IDE/components/Editor/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/modules/IDE/components/Editor/index.jsx b/client/modules/IDE/components/Editor/index.jsx index dd05bc7292..cefa9a17ae 100644 --- a/client/modules/IDE/components/Editor/index.jsx +++ b/client/modules/IDE/components/Editor/index.jsx @@ -31,7 +31,7 @@ import { EditorContainer, EditorHolder } from './MobileEditor'; import { FolderIcon } from '../../../../common/icons'; import IconButton from '../../../../common/IconButton'; -import useCodeMirror from './codeMirror'; +import useCodeMirror from './codemirror'; import { useEffectWithComparison } from '../../hooks/custom-hooks'; function Editor({ From ffe812731d5ca84ca110f3ee2c285577e0f5ec49 Mon Sep 17 00:00:00 2001 From: Connie Ye Date: Fri, 30 May 2025 08:28:40 -0700 Subject: [PATCH 6/7] update comments tracking TODOs --- client/modules/IDE/components/Editor/codemirror.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/modules/IDE/components/Editor/codemirror.js b/client/modules/IDE/components/Editor/codemirror.js index 3ec86cf54c..48c6698f36 100644 --- a/client/modules/IDE/components/Editor/codemirror.js +++ b/client/modules/IDE/components/Editor/codemirror.js @@ -25,15 +25,13 @@ import tidyCodeWithPrettier from './tidier'; // - p5-javascript // - search, find & replace // - color themes +// - any features lost in the p5 conversion git merge // - javascript color picker (extension works for css but needs to be forked for js) // - revisit keymap differences, esp around sublime // - emmet doesn't trigger if text is copy pasted in // - need to re-implement emmet auto rename tag // - color picker should be triggered by metakey cmd k // - clike addon -// ----- QUESTIONS ----- -// do we want shift tab to indent less? existing behavior is explicitly turned off but i think its nice to have -// do we want any extra emmet functionality? https://www.npmjs.com/package/@emmetio/codemirror6-plugin /** This is a custom React hook that manages CodeMirror state. */ export default function useCodeMirror({ From c7b2776a5a78061407b0106f1eb57412441f140f Mon Sep 17 00:00:00 2001 From: Connie Ye Date: Fri, 30 May 2025 08:38:16 -0700 Subject: [PATCH 7/7] fix getfilemode not exported --- client/modules/IDE/components/Editor/stateUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/modules/IDE/components/Editor/stateUtils.js b/client/modules/IDE/components/Editor/stateUtils.js index b5599455f9..193a766e8c 100644 --- a/client/modules/IDE/components/Editor/stateUtils.js +++ b/client/modules/IDE/components/Editor/stateUtils.js @@ -56,7 +56,7 @@ import tidyCodeWithPrettier from './tidier'; // - add docstrings for all exported functions /** Detects what mode the file is based on the name. */ -function getFileMode(fileName) { +export function getFileMode(fileName) { let mode; if (fileName.match(/.+\.js$/i)) { mode = 'javascript';