vue-codemirror6
Version:
CodeMirror6 Component for vue2 and vue3.
527 lines (525 loc) • 17.4 kB
JavaScript
/**
* 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.18
* @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.18",
date: "2025-06-09T13:13:13.017Z"
}, 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
};