import {basicSetup} from "codemirror"; import {EditorView, keymap} from "@codemirror/view"; import {EditorState} from "@codemirror/state"; import {python} from "@codemirror/lang-python"; import {r} from "codemirror-lang-r"; import {javascript} from "@codemirror/lang-javascript"; import {elm} from "@codemirror/legacy-modes/mode/elm"; import {StreamLanguage} from "@codemirror/language" import {vim} from "@replit/codemirror-vim"; import {indentWithTab} from "@codemirror/commands"; window.EditorView = EditorView; const languages = { 'python': python, 'r': r, 'javascript': javascript, 'elm': () => StreamLanguage.define(elm), } export function codemirror_editor(language, options) { const language_plugin = languages[language]; options = Object.assign({ extensions: [ vim(), basicSetup, keymap.of([indentWithTab]), EditorView.updateListener.of(update => { if(!options?.onChange || update.changes.desc.empty) { return; } options.onChange(update); }) ] }, options); if(language_plugin) { options.extensions.push(language_plugin()); } let editor = new EditorView(options); return editor; } export class CodeEditorElement extends HTMLElement { constructor() { super(); const shadowRoot = this.attachShadow({mode: 'open'}); } connectedCallback() { this.init_editor(); } init_editor() { const code = this.textContent; const code_tag = this.shadowRoot; const language = this.getAttribute('language') || ''; this.codeMirror = codemirror_editor( language, { doc: code, parent: code_tag, root: this.shadowRoot, onChange: update => this.onChange(update) } ); } onChange() { const code = this.codeMirror.state.doc.toString(); this.value = code; this.dispatchEvent(new CustomEvent('change')); } } customElements.define("code-editor", CodeEditorElement);