@scalar/use-codemirror 0.14.9 → 0.14.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,362 +1,330 @@
1
- import { autocompletion, closeBrackets, closeBracketsKeymap, completionKeymap } from "@codemirror/autocomplete";
2
- import { history, historyKeymap, indentWithTab, insertNewline } from "@codemirror/commands";
3
- import { css } from "@codemirror/lang-css";
4
- import { html } from "@codemirror/lang-html";
5
- import { json } from "@codemirror/lang-json";
6
- import { xml } from "@codemirror/lang-xml";
7
- import { yaml } from "@codemirror/lang-yaml";
8
- import {
9
- bracketMatching,
10
- defaultHighlightStyle,
11
- foldGutter,
12
- indentOnInput,
13
- syntaxHighlighting
14
- } from "@codemirror/language";
15
- import { linter } from "@codemirror/lint";
16
- import { StateEffect } from "@codemirror/state";
17
- import {
18
- EditorView,
19
- highlightSpecialChars,
20
- keymap,
21
- lineNumbers as lineNumbersExtension,
22
- placeholder as placeholderExtension
23
- } from "@codemirror/view";
24
- import { ScalarIcon } from "@scalar/components";
25
- import { computed, h, onBeforeUnmount, ref, render, toValue, watch } from "vue";
26
- import { customTheme } from "../themes/index.js";
27
- import { variables } from "./variables.js";
28
- const hasProvider = (params) => "provider" in params && !!toValue(params.provider);
1
+ import { autocompletion, closeBrackets, closeBracketsKeymap, completionKeymap } from '@codemirror/autocomplete';
2
+ import { history, historyKeymap, indentWithTab, insertNewline } from '@codemirror/commands';
3
+ import { css } from '@codemirror/lang-css';
4
+ import { html } from '@codemirror/lang-html';
5
+ import { json } from '@codemirror/lang-json';
6
+ import { xml } from '@codemirror/lang-xml';
7
+ import { yaml } from '@codemirror/lang-yaml';
8
+ import { bracketMatching, defaultHighlightStyle, foldGutter, indentOnInput, syntaxHighlighting, } from '@codemirror/language';
9
+ import { linter } from '@codemirror/lint';
10
+ import { StateEffect } from '@codemirror/state';
11
+ import { EditorView, highlightSpecialChars, keymap, lineNumbers as lineNumbersExtension, placeholder as placeholderExtension, } from '@codemirror/view';
12
+ import { computed, onBeforeUnmount, ref, toValue, watch } from 'vue';
13
+ const CHEVRON_DOWN = '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m18 10-6 6-6-6" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/></svg>';
14
+ const CHEVRON_RIGHT = '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m9 18 6-6-6-6" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/></svg>';
15
+ import { customTheme } from '../themes/index.js';
16
+ import { variables } from './variables.js';
17
+ /**
18
+ * This was an insane bug that only exists in chrome
19
+ *
20
+ * Found these issues which may be related, it says che chrome one is fixed, they possibly had a regression?
21
+ *
22
+ * @see https://issues.chromium.org/issues/375711382
23
+ * @see https://discuss.codemirror.net/t/experimental-support-for-editcontext/8144
24
+ * @see https://github.com/codemirror/dev/issues/1458
25
+ */
26
+ // @ts-expect-error this is the workaround suggested by codemirror
27
+ EditorView.EDIT_CONTEXT = false;
28
+ /** Check if the hook has a provider. In provider mode we ignore the content variable */
29
+ const hasProvider = (params) => 'provider' in params && !!toValue(params.provider);
29
30
  const selectAllKeyBinding = {
30
- key: "Mod-a",
31
- run: (view) => {
32
- view.dispatch({
33
- selection: { anchor: 0, head: view.state.doc.length },
34
- scrollIntoView: false
35
- });
36
- return true;
37
- }
38
- };
39
- const useCodeMirror = (params) => {
40
- const codeMirror = ref(null);
41
- const setCodeMirrorContent = (newValue = "") => {
42
- if (!codeMirror.value) {
43
- return;
44
- }
45
- if (codeMirror.value.state.doc.toString() === newValue) {
46
- return;
47
- }
48
- codeMirror.value.dispatch({
49
- changes: {
50
- from: 0,
51
- to: codeMirror.value.state.doc.length,
52
- insert: newValue
53
- },
54
- selection: {
55
- anchor: Math.min(codeMirror.value.state.selection.main.anchor, newValue.length)
56
- }
57
- });
58
- };
59
- const extensionConfig = computed(() => ({
60
- onChange: params.onChange,
61
- onBlur: params.onBlur,
62
- onFocus: params.onFocus,
63
- disableTabIndent: toValue(params.disableTabIndent),
64
- language: toValue(params.language),
65
- classes: toValue(params.classes),
66
- readOnly: toValue(params.readOnly),
67
- lineNumbers: toValue(params.lineNumbers),
68
- withVariables: toValue(params.withVariables),
69
- forceFoldGutter: toValue(params.forceFoldGutter),
70
- disableEnter: toValue(params.disableEnter),
71
- disableCloseBrackets: toValue(params.disableCloseBrackets),
72
- withoutTheme: toValue(params.withoutTheme),
73
- lint: toValue(params.lint),
74
- additionalExtensions: toValue(params.extensions),
75
- placeholder: toValue(params.placeholder)
76
- }));
77
- watch(
78
- params.codeMirrorRef,
79
- () => {
80
- codeMirror.value?.destroy();
81
- mountCodeMirror();
31
+ key: 'Mod-a',
32
+ run: (view) => {
33
+ // Select the entire content
34
+ view.dispatch({
35
+ selection: { anchor: 0, head: view.state.doc.length },
36
+ scrollIntoView: false,
37
+ });
38
+ return true;
82
39
  },
83
- { immediate: true }
84
- );
85
- onBeforeUnmount(() => codeMirror.value?.destroy());
86
- function mountCodeMirror() {
87
- if (params.codeMirrorRef.value) {
88
- const provider = hasProvider(params) ? toValue(params.provider) : null;
89
- const extensions = getCodeMirrorExtensions({
90
- ...extensionConfig.value,
91
- provider
92
- });
93
- codeMirror.value = new EditorView({
94
- parent: params.codeMirrorRef.value,
95
- extensions
96
- });
97
- if (!hasProvider(params)) {
98
- setCodeMirrorContent(toValue(params.content));
99
- }
100
- }
101
- }
102
- watch(
103
- () => hasProvider(params) ? toValue(params.provider) : null,
104
- () => {
105
- if (hasProvider(params)) {
40
+ };
41
+ /** Reactive CodeMirror Integration */
42
+ export const useCodeMirror = (params) => {
43
+ const codeMirror = ref(null);
44
+ /** Set the codemirror content value */
45
+ const setCodeMirrorContent = (newValue = '') => {
46
+ if (!codeMirror.value) {
47
+ return;
48
+ }
49
+ // No need to set the CodeMirror content if nothing has changed
50
+ if (codeMirror.value.state.doc.toString() === newValue) {
51
+ return;
52
+ }
53
+ codeMirror.value.dispatch({
54
+ changes: {
55
+ from: 0,
56
+ to: codeMirror.value.state.doc.length,
57
+ insert: newValue,
58
+ },
59
+ selection: {
60
+ anchor: Math.min(codeMirror.value.state.selection.main.anchor, newValue.length),
61
+ },
62
+ });
63
+ };
64
+ // All options except provider
65
+ const extensionConfig = computed(() => ({
66
+ onChange: params.onChange,
67
+ onBlur: params.onBlur,
68
+ onFocus: params.onFocus,
69
+ disableTabIndent: toValue(params.disableTabIndent),
70
+ language: toValue(params.language),
71
+ classes: toValue(params.classes),
72
+ readOnly: toValue(params.readOnly),
73
+ lineNumbers: toValue(params.lineNumbers),
74
+ withVariables: toValue(params.withVariables),
75
+ forceFoldGutter: toValue(params.forceFoldGutter),
76
+ disableEnter: toValue(params.disableEnter),
77
+ disableCloseBrackets: toValue(params.disableCloseBrackets),
78
+ withoutTheme: toValue(params.withoutTheme),
79
+ lint: toValue(params.lint),
80
+ additionalExtensions: toValue(params.extensions),
81
+ placeholder: toValue(params.placeholder),
82
+ }));
83
+ // Unmounts CodeMirror if it's mounted already, and mounts CodeMirror, if the given ref exists.
84
+ watch(params.codeMirrorRef, () => {
106
85
  codeMirror.value?.destroy();
107
86
  mountCodeMirror();
108
- }
87
+ }, { immediate: true });
88
+ // Cleanup codemirror
89
+ onBeforeUnmount(() => codeMirror.value?.destroy());
90
+ // Initializes CodeMirror.
91
+ function mountCodeMirror() {
92
+ if (params.codeMirrorRef.value) {
93
+ const provider = hasProvider(params) ? toValue(params.provider) : null;
94
+ const extensions = getCodeMirrorExtensions({
95
+ ...extensionConfig.value,
96
+ provider,
97
+ });
98
+ codeMirror.value = new EditorView({
99
+ parent: params.codeMirrorRef.value,
100
+ extensions,
101
+ });
102
+ // Set the initial content if a provider is not in use
103
+ if (!hasProvider(params)) {
104
+ setCodeMirrorContent(toValue(params.content));
105
+ }
106
+ }
109
107
  }
110
- );
111
- watch(
112
- extensionConfig,
113
- () => {
114
- if (!codeMirror.value) {
115
- return;
116
- }
117
- const provider = hasProvider(params) ? toValue(params.provider) : null;
118
- const extensions = getCodeMirrorExtensions({
119
- ...extensionConfig.value,
120
- provider
121
- });
122
- requestAnimationFrame(() => {
123
- codeMirror.value?.dispatch({
124
- effects: StateEffect.reconfigure.of(extensions)
108
+ // ---------------------------------------------------------------------------
109
+ // Provider must be watched separately because we need to restart codemirror if the provider changes
110
+ watch(() => (hasProvider(params) ? toValue(params.provider) : null), () => {
111
+ if (hasProvider(params)) {
112
+ codeMirror.value?.destroy();
113
+ mountCodeMirror();
114
+ }
115
+ });
116
+ // Update the extensions whenever parameters changes
117
+ watch(extensionConfig, () => {
118
+ if (!codeMirror.value) {
119
+ return;
120
+ }
121
+ // If a provider is
122
+ const provider = hasProvider(params) ? toValue(params.provider) : null;
123
+ const extensions = getCodeMirrorExtensions({
124
+ ...extensionConfig.value,
125
+ provider,
125
126
  });
126
- });
127
- },
128
- { immediate: true }
129
- );
130
- watch(
131
- () => toValue(params.content),
132
- () => {
133
- if (hasProvider(params)) {
134
- return;
135
- }
136
- setCodeMirrorContent(toValue(params.content));
137
- },
138
- { immediate: true }
139
- );
140
- return {
141
- /** Replaces the current content with the given value. */
142
- setCodeMirrorContent,
143
- /** Codemirror instance */
144
- codeMirror
145
- };
127
+ requestAnimationFrame(() => {
128
+ codeMirror.value?.dispatch({
129
+ effects: StateEffect.reconfigure.of(extensions),
130
+ });
131
+ });
132
+ }, { immediate: true });
133
+ // ---------------------------------------------------------------------------
134
+ // Keep the content in sync when the content is managed externally
135
+ watch(() => toValue(params.content), () => {
136
+ // When a provider is in use we do not map the content value back to the codemirror instance
137
+ if (hasProvider(params)) {
138
+ return;
139
+ }
140
+ setCodeMirrorContent(toValue(params.content));
141
+ }, { immediate: true });
142
+ return {
143
+ /** Replaces the current content with the given value. */
144
+ setCodeMirrorContent,
145
+ /** Codemirror instance */
146
+ codeMirror,
147
+ };
146
148
  };
149
+ // ---------------------------------------------------------------------------
147
150
  const languageExtensions = {
148
- html,
149
- json,
150
- yaml,
151
- css,
152
- xml
151
+ html: html,
152
+ json: json,
153
+ yaml: yaml,
154
+ css: css,
155
+ xml: xml,
153
156
  };
154
- function getCodeMirrorExtensions({
155
- onChange,
156
- onBlur,
157
- onFocus,
158
- provider,
159
- language,
160
- classes = [],
161
- readOnly = false,
162
- lineNumbers = false,
163
- withVariables = false,
164
- forceFoldGutter = false,
165
- disableEnter = false,
166
- disableCloseBrackets = false,
167
- disableTabIndent = false,
168
- withoutTheme = false,
169
- lint = false,
170
- additionalExtensions = [],
171
- placeholder
172
- }) {
173
- const extensions = [
174
- highlightSpecialChars(),
175
- history(),
176
- keymap.of(historyKeymap),
177
- syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
178
- EditorView.theme({
179
- ".cm-line": {
180
- lineHeight: "22px",
181
- padding: "0 2px 0 4px"
182
- },
183
- ".cm-gutterElement": {
184
- lineHeight: "22px"
185
- },
186
- ".cm-tooltip": {
187
- border: "1px solid #f5c6cb",
188
- fontSize: "12px"
189
- },
190
- ".cm-tooltip-lint": {
191
- backgroundColor: "#ffffff"
192
- },
193
- ".cm-diagnostic-error": {
194
- borderLeft: "0",
195
- color: "#dc1b19"
196
- },
197
- ".cm-foldPlaceholder": {
198
- background: "var(--scalar-background-1)",
199
- border: "none",
200
- fontFamily: "var(--scalar-font)"
201
- }
202
- }),
203
- // Listen to updates
204
- EditorView.updateListener.of((v) => {
205
- if (!v.docChanged) {
206
- return;
207
- }
208
- onChange?.(v.state.doc.toString());
209
- }),
210
- EditorView.domEventHandlers({
211
- blur: (_event, view) => {
212
- onBlur?.(view.state.doc.toString());
213
- },
214
- focus: (_event, view) => {
215
- onFocus?.(view.state.doc.toString());
216
- }
217
- }),
218
- // Add Classes
219
- EditorView.editorAttributes.of({ class: classes.join(" ") }),
220
- ...additionalExtensions
221
- ];
222
- if (provider) {
223
- extensions.push(provider);
224
- }
225
- if (!withoutTheme) {
226
- extensions.push(customTheme);
227
- }
228
- if (readOnly) {
229
- extensions.push(EditorView.editable.of(false));
230
- } else {
231
- extensions.push(
232
- indentOnInput(),
233
- bracketMatching(),
234
- autocompletion(),
235
- keymap.of([...completionKeymap, selectAllKeyBinding]),
236
- bracketMatching()
237
- );
238
- if (!disableCloseBrackets) {
239
- extensions.push(closeBrackets(), keymap.of([...closeBracketsKeymap]));
157
+ /** Generate the list of extension from parameters */
158
+ function getCodeMirrorExtensions({ onChange, onBlur, onFocus, provider, language, classes = [], readOnly = false, lineNumbers = false, withVariables = false, forceFoldGutter = false, disableEnter = false, disableCloseBrackets = false, disableTabIndent = false, withoutTheme = false, lint = false, additionalExtensions = [], placeholder, }) {
159
+ const extensions = [
160
+ highlightSpecialChars(),
161
+ history(),
162
+ keymap.of(historyKeymap),
163
+ syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
164
+ EditorView.theme({
165
+ '.cm-line': {
166
+ lineHeight: '22px',
167
+ padding: '0 2px 0 4px',
168
+ },
169
+ '.cm-gutterElement': {
170
+ lineHeight: '22px',
171
+ },
172
+ '.cm-tooltip': {
173
+ border: '1px solid #f5c6cb',
174
+ fontSize: '12px',
175
+ },
176
+ '.cm-tooltip-lint': {
177
+ backgroundColor: '#ffffff',
178
+ },
179
+ '.cm-diagnostic-error': {
180
+ borderLeft: '0',
181
+ color: '#dc1b19',
182
+ },
183
+ '.cm-foldPlaceholder': {
184
+ background: 'var(--scalar-background-1)',
185
+ border: 'none',
186
+ fontFamily: 'var(--scalar-font)',
187
+ },
188
+ }),
189
+ // Listen to updates
190
+ EditorView.updateListener.of((v) => {
191
+ if (!v.docChanged) {
192
+ return;
193
+ }
194
+ onChange?.(v.state.doc.toString());
195
+ }),
196
+ EditorView.domEventHandlers({
197
+ blur: (event, view) => {
198
+ onBlur?.(view.state.doc.toString(), event);
199
+ },
200
+ focus: (event, view) => {
201
+ onFocus?.(view.state.doc.toString(), event);
202
+ },
203
+ }),
204
+ // Add Classes
205
+ EditorView.editorAttributes.of({ class: classes.join(' ') }),
206
+ ...additionalExtensions,
207
+ ];
208
+ // Enable the provider
209
+ if (provider) {
210
+ extensions.push(provider);
240
211
  }
241
- if (disableTabIndent) {
242
- extensions.push(
243
- keymap.of([
244
- {
245
- key: "Tab",
246
- run: () => false,
247
- // Prevent default Tab behavior
248
- shift: () => false
249
- // Prevent default Shift+Tab behavior
250
- }
251
- ])
252
- );
253
- } else {
254
- extensions.push(keymap.of([indentWithTab]));
212
+ // Add the theme as needed
213
+ if (!withoutTheme) {
214
+ extensions.push(customTheme);
255
215
  }
256
- }
257
- if (placeholder) {
258
- extensions.push(placeholderExtension(placeholder));
259
- }
260
- if (lineNumbers) {
261
- extensions.push(lineNumbersExtension());
262
- }
263
- if (forceFoldGutter) {
264
- extensions.push(
265
- foldGutter({
266
- markerDOM: (open) => {
267
- const icon = document.createElement("div");
268
- icon.classList.add("cm-foldMarker");
269
- const vnode = h(ScalarIcon, {
270
- icon: open ? "ChevronDown" : "ChevronRight",
271
- size: "md"
272
- });
273
- render(vnode, icon);
274
- return icon;
275
- }
276
- })
277
- );
278
- }
279
- if (language && languageExtensions[language]) {
280
- extensions.push(languageExtensions[language]());
281
- if (!forceFoldGutter) {
282
- extensions.push(
283
- foldGutter({
284
- markerDOM: (open) => {
285
- const icon = document.createElement("div");
286
- icon.classList.add("cm-foldMarker");
287
- const vnode = h(ScalarIcon, {
288
- icon: open ? "ChevronDown" : "ChevronRight",
289
- size: "md"
290
- });
291
- render(vnode, icon);
292
- return icon;
293
- }
294
- })
295
- );
216
+ // Read only
217
+ if (readOnly) {
218
+ extensions.push(EditorView.editable.of(false));
296
219
  }
297
- }
298
- if (lint && language === "json") {
299
- const jsonLinter = linter((view) => {
300
- const diagnostics = [];
301
- const content = view.state.doc.toString();
302
- if (content.trim()) {
303
- try {
304
- JSON.parse(content);
305
- } catch (e) {
306
- if (e instanceof Error) {
307
- diagnostics.push({
308
- from: 0,
309
- to: view.state.doc.length,
310
- severity: "error",
311
- message: e.message
312
- });
313
- }
220
+ else {
221
+ extensions.push(indentOnInput(), bracketMatching(), autocompletion(), keymap.of([...completionKeymap, selectAllKeyBinding]), bracketMatching());
222
+ if (!disableCloseBrackets) {
223
+ extensions.push(closeBrackets(), keymap.of([...closeBracketsKeymap]));
314
224
  }
315
- }
316
- return diagnostics;
317
- });
318
- extensions.push(jsonLinter);
319
- }
320
- if (withVariables) {
321
- extensions.push(variables());
322
- }
323
- if (disableEnter) {
324
- extensions.push(
325
- keymap.of([
326
- {
327
- key: "Enter",
328
- run: () => {
329
- return true;
330
- }
331
- },
332
- {
333
- key: "Ctrl-Enter",
334
- mac: "Cmd-Enter",
335
- run: () => {
336
- return true;
337
- }
338
- },
339
- {
340
- key: "Shift-Enter",
341
- run: () => {
342
- return true;
343
- }
225
+ if (disableTabIndent) {
226
+ extensions.push(keymap.of([
227
+ {
228
+ key: 'Tab',
229
+ run: () => false, // Prevent default Tab behavior
230
+ shift: () => false, // Prevent default Shift+Tab behavior
231
+ },
232
+ ]));
344
233
  }
345
- ])
346
- );
347
- } else {
348
- extensions.push(
349
- keymap.of([
350
- {
351
- key: "Enter",
352
- run: insertNewline
234
+ else {
235
+ extensions.push(keymap.of([indentWithTab]));
236
+ }
237
+ }
238
+ // Add placeholder extension if placeholder is provided
239
+ if (placeholder) {
240
+ extensions.push(placeholderExtension(placeholder));
241
+ }
242
+ // Line numbers
243
+ if (lineNumbers) {
244
+ extensions.push(lineNumbersExtension());
245
+ }
246
+ if (forceFoldGutter) {
247
+ extensions.push(foldGutter({
248
+ markerDOM: (open) => {
249
+ const icon = document.createElement('div');
250
+ icon.classList.add('cm-foldMarker');
251
+ icon.innerHTML = open ? CHEVRON_DOWN : CHEVRON_RIGHT;
252
+ return icon;
253
+ },
254
+ }));
255
+ }
256
+ // Syntax highlighting
257
+ if (language && languageExtensions[language]) {
258
+ extensions.push(languageExtensions[language]());
259
+ if (!forceFoldGutter) {
260
+ extensions.push(foldGutter({
261
+ markerDOM: (open) => {
262
+ const icon = document.createElement('div');
263
+ icon.classList.add('cm-foldMarker');
264
+ icon.innerHTML = open ? CHEVRON_DOWN : CHEVRON_RIGHT;
265
+ return icon;
266
+ },
267
+ }));
353
268
  }
354
- ])
355
- );
356
- }
357
- return extensions;
269
+ }
270
+ // JSON Linter
271
+ if (lint && language === 'json') {
272
+ const jsonLinter = linter((view) => {
273
+ const diagnostics = [];
274
+ const content = view.state.doc.toString();
275
+ if (content.trim()) {
276
+ try {
277
+ JSON.parse(content);
278
+ }
279
+ catch (e) {
280
+ if (e instanceof Error) {
281
+ diagnostics.push({
282
+ from: 0,
283
+ to: view.state.doc.length,
284
+ severity: 'error',
285
+ message: e.message,
286
+ });
287
+ }
288
+ }
289
+ }
290
+ return diagnostics;
291
+ });
292
+ extensions.push(jsonLinter);
293
+ }
294
+ // Highlight variables
295
+ if (withVariables) {
296
+ extensions.push(variables());
297
+ }
298
+ if (disableEnter) {
299
+ extensions.push(keymap.of([
300
+ {
301
+ key: 'Enter',
302
+ run: () => {
303
+ return true;
304
+ },
305
+ },
306
+ {
307
+ key: 'Ctrl-Enter',
308
+ mac: 'Cmd-Enter',
309
+ run: () => {
310
+ return true;
311
+ },
312
+ },
313
+ {
314
+ key: 'Shift-Enter',
315
+ run: () => {
316
+ return true;
317
+ },
318
+ },
319
+ ]));
320
+ }
321
+ else {
322
+ extensions.push(keymap.of([
323
+ {
324
+ key: 'Enter',
325
+ run: insertNewline,
326
+ },
327
+ ]));
328
+ }
329
+ return extensions;
358
330
  }
359
- export {
360
- useCodeMirror
361
- };
362
- //# sourceMappingURL=useCodeMirror.js.map