bytebeat-composer/src/code-editor/codemirror.ts
2024-08-09 15:07:45 -04:00

71 lines
1.9 KiB
TypeScript

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;
}