96 lines · 3.1 KB
import { useEffect, useRef } from "react";
import { EditorState, Compartment } from "@codemirror/state";
import { EditorView, keymap } from "@codemirror/view";
import { indentWithTab } from "@codemirror/commands";
import { basicSetup } from "codemirror";
import { oneDark } from "@codemirror/theme-one-dark";
import { languages } from "@codemirror/language-data";
import { LanguageDescription } from "@codemirror/language";

const LINE_HEIGHT_EM = 1.5;

const theme = EditorView.theme(
  {
    "&": { backgroundColor: "rgb(3 7 18)", fontSize: "13px", minHeight: "var(--editor-min-height)" },
    ".cm-scroller": { fontFamily: "var(--font-mono)", lineHeight: "1.5", minHeight: "var(--editor-min-height)" },
    ".cm-content": { fontFamily: "var(--font-mono)", padding: "12px 0" },
    ".cm-gutters": { backgroundColor: "rgb(3 7 18)", borderRight: "1px solid rgb(31 41 55)", color: "rgb(75 85 99)" },
    ".cm-activeLineGutter, .cm-activeLine": { backgroundColor: "rgb(255 255 255 / 0.03)" },
    "&.cm-focused": { outline: "none" },
  },
  { dark: true },
);

type Props = {
  value: string;
  onChange: (value: string) => void;
  filename: string;
  minLines?: number;
  id?: string;
};

/** CodeMirror 6 editor with line numbers and a language loaded from the file name; the view owns the text. */
export function CodeEditor({ value, onChange, filename, minLines = 28, id }: Props) {
  const host = useRef<HTMLDivElement>(null);
  const view = useRef<EditorView | null>(null);
  const language = useRef(new Compartment());
  const latest = useRef(onChange);
  latest.current = onChange;
  const initial = useRef(value);