import { EditorState } from "@codemirror/state"; import { EditorView, keymap, highlightSpecialChars } from "@codemirror/view"; import { history, historyKeymap, defaultKeymap, insertNewline, indentLess, } from "@codemirror/commands"; import { searchKeymap, highlightSelectionMatches } from "@codemirror/search"; import { indentUnit, bracketMatching, syntaxHighlighting } from "@codemirror/language"; import { javascript } from "@codemirror/lang-javascript"; import { classHighlighter } from "@lezer/highlight"; function createCodemirror(inputListener?: () => void) { const editor = new EditorView({ state: EditorState.create({ extensions: [ keymap.of([ { key: "Enter", run: insertNewline }, { key: "Tab", run: ({ state, dispatch }) => (dispatch(state.replaceSelection("\t")), true), shift: indentLess, }, ...searchKeymap, ...historyKeymap, ...defaultKeymap, ]), EditorView.lineWrapping, EditorState.tabSize.of(3), indentUnit.of("\t"), history(), bracketMatching(), highlightSelectionMatches(), highlightSpecialChars(), javascript(), syntaxHighlighting(classHighlighter), EditorView.updateListener.of(v => { if (v.docChanged && inputListener) { inputListener(); } }), ], }), }); editor.dom.id = "code-editor"; editor.dom.ariaLabel = "Code editor"; return editor; } // replace a textarea with codemirror export function replace(editCallback: () => void, textarea: HTMLTextAreaElement): EditorView { const codemirror = createCodemirror(editCallback); codemirror.dispatch({ changes: { from: 0, insert: textarea.value } }); let selection; if (document.activeElement === textarea) { selection = { anchor: textarea.selectionStart, head: textarea.selectionEnd, }; } textarea.replaceWith(codemirror.dom); if (selection) { codemirror.focus(); codemirror.dispatch({ selection }); } return codemirror; }