// CodeMirror YAML editor. import { defaultKeymap, history, historyKeymap, indentWithTab } from "@codemirror/commands"; import { HighlightStyle, StreamLanguage, syntaxHighlighting } from "@codemirror/language"; import { yaml } from "@codemirror/legacy-modes/mode/yaml"; import { EditorState } from "@codemirror/state"; import { EditorView, drawSelection, highlightActiveLine, keymap, lineNumbers } from "@codemirror/view"; import { tags } from "@lezer/highlight"; const highlight = HighlightStyle.define([ { tag: tags.atom, color: "#e0a23a" }, { tag: tags.number, color: "#9cdcfe" }, { tag: tags.string, color: "#ce9178" }, { tag: tags.comment, color: "#6a9955", fontStyle: "italic" }, { tag: tags.keyword, color: "#c586c0" }, { tag: tags.meta, color: "#9aa0a8" }, ]); const theme = EditorView.theme({ "&": { backgroundColor: "#111318", color: "#e6e6e6" }, ".cm-gutters": { backgroundColor: "#15181d", color: "#5e646c", border: "none" }, ".cm-activeLine": { backgroundColor: "#1a1e25" }, ".cm-activeLineGutter": { backgroundColor: "#1a1e25" }, "&.cm-focused .cm-cursor": { borderLeftColor: "#e6e6e6" }, "&.cm-focused .cm-selectionBackground, ::selection": { backgroundColor: "#2c3a55" }, }, { dark: true }); export class YamlEditor { readonly view: EditorView; private suppress = false; constructor(parent: HTMLElement, initial: string, onChange: (text: string) => void) { let timer: number | undefined; this.view = new EditorView({ parent, state: EditorState.create({ doc: initial, extensions: [ lineNumbers(), history(), drawSelection(), highlightActiveLine(), keymap.of([...defaultKeymap, ...historyKeymap, indentWithTab]), StreamLanguage.define(yaml), syntaxHighlighting(highlight), theme, EditorView.updateListener.of((u) => { if (!u.docChanged || this.suppress) return; window.clearTimeout(timer); timer = window.setTimeout(() => onChange(this.text), 250); }), ], }), }); } get text(): string { return this.view.state.doc.toString(); } setText(text: string) { if (text === this.text) return; this.suppress = true; this.view.dispatch({ changes: { from: 0, to: this.view.state.doc.length, insert: text } }); this.suppress = false; } }