1import { StreamLanguage } from "@codemirror/language";
2import { octave } from "@codemirror/legacy-modes/mode/octave";
3import { EditorState } from "@codemirror/state";
4import { keymap } from "@codemirror/view";
5import { basicSetup, EditorView } from "codemirror";
6import { useEffect, useRef } from "react";
8export function Editor({ value, onChange, onRun }: { value: string; onChange: (s: string) => void; onRun: () => void }) {
9 const host = useRef<HTMLDivElement>(null);
10 const view = useRef<EditorView | null>(null);
11 const cb = useRef({ onChange, onRun });
12 cb.current = { onChange, onRun };
14 useEffect(() => {
15 const v = new EditorView({
16 parent: host.current!,
17 state: EditorState.create({
18 doc: value,
19 extensions: [
20 keymap.of([{ key: "Mod-Enter", run: () => (cb.current.onRun(), true) }]),
21 basicSetup,
22 StreamLanguage.define(octave),
23 EditorView.updateListener.of(u => {
24 if (u.docChanged) cb.current.onChange(u.state.doc.toString());
25 }),
26 ],
27 }),
28 });
29 view.current = v;
30 return () => v.destroy();
31 // eslint-disable-next-line react-hooks/exhaustive-deps
32 }, []);
34 useEffect(() => {
35 const v = view.current;
36 if (v && v.state.doc.toString() !== value) {
37 v.dispatch({ changes: { from: 0, to: v.state.doc.length, insert: value } });
38 }
39 }, [value]);
41 return <div ref={host} className="editor" />;
42}