UNPKG

vue-codemirror6

Version:

CodeMirror6 Component for vue2 and vue3.

527 lines (525 loc) 17.4 kB
/** * vue-codemirror6 * * @description CodeMirror6 Component for vue2 and vue3. * @author Logue <logue@hotmail.co.jp> * @copyright 2022-2025 By Masashi Yoshikawa All rights reserved. * @license MIT * @version 1.3.20 * @see {@link https://github.com/logue/vue-codemirror6} */ import { indentWithTab as W } from "@codemirror/commands"; import { indentUnit as G } from "@codemirror/language"; import { linter as P, lintGutter as Z, forceLinting as w, diagnosticCount as q } from "@codemirror/lint"; import { EditorState as s, Compartment as C, StateEffect as S, EditorSelection as O } from "@codemirror/state"; import { EditorView as c, placeholder as H, keymap as K } from "@codemirror/view"; import { basicSetup as Q, minimalSetup as X } from "codemirror"; import { isVue2 as Y, h as V, defineComponent as ee, ref as m, shallowRef as te, computed as d, watch as B, onMounted as ae, nextTick as ne, onUnmounted as le } from "vue-demi"; const he = { version: "1.3.20", date: "2025-06-20T15:57:15.852Z" }, ie = (e) => e ? Object.entries(e).reduce((i, [l, u]) => (l = l.charAt(0).toUpperCase() + l.slice(1), l = `on${l}`, { ...i, [l]: u }), {}) : {}; function M(e, i = {}, l) { if (Y) return V(e, i, l); const { props: u, domProps: t, on: f, ...v } = i, h = f ? ie(f) : {}; return V( e, { ...v, ...u, ...t, ...h }, l ); } const oe = (e) => typeof e == "function" ? e() : e; var se = ee({ /** Component Name */ name: "CodeMirror", /** Model Definition */ model: { prop: "modelValue", event: "update:modelValue" }, /** Props Definition */ props: { /** Model value */ modelValue: { type: String, default: "" }, /** * Theme * * @see {@link https://codemirror.net/docs/ref/#view.EditorView^theme} */ theme: { type: Object, default: () => ({}) }, /** Dark Mode */ dark: { type: Boolean, default: !1 }, /** * Use Basic Setup * * @see {@link https://codemirror.net/docs/ref/#codemirror.basicSetup} */ basic: { type: Boolean, default: !1 }, /** * Use Minimal Setup (The basic setting has priority.) * * @see {@link https://codemirror.net/docs/ref/#codemirror.minimalSetup} */ minimal: { type: Boolean, default: !1 }, /** * Placeholder * * @see {@link https://codemirror.net/docs/ref/#view.placeholder} */ placeholder: { type: String, default: void 0 }, /** * Line wrapping * * An extension that enables line wrapping in the editor (by setting CSS white-space to pre-wrap in the content). * * @see {@link https://codemirror.net/docs/ref/#view.EditorView%5ElineWrapping} */ wrap: { type: Boolean, default: !1 }, /** * Allow tab key indent. * * @see {@link https://codemirror.net/examples/tab/} */ tab: { type: Boolean, default: !1 }, /** * Tab character * This is the unit of indentation used when the editor is configured to indent with tabs. * It is also used to determine the size of the tab character when the editor is configured to use tabs for indentation.. * * @see {@link https://codemirror.net/docs/ref/#state.EditorState^indentUnit} */ indentUnit: { type: String, default: void 0 }, /** * Allow Multiple Selection. * This allows the editor to have multiple selections at the same time. * This is useful for editing multiple parts of the document at once. * If this is set to true, the editor will allow multiple selections. * If this is set to false, the editor will only allow a single selection. * * @see {@link https://codemirror.net/docs/ref/#state.EditorState^allowMultipleSelections} */ allowMultipleSelections: { type: Boolean, default: !1 }, /** * Tab size * This is the number of spaces that a tab character represents in the editor. * It is used to determine the size of the tab character when the editor is configured to use tabs for indentation. * If this is set to a number, the editor will use that number of spaces for each tab character. * * @see {@link https://codemirror.net/docs/ref/#state.EditorState^tabSize} */ tabSize: { type: Number, default: void 0 }, /** * Set line break (separetor) char. * * This is the character that is used to separate lines in the editor. * It is used to determine the line break character when the editor is configured to use a specific line break character. * * @see {@link https://codemirror.net/docs/ref/#state.EditorState^lineSeparator} */ lineSeparator: { type: String, default: void 0 }, /** * Readonly * * This is a CodeMirror Facet that allows you to set the editor to read-only mode. * When this is set to true, the editor will not allow any changes to be made to the document. * This is useful for displaying code that should not be edited, such as documentation or examples. * If this is set to false, the editor will allow changes to be made to the document. * * @see {@link https://codemirror.net/docs/ref/#state.EditorState^readOnly} */ readonly: { type: Boolean, default: !1 }, /** * Disable input. * * This is the reversed value of the CodeMirror editable. * Similar to `readonly`, but setting this value to true disables dragging. * * @see {@link https://codemirror.net/docs/ref/#view.EditorView^editable} */ disabled: { type: Boolean, default: !1 }, /** * Additional Extension * * You can use this to add any additional extensions that you want to use in the editor. * * @see {@link https://codemirror.net/docs/ref/#state.Extension} */ extensions: { type: Array, default: () => [] }, /** * Language Phreses * * This is a CodeMirror Facet that allows you to define custom phrases for the editor. * It can be used to override default phrases or add new ones. * This is useful for translating the editor to different languages or for customizing the editor's UI. * * @see {@link https://codemirror.net/examples/translate/} */ phrases: { type: Object, default: void 0 }, /** * CodeMirror Language * * This is a CodeMirror Facet that allows you to define the language of the editor. * It can be used to enable syntax highlighting and other language-specific features. * It is useful for displaying code in a specific language, such as JavaScript, Python, or HTML. * * @see {@link https://codemirror.net/docs/ref/#language} */ lang: { type: Object, default: void 0 }, /** * CodeMirror Linter * * This is a CodeMirror Facet that allows you to define a linter for the editor. * It can be used to check the code for errors and warnings, and to provide feedback to the user. * It is useful for displaying code in a specific language, such as JavaScript, Python, or HTML. * This is useful for providing feedback to the user about the code they are writing. * * @see {@link https://codemirror.net/docs/ref/#lint.linter} */ linter: { type: Function, default: void 0 }, /** * Linter Config * * This is a CodeMirror Facet that allows you to define the configuration for the linter. * It can be used to specify options for the linter, such as the severity of errors and warnings, and to customize the behavior of the linter. * This is useful for providing feedback to the user about the code they are writing. * * @see {@link https://codemirror.net/docs/ref/#lint.linter^config} */ linterConfig: { type: Object, default: () => ({}) }, /** * Forces any linters configured to run when the editor is idle to run right away. * * This is useful for running linters on the initial load of the editor, or when the user has made changes to the code and wants to see the results immediately. * * @see {@link https://codemirror.net/docs/ref/#lint.forceLinting} */ forceLinting: { type: Boolean, default: !1 }, /** * Show Linter Gutter * * An area to 🔴 the lines with errors will be displayed. * This feature is not enabled if `linter` is not specified. * * @see {@link https://codemirror.net/docs/ref/#lint.lintGutter} */ gutter: { type: Boolean, default: !1 }, /** * Gutter Config * * This is a CodeMirror Facet that allows you to define the configuration for the gutter. * It can be used to specify options for the gutter, such as the size of the gutter, the position of the gutter, and to customize the behavior of the gutter. * This is useful for providing feedback to the user about the code they are writing. * * @see {@link https://codemirror.net/docs/ref/#lint.lintGutter^config} */ gutterConfig: { type: Object, default: void 0 }, /** * Using tag */ tag: { type: String, default: "div" }, /** * Allows an external update to scroll the form. * * This is useful for scrolling the editor to a specific position when the user has made changes to the code and wants to see the results immediately. * If this is set to true, the editor will scroll to the position specified in the transaction. * If this is set to false, the editor will not scroll to the position specified in the transaction. * * @see {@link https://codemirror.net/docs/ref/#state.TransactionSpec.scrollIntoView} */ scrollIntoView: { type: Boolean, default: !0 }, /** * Key map * This is a CodeMirror Facet that allows you to define custom key bindings. * It can be used to override default key bindings or add new ones. * * @see {@link https://codemirror.net/docs/ref/#view.keymap} */ keymap: { type: Array, default: () => [] } }, /** Emits */ emits: { /** Model Update */ "update:modelValue": (e = "") => !0, /** CodeMirror ViewUpdate */ update: (e) => !0, /** CodeMirror onReady */ ready: (e) => !0, /** CodeMirror onFocus */ focus: (e) => !0, /** State Changed */ change: (e) => !0, /** CodeMirror onDestroy */ destroy: () => !0 }, /** * Setup * * @param props - Props * @param context - Context */ setup(e, i) { const l = m(), u = m(e.modelValue), t = te(new c()), f = d({ get: () => t.value.hasFocus, set: (a) => { a && t.value.focus(); } }), v = d({ get: () => t.value.state.selection, set: (a) => t.value.dispatch({ selection: a }) }), h = d({ get: () => t.value.state.selection.main.head, set: (a) => t.value.dispatch({ selection: { anchor: a } }) }), $ = d( { get: () => t.value.state.toJSON(), set: (a) => t.value.setState(s.fromJSON(a)) } ), p = m(0), g = m(0), y = d(() => { const a = new C(), n = new C(); if (e.basic && e.minimal) throw new Error( "[Vue CodeMirror] Both basic and minimal cannot be specified." ); let o = []; return e.keymap && e.keymap.length > 0 && (o = e.keymap), e.tab && o.push(W), [ // Toggle basic setup e.basic && !e.minimal ? Q : void 0, // Toggle minimal setup e.minimal && !e.basic ? X : void 0, // ViewUpdate event listener c.updateListener.of((r) => { i.emit("focus", t.value.hasFocus), p.value = t.value.state.doc?.length, !(r.changes.empty || !r.docChanged) && (e.linter && (e.forceLinting && w(t.value), g.value = e.linter(t.value).length), i.emit("update", r)); }), // Toggle light/dark mode. c.theme(e.theme, { dark: e.dark }), // Toggle line wrapping e.wrap ? c.lineWrapping : void 0, // Tab character e.indentUnit ? G.of(e.indentUnit) : void 0, // Allow Multiple Selections s.allowMultipleSelections.of(e.allowMultipleSelections), // Indent tab size e.tabSize ? n.of(s.tabSize.of(e.tabSize)) : void 0, // locale settings e.phrases ? s.phrases.of(e.phrases) : void 0, // Readonly option s.readOnly.of(e.readonly), // Editable option c.editable.of(!e.disabled), // Set Line break char e.lineSeparator ? s.lineSeparator.of(e.lineSeparator) : void 0, // Lang e.lang ? a.of(e.lang) : void 0, // Append Linter settings e.linter ? P(e.linter, e.linterConfig) : void 0, // Show 🔴 to error line when linter enabled. e.linter && e.gutter ? Z(e.gutterConfig) : void 0, // Placeholder e.placeholder ? H(e.placeholder) : void 0, // Keymap and Indent with Tab o.length !== 0 ? K.of(o) : void 0, // Append Extensions ...e.extensions ].filter((r) => !!r); }); B( y, (a) => t.value?.dispatch({ effects: S.reconfigure.of(a) }), { immediate: !0 } ), B( () => e.modelValue, async (a) => { if (t.value.composing || // IME fix t.value.state.doc.toJSON().join(e.lineSeparator ?? ` `) === a) return; const n = !t.value.state.selection.ranges.every( (o) => o.anchor < a.length && o.head < a.length ); t.value.dispatch({ changes: { from: 0, to: t.value.state.doc.length, insert: a }, selection: n ? { anchor: 0, head: 0 } : t.value.state.selection, scrollIntoView: e.scrollIntoView }); }, { immediate: !0 } ), ae(async () => { let a = u.value; l.value && (l.value.childNodes[0] && (u.value !== "" && console.warn( "[CodeMirror.vue] The <code-mirror> tag contains child elements that overwrite the `v-model` values." ), a = l.value.childNodes[0].innerText.trim()), t.value = new c({ parent: l.value, state: s.create({ doc: a, extensions: y.value }), dispatch: (n) => { t.value.update([n]), !(n.changes.empty || !n.docChanged) && (i.emit("update:modelValue", n.state.doc.toString()), i.emit("change", n.state)); } }), await ne(), i.emit("ready", { view: t.value, state: t.value.state, container: l.value })); }), le(() => { t.value.destroy(), i.emit("destroy"); }); const j = () => { !e.linter || !t.value || (e.forceLinting && w(t.value), g.value = q(t.value.state)); }, x = () => { t.value?.dispatch({ effects: S.reconfigure.of([]) }), t.value?.dispatch({ effects: S.appendConfig.of(y.value) }); }, _ = (a, n) => t.value.state.sliceDoc(a, n), L = (a) => t.value.state.doc.line(a + 1).text, N = () => t.value.state.doc.lines, U = () => t.value.state.selection.main.head, k = () => { let a; return (a = t.value.state.selection.ranges) !== null && a !== void 0 ? a : []; }, z = () => { let a; return (a = t.value.state.sliceDoc( t.value.state.selection.main.from, t.value.state.selection.main.to )) !== null && a !== void 0 ? a : ""; }, E = () => { const a = t.value.state; return a ? a.selection.ranges.map( (n) => a.sliceDoc(n.from, n.to) ) : []; }, R = () => t.value.state.selection.ranges.some( (a) => !a.empty ), T = (a, n, o) => t.value.dispatch({ changes: { from: n, to: o, insert: a } }), D = (a) => t.value.dispatch(t.value.state.replaceSelection(a)), A = (a) => t.value.dispatch({ selection: { anchor: a } }), F = (a, n) => t.value.dispatch({ selection: { anchor: a, head: n } }), I = (a, n) => t.value.dispatch({ selection: O.create(a, n) }), J = (a) => t.value.dispatch({ selection: O.create( v.value.ranges.map((n) => n.extend(a(n))) ) }), b = { editor: l, view: t, cursor: h, selection: v, focus: f, length: p, json: $, diagnosticCount: g, dom: t.value.contentDOM, lint: j, forceReconfigure: x, // Bellow is CodeMirror5's function getRange: _, getLine: L, lineCount: N, getCursor: U, listSelections: k, getSelection: z, getSelections: E, somethingSelected: R, replaceRange: T, replaceSelection: D, setCursor: A, setSelection: F, setSelections: I, extendSelectionsBy: J }; return i.expose(b), b; }, render() { return M( this.$props.tag, { ref: "editor", class: "vue-codemirror" }, this.$slots.default ? ( // Hide original content M( "aside", { style: "display: none;", "aria-hidden": "true" }, oe(this.$slots.default) ) ) : void 0 ); } }); const ge = (e) => e.component("CodeMirror", se); export { he as Meta, se as default, ge as install };