@prosemark/core 0.0.2 → 0.0.4

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.
Files changed (3) hide show
  1. package/dist/main.d.ts +52 -12
  2. package/dist/main.js +314 -3144
  3. package/package.json +20 -4
package/dist/main.js CHANGED
@@ -1,3071 +1,13 @@
1
- import { Decoration, Direction, EditorView, EditorView as EditorView$1, ViewPlugin, WidgetType, dropCursor, getPanel, getTooltip, hoverTooltip, keymap, logException, runScopeHandlers, showPanel, showTooltip } from "@codemirror/view";
2
- import { Annotation, CharCategory, EditorSelection, EditorState, Facet, MapMode, Prec, RangeSet, RangeSetBuilder, RangeValue, StateEffect, StateField, codePointAt, codePointSize, combineConfig, findClusterBreak, fromCodePoint } from "@codemirror/state";
1
+ import { Decoration, EditorView, EditorView as EditorView$1, ViewPlugin, ViewUpdate, WidgetType, dropCursor, keymap } from "@codemirror/view";
2
+ import { Annotation, CharCategory, EditorSelection, EditorState, Facet, Line, RangeSet, RangeSetBuilder, StateField, findClusterBreak } from "@codemirror/state";
3
3
  import { HighlightStyle, bracketMatching, foldGutter, foldKeymap, indentOnInput, syntaxHighlighting, syntaxTree } from "@codemirror/language";
4
4
  import { defaultKeymap, history, historyKeymap, indentWithTab } from "@codemirror/commands";
5
+ import { searchKeymap } from "@codemirror/search";
6
+ import { autocompletion, closeBrackets, closeBracketsKeymap, completionKeymap } from "@codemirror/autocomplete";
7
+ import { lintKeymap } from "@codemirror/lint";
5
8
  import { Tag, styleTags, tags } from "@lezer/highlight";
6
9
  import * as emoji from "node-emoji";
7
10
 
8
- //#region ../../node_modules/crelt/index.js
9
- function crelt() {
10
- var elt = arguments[0];
11
- if (typeof elt == "string") elt = document.createElement(elt);
12
- var i = 1, next = arguments[1];
13
- if (next && typeof next == "object" && next.nodeType == null && !Array.isArray(next)) {
14
- for (var name in next) if (Object.prototype.hasOwnProperty.call(next, name)) {
15
- var value = next[name];
16
- if (typeof value == "string") elt.setAttribute(name, value);
17
- else if (value != null) elt[name] = value;
18
- }
19
- i++;
20
- }
21
- for (; i < arguments.length; i++) add(elt, arguments[i]);
22
- return elt;
23
- }
24
- function add(elt, child) {
25
- if (typeof child == "string") elt.appendChild(document.createTextNode(child));
26
- else if (child == null) {} else if (child.nodeType != null) elt.appendChild(child);
27
- else if (Array.isArray(child)) for (var i = 0; i < child.length; i++) add(elt, child[i]);
28
- else throw new RangeError("Unsupported child node: " + child);
29
- }
30
-
31
- //#endregion
32
- //#region ../../node_modules/@codemirror/search/dist/index.js
33
- const basicNormalize = typeof String.prototype.normalize == "function" ? (x) => x.normalize("NFKD") : (x) => x;
34
- /**
35
- A search cursor provides an iterator over text matches in a
36
- document.
37
- */
38
- var SearchCursor = class {
39
- /**
40
- Create a text cursor. The query is the search string, `from` to
41
- `to` provides the region to search.
42
-
43
- When `normalize` is given, it will be called, on both the query
44
- string and the content it is matched against, before comparing.
45
- You can, for example, create a case-insensitive search by
46
- passing `s => s.toLowerCase()`.
47
-
48
- Text is always normalized with
49
- [`.normalize("NFKD")`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize)
50
- (when supported).
51
- */
52
- constructor(text, query, from = 0, to = text.length, normalize, test) {
53
- this.test = test;
54
- /**
55
- The current match (only holds a meaningful value after
56
- [`next`](https://codemirror.net/6/docs/ref/#search.SearchCursor.next) has been called and when
57
- `done` is false).
58
- */
59
- this.value = {
60
- from: 0,
61
- to: 0
62
- };
63
- /**
64
- Whether the end of the iterated region has been reached.
65
- */
66
- this.done = false;
67
- this.matches = [];
68
- this.buffer = "";
69
- this.bufferPos = 0;
70
- this.iter = text.iterRange(from, to);
71
- this.bufferStart = from;
72
- this.normalize = normalize ? (x) => normalize(basicNormalize(x)) : basicNormalize;
73
- this.query = this.normalize(query);
74
- }
75
- peek() {
76
- if (this.bufferPos == this.buffer.length) {
77
- this.bufferStart += this.buffer.length;
78
- this.iter.next();
79
- if (this.iter.done) return -1;
80
- this.bufferPos = 0;
81
- this.buffer = this.iter.value;
82
- }
83
- return codePointAt(this.buffer, this.bufferPos);
84
- }
85
- /**
86
- Look for the next match. Updates the iterator's
87
- [`value`](https://codemirror.net/6/docs/ref/#search.SearchCursor.value) and
88
- [`done`](https://codemirror.net/6/docs/ref/#search.SearchCursor.done) properties. Should be called
89
- at least once before using the cursor.
90
- */
91
- next() {
92
- while (this.matches.length) this.matches.pop();
93
- return this.nextOverlapping();
94
- }
95
- /**
96
- The `next` method will ignore matches that partially overlap a
97
- previous match. This method behaves like `next`, but includes
98
- such matches.
99
- */
100
- nextOverlapping() {
101
- for (;;) {
102
- let next = this.peek();
103
- if (next < 0) {
104
- this.done = true;
105
- return this;
106
- }
107
- let str = fromCodePoint(next), start = this.bufferStart + this.bufferPos;
108
- this.bufferPos += codePointSize(next);
109
- let norm = this.normalize(str);
110
- if (norm.length) for (let i = 0, pos = start;; i++) {
111
- let code = norm.charCodeAt(i);
112
- let match = this.match(code, pos, this.bufferPos + this.bufferStart);
113
- if (i == norm.length - 1) {
114
- if (match) {
115
- this.value = match;
116
- return this;
117
- }
118
- break;
119
- }
120
- if (pos == start && i < str.length && str.charCodeAt(i) == code) pos++;
121
- }
122
- }
123
- }
124
- match(code, pos, end) {
125
- let match = null;
126
- for (let i = 0; i < this.matches.length; i += 2) {
127
- let index = this.matches[i], keep = false;
128
- if (this.query.charCodeAt(index) == code) if (index == this.query.length - 1) match = {
129
- from: this.matches[i + 1],
130
- to: end
131
- };
132
- else {
133
- this.matches[i]++;
134
- keep = true;
135
- }
136
- if (!keep) {
137
- this.matches.splice(i, 2);
138
- i -= 2;
139
- }
140
- }
141
- if (this.query.charCodeAt(0) == code) if (this.query.length == 1) match = {
142
- from: pos,
143
- to: end
144
- };
145
- else this.matches.push(1, pos);
146
- if (match && this.test && !this.test(match.from, match.to, this.buffer, this.bufferStart)) match = null;
147
- return match;
148
- }
149
- };
150
- if (typeof Symbol != "undefined") SearchCursor.prototype[Symbol.iterator] = function() {
151
- return this;
152
- };
153
- const empty = {
154
- from: -1,
155
- to: -1,
156
- match: /* @__PURE__ */ /.*/.exec("")
157
- };
158
- const baseFlags = "gm" + (/x/.unicode == null ? "" : "u");
159
- /**
160
- This class is similar to [`SearchCursor`](https://codemirror.net/6/docs/ref/#search.SearchCursor)
161
- but searches for a regular expression pattern instead of a plain
162
- string.
163
- */
164
- var RegExpCursor = class {
165
- /**
166
- Create a cursor that will search the given range in the given
167
- document. `query` should be the raw pattern (as you'd pass it to
168
- `new RegExp`).
169
- */
170
- constructor(text, query, options, from = 0, to = text.length) {
171
- this.text = text;
172
- this.to = to;
173
- this.curLine = "";
174
- /**
175
- Set to `true` when the cursor has reached the end of the search
176
- range.
177
- */
178
- this.done = false;
179
- /**
180
- Will contain an object with the extent of the match and the
181
- match object when [`next`](https://codemirror.net/6/docs/ref/#search.RegExpCursor.next)
182
- sucessfully finds a match.
183
- */
184
- this.value = empty;
185
- if (/\\[sWDnr]|\n|\r|\[\^/.test(query)) return new MultilineRegExpCursor(text, query, options, from, to);
186
- this.re = new RegExp(query, baseFlags + ((options === null || options === void 0 ? void 0 : options.ignoreCase) ? "i" : ""));
187
- this.test = options === null || options === void 0 ? void 0 : options.test;
188
- this.iter = text.iter();
189
- this.curLineStart = text.lineAt(from).from;
190
- this.matchPos = toCharEnd(text, from);
191
- this.getLine(this.curLineStart);
192
- }
193
- getLine(skip) {
194
- this.iter.next(skip);
195
- if (this.iter.lineBreak) this.curLine = "";
196
- else {
197
- this.curLine = this.iter.value;
198
- if (this.curLineStart + this.curLine.length > this.to) this.curLine = this.curLine.slice(0, this.to - this.curLineStart);
199
- this.iter.next();
200
- }
201
- }
202
- nextLine() {
203
- this.curLineStart = this.curLineStart + this.curLine.length + 1;
204
- if (this.curLineStart > this.to) this.curLine = "";
205
- else this.getLine(0);
206
- }
207
- /**
208
- Move to the next match, if there is one.
209
- */
210
- next() {
211
- for (let off = this.matchPos - this.curLineStart;;) {
212
- this.re.lastIndex = off;
213
- let match = this.matchPos <= this.to && this.re.exec(this.curLine);
214
- if (match) {
215
- let from = this.curLineStart + match.index, to = from + match[0].length;
216
- this.matchPos = toCharEnd(this.text, to + (from == to ? 1 : 0));
217
- if (from == this.curLineStart + this.curLine.length) this.nextLine();
218
- if ((from < to || from > this.value.to) && (!this.test || this.test(from, to, match))) {
219
- this.value = {
220
- from,
221
- to,
222
- match
223
- };
224
- return this;
225
- }
226
- off = this.matchPos - this.curLineStart;
227
- } else if (this.curLineStart + this.curLine.length < this.to) {
228
- this.nextLine();
229
- off = 0;
230
- } else {
231
- this.done = true;
232
- return this;
233
- }
234
- }
235
- }
236
- };
237
- const flattened = /* @__PURE__ */ new WeakMap();
238
- var FlattenedDoc = class FlattenedDoc {
239
- constructor(from, text) {
240
- this.from = from;
241
- this.text = text;
242
- }
243
- get to() {
244
- return this.from + this.text.length;
245
- }
246
- static get(doc, from, to) {
247
- let cached = flattened.get(doc);
248
- if (!cached || cached.from >= to || cached.to <= from) {
249
- let flat = new FlattenedDoc(from, doc.sliceString(from, to));
250
- flattened.set(doc, flat);
251
- return flat;
252
- }
253
- if (cached.from == from && cached.to == to) return cached;
254
- let { text, from: cachedFrom } = cached;
255
- if (cachedFrom > from) {
256
- text = doc.sliceString(from, cachedFrom) + text;
257
- cachedFrom = from;
258
- }
259
- if (cached.to < to) text += doc.sliceString(cached.to, to);
260
- flattened.set(doc, new FlattenedDoc(cachedFrom, text));
261
- return new FlattenedDoc(from, text.slice(from - cachedFrom, to - cachedFrom));
262
- }
263
- };
264
- var MultilineRegExpCursor = class {
265
- constructor(text, query, options, from, to) {
266
- this.text = text;
267
- this.to = to;
268
- this.done = false;
269
- this.value = empty;
270
- this.matchPos = toCharEnd(text, from);
271
- this.re = new RegExp(query, baseFlags + ((options === null || options === void 0 ? void 0 : options.ignoreCase) ? "i" : ""));
272
- this.test = options === null || options === void 0 ? void 0 : options.test;
273
- this.flat = FlattenedDoc.get(text, from, this.chunkEnd(from + 5e3));
274
- }
275
- chunkEnd(pos) {
276
- return pos >= this.to ? this.to : this.text.lineAt(pos).to;
277
- }
278
- next() {
279
- for (;;) {
280
- let off = this.re.lastIndex = this.matchPos - this.flat.from;
281
- let match = this.re.exec(this.flat.text);
282
- if (match && !match[0] && match.index == off) {
283
- this.re.lastIndex = off + 1;
284
- match = this.re.exec(this.flat.text);
285
- }
286
- if (match) {
287
- let from = this.flat.from + match.index, to = from + match[0].length;
288
- if ((this.flat.to >= this.to || match.index + match[0].length <= this.flat.text.length - 10) && (!this.test || this.test(from, to, match))) {
289
- this.value = {
290
- from,
291
- to,
292
- match
293
- };
294
- this.matchPos = toCharEnd(this.text, to + (from == to ? 1 : 0));
295
- return this;
296
- }
297
- }
298
- if (this.flat.to == this.to) {
299
- this.done = true;
300
- return this;
301
- }
302
- this.flat = FlattenedDoc.get(this.text, this.flat.from, this.chunkEnd(this.flat.from + this.flat.text.length * 2));
303
- }
304
- }
305
- };
306
- if (typeof Symbol != "undefined") RegExpCursor.prototype[Symbol.iterator] = MultilineRegExpCursor.prototype[Symbol.iterator] = function() {
307
- return this;
308
- };
309
- function validRegExp(source) {
310
- try {
311
- new RegExp(source, baseFlags);
312
- return true;
313
- } catch (_a) {
314
- return false;
315
- }
316
- }
317
- function toCharEnd(text, pos) {
318
- if (pos >= text.length) return pos;
319
- let line = text.lineAt(pos), next;
320
- while (pos < line.to && (next = line.text.charCodeAt(pos - line.from)) >= 56320 && next < 57344) pos++;
321
- return pos;
322
- }
323
- function createLineDialog(view) {
324
- let line = String(view.state.doc.lineAt(view.state.selection.main.head).number);
325
- let input = crelt("input", {
326
- class: "cm-textfield",
327
- name: "line",
328
- value: line
329
- });
330
- let dom = crelt("form", {
331
- class: "cm-gotoLine",
332
- onkeydown: (event) => {
333
- if (event.keyCode == 27) {
334
- event.preventDefault();
335
- view.dispatch({ effects: dialogEffect.of(false) });
336
- view.focus();
337
- } else if (event.keyCode == 13) {
338
- event.preventDefault();
339
- go();
340
- }
341
- },
342
- onsubmit: (event) => {
343
- event.preventDefault();
344
- go();
345
- }
346
- }, crelt("label", view.state.phrase("Go to line"), ": ", input), " ", crelt("button", {
347
- class: "cm-button",
348
- type: "submit"
349
- }, view.state.phrase("go")), crelt("button", {
350
- name: "close",
351
- onclick: () => {
352
- view.dispatch({ effects: dialogEffect.of(false) });
353
- view.focus();
354
- },
355
- "aria-label": view.state.phrase("close"),
356
- type: "button"
357
- }, ["×"]));
358
- function go() {
359
- let match = /^([+-])?(\d+)?(:\d+)?(%)?$/.exec(input.value);
360
- if (!match) return;
361
- let { state } = view, startLine = state.doc.lineAt(state.selection.main.head);
362
- let [, sign, ln, cl, percent] = match;
363
- let col = cl ? +cl.slice(1) : 0;
364
- let line$1 = ln ? +ln : startLine.number;
365
- if (ln && percent) {
366
- let pc = line$1 / 100;
367
- if (sign) pc = pc * (sign == "-" ? -1 : 1) + startLine.number / state.doc.lines;
368
- line$1 = Math.round(state.doc.lines * pc);
369
- } else if (ln && sign) line$1 = line$1 * (sign == "-" ? -1 : 1) + startLine.number;
370
- let docLine = state.doc.line(Math.max(1, Math.min(state.doc.lines, line$1)));
371
- let selection = EditorSelection.cursor(docLine.from + Math.max(0, Math.min(col, docLine.length)));
372
- view.dispatch({
373
- effects: [dialogEffect.of(false), EditorView.scrollIntoView(selection.from, { y: "center" })],
374
- selection
375
- });
376
- view.focus();
377
- }
378
- return { dom };
379
- }
380
- const dialogEffect = /* @__PURE__ */ StateEffect.define();
381
- const dialogField = /* @__PURE__ */ StateField.define({
382
- create() {
383
- return true;
384
- },
385
- update(value, tr) {
386
- for (let e of tr.effects) if (e.is(dialogEffect)) value = e.value;
387
- return value;
388
- },
389
- provide: (f) => showPanel.from(f, (val) => val ? createLineDialog : null)
390
- });
391
- /**
392
- Command that shows a dialog asking the user for a line number, and
393
- when a valid position is provided, moves the cursor to that line.
394
-
395
- Supports line numbers, relative line offsets prefixed with `+` or
396
- `-`, document percentages suffixed with `%`, and an optional
397
- column position by adding `:` and a second number after the line
398
- number.
399
- */
400
- const gotoLine = (view) => {
401
- let panel = getPanel(view, createLineDialog);
402
- if (!panel) {
403
- let effects = [dialogEffect.of(true)];
404
- if (view.state.field(dialogField, false) == null) effects.push(StateEffect.appendConfig.of([dialogField, baseTheme$1$1]));
405
- view.dispatch({ effects });
406
- panel = getPanel(view, createLineDialog);
407
- }
408
- if (panel) panel.dom.querySelector("input").select();
409
- return true;
410
- };
411
- const baseTheme$1$1 = /* @__PURE__ */ EditorView.baseTheme({ ".cm-panel.cm-gotoLine": {
412
- padding: "2px 6px 4px",
413
- position: "relative",
414
- "& label": { fontSize: "80%" },
415
- "& [name=close]": {
416
- position: "absolute",
417
- top: "0",
418
- bottom: "0",
419
- right: "4px",
420
- backgroundColor: "inherit",
421
- border: "none",
422
- font: "inherit",
423
- padding: "0"
424
- }
425
- } });
426
- const selectWord = ({ state, dispatch }) => {
427
- let { selection } = state;
428
- let newSel = EditorSelection.create(selection.ranges.map((range) => state.wordAt(range.head) || EditorSelection.cursor(range.head)), selection.mainIndex);
429
- if (newSel.eq(selection)) return false;
430
- dispatch(state.update({ selection: newSel }));
431
- return true;
432
- };
433
- function findNextOccurrence(state, query) {
434
- let { main, ranges } = state.selection;
435
- let word = state.wordAt(main.head), fullWord = word && word.from == main.from && word.to == main.to;
436
- for (let cycled = false, cursor = new SearchCursor(state.doc, query, ranges[ranges.length - 1].to);;) {
437
- cursor.next();
438
- if (cursor.done) {
439
- if (cycled) return null;
440
- cursor = new SearchCursor(state.doc, query, 0, Math.max(0, ranges[ranges.length - 1].from - 1));
441
- cycled = true;
442
- } else {
443
- if (cycled && ranges.some((r) => r.from == cursor.value.from)) continue;
444
- if (fullWord) {
445
- let word$1 = state.wordAt(cursor.value.from);
446
- if (!word$1 || word$1.from != cursor.value.from || word$1.to != cursor.value.to) continue;
447
- }
448
- return cursor.value;
449
- }
450
- }
451
- }
452
- /**
453
- Select next occurrence of the current selection. Expand selection
454
- to the surrounding word when the selection is empty.
455
- */
456
- const selectNextOccurrence = ({ state, dispatch }) => {
457
- let { ranges } = state.selection;
458
- if (ranges.some((sel) => sel.from === sel.to)) return selectWord({
459
- state,
460
- dispatch
461
- });
462
- let searchedText = state.sliceDoc(ranges[0].from, ranges[0].to);
463
- if (state.selection.ranges.some((r) => state.sliceDoc(r.from, r.to) != searchedText)) return false;
464
- let range = findNextOccurrence(state, searchedText);
465
- if (!range) return false;
466
- dispatch(state.update({
467
- selection: state.selection.addRange(EditorSelection.range(range.from, range.to), false),
468
- effects: EditorView.scrollIntoView(range.to)
469
- }));
470
- return true;
471
- };
472
- const searchConfigFacet = /* @__PURE__ */ Facet.define({ combine(configs) {
473
- return combineConfig(configs, {
474
- top: false,
475
- caseSensitive: false,
476
- literal: false,
477
- regexp: false,
478
- wholeWord: false,
479
- createPanel: (view) => new SearchPanel(view),
480
- scrollToMatch: (range) => EditorView.scrollIntoView(range)
481
- });
482
- } });
483
- /**
484
- A search query. Part of the editor's search state.
485
- */
486
- var SearchQuery = class {
487
- /**
488
- Create a query object.
489
- */
490
- constructor(config$1) {
491
- this.search = config$1.search;
492
- this.caseSensitive = !!config$1.caseSensitive;
493
- this.literal = !!config$1.literal;
494
- this.regexp = !!config$1.regexp;
495
- this.replace = config$1.replace || "";
496
- this.valid = !!this.search && (!this.regexp || validRegExp(this.search));
497
- this.unquoted = this.unquote(this.search);
498
- this.wholeWord = !!config$1.wholeWord;
499
- }
500
- /**
501
- @internal
502
- */
503
- unquote(text) {
504
- return this.literal ? text : text.replace(/\\([nrt\\])/g, (_, ch) => ch == "n" ? "\n" : ch == "r" ? "\r" : ch == "t" ? " " : "\\");
505
- }
506
- /**
507
- Compare this query to another query.
508
- */
509
- eq(other) {
510
- return this.search == other.search && this.replace == other.replace && this.caseSensitive == other.caseSensitive && this.regexp == other.regexp && this.wholeWord == other.wholeWord;
511
- }
512
- /**
513
- @internal
514
- */
515
- create() {
516
- return this.regexp ? new RegExpQuery(this) : new StringQuery(this);
517
- }
518
- /**
519
- Get a search cursor for this query, searching through the given
520
- range in the given state.
521
- */
522
- getCursor(state, from = 0, to) {
523
- let st = state.doc ? state : EditorState.create({ doc: state });
524
- if (to == null) to = st.doc.length;
525
- return this.regexp ? regexpCursor(this, st, from, to) : stringCursor(this, st, from, to);
526
- }
527
- };
528
- var QueryType = class {
529
- constructor(spec) {
530
- this.spec = spec;
531
- }
532
- };
533
- function stringCursor(spec, state, from, to) {
534
- return new SearchCursor(state.doc, spec.unquoted, from, to, spec.caseSensitive ? void 0 : (x) => x.toLowerCase(), spec.wholeWord ? stringWordTest(state.doc, state.charCategorizer(state.selection.main.head)) : void 0);
535
- }
536
- function stringWordTest(doc, categorizer) {
537
- return (from, to, buf, bufPos) => {
538
- if (bufPos > from || bufPos + buf.length < to) {
539
- bufPos = Math.max(0, from - 2);
540
- buf = doc.sliceString(bufPos, Math.min(doc.length, to + 2));
541
- }
542
- return (categorizer(charBefore(buf, from - bufPos)) != CharCategory.Word || categorizer(charAfter(buf, from - bufPos)) != CharCategory.Word) && (categorizer(charAfter(buf, to - bufPos)) != CharCategory.Word || categorizer(charBefore(buf, to - bufPos)) != CharCategory.Word);
543
- };
544
- }
545
- var StringQuery = class extends QueryType {
546
- constructor(spec) {
547
- super(spec);
548
- }
549
- nextMatch(state, curFrom, curTo) {
550
- let cursor = stringCursor(this.spec, state, curTo, state.doc.length).nextOverlapping();
551
- if (cursor.done) {
552
- let end = Math.min(state.doc.length, curFrom + this.spec.unquoted.length);
553
- cursor = stringCursor(this.spec, state, 0, end).nextOverlapping();
554
- }
555
- return cursor.done || cursor.value.from == curFrom && cursor.value.to == curTo ? null : cursor.value;
556
- }
557
- prevMatchInRange(state, from, to) {
558
- for (let pos = to;;) {
559
- let start = Math.max(from, pos - 1e4 - this.spec.unquoted.length);
560
- let cursor = stringCursor(this.spec, state, start, pos), range = null;
561
- while (!cursor.nextOverlapping().done) range = cursor.value;
562
- if (range) return range;
563
- if (start == from) return null;
564
- pos -= 1e4;
565
- }
566
- }
567
- prevMatch(state, curFrom, curTo) {
568
- let found = this.prevMatchInRange(state, 0, curFrom);
569
- if (!found) found = this.prevMatchInRange(state, Math.max(0, curTo - this.spec.unquoted.length), state.doc.length);
570
- return found && (found.from != curFrom || found.to != curTo) ? found : null;
571
- }
572
- getReplacement(_result) {
573
- return this.spec.unquote(this.spec.replace);
574
- }
575
- matchAll(state, limit) {
576
- let cursor = stringCursor(this.spec, state, 0, state.doc.length), ranges = [];
577
- while (!cursor.next().done) {
578
- if (ranges.length >= limit) return null;
579
- ranges.push(cursor.value);
580
- }
581
- return ranges;
582
- }
583
- highlight(state, from, to, add$1) {
584
- let cursor = stringCursor(this.spec, state, Math.max(0, from - this.spec.unquoted.length), Math.min(to + this.spec.unquoted.length, state.doc.length));
585
- while (!cursor.next().done) add$1(cursor.value.from, cursor.value.to);
586
- }
587
- };
588
- function regexpCursor(spec, state, from, to) {
589
- return new RegExpCursor(state.doc, spec.search, {
590
- ignoreCase: !spec.caseSensitive,
591
- test: spec.wholeWord ? regexpWordTest(state.charCategorizer(state.selection.main.head)) : void 0
592
- }, from, to);
593
- }
594
- function charBefore(str, index) {
595
- return str.slice(findClusterBreak(str, index, false), index);
596
- }
597
- function charAfter(str, index) {
598
- return str.slice(index, findClusterBreak(str, index));
599
- }
600
- function regexpWordTest(categorizer) {
601
- return (_from, _to, match) => !match[0].length || (categorizer(charBefore(match.input, match.index)) != CharCategory.Word || categorizer(charAfter(match.input, match.index)) != CharCategory.Word) && (categorizer(charAfter(match.input, match.index + match[0].length)) != CharCategory.Word || categorizer(charBefore(match.input, match.index + match[0].length)) != CharCategory.Word);
602
- }
603
- var RegExpQuery = class extends QueryType {
604
- nextMatch(state, curFrom, curTo) {
605
- let cursor = regexpCursor(this.spec, state, curTo, state.doc.length).next();
606
- if (cursor.done) cursor = regexpCursor(this.spec, state, 0, curFrom).next();
607
- return cursor.done ? null : cursor.value;
608
- }
609
- prevMatchInRange(state, from, to) {
610
- for (let size = 1;; size++) {
611
- let start = Math.max(from, to - size * 1e4);
612
- let cursor = regexpCursor(this.spec, state, start, to), range = null;
613
- while (!cursor.next().done) range = cursor.value;
614
- if (range && (start == from || range.from > start + 10)) return range;
615
- if (start == from) return null;
616
- }
617
- }
618
- prevMatch(state, curFrom, curTo) {
619
- return this.prevMatchInRange(state, 0, curFrom) || this.prevMatchInRange(state, curTo, state.doc.length);
620
- }
621
- getReplacement(result) {
622
- return this.spec.unquote(this.spec.replace).replace(/\$([$&]|\d+)/g, (m, i) => {
623
- if (i == "&") return result.match[0];
624
- if (i == "$") return "$";
625
- for (let l = i.length; l > 0; l--) {
626
- let n = +i.slice(0, l);
627
- if (n > 0 && n < result.match.length) return result.match[n] + i.slice(l);
628
- }
629
- return m;
630
- });
631
- }
632
- matchAll(state, limit) {
633
- let cursor = regexpCursor(this.spec, state, 0, state.doc.length), ranges = [];
634
- while (!cursor.next().done) {
635
- if (ranges.length >= limit) return null;
636
- ranges.push(cursor.value);
637
- }
638
- return ranges;
639
- }
640
- highlight(state, from, to, add$1) {
641
- let cursor = regexpCursor(this.spec, state, Math.max(0, from - 250), Math.min(to + 250, state.doc.length));
642
- while (!cursor.next().done) add$1(cursor.value.from, cursor.value.to);
643
- }
644
- };
645
- /**
646
- A state effect that updates the current search query. Note that
647
- this only has an effect if the search state has been initialized
648
- (by including [`search`](https://codemirror.net/6/docs/ref/#search.search) in your configuration or
649
- by running [`openSearchPanel`](https://codemirror.net/6/docs/ref/#search.openSearchPanel) at least
650
- once).
651
- */
652
- const setSearchQuery = /* @__PURE__ */ StateEffect.define();
653
- const togglePanel$1 = /* @__PURE__ */ StateEffect.define();
654
- const searchState = /* @__PURE__ */ StateField.define({
655
- create(state) {
656
- return new SearchState(defaultQuery(state).create(), null);
657
- },
658
- update(value, tr) {
659
- for (let effect of tr.effects) if (effect.is(setSearchQuery)) value = new SearchState(effect.value.create(), value.panel);
660
- else if (effect.is(togglePanel$1)) value = new SearchState(value.query, effect.value ? createSearchPanel : null);
661
- return value;
662
- },
663
- provide: (f) => showPanel.from(f, (val) => val.panel)
664
- });
665
- var SearchState = class {
666
- constructor(query, panel) {
667
- this.query = query;
668
- this.panel = panel;
669
- }
670
- };
671
- const matchMark = /* @__PURE__ */ Decoration.mark({ class: "cm-searchMatch" }), selectedMatchMark = /* @__PURE__ */ Decoration.mark({ class: "cm-searchMatch cm-searchMatch-selected" });
672
- const searchHighlighter = /* @__PURE__ */ ViewPlugin.fromClass(class {
673
- constructor(view) {
674
- this.view = view;
675
- this.decorations = this.highlight(view.state.field(searchState));
676
- }
677
- update(update) {
678
- let state = update.state.field(searchState);
679
- if (state != update.startState.field(searchState) || update.docChanged || update.selectionSet || update.viewportChanged) this.decorations = this.highlight(state);
680
- }
681
- highlight({ query, panel }) {
682
- if (!panel || !query.spec.valid) return Decoration.none;
683
- let { view } = this;
684
- let builder = new RangeSetBuilder();
685
- for (let i = 0, ranges = view.visibleRanges, l = ranges.length; i < l; i++) {
686
- let { from, to } = ranges[i];
687
- while (i < l - 1 && to > ranges[i + 1].from - 500) to = ranges[++i].to;
688
- query.highlight(view.state, from, to, (from$1, to$1) => {
689
- let selected = view.state.selection.ranges.some((r) => r.from == from$1 && r.to == to$1);
690
- builder.add(from$1, to$1, selected ? selectedMatchMark : matchMark);
691
- });
692
- }
693
- return builder.finish();
694
- }
695
- }, { decorations: (v) => v.decorations });
696
- function searchCommand(f) {
697
- return (view) => {
698
- let state = view.state.field(searchState, false);
699
- return state && state.query.spec.valid ? f(view, state) : openSearchPanel(view);
700
- };
701
- }
702
- /**
703
- Open the search panel if it isn't already open, and move the
704
- selection to the first match after the current main selection.
705
- Will wrap around to the start of the document when it reaches the
706
- end.
707
- */
708
- const findNext = /* @__PURE__ */ searchCommand((view, { query }) => {
709
- let { to } = view.state.selection.main;
710
- let next = query.nextMatch(view.state, to, to);
711
- if (!next) return false;
712
- let selection = EditorSelection.single(next.from, next.to);
713
- let config$1 = view.state.facet(searchConfigFacet);
714
- view.dispatch({
715
- selection,
716
- effects: [announceMatch(view, next), config$1.scrollToMatch(selection.main, view)],
717
- userEvent: "select.search"
718
- });
719
- selectSearchInput(view);
720
- return true;
721
- });
722
- /**
723
- Move the selection to the previous instance of the search query,
724
- before the current main selection. Will wrap past the start
725
- of the document to start searching at the end again.
726
- */
727
- const findPrevious = /* @__PURE__ */ searchCommand((view, { query }) => {
728
- let { state } = view, { from } = state.selection.main;
729
- let prev = query.prevMatch(state, from, from);
730
- if (!prev) return false;
731
- let selection = EditorSelection.single(prev.from, prev.to);
732
- let config$1 = view.state.facet(searchConfigFacet);
733
- view.dispatch({
734
- selection,
735
- effects: [announceMatch(view, prev), config$1.scrollToMatch(selection.main, view)],
736
- userEvent: "select.search"
737
- });
738
- selectSearchInput(view);
739
- return true;
740
- });
741
- /**
742
- Select all instances of the search query.
743
- */
744
- const selectMatches = /* @__PURE__ */ searchCommand((view, { query }) => {
745
- let ranges = query.matchAll(view.state, 1e3);
746
- if (!ranges || !ranges.length) return false;
747
- view.dispatch({
748
- selection: EditorSelection.create(ranges.map((r) => EditorSelection.range(r.from, r.to))),
749
- userEvent: "select.search.matches"
750
- });
751
- return true;
752
- });
753
- /**
754
- Select all instances of the currently selected text.
755
- */
756
- const selectSelectionMatches = ({ state, dispatch }) => {
757
- let sel = state.selection;
758
- if (sel.ranges.length > 1 || sel.main.empty) return false;
759
- let { from, to } = sel.main;
760
- let ranges = [], main = 0;
761
- for (let cur$1 = new SearchCursor(state.doc, state.sliceDoc(from, to)); !cur$1.next().done;) {
762
- if (ranges.length > 1e3) return false;
763
- if (cur$1.value.from == from) main = ranges.length;
764
- ranges.push(EditorSelection.range(cur$1.value.from, cur$1.value.to));
765
- }
766
- dispatch(state.update({
767
- selection: EditorSelection.create(ranges, main),
768
- userEvent: "select.search.matches"
769
- }));
770
- return true;
771
- };
772
- /**
773
- Replace the current match of the search query.
774
- */
775
- const replaceNext = /* @__PURE__ */ searchCommand((view, { query }) => {
776
- let { state } = view, { from, to } = state.selection.main;
777
- if (state.readOnly) return false;
778
- let match = query.nextMatch(state, from, from);
779
- if (!match) return false;
780
- let next = match;
781
- let changes = [], selection, replacement;
782
- let effects = [];
783
- if (next.from == from && next.to == to) {
784
- replacement = state.toText(query.getReplacement(next));
785
- changes.push({
786
- from: next.from,
787
- to: next.to,
788
- insert: replacement
789
- });
790
- next = query.nextMatch(state, next.from, next.to);
791
- effects.push(EditorView.announce.of(state.phrase("replaced match on line $", state.doc.lineAt(from).number) + "."));
792
- }
793
- let changeSet = view.state.changes(changes);
794
- if (next) {
795
- selection = EditorSelection.single(next.from, next.to).map(changeSet);
796
- effects.push(announceMatch(view, next));
797
- effects.push(state.facet(searchConfigFacet).scrollToMatch(selection.main, view));
798
- }
799
- view.dispatch({
800
- changes: changeSet,
801
- selection,
802
- effects,
803
- userEvent: "input.replace"
804
- });
805
- return true;
806
- });
807
- /**
808
- Replace all instances of the search query with the given
809
- replacement.
810
- */
811
- const replaceAll = /* @__PURE__ */ searchCommand((view, { query }) => {
812
- if (view.state.readOnly) return false;
813
- let changes = query.matchAll(view.state, 1e9).map((match) => {
814
- let { from, to } = match;
815
- return {
816
- from,
817
- to,
818
- insert: query.getReplacement(match)
819
- };
820
- });
821
- if (!changes.length) return false;
822
- let announceText = view.state.phrase("replaced $ matches", changes.length) + ".";
823
- view.dispatch({
824
- changes,
825
- effects: EditorView.announce.of(announceText),
826
- userEvent: "input.replace.all"
827
- });
828
- return true;
829
- });
830
- function createSearchPanel(view) {
831
- return view.state.facet(searchConfigFacet).createPanel(view);
832
- }
833
- function defaultQuery(state, fallback) {
834
- var _a, _b, _c, _d, _e;
835
- let sel = state.selection.main;
836
- let selText = sel.empty || sel.to > sel.from + 100 ? "" : state.sliceDoc(sel.from, sel.to);
837
- if (fallback && !selText) return fallback;
838
- let config$1 = state.facet(searchConfigFacet);
839
- return new SearchQuery({
840
- search: ((_a = fallback === null || fallback === void 0 ? void 0 : fallback.literal) !== null && _a !== void 0 ? _a : config$1.literal) ? selText : selText.replace(/\n/g, "\\n"),
841
- caseSensitive: (_b = fallback === null || fallback === void 0 ? void 0 : fallback.caseSensitive) !== null && _b !== void 0 ? _b : config$1.caseSensitive,
842
- literal: (_c = fallback === null || fallback === void 0 ? void 0 : fallback.literal) !== null && _c !== void 0 ? _c : config$1.literal,
843
- regexp: (_d = fallback === null || fallback === void 0 ? void 0 : fallback.regexp) !== null && _d !== void 0 ? _d : config$1.regexp,
844
- wholeWord: (_e = fallback === null || fallback === void 0 ? void 0 : fallback.wholeWord) !== null && _e !== void 0 ? _e : config$1.wholeWord
845
- });
846
- }
847
- function getSearchInput(view) {
848
- let panel = getPanel(view, createSearchPanel);
849
- return panel && panel.dom.querySelector("[main-field]");
850
- }
851
- function selectSearchInput(view) {
852
- let input = getSearchInput(view);
853
- if (input && input == view.root.activeElement) input.select();
854
- }
855
- /**
856
- Make sure the search panel is open and focused.
857
- */
858
- const openSearchPanel = (view) => {
859
- let state = view.state.field(searchState, false);
860
- if (state && state.panel) {
861
- let searchInput = getSearchInput(view);
862
- if (searchInput && searchInput != view.root.activeElement) {
863
- let query = defaultQuery(view.state, state.query.spec);
864
- if (query.valid) view.dispatch({ effects: setSearchQuery.of(query) });
865
- searchInput.focus();
866
- searchInput.select();
867
- }
868
- } else view.dispatch({ effects: [togglePanel$1.of(true), state ? setSearchQuery.of(defaultQuery(view.state, state.query.spec)) : StateEffect.appendConfig.of(searchExtensions)] });
869
- return true;
870
- };
871
- /**
872
- Close the search panel.
873
- */
874
- const closeSearchPanel = (view) => {
875
- let state = view.state.field(searchState, false);
876
- if (!state || !state.panel) return false;
877
- let panel = getPanel(view, createSearchPanel);
878
- if (panel && panel.dom.contains(view.root.activeElement)) view.focus();
879
- view.dispatch({ effects: togglePanel$1.of(false) });
880
- return true;
881
- };
882
- /**
883
- Default search-related key bindings.
884
-
885
- - Mod-f: [`openSearchPanel`](https://codemirror.net/6/docs/ref/#search.openSearchPanel)
886
- - F3, Mod-g: [`findNext`](https://codemirror.net/6/docs/ref/#search.findNext)
887
- - Shift-F3, Shift-Mod-g: [`findPrevious`](https://codemirror.net/6/docs/ref/#search.findPrevious)
888
- - Mod-Alt-g: [`gotoLine`](https://codemirror.net/6/docs/ref/#search.gotoLine)
889
- - Mod-d: [`selectNextOccurrence`](https://codemirror.net/6/docs/ref/#search.selectNextOccurrence)
890
- */
891
- const searchKeymap = [
892
- {
893
- key: "Mod-f",
894
- run: openSearchPanel,
895
- scope: "editor search-panel"
896
- },
897
- {
898
- key: "F3",
899
- run: findNext,
900
- shift: findPrevious,
901
- scope: "editor search-panel",
902
- preventDefault: true
903
- },
904
- {
905
- key: "Mod-g",
906
- run: findNext,
907
- shift: findPrevious,
908
- scope: "editor search-panel",
909
- preventDefault: true
910
- },
911
- {
912
- key: "Escape",
913
- run: closeSearchPanel,
914
- scope: "editor search-panel"
915
- },
916
- {
917
- key: "Mod-Shift-l",
918
- run: selectSelectionMatches
919
- },
920
- {
921
- key: "Mod-Alt-g",
922
- run: gotoLine
923
- },
924
- {
925
- key: "Mod-d",
926
- run: selectNextOccurrence,
927
- preventDefault: true
928
- }
929
- ];
930
- var SearchPanel = class {
931
- constructor(view) {
932
- this.view = view;
933
- let query = this.query = view.state.field(searchState).query.spec;
934
- this.commit = this.commit.bind(this);
935
- this.searchField = crelt("input", {
936
- value: query.search,
937
- placeholder: phrase(view, "Find"),
938
- "aria-label": phrase(view, "Find"),
939
- class: "cm-textfield",
940
- name: "search",
941
- form: "",
942
- "main-field": "true",
943
- onchange: this.commit,
944
- onkeyup: this.commit
945
- });
946
- this.replaceField = crelt("input", {
947
- value: query.replace,
948
- placeholder: phrase(view, "Replace"),
949
- "aria-label": phrase(view, "Replace"),
950
- class: "cm-textfield",
951
- name: "replace",
952
- form: "",
953
- onchange: this.commit,
954
- onkeyup: this.commit
955
- });
956
- this.caseField = crelt("input", {
957
- type: "checkbox",
958
- name: "case",
959
- form: "",
960
- checked: query.caseSensitive,
961
- onchange: this.commit
962
- });
963
- this.reField = crelt("input", {
964
- type: "checkbox",
965
- name: "re",
966
- form: "",
967
- checked: query.regexp,
968
- onchange: this.commit
969
- });
970
- this.wordField = crelt("input", {
971
- type: "checkbox",
972
- name: "word",
973
- form: "",
974
- checked: query.wholeWord,
975
- onchange: this.commit
976
- });
977
- function button(name, onclick, content) {
978
- return crelt("button", {
979
- class: "cm-button",
980
- name,
981
- onclick,
982
- type: "button"
983
- }, content);
984
- }
985
- this.dom = crelt("div", {
986
- onkeydown: (e) => this.keydown(e),
987
- class: "cm-search"
988
- }, [
989
- this.searchField,
990
- button("next", () => findNext(view), [phrase(view, "next")]),
991
- button("prev", () => findPrevious(view), [phrase(view, "previous")]),
992
- button("select", () => selectMatches(view), [phrase(view, "all")]),
993
- crelt("label", null, [this.caseField, phrase(view, "match case")]),
994
- crelt("label", null, [this.reField, phrase(view, "regexp")]),
995
- crelt("label", null, [this.wordField, phrase(view, "by word")]),
996
- ...view.state.readOnly ? [] : [
997
- crelt("br"),
998
- this.replaceField,
999
- button("replace", () => replaceNext(view), [phrase(view, "replace")]),
1000
- button("replaceAll", () => replaceAll(view), [phrase(view, "replace all")])
1001
- ],
1002
- crelt("button", {
1003
- name: "close",
1004
- onclick: () => closeSearchPanel(view),
1005
- "aria-label": phrase(view, "close"),
1006
- type: "button"
1007
- }, ["×"])
1008
- ]);
1009
- }
1010
- commit() {
1011
- let query = new SearchQuery({
1012
- search: this.searchField.value,
1013
- caseSensitive: this.caseField.checked,
1014
- regexp: this.reField.checked,
1015
- wholeWord: this.wordField.checked,
1016
- replace: this.replaceField.value
1017
- });
1018
- if (!query.eq(this.query)) {
1019
- this.query = query;
1020
- this.view.dispatch({ effects: setSearchQuery.of(query) });
1021
- }
1022
- }
1023
- keydown(e) {
1024
- if (runScopeHandlers(this.view, e, "search-panel")) e.preventDefault();
1025
- else if (e.keyCode == 13 && e.target == this.searchField) {
1026
- e.preventDefault();
1027
- (e.shiftKey ? findPrevious : findNext)(this.view);
1028
- } else if (e.keyCode == 13 && e.target == this.replaceField) {
1029
- e.preventDefault();
1030
- replaceNext(this.view);
1031
- }
1032
- }
1033
- update(update) {
1034
- for (let tr of update.transactions) for (let effect of tr.effects) if (effect.is(setSearchQuery) && !effect.value.eq(this.query)) this.setQuery(effect.value);
1035
- }
1036
- setQuery(query) {
1037
- this.query = query;
1038
- this.searchField.value = query.search;
1039
- this.replaceField.value = query.replace;
1040
- this.caseField.checked = query.caseSensitive;
1041
- this.reField.checked = query.regexp;
1042
- this.wordField.checked = query.wholeWord;
1043
- }
1044
- mount() {
1045
- this.searchField.select();
1046
- }
1047
- get pos() {
1048
- return 80;
1049
- }
1050
- get top() {
1051
- return this.view.state.facet(searchConfigFacet).top;
1052
- }
1053
- };
1054
- function phrase(view, phrase$1) {
1055
- return view.state.phrase(phrase$1);
1056
- }
1057
- const AnnounceMargin = 30;
1058
- const Break = /[\s\.,:;?!]/;
1059
- function announceMatch(view, { from, to }) {
1060
- let line = view.state.doc.lineAt(from), lineEnd = view.state.doc.lineAt(to).to;
1061
- let start = Math.max(line.from, from - AnnounceMargin), end = Math.min(lineEnd, to + AnnounceMargin);
1062
- let text = view.state.sliceDoc(start, end);
1063
- if (start != line.from) {
1064
- for (let i = 0; i < AnnounceMargin; i++) if (!Break.test(text[i + 1]) && Break.test(text[i])) {
1065
- text = text.slice(i);
1066
- break;
1067
- }
1068
- }
1069
- if (end != lineEnd) {
1070
- for (let i = text.length - 1; i > text.length - AnnounceMargin; i--) if (!Break.test(text[i - 1]) && Break.test(text[i])) {
1071
- text = text.slice(0, i);
1072
- break;
1073
- }
1074
- }
1075
- return EditorView.announce.of(`${view.state.phrase("current match")}. ${text} ${view.state.phrase("on line")} ${line.number}.`);
1076
- }
1077
- const baseTheme$3 = /* @__PURE__ */ EditorView.baseTheme({
1078
- ".cm-panel.cm-search": {
1079
- padding: "2px 6px 4px",
1080
- position: "relative",
1081
- "& [name=close]": {
1082
- position: "absolute",
1083
- top: "0",
1084
- right: "4px",
1085
- backgroundColor: "inherit",
1086
- border: "none",
1087
- font: "inherit",
1088
- padding: 0,
1089
- margin: 0
1090
- },
1091
- "& input, & button, & label": { margin: ".2em .6em .2em 0" },
1092
- "& input[type=checkbox]": { marginRight: ".2em" },
1093
- "& label": {
1094
- fontSize: "80%",
1095
- whiteSpace: "pre"
1096
- }
1097
- },
1098
- "&light .cm-searchMatch": { backgroundColor: "#ffff0054" },
1099
- "&dark .cm-searchMatch": { backgroundColor: "#00ffff8a" },
1100
- "&light .cm-searchMatch-selected": { backgroundColor: "#ff6a0054" },
1101
- "&dark .cm-searchMatch-selected": { backgroundColor: "#ff00ff8a" }
1102
- });
1103
- const searchExtensions = [
1104
- searchState,
1105
- /* @__PURE__ */ Prec.low(searchHighlighter),
1106
- baseTheme$3
1107
- ];
1108
-
1109
- //#endregion
1110
- //#region ../../node_modules/@codemirror/autocomplete/dist/index.js
1111
- /**
1112
- An instance of this is passed to completion source functions.
1113
- */
1114
- var CompletionContext = class {
1115
- /**
1116
- Create a new completion context. (Mostly useful for testing
1117
- completion sources—in the editor, the extension will create
1118
- these for you.)
1119
- */
1120
- constructor(state, pos, explicit, view) {
1121
- this.state = state;
1122
- this.pos = pos;
1123
- this.explicit = explicit;
1124
- this.view = view;
1125
- /**
1126
- @internal
1127
- */
1128
- this.abortListeners = [];
1129
- /**
1130
- @internal
1131
- */
1132
- this.abortOnDocChange = false;
1133
- }
1134
- /**
1135
- Get the extent, content, and (if there is a token) type of the
1136
- token before `this.pos`.
1137
- */
1138
- tokenBefore(types) {
1139
- let token = syntaxTree(this.state).resolveInner(this.pos, -1);
1140
- while (token && types.indexOf(token.name) < 0) token = token.parent;
1141
- return token ? {
1142
- from: token.from,
1143
- to: this.pos,
1144
- text: this.state.sliceDoc(token.from, this.pos),
1145
- type: token.type
1146
- } : null;
1147
- }
1148
- /**
1149
- Get the match of the given expression directly before the
1150
- cursor.
1151
- */
1152
- matchBefore(expr) {
1153
- let line = this.state.doc.lineAt(this.pos);
1154
- let start = Math.max(line.from, this.pos - 250);
1155
- let str = line.text.slice(start - line.from, this.pos - line.from);
1156
- let found = str.search(ensureAnchor(expr, false));
1157
- return found < 0 ? null : {
1158
- from: start + found,
1159
- to: this.pos,
1160
- text: str.slice(found)
1161
- };
1162
- }
1163
- /**
1164
- Yields true when the query has been aborted. Can be useful in
1165
- asynchronous queries to avoid doing work that will be ignored.
1166
- */
1167
- get aborted() {
1168
- return this.abortListeners == null;
1169
- }
1170
- /**
1171
- Allows you to register abort handlers, which will be called when
1172
- the query is
1173
- [aborted](https://codemirror.net/6/docs/ref/#autocomplete.CompletionContext.aborted).
1174
-
1175
- By default, running queries will not be aborted for regular
1176
- typing or backspacing, on the assumption that they are likely to
1177
- return a result with a
1178
- [`validFor`](https://codemirror.net/6/docs/ref/#autocomplete.CompletionResult.validFor) field that
1179
- allows the result to be used after all. Passing `onDocChange:
1180
- true` will cause this query to be aborted for any document
1181
- change.
1182
- */
1183
- addEventListener(type, listener, options) {
1184
- if (type == "abort" && this.abortListeners) {
1185
- this.abortListeners.push(listener);
1186
- if (options && options.onDocChange) this.abortOnDocChange = true;
1187
- }
1188
- }
1189
- };
1190
- function toSet(chars) {
1191
- let flat = Object.keys(chars).join("");
1192
- let words = /\w/.test(flat);
1193
- if (words) flat = flat.replace(/\w/g, "");
1194
- return `[${words ? "\\w" : ""}${flat.replace(/[^\w\s]/g, "\\$&")}]`;
1195
- }
1196
- function prefixMatch(options) {
1197
- let first = Object.create(null), rest = Object.create(null);
1198
- for (let { label } of options) {
1199
- first[label[0]] = true;
1200
- for (let i = 1; i < label.length; i++) rest[label[i]] = true;
1201
- }
1202
- let source = toSet(first) + toSet(rest) + "*$";
1203
- return [/* @__PURE__ */ new RegExp("^" + source), new RegExp(source)];
1204
- }
1205
- /**
1206
- Given a a fixed array of options, return an autocompleter that
1207
- completes them.
1208
- */
1209
- function completeFromList(list) {
1210
- let options = list.map((o) => typeof o == "string" ? { label: o } : o);
1211
- let [validFor, match] = options.every((o) => /^\w+$/.test(o.label)) ? [/\w*$/, /\w+$/] : prefixMatch(options);
1212
- return (context) => {
1213
- let token = context.matchBefore(match);
1214
- return token || context.explicit ? {
1215
- from: token ? token.from : context.pos,
1216
- options,
1217
- validFor
1218
- } : null;
1219
- };
1220
- }
1221
- var Option = class {
1222
- constructor(completion, source, match, score$1) {
1223
- this.completion = completion;
1224
- this.source = source;
1225
- this.match = match;
1226
- this.score = score$1;
1227
- }
1228
- };
1229
- function cur(state) {
1230
- return state.selection.main.from;
1231
- }
1232
- function ensureAnchor(expr, start) {
1233
- var _a;
1234
- let { source } = expr;
1235
- let addStart = start && source[0] != "^", addEnd = source[source.length - 1] != "$";
1236
- if (!addStart && !addEnd) return expr;
1237
- return new RegExp(`${addStart ? "^" : ""}(?:${source})${addEnd ? "$" : ""}`, (_a = expr.flags) !== null && _a !== void 0 ? _a : expr.ignoreCase ? "i" : "");
1238
- }
1239
- /**
1240
- This annotation is added to transactions that are produced by
1241
- picking a completion.
1242
- */
1243
- const pickedCompletion = /* @__PURE__ */ Annotation.define();
1244
- /**
1245
- Helper function that returns a transaction spec which inserts a
1246
- completion's text in the main selection range, and any other
1247
- selection range that has the same text in front of it.
1248
- */
1249
- function insertCompletionText(state, text, from, to) {
1250
- let { main } = state.selection, fromOff = from - main.from, toOff = to - main.from;
1251
- return Object.assign(Object.assign({}, state.changeByRange((range) => {
1252
- if (range != main && from != to && state.sliceDoc(range.from + fromOff, range.from + toOff) != state.sliceDoc(from, to)) return { range };
1253
- let lines = state.toText(text);
1254
- return {
1255
- changes: {
1256
- from: range.from + fromOff,
1257
- to: to == main.from ? range.to : range.from + toOff,
1258
- insert: lines
1259
- },
1260
- range: EditorSelection.cursor(range.from + fromOff + lines.length)
1261
- };
1262
- })), {
1263
- scrollIntoView: true,
1264
- userEvent: "input.complete"
1265
- });
1266
- }
1267
- const SourceCache = /* @__PURE__ */ new WeakMap();
1268
- function asSource(source) {
1269
- if (!Array.isArray(source)) return source;
1270
- let known = SourceCache.get(source);
1271
- if (!known) SourceCache.set(source, known = completeFromList(source));
1272
- return known;
1273
- }
1274
- const startCompletionEffect = /* @__PURE__ */ StateEffect.define();
1275
- const closeCompletionEffect = /* @__PURE__ */ StateEffect.define();
1276
- var FuzzyMatcher = class {
1277
- constructor(pattern) {
1278
- this.pattern = pattern;
1279
- this.chars = [];
1280
- this.folded = [];
1281
- this.any = [];
1282
- this.precise = [];
1283
- this.byWord = [];
1284
- this.score = 0;
1285
- this.matched = [];
1286
- for (let p = 0; p < pattern.length;) {
1287
- let char = codePointAt(pattern, p), size = codePointSize(char);
1288
- this.chars.push(char);
1289
- let part = pattern.slice(p, p + size), upper = part.toUpperCase();
1290
- this.folded.push(codePointAt(upper == part ? part.toLowerCase() : upper, 0));
1291
- p += size;
1292
- }
1293
- this.astral = pattern.length != this.chars.length;
1294
- }
1295
- ret(score$1, matched) {
1296
- this.score = score$1;
1297
- this.matched = matched;
1298
- return this;
1299
- }
1300
- match(word) {
1301
- if (this.pattern.length == 0) return this.ret(-100, []);
1302
- if (word.length < this.pattern.length) return null;
1303
- let { chars, folded, any, precise, byWord } = this;
1304
- if (chars.length == 1) {
1305
- let first = codePointAt(word, 0), firstSize = codePointSize(first);
1306
- let score$1 = firstSize == word.length ? 0 : -100;
1307
- if (first == chars[0]);
1308
- else if (first == folded[0]) score$1 += -200;
1309
- else return null;
1310
- return this.ret(score$1, [0, firstSize]);
1311
- }
1312
- let direct = word.indexOf(this.pattern);
1313
- if (direct == 0) return this.ret(word.length == this.pattern.length ? 0 : -100, [0, this.pattern.length]);
1314
- let len = chars.length, anyTo = 0;
1315
- if (direct < 0) {
1316
- for (let i = 0, e = Math.min(word.length, 200); i < e && anyTo < len;) {
1317
- let next = codePointAt(word, i);
1318
- if (next == chars[anyTo] || next == folded[anyTo]) any[anyTo++] = i;
1319
- i += codePointSize(next);
1320
- }
1321
- if (anyTo < len) return null;
1322
- }
1323
- let preciseTo = 0;
1324
- let byWordTo = 0, byWordFolded = false;
1325
- let adjacentTo = 0, adjacentStart = -1, adjacentEnd = -1;
1326
- let hasLower = /[a-z]/.test(word), wordAdjacent = true;
1327
- for (let i = 0, e = Math.min(word.length, 200), prevType = 0; i < e && byWordTo < len;) {
1328
- let next = codePointAt(word, i);
1329
- if (direct < 0) {
1330
- if (preciseTo < len && next == chars[preciseTo]) precise[preciseTo++] = i;
1331
- if (adjacentTo < len) if (next == chars[adjacentTo] || next == folded[adjacentTo]) {
1332
- if (adjacentTo == 0) adjacentStart = i;
1333
- adjacentEnd = i + 1;
1334
- adjacentTo++;
1335
- } else adjacentTo = 0;
1336
- }
1337
- let ch, type = next < 255 ? next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 : next >= 65 && next <= 90 ? 1 : 0 : (ch = fromCodePoint(next)) != ch.toLowerCase() ? 1 : ch != ch.toUpperCase() ? 2 : 0;
1338
- if (!i || type == 1 && hasLower || prevType == 0 && type != 0) {
1339
- if (chars[byWordTo] == next || folded[byWordTo] == next && (byWordFolded = true)) byWord[byWordTo++] = i;
1340
- else if (byWord.length) wordAdjacent = false;
1341
- }
1342
- prevType = type;
1343
- i += codePointSize(next);
1344
- }
1345
- if (byWordTo == len && byWord[0] == 0 && wordAdjacent) return this.result(-100 + (byWordFolded ? -200 : 0), byWord, word);
1346
- if (adjacentTo == len && adjacentStart == 0) return this.ret(-200 - word.length + (adjacentEnd == word.length ? 0 : -100), [0, adjacentEnd]);
1347
- if (direct > -1) return this.ret(-700 - word.length, [direct, direct + this.pattern.length]);
1348
- if (adjacentTo == len) return this.ret(-900 - word.length, [adjacentStart, adjacentEnd]);
1349
- if (byWordTo == len) return this.result(-100 + (byWordFolded ? -200 : 0) + -700 + (wordAdjacent ? 0 : -1100), byWord, word);
1350
- return chars.length == 2 ? null : this.result((any[0] ? -700 : 0) + -200 + -1100, any, word);
1351
- }
1352
- result(score$1, positions, word) {
1353
- let result = [], i = 0;
1354
- for (let pos of positions) {
1355
- let to = pos + (this.astral ? codePointSize(codePointAt(word, pos)) : 1);
1356
- if (i && result[i - 1] == pos) result[i - 1] = to;
1357
- else {
1358
- result[i++] = pos;
1359
- result[i++] = to;
1360
- }
1361
- }
1362
- return this.ret(score$1 - word.length, result);
1363
- }
1364
- };
1365
- var StrictMatcher = class {
1366
- constructor(pattern) {
1367
- this.pattern = pattern;
1368
- this.matched = [];
1369
- this.score = 0;
1370
- this.folded = pattern.toLowerCase();
1371
- }
1372
- match(word) {
1373
- if (word.length < this.pattern.length) return null;
1374
- let start = word.slice(0, this.pattern.length);
1375
- let match = start == this.pattern ? 0 : start.toLowerCase() == this.folded ? -200 : null;
1376
- if (match == null) return null;
1377
- this.matched = [0, start.length];
1378
- this.score = match + (word.length == this.pattern.length ? 0 : -100);
1379
- return this;
1380
- }
1381
- };
1382
- const completionConfig = /* @__PURE__ */ Facet.define({ combine(configs) {
1383
- return combineConfig(configs, {
1384
- activateOnTyping: true,
1385
- activateOnCompletion: () => false,
1386
- activateOnTypingDelay: 100,
1387
- selectOnOpen: true,
1388
- override: null,
1389
- closeOnBlur: true,
1390
- maxRenderedOptions: 100,
1391
- defaultKeymap: true,
1392
- tooltipClass: () => "",
1393
- optionClass: () => "",
1394
- aboveCursor: false,
1395
- icons: true,
1396
- addToOptions: [],
1397
- positionInfo: defaultPositionInfo,
1398
- filterStrict: false,
1399
- compareCompletions: (a, b) => a.label.localeCompare(b.label),
1400
- interactionDelay: 75,
1401
- updateSyncTime: 100
1402
- }, {
1403
- defaultKeymap: (a, b) => a && b,
1404
- closeOnBlur: (a, b) => a && b,
1405
- icons: (a, b) => a && b,
1406
- tooltipClass: (a, b) => (c) => joinClass(a(c), b(c)),
1407
- optionClass: (a, b) => (c) => joinClass(a(c), b(c)),
1408
- addToOptions: (a, b) => a.concat(b),
1409
- filterStrict: (a, b) => a || b
1410
- });
1411
- } });
1412
- function joinClass(a, b) {
1413
- return a ? b ? a + " " + b : a : b;
1414
- }
1415
- function defaultPositionInfo(view, list, option, info, space, tooltip) {
1416
- let rtl = view.textDirection == Direction.RTL, left = rtl, narrow = false;
1417
- let side = "top", offset, maxWidth;
1418
- let spaceLeft = list.left - space.left, spaceRight = space.right - list.right;
1419
- let infoWidth = info.right - info.left, infoHeight = info.bottom - info.top;
1420
- if (left && spaceLeft < Math.min(infoWidth, spaceRight)) left = false;
1421
- else if (!left && spaceRight < Math.min(infoWidth, spaceLeft)) left = true;
1422
- if (infoWidth <= (left ? spaceLeft : spaceRight)) {
1423
- offset = Math.max(space.top, Math.min(option.top, space.bottom - infoHeight)) - list.top;
1424
- maxWidth = Math.min(400, left ? spaceLeft : spaceRight);
1425
- } else {
1426
- narrow = true;
1427
- maxWidth = Math.min(400, (rtl ? list.right : space.right - list.left) - 30);
1428
- let spaceBelow = space.bottom - list.bottom;
1429
- if (spaceBelow >= infoHeight || spaceBelow > list.top) offset = option.bottom - list.top;
1430
- else {
1431
- side = "bottom";
1432
- offset = list.bottom - option.top;
1433
- }
1434
- }
1435
- let scaleY = (list.bottom - list.top) / tooltip.offsetHeight;
1436
- let scaleX = (list.right - list.left) / tooltip.offsetWidth;
1437
- return {
1438
- style: `${side}: ${offset / scaleY}px; max-width: ${maxWidth / scaleX}px`,
1439
- class: "cm-completionInfo-" + (narrow ? rtl ? "left-narrow" : "right-narrow" : left ? "left" : "right")
1440
- };
1441
- }
1442
- function optionContent(config$1) {
1443
- let content = config$1.addToOptions.slice();
1444
- if (config$1.icons) content.push({
1445
- render(completion) {
1446
- let icon = document.createElement("div");
1447
- icon.classList.add("cm-completionIcon");
1448
- if (completion.type) icon.classList.add(...completion.type.split(/\s+/g).map((cls) => "cm-completionIcon-" + cls));
1449
- icon.setAttribute("aria-hidden", "true");
1450
- return icon;
1451
- },
1452
- position: 20
1453
- });
1454
- content.push({
1455
- render(completion, _s, _v, match) {
1456
- let labelElt = document.createElement("span");
1457
- labelElt.className = "cm-completionLabel";
1458
- let label = completion.displayLabel || completion.label, off = 0;
1459
- for (let j = 0; j < match.length;) {
1460
- let from = match[j++], to = match[j++];
1461
- if (from > off) labelElt.appendChild(document.createTextNode(label.slice(off, from)));
1462
- let span = labelElt.appendChild(document.createElement("span"));
1463
- span.appendChild(document.createTextNode(label.slice(from, to)));
1464
- span.className = "cm-completionMatchedText";
1465
- off = to;
1466
- }
1467
- if (off < label.length) labelElt.appendChild(document.createTextNode(label.slice(off)));
1468
- return labelElt;
1469
- },
1470
- position: 50
1471
- }, {
1472
- render(completion) {
1473
- if (!completion.detail) return null;
1474
- let detailElt = document.createElement("span");
1475
- detailElt.className = "cm-completionDetail";
1476
- detailElt.textContent = completion.detail;
1477
- return detailElt;
1478
- },
1479
- position: 80
1480
- });
1481
- return content.sort((a, b) => a.position - b.position).map((a) => a.render);
1482
- }
1483
- function rangeAroundSelected(total, selected, max) {
1484
- if (total <= max) return {
1485
- from: 0,
1486
- to: total
1487
- };
1488
- if (selected < 0) selected = 0;
1489
- if (selected <= total >> 1) {
1490
- let off$1 = Math.floor(selected / max);
1491
- return {
1492
- from: off$1 * max,
1493
- to: (off$1 + 1) * max
1494
- };
1495
- }
1496
- let off = Math.floor((total - selected) / max);
1497
- return {
1498
- from: total - (off + 1) * max,
1499
- to: total - off * max
1500
- };
1501
- }
1502
- var CompletionTooltip = class {
1503
- constructor(view, stateField, applyCompletion$1) {
1504
- this.view = view;
1505
- this.stateField = stateField;
1506
- this.applyCompletion = applyCompletion$1;
1507
- this.info = null;
1508
- this.infoDestroy = null;
1509
- this.placeInfoReq = {
1510
- read: () => this.measureInfo(),
1511
- write: (pos) => this.placeInfo(pos),
1512
- key: this
1513
- };
1514
- this.space = null;
1515
- this.currentClass = "";
1516
- let cState = view.state.field(stateField);
1517
- let { options, selected } = cState.open;
1518
- let config$1 = view.state.facet(completionConfig);
1519
- this.optionContent = optionContent(config$1);
1520
- this.optionClass = config$1.optionClass;
1521
- this.tooltipClass = config$1.tooltipClass;
1522
- this.range = rangeAroundSelected(options.length, selected, config$1.maxRenderedOptions);
1523
- this.dom = document.createElement("div");
1524
- this.dom.className = "cm-tooltip-autocomplete";
1525
- this.updateTooltipClass(view.state);
1526
- this.dom.addEventListener("mousedown", (e) => {
1527
- let { options: options$1 } = view.state.field(stateField).open;
1528
- for (let dom = e.target, match; dom && dom != this.dom; dom = dom.parentNode) if (dom.nodeName == "LI" && (match = /-(\d+)$/.exec(dom.id)) && +match[1] < options$1.length) {
1529
- this.applyCompletion(view, options$1[+match[1]]);
1530
- e.preventDefault();
1531
- return;
1532
- }
1533
- });
1534
- this.dom.addEventListener("focusout", (e) => {
1535
- let state = view.state.field(this.stateField, false);
1536
- if (state && state.tooltip && view.state.facet(completionConfig).closeOnBlur && e.relatedTarget != view.contentDOM) view.dispatch({ effects: closeCompletionEffect.of(null) });
1537
- });
1538
- this.showOptions(options, cState.id);
1539
- }
1540
- mount() {
1541
- this.updateSel();
1542
- }
1543
- showOptions(options, id) {
1544
- if (this.list) this.list.remove();
1545
- this.list = this.dom.appendChild(this.createListBox(options, id, this.range));
1546
- this.list.addEventListener("scroll", () => {
1547
- if (this.info) this.view.requestMeasure(this.placeInfoReq);
1548
- });
1549
- }
1550
- update(update) {
1551
- var _a;
1552
- let cState = update.state.field(this.stateField);
1553
- let prevState = update.startState.field(this.stateField);
1554
- this.updateTooltipClass(update.state);
1555
- if (cState != prevState) {
1556
- let { options, selected, disabled } = cState.open;
1557
- if (!prevState.open || prevState.open.options != options) {
1558
- this.range = rangeAroundSelected(options.length, selected, update.state.facet(completionConfig).maxRenderedOptions);
1559
- this.showOptions(options, cState.id);
1560
- }
1561
- this.updateSel();
1562
- if (disabled != ((_a = prevState.open) === null || _a === void 0 ? void 0 : _a.disabled)) this.dom.classList.toggle("cm-tooltip-autocomplete-disabled", !!disabled);
1563
- }
1564
- }
1565
- updateTooltipClass(state) {
1566
- let cls = this.tooltipClass(state);
1567
- if (cls != this.currentClass) {
1568
- for (let c of this.currentClass.split(" ")) if (c) this.dom.classList.remove(c);
1569
- for (let c of cls.split(" ")) if (c) this.dom.classList.add(c);
1570
- this.currentClass = cls;
1571
- }
1572
- }
1573
- positioned(space) {
1574
- this.space = space;
1575
- if (this.info) this.view.requestMeasure(this.placeInfoReq);
1576
- }
1577
- updateSel() {
1578
- let cState = this.view.state.field(this.stateField), open = cState.open;
1579
- if (open.selected > -1 && open.selected < this.range.from || open.selected >= this.range.to) {
1580
- this.range = rangeAroundSelected(open.options.length, open.selected, this.view.state.facet(completionConfig).maxRenderedOptions);
1581
- this.showOptions(open.options, cState.id);
1582
- }
1583
- if (this.updateSelectedOption(open.selected)) {
1584
- this.destroyInfo();
1585
- let { completion } = open.options[open.selected];
1586
- let { info } = completion;
1587
- if (!info) return;
1588
- let infoResult = typeof info === "string" ? document.createTextNode(info) : info(completion);
1589
- if (!infoResult) return;
1590
- if ("then" in infoResult) infoResult.then((obj) => {
1591
- if (obj && this.view.state.field(this.stateField, false) == cState) this.addInfoPane(obj, completion);
1592
- }).catch((e) => logException(this.view.state, e, "completion info"));
1593
- else this.addInfoPane(infoResult, completion);
1594
- }
1595
- }
1596
- addInfoPane(content, completion) {
1597
- this.destroyInfo();
1598
- let wrap = this.info = document.createElement("div");
1599
- wrap.className = "cm-tooltip cm-completionInfo";
1600
- if (content.nodeType != null) {
1601
- wrap.appendChild(content);
1602
- this.infoDestroy = null;
1603
- } else {
1604
- let { dom, destroy } = content;
1605
- wrap.appendChild(dom);
1606
- this.infoDestroy = destroy || null;
1607
- }
1608
- this.dom.appendChild(wrap);
1609
- this.view.requestMeasure(this.placeInfoReq);
1610
- }
1611
- updateSelectedOption(selected) {
1612
- let set = null;
1613
- for (let opt = this.list.firstChild, i = this.range.from; opt; opt = opt.nextSibling, i++) if (opt.nodeName != "LI" || !opt.id) i--;
1614
- else if (i == selected) {
1615
- if (!opt.hasAttribute("aria-selected")) {
1616
- opt.setAttribute("aria-selected", "true");
1617
- set = opt;
1618
- }
1619
- } else if (opt.hasAttribute("aria-selected")) opt.removeAttribute("aria-selected");
1620
- if (set) scrollIntoView(this.list, set);
1621
- return set;
1622
- }
1623
- measureInfo() {
1624
- let sel = this.dom.querySelector("[aria-selected]");
1625
- if (!sel || !this.info) return null;
1626
- let listRect = this.dom.getBoundingClientRect();
1627
- let infoRect = this.info.getBoundingClientRect();
1628
- let selRect = sel.getBoundingClientRect();
1629
- let space = this.space;
1630
- if (!space) {
1631
- let docElt = this.dom.ownerDocument.documentElement;
1632
- space = {
1633
- left: 0,
1634
- top: 0,
1635
- right: docElt.clientWidth,
1636
- bottom: docElt.clientHeight
1637
- };
1638
- }
1639
- if (selRect.top > Math.min(space.bottom, listRect.bottom) - 10 || selRect.bottom < Math.max(space.top, listRect.top) + 10) return null;
1640
- return this.view.state.facet(completionConfig).positionInfo(this.view, listRect, selRect, infoRect, space, this.dom);
1641
- }
1642
- placeInfo(pos) {
1643
- if (this.info) if (pos) {
1644
- if (pos.style) this.info.style.cssText = pos.style;
1645
- this.info.className = "cm-tooltip cm-completionInfo " + (pos.class || "");
1646
- } else this.info.style.cssText = "top: -1e6px";
1647
- }
1648
- createListBox(options, id, range) {
1649
- const ul = document.createElement("ul");
1650
- ul.id = id;
1651
- ul.setAttribute("role", "listbox");
1652
- ul.setAttribute("aria-expanded", "true");
1653
- ul.setAttribute("aria-label", this.view.state.phrase("Completions"));
1654
- ul.addEventListener("mousedown", (e) => {
1655
- if (e.target == ul) e.preventDefault();
1656
- });
1657
- let curSection = null;
1658
- for (let i = range.from; i < range.to; i++) {
1659
- let { completion, match } = options[i], { section } = completion;
1660
- if (section) {
1661
- let name = typeof section == "string" ? section : section.name;
1662
- if (name != curSection && (i > range.from || range.from == 0)) {
1663
- curSection = name;
1664
- if (typeof section != "string" && section.header) ul.appendChild(section.header(section));
1665
- else {
1666
- let header = ul.appendChild(document.createElement("completion-section"));
1667
- header.textContent = name;
1668
- }
1669
- }
1670
- }
1671
- const li = ul.appendChild(document.createElement("li"));
1672
- li.id = id + "-" + i;
1673
- li.setAttribute("role", "option");
1674
- let cls = this.optionClass(completion);
1675
- if (cls) li.className = cls;
1676
- for (let source of this.optionContent) {
1677
- let node = source(completion, this.view.state, this.view, match);
1678
- if (node) li.appendChild(node);
1679
- }
1680
- }
1681
- if (range.from) ul.classList.add("cm-completionListIncompleteTop");
1682
- if (range.to < options.length) ul.classList.add("cm-completionListIncompleteBottom");
1683
- return ul;
1684
- }
1685
- destroyInfo() {
1686
- if (this.info) {
1687
- if (this.infoDestroy) this.infoDestroy();
1688
- this.info.remove();
1689
- this.info = null;
1690
- }
1691
- }
1692
- destroy() {
1693
- this.destroyInfo();
1694
- }
1695
- };
1696
- function completionTooltip(stateField, applyCompletion$1) {
1697
- return (view) => new CompletionTooltip(view, stateField, applyCompletion$1);
1698
- }
1699
- function scrollIntoView(container, element) {
1700
- let parent = container.getBoundingClientRect();
1701
- let self = element.getBoundingClientRect();
1702
- let scaleY = parent.height / container.offsetHeight;
1703
- if (self.top < parent.top) container.scrollTop -= (parent.top - self.top) / scaleY;
1704
- else if (self.bottom > parent.bottom) container.scrollTop += (self.bottom - parent.bottom) / scaleY;
1705
- }
1706
- function score(option) {
1707
- return (option.boost || 0) * 100 + (option.apply ? 10 : 0) + (option.info ? 5 : 0) + (option.type ? 1 : 0);
1708
- }
1709
- function sortOptions(active, state) {
1710
- let options = [];
1711
- let sections = null;
1712
- let addOption = (option) => {
1713
- options.push(option);
1714
- let { section } = option.completion;
1715
- if (section) {
1716
- if (!sections) sections = [];
1717
- let name = typeof section == "string" ? section : section.name;
1718
- if (!sections.some((s) => s.name == name)) sections.push(typeof section == "string" ? { name } : section);
1719
- }
1720
- };
1721
- let conf = state.facet(completionConfig);
1722
- for (let a of active) if (a.hasResult()) {
1723
- let getMatch = a.result.getMatch;
1724
- if (a.result.filter === false) for (let option of a.result.options) addOption(new Option(option, a.source, getMatch ? getMatch(option) : [], 1e9 - options.length));
1725
- else {
1726
- let pattern = state.sliceDoc(a.from, a.to), match;
1727
- let matcher = conf.filterStrict ? new StrictMatcher(pattern) : new FuzzyMatcher(pattern);
1728
- for (let option of a.result.options) if (match = matcher.match(option.label)) {
1729
- let matched = !option.displayLabel ? match.matched : getMatch ? getMatch(option, match.matched) : [];
1730
- addOption(new Option(option, a.source, matched, match.score + (option.boost || 0)));
1731
- }
1732
- }
1733
- }
1734
- if (sections) {
1735
- let sectionOrder = Object.create(null), pos = 0;
1736
- let cmp = (a, b) => {
1737
- var _a, _b;
1738
- return ((_a = a.rank) !== null && _a !== void 0 ? _a : 1e9) - ((_b = b.rank) !== null && _b !== void 0 ? _b : 1e9) || (a.name < b.name ? -1 : 1);
1739
- };
1740
- for (let s of sections.sort(cmp)) {
1741
- pos -= 1e5;
1742
- sectionOrder[s.name] = pos;
1743
- }
1744
- for (let option of options) {
1745
- let { section } = option.completion;
1746
- if (section) option.score += sectionOrder[typeof section == "string" ? section : section.name];
1747
- }
1748
- }
1749
- let result = [], prev = null;
1750
- let compare = conf.compareCompletions;
1751
- for (let opt of options.sort((a, b) => b.score - a.score || compare(a.completion, b.completion))) {
1752
- let cur$1 = opt.completion;
1753
- if (!prev || prev.label != cur$1.label || prev.detail != cur$1.detail || prev.type != null && cur$1.type != null && prev.type != cur$1.type || prev.apply != cur$1.apply || prev.boost != cur$1.boost) result.push(opt);
1754
- else if (score(opt.completion) > score(prev)) result[result.length - 1] = opt;
1755
- prev = opt.completion;
1756
- }
1757
- return result;
1758
- }
1759
- var CompletionDialog = class CompletionDialog {
1760
- constructor(options, attrs, tooltip, timestamp, selected, disabled) {
1761
- this.options = options;
1762
- this.attrs = attrs;
1763
- this.tooltip = tooltip;
1764
- this.timestamp = timestamp;
1765
- this.selected = selected;
1766
- this.disabled = disabled;
1767
- }
1768
- setSelected(selected, id) {
1769
- return selected == this.selected || selected >= this.options.length ? this : new CompletionDialog(this.options, makeAttrs(id, selected), this.tooltip, this.timestamp, selected, this.disabled);
1770
- }
1771
- static build(active, state, id, prev, conf, didSetActive) {
1772
- if (prev && !didSetActive && active.some((s) => s.isPending)) return prev.setDisabled();
1773
- let options = sortOptions(active, state);
1774
- if (!options.length) return prev && active.some((a) => a.isPending) ? prev.setDisabled() : null;
1775
- let selected = state.facet(completionConfig).selectOnOpen ? 0 : -1;
1776
- if (prev && prev.selected != selected && prev.selected != -1) {
1777
- let selectedValue = prev.options[prev.selected].completion;
1778
- for (let i = 0; i < options.length; i++) if (options[i].completion == selectedValue) {
1779
- selected = i;
1780
- break;
1781
- }
1782
- }
1783
- return new CompletionDialog(options, makeAttrs(id, selected), {
1784
- pos: active.reduce((a, b) => b.hasResult() ? Math.min(a, b.from) : a, 1e8),
1785
- create: createTooltip,
1786
- above: conf.aboveCursor
1787
- }, prev ? prev.timestamp : Date.now(), selected, false);
1788
- }
1789
- map(changes) {
1790
- return new CompletionDialog(this.options, this.attrs, Object.assign(Object.assign({}, this.tooltip), { pos: changes.mapPos(this.tooltip.pos) }), this.timestamp, this.selected, this.disabled);
1791
- }
1792
- setDisabled() {
1793
- return new CompletionDialog(this.options, this.attrs, this.tooltip, this.timestamp, this.selected, true);
1794
- }
1795
- };
1796
- var CompletionState = class CompletionState {
1797
- constructor(active, id, open) {
1798
- this.active = active;
1799
- this.id = id;
1800
- this.open = open;
1801
- }
1802
- static start() {
1803
- return new CompletionState(none, "cm-ac-" + Math.floor(Math.random() * 2e6).toString(36), null);
1804
- }
1805
- update(tr) {
1806
- let { state } = tr, conf = state.facet(completionConfig);
1807
- let active = (conf.override || state.languageDataAt("autocomplete", cur(state)).map(asSource)).map((source) => {
1808
- return (this.active.find((s) => s.source == source) || new ActiveSource(source, this.active.some((a) => a.state != 0) ? 1 : 0)).update(tr, conf);
1809
- });
1810
- if (active.length == this.active.length && active.every((a, i) => a == this.active[i])) active = this.active;
1811
- let open = this.open, didSet = tr.effects.some((e) => e.is(setActiveEffect));
1812
- if (open && tr.docChanged) open = open.map(tr.changes);
1813
- if (tr.selection || active.some((a) => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) || !sameResults(active, this.active) || didSet) open = CompletionDialog.build(active, state, this.id, open, conf, didSet);
1814
- else if (open && open.disabled && !active.some((a) => a.isPending)) open = null;
1815
- if (!open && active.every((a) => !a.isPending) && active.some((a) => a.hasResult())) active = active.map((a) => a.hasResult() ? new ActiveSource(a.source, 0) : a);
1816
- for (let effect of tr.effects) if (effect.is(setSelectedEffect)) open = open && open.setSelected(effect.value, this.id);
1817
- return active == this.active && open == this.open ? this : new CompletionState(active, this.id, open);
1818
- }
1819
- get tooltip() {
1820
- return this.open ? this.open.tooltip : null;
1821
- }
1822
- get attrs() {
1823
- return this.open ? this.open.attrs : this.active.length ? baseAttrs : noAttrs;
1824
- }
1825
- };
1826
- function sameResults(a, b) {
1827
- if (a == b) return true;
1828
- for (let iA = 0, iB = 0;;) {
1829
- while (iA < a.length && !a[iA].hasResult()) iA++;
1830
- while (iB < b.length && !b[iB].hasResult()) iB++;
1831
- let endA = iA == a.length, endB = iB == b.length;
1832
- if (endA || endB) return endA == endB;
1833
- if (a[iA++].result != b[iB++].result) return false;
1834
- }
1835
- }
1836
- const baseAttrs = { "aria-autocomplete": "list" };
1837
- const noAttrs = {};
1838
- function makeAttrs(id, selected) {
1839
- let result = {
1840
- "aria-autocomplete": "list",
1841
- "aria-haspopup": "listbox",
1842
- "aria-controls": id
1843
- };
1844
- if (selected > -1) result["aria-activedescendant"] = id + "-" + selected;
1845
- return result;
1846
- }
1847
- const none = [];
1848
- function getUpdateType(tr, conf) {
1849
- if (tr.isUserEvent("input.complete")) {
1850
- let completion = tr.annotation(pickedCompletion);
1851
- if (completion && conf.activateOnCompletion(completion)) return 12;
1852
- }
1853
- let typing = tr.isUserEvent("input.type");
1854
- return typing && conf.activateOnTyping ? 5 : typing ? 1 : tr.isUserEvent("delete.backward") ? 2 : tr.selection ? 8 : tr.docChanged ? 16 : 0;
1855
- }
1856
- var ActiveSource = class ActiveSource {
1857
- constructor(source, state, explicit = false) {
1858
- this.source = source;
1859
- this.state = state;
1860
- this.explicit = explicit;
1861
- }
1862
- hasResult() {
1863
- return false;
1864
- }
1865
- get isPending() {
1866
- return this.state == 1;
1867
- }
1868
- update(tr, conf) {
1869
- let type = getUpdateType(tr, conf), value = this;
1870
- if (type & 8 || type & 16 && this.touches(tr)) value = new ActiveSource(value.source, 0);
1871
- if (type & 4 && value.state == 0) value = new ActiveSource(this.source, 1);
1872
- value = value.updateFor(tr, type);
1873
- for (let effect of tr.effects) if (effect.is(startCompletionEffect)) value = new ActiveSource(value.source, 1, effect.value);
1874
- else if (effect.is(closeCompletionEffect)) value = new ActiveSource(value.source, 0);
1875
- else if (effect.is(setActiveEffect)) {
1876
- for (let active of effect.value) if (active.source == value.source) value = active;
1877
- }
1878
- return value;
1879
- }
1880
- updateFor(tr, type) {
1881
- return this.map(tr.changes);
1882
- }
1883
- map(changes) {
1884
- return this;
1885
- }
1886
- touches(tr) {
1887
- return tr.changes.touchesRange(cur(tr.state));
1888
- }
1889
- };
1890
- var ActiveResult = class ActiveResult extends ActiveSource {
1891
- constructor(source, explicit, limit, result, from, to) {
1892
- super(source, 3, explicit);
1893
- this.limit = limit;
1894
- this.result = result;
1895
- this.from = from;
1896
- this.to = to;
1897
- }
1898
- hasResult() {
1899
- return true;
1900
- }
1901
- updateFor(tr, type) {
1902
- var _a;
1903
- if (!(type & 3)) return this.map(tr.changes);
1904
- let result = this.result;
1905
- if (result.map && !tr.changes.empty) result = result.map(result, tr.changes);
1906
- let from = tr.changes.mapPos(this.from), to = tr.changes.mapPos(this.to, 1);
1907
- let pos = cur(tr.state);
1908
- if (pos > to || !result || type & 2 && (cur(tr.startState) == this.from || pos < this.limit)) return new ActiveSource(this.source, type & 4 ? 1 : 0);
1909
- let limit = tr.changes.mapPos(this.limit);
1910
- if (checkValid(result.validFor, tr.state, from, to)) return new ActiveResult(this.source, this.explicit, limit, result, from, to);
1911
- if (result.update && (result = result.update(result, from, to, new CompletionContext(tr.state, pos, false)))) return new ActiveResult(this.source, this.explicit, limit, result, result.from, (_a = result.to) !== null && _a !== void 0 ? _a : cur(tr.state));
1912
- return new ActiveSource(this.source, 1, this.explicit);
1913
- }
1914
- map(mapping) {
1915
- if (mapping.empty) return this;
1916
- if (!(this.result.map ? this.result.map(this.result, mapping) : this.result)) return new ActiveSource(this.source, 0);
1917
- return new ActiveResult(this.source, this.explicit, mapping.mapPos(this.limit), this.result, mapping.mapPos(this.from), mapping.mapPos(this.to, 1));
1918
- }
1919
- touches(tr) {
1920
- return tr.changes.touchesRange(this.from, this.to);
1921
- }
1922
- };
1923
- function checkValid(validFor, state, from, to) {
1924
- if (!validFor) return false;
1925
- let text = state.sliceDoc(from, to);
1926
- return typeof validFor == "function" ? validFor(text, from, to, state) : ensureAnchor(validFor, true).test(text);
1927
- }
1928
- const setActiveEffect = /* @__PURE__ */ StateEffect.define({ map(sources, mapping) {
1929
- return sources.map((s) => s.map(mapping));
1930
- } });
1931
- const setSelectedEffect = /* @__PURE__ */ StateEffect.define();
1932
- const completionState = /* @__PURE__ */ StateField.define({
1933
- create() {
1934
- return CompletionState.start();
1935
- },
1936
- update(value, tr) {
1937
- return value.update(tr);
1938
- },
1939
- provide: (f) => [showTooltip.from(f, (val) => val.tooltip), EditorView.contentAttributes.from(f, (state) => state.attrs)]
1940
- });
1941
- function applyCompletion(view, option) {
1942
- const apply = option.completion.apply || option.completion.label;
1943
- let result = view.state.field(completionState).active.find((a) => a.source == option.source);
1944
- if (!(result instanceof ActiveResult)) return false;
1945
- if (typeof apply == "string") view.dispatch(Object.assign(Object.assign({}, insertCompletionText(view.state, apply, result.from, result.to)), { annotations: pickedCompletion.of(option.completion) }));
1946
- else apply(view, option.completion, result.from, result.to);
1947
- return true;
1948
- }
1949
- const createTooltip = /* @__PURE__ */ completionTooltip(completionState, applyCompletion);
1950
- /**
1951
- Returns a command that moves the completion selection forward or
1952
- backward by the given amount.
1953
- */
1954
- function moveCompletionSelection(forward, by = "option") {
1955
- return (view) => {
1956
- let cState = view.state.field(completionState, false);
1957
- if (!cState || !cState.open || cState.open.disabled || Date.now() - cState.open.timestamp < view.state.facet(completionConfig).interactionDelay) return false;
1958
- let step = 1, tooltip;
1959
- if (by == "page" && (tooltip = getTooltip(view, cState.open.tooltip))) step = Math.max(2, Math.floor(tooltip.dom.offsetHeight / tooltip.dom.querySelector("li").offsetHeight) - 1);
1960
- let { length } = cState.open.options;
1961
- let selected = cState.open.selected > -1 ? cState.open.selected + step * (forward ? 1 : -1) : forward ? 0 : length - 1;
1962
- if (selected < 0) selected = by == "page" ? 0 : length - 1;
1963
- else if (selected >= length) selected = by == "page" ? length - 1 : 0;
1964
- view.dispatch({ effects: setSelectedEffect.of(selected) });
1965
- return true;
1966
- };
1967
- }
1968
- /**
1969
- Accept the current completion.
1970
- */
1971
- const acceptCompletion = (view) => {
1972
- let cState = view.state.field(completionState, false);
1973
- if (view.state.readOnly || !cState || !cState.open || cState.open.selected < 0 || cState.open.disabled || Date.now() - cState.open.timestamp < view.state.facet(completionConfig).interactionDelay) return false;
1974
- return applyCompletion(view, cState.open.options[cState.open.selected]);
1975
- };
1976
- /**
1977
- Explicitly start autocompletion.
1978
- */
1979
- const startCompletion = (view) => {
1980
- if (!view.state.field(completionState, false)) return false;
1981
- view.dispatch({ effects: startCompletionEffect.of(true) });
1982
- return true;
1983
- };
1984
- /**
1985
- Close the currently active completion.
1986
- */
1987
- const closeCompletion = (view) => {
1988
- let cState = view.state.field(completionState, false);
1989
- if (!cState || !cState.active.some((a) => a.state != 0)) return false;
1990
- view.dispatch({ effects: closeCompletionEffect.of(null) });
1991
- return true;
1992
- };
1993
- var RunningQuery = class {
1994
- constructor(active, context) {
1995
- this.active = active;
1996
- this.context = context;
1997
- this.time = Date.now();
1998
- this.updates = [];
1999
- this.done = void 0;
2000
- }
2001
- };
2002
- const MaxUpdateCount = 50, MinAbortTime = 1e3;
2003
- const completionPlugin = /* @__PURE__ */ ViewPlugin.fromClass(class {
2004
- constructor(view) {
2005
- this.view = view;
2006
- this.debounceUpdate = -1;
2007
- this.running = [];
2008
- this.debounceAccept = -1;
2009
- this.pendingStart = false;
2010
- this.composing = 0;
2011
- for (let active of view.state.field(completionState).active) if (active.isPending) this.startQuery(active);
2012
- }
2013
- update(update) {
2014
- let cState = update.state.field(completionState);
2015
- let conf = update.state.facet(completionConfig);
2016
- if (!update.selectionSet && !update.docChanged && update.startState.field(completionState) == cState) return;
2017
- let doesReset = update.transactions.some((tr) => {
2018
- let type = getUpdateType(tr, conf);
2019
- return type & 8 || (tr.selection || tr.docChanged) && !(type & 3);
2020
- });
2021
- for (let i = 0; i < this.running.length; i++) {
2022
- let query = this.running[i];
2023
- if (doesReset || query.context.abortOnDocChange && update.docChanged || query.updates.length + update.transactions.length > MaxUpdateCount && Date.now() - query.time > MinAbortTime) {
2024
- for (let handler of query.context.abortListeners) try {
2025
- handler();
2026
- } catch (e) {
2027
- logException(this.view.state, e);
2028
- }
2029
- query.context.abortListeners = null;
2030
- this.running.splice(i--, 1);
2031
- } else query.updates.push(...update.transactions);
2032
- }
2033
- if (this.debounceUpdate > -1) clearTimeout(this.debounceUpdate);
2034
- if (update.transactions.some((tr) => tr.effects.some((e) => e.is(startCompletionEffect)))) this.pendingStart = true;
2035
- let delay = this.pendingStart ? 50 : conf.activateOnTypingDelay;
2036
- this.debounceUpdate = cState.active.some((a) => a.isPending && !this.running.some((q) => q.active.source == a.source)) ? setTimeout(() => this.startUpdate(), delay) : -1;
2037
- if (this.composing != 0) {
2038
- for (let tr of update.transactions) if (tr.isUserEvent("input.type")) this.composing = 2;
2039
- else if (this.composing == 2 && tr.selection) this.composing = 3;
2040
- }
2041
- }
2042
- startUpdate() {
2043
- this.debounceUpdate = -1;
2044
- this.pendingStart = false;
2045
- let { state } = this.view, cState = state.field(completionState);
2046
- for (let active of cState.active) if (active.isPending && !this.running.some((r) => r.active.source == active.source)) this.startQuery(active);
2047
- if (this.running.length && cState.open && cState.open.disabled) this.debounceAccept = setTimeout(() => this.accept(), this.view.state.facet(completionConfig).updateSyncTime);
2048
- }
2049
- startQuery(active) {
2050
- let { state } = this.view, pos = cur(state);
2051
- let context = new CompletionContext(state, pos, active.explicit, this.view);
2052
- let pending = new RunningQuery(active, context);
2053
- this.running.push(pending);
2054
- Promise.resolve(active.source(context)).then((result) => {
2055
- if (!pending.context.aborted) {
2056
- pending.done = result || null;
2057
- this.scheduleAccept();
2058
- }
2059
- }, (err) => {
2060
- this.view.dispatch({ effects: closeCompletionEffect.of(null) });
2061
- logException(this.view.state, err);
2062
- });
2063
- }
2064
- scheduleAccept() {
2065
- if (this.running.every((q) => q.done !== void 0)) this.accept();
2066
- else if (this.debounceAccept < 0) this.debounceAccept = setTimeout(() => this.accept(), this.view.state.facet(completionConfig).updateSyncTime);
2067
- }
2068
- accept() {
2069
- var _a;
2070
- if (this.debounceAccept > -1) clearTimeout(this.debounceAccept);
2071
- this.debounceAccept = -1;
2072
- let updated = [];
2073
- let conf = this.view.state.facet(completionConfig), cState = this.view.state.field(completionState);
2074
- for (let i = 0; i < this.running.length; i++) {
2075
- let query = this.running[i];
2076
- if (query.done === void 0) continue;
2077
- this.running.splice(i--, 1);
2078
- if (query.done) {
2079
- let pos = cur(query.updates.length ? query.updates[0].startState : this.view.state);
2080
- let limit = Math.min(pos, query.done.from + (query.active.explicit ? 0 : 1));
2081
- let active = new ActiveResult(query.active.source, query.active.explicit, limit, query.done, query.done.from, (_a = query.done.to) !== null && _a !== void 0 ? _a : pos);
2082
- for (let tr of query.updates) active = active.update(tr, conf);
2083
- if (active.hasResult()) {
2084
- updated.push(active);
2085
- continue;
2086
- }
2087
- }
2088
- let current = cState.active.find((a) => a.source == query.active.source);
2089
- if (current && current.isPending) if (query.done == null) {
2090
- let active = new ActiveSource(query.active.source, 0);
2091
- for (let tr of query.updates) active = active.update(tr, conf);
2092
- if (!active.isPending) updated.push(active);
2093
- } else this.startQuery(current);
2094
- }
2095
- if (updated.length || cState.open && cState.open.disabled) this.view.dispatch({ effects: setActiveEffect.of(updated) });
2096
- }
2097
- }, { eventHandlers: {
2098
- blur(event) {
2099
- let state = this.view.state.field(completionState, false);
2100
- if (state && state.tooltip && this.view.state.facet(completionConfig).closeOnBlur) {
2101
- let dialog = state.open && getTooltip(this.view, state.open.tooltip);
2102
- if (!dialog || !dialog.dom.contains(event.relatedTarget)) setTimeout(() => this.view.dispatch({ effects: closeCompletionEffect.of(null) }), 10);
2103
- }
2104
- },
2105
- compositionstart() {
2106
- this.composing = 1;
2107
- },
2108
- compositionend() {
2109
- if (this.composing == 3) setTimeout(() => this.view.dispatch({ effects: startCompletionEffect.of(false) }), 20);
2110
- this.composing = 0;
2111
- }
2112
- } });
2113
- const windows = typeof navigator == "object" && /* @__PURE__ */ /Win/.test(navigator.platform);
2114
- const commitCharacters = /* @__PURE__ */ Prec.highest(/* @__PURE__ */ EditorView.domEventHandlers({ keydown(event, view) {
2115
- let field = view.state.field(completionState, false);
2116
- if (!field || !field.open || field.open.disabled || field.open.selected < 0 || event.key.length > 1 || event.ctrlKey && !(windows && event.altKey) || event.metaKey) return false;
2117
- let option = field.open.options[field.open.selected];
2118
- let result = field.active.find((a) => a.source == option.source);
2119
- let commitChars = option.completion.commitCharacters || result.result.commitCharacters;
2120
- if (commitChars && commitChars.indexOf(event.key) > -1) applyCompletion(view, option);
2121
- return false;
2122
- } }));
2123
- const baseTheme$2 = /* @__PURE__ */ EditorView.baseTheme({
2124
- ".cm-tooltip.cm-tooltip-autocomplete": { "& > ul": {
2125
- fontFamily: "monospace",
2126
- whiteSpace: "nowrap",
2127
- overflow: "hidden auto",
2128
- maxWidth_fallback: "700px",
2129
- maxWidth: "min(700px, 95vw)",
2130
- minWidth: "250px",
2131
- maxHeight: "10em",
2132
- height: "100%",
2133
- listStyle: "none",
2134
- margin: 0,
2135
- padding: 0,
2136
- "& > li, & > completion-section": {
2137
- padding: "1px 3px",
2138
- lineHeight: 1.2
2139
- },
2140
- "& > li": {
2141
- overflowX: "hidden",
2142
- textOverflow: "ellipsis",
2143
- cursor: "pointer"
2144
- },
2145
- "& > completion-section": {
2146
- display: "list-item",
2147
- borderBottom: "1px solid silver",
2148
- paddingLeft: "0.5em",
2149
- opacity: .7
2150
- }
2151
- } },
2152
- "&light .cm-tooltip-autocomplete ul li[aria-selected]": {
2153
- background: "#17c",
2154
- color: "white"
2155
- },
2156
- "&light .cm-tooltip-autocomplete-disabled ul li[aria-selected]": { background: "#777" },
2157
- "&dark .cm-tooltip-autocomplete ul li[aria-selected]": {
2158
- background: "#347",
2159
- color: "white"
2160
- },
2161
- "&dark .cm-tooltip-autocomplete-disabled ul li[aria-selected]": { background: "#444" },
2162
- ".cm-completionListIncompleteTop:before, .cm-completionListIncompleteBottom:after": {
2163
- content: "\"···\"",
2164
- opacity: .5,
2165
- display: "block",
2166
- textAlign: "center"
2167
- },
2168
- ".cm-tooltip.cm-completionInfo": {
2169
- position: "absolute",
2170
- padding: "3px 9px",
2171
- width: "max-content",
2172
- maxWidth: `400px`,
2173
- boxSizing: "border-box",
2174
- whiteSpace: "pre-line"
2175
- },
2176
- ".cm-completionInfo.cm-completionInfo-left": { right: "100%" },
2177
- ".cm-completionInfo.cm-completionInfo-right": { left: "100%" },
2178
- ".cm-completionInfo.cm-completionInfo-left-narrow": { right: `30px` },
2179
- ".cm-completionInfo.cm-completionInfo-right-narrow": { left: `30px` },
2180
- "&light .cm-snippetField": { backgroundColor: "#00000022" },
2181
- "&dark .cm-snippetField": { backgroundColor: "#ffffff22" },
2182
- ".cm-snippetFieldPosition": {
2183
- verticalAlign: "text-top",
2184
- width: 0,
2185
- height: "1.15em",
2186
- display: "inline-block",
2187
- margin: "0 -0.7px -.7em",
2188
- borderLeft: "1.4px dotted #888"
2189
- },
2190
- ".cm-completionMatchedText": { textDecoration: "underline" },
2191
- ".cm-completionDetail": {
2192
- marginLeft: "0.5em",
2193
- fontStyle: "italic"
2194
- },
2195
- ".cm-completionIcon": {
2196
- fontSize: "90%",
2197
- width: ".8em",
2198
- display: "inline-block",
2199
- textAlign: "center",
2200
- paddingRight: ".6em",
2201
- opacity: "0.6",
2202
- boxSizing: "content-box"
2203
- },
2204
- ".cm-completionIcon-function, .cm-completionIcon-method": { "&:after": { content: "'ƒ'" } },
2205
- ".cm-completionIcon-class": { "&:after": { content: "'○'" } },
2206
- ".cm-completionIcon-interface": { "&:after": { content: "'◌'" } },
2207
- ".cm-completionIcon-variable": { "&:after": { content: "'𝑥'" } },
2208
- ".cm-completionIcon-constant": { "&:after": { content: "'𝐶'" } },
2209
- ".cm-completionIcon-type": { "&:after": { content: "'𝑡'" } },
2210
- ".cm-completionIcon-enum": { "&:after": { content: "'∪'" } },
2211
- ".cm-completionIcon-property": { "&:after": { content: "'□'" } },
2212
- ".cm-completionIcon-keyword": { "&:after": { content: "'🔑︎'" } },
2213
- ".cm-completionIcon-namespace": { "&:after": { content: "'▢'" } },
2214
- ".cm-completionIcon-text": { "&:after": {
2215
- content: "'abc'",
2216
- fontSize: "50%",
2217
- verticalAlign: "middle"
2218
- } }
2219
- });
2220
- const defaults = {
2221
- brackets: [
2222
- "(",
2223
- "[",
2224
- "{",
2225
- "'",
2226
- "\""
2227
- ],
2228
- before: ")]}:;>",
2229
- stringPrefixes: []
2230
- };
2231
- const closeBracketEffect = /* @__PURE__ */ StateEffect.define({ map(value, mapping) {
2232
- let mapped = mapping.mapPos(value, -1, MapMode.TrackAfter);
2233
- return mapped == null ? void 0 : mapped;
2234
- } });
2235
- const closedBracket = /* @__PURE__ */ new class extends RangeValue {}();
2236
- closedBracket.startSide = 1;
2237
- closedBracket.endSide = -1;
2238
- const bracketState = /* @__PURE__ */ StateField.define({
2239
- create() {
2240
- return RangeSet.empty;
2241
- },
2242
- update(value, tr) {
2243
- value = value.map(tr.changes);
2244
- if (tr.selection) {
2245
- let line = tr.state.doc.lineAt(tr.selection.main.head);
2246
- value = value.update({ filter: (from) => from >= line.from && from <= line.to });
2247
- }
2248
- for (let effect of tr.effects) if (effect.is(closeBracketEffect)) value = value.update({ add: [closedBracket.range(effect.value, effect.value + 1)] });
2249
- return value;
2250
- }
2251
- });
2252
- /**
2253
- Extension to enable bracket-closing behavior. When a closeable
2254
- bracket is typed, its closing bracket is immediately inserted
2255
- after the cursor. When closing a bracket directly in front of a
2256
- closing bracket inserted by the extension, the cursor moves over
2257
- that bracket.
2258
- */
2259
- function closeBrackets() {
2260
- return [inputHandler, bracketState];
2261
- }
2262
- const definedClosing = "()[]{}<>«»»«[]{}";
2263
- function closing(ch) {
2264
- for (let i = 0; i < 16; i += 2) if (definedClosing.charCodeAt(i) == ch) return definedClosing.charAt(i + 1);
2265
- return fromCodePoint(ch < 128 ? ch : ch + 1);
2266
- }
2267
- function config(state, pos) {
2268
- return state.languageDataAt("closeBrackets", pos)[0] || defaults;
2269
- }
2270
- const android = typeof navigator == "object" && /* @__PURE__ */ /Android\b/.test(navigator.userAgent);
2271
- const inputHandler = /* @__PURE__ */ EditorView.inputHandler.of((view, from, to, insert) => {
2272
- if ((android ? view.composing : view.compositionStarted) || view.state.readOnly) return false;
2273
- let sel = view.state.selection.main;
2274
- if (insert.length > 2 || insert.length == 2 && codePointSize(codePointAt(insert, 0)) == 1 || from != sel.from || to != sel.to) return false;
2275
- let tr = insertBracket(view.state, insert);
2276
- if (!tr) return false;
2277
- view.dispatch(tr);
2278
- return true;
2279
- });
2280
- /**
2281
- Command that implements deleting a pair of matching brackets when
2282
- the cursor is between them.
2283
- */
2284
- const deleteBracketPair = ({ state, dispatch }) => {
2285
- if (state.readOnly) return false;
2286
- let tokens = config(state, state.selection.main.head).brackets || defaults.brackets;
2287
- let dont = null, changes = state.changeByRange((range) => {
2288
- if (range.empty) {
2289
- let before = prevChar(state.doc, range.head);
2290
- for (let token of tokens) if (token == before && nextChar(state.doc, range.head) == closing(codePointAt(token, 0))) return {
2291
- changes: {
2292
- from: range.head - token.length,
2293
- to: range.head + token.length
2294
- },
2295
- range: EditorSelection.cursor(range.head - token.length)
2296
- };
2297
- }
2298
- return { range: dont = range };
2299
- });
2300
- if (!dont) dispatch(state.update(changes, {
2301
- scrollIntoView: true,
2302
- userEvent: "delete.backward"
2303
- }));
2304
- return !dont;
2305
- };
2306
- /**
2307
- Close-brackets related key bindings. Binds Backspace to
2308
- [`deleteBracketPair`](https://codemirror.net/6/docs/ref/#autocomplete.deleteBracketPair).
2309
- */
2310
- const closeBracketsKeymap = [{
2311
- key: "Backspace",
2312
- run: deleteBracketPair
2313
- }];
2314
- /**
2315
- Implements the extension's behavior on text insertion. If the
2316
- given string counts as a bracket in the language around the
2317
- selection, and replacing the selection with it requires custom
2318
- behavior (inserting a closing version or skipping past a
2319
- previously-closed bracket), this function returns a transaction
2320
- representing that custom behavior. (You only need this if you want
2321
- to programmatically insert brackets—the
2322
- [`closeBrackets`](https://codemirror.net/6/docs/ref/#autocomplete.closeBrackets) extension will
2323
- take care of running this for user input.)
2324
- */
2325
- function insertBracket(state, bracket) {
2326
- let conf = config(state, state.selection.main.head);
2327
- let tokens = conf.brackets || defaults.brackets;
2328
- for (let tok of tokens) {
2329
- let closed = closing(codePointAt(tok, 0));
2330
- if (bracket == tok) return closed == tok ? handleSame(state, tok, tokens.indexOf(tok + tok + tok) > -1, conf) : handleOpen(state, tok, closed, conf.before || defaults.before);
2331
- if (bracket == closed && closedBracketAt(state, state.selection.main.from)) return handleClose(state, tok, closed);
2332
- }
2333
- return null;
2334
- }
2335
- function closedBracketAt(state, pos) {
2336
- let found = false;
2337
- state.field(bracketState).between(0, state.doc.length, (from) => {
2338
- if (from == pos) found = true;
2339
- });
2340
- return found;
2341
- }
2342
- function nextChar(doc, pos) {
2343
- let next = doc.sliceString(pos, pos + 2);
2344
- return next.slice(0, codePointSize(codePointAt(next, 0)));
2345
- }
2346
- function prevChar(doc, pos) {
2347
- let prev = doc.sliceString(pos - 2, pos);
2348
- return codePointSize(codePointAt(prev, 0)) == prev.length ? prev : prev.slice(1);
2349
- }
2350
- function handleOpen(state, open, close, closeBefore) {
2351
- let dont = null, changes = state.changeByRange((range) => {
2352
- if (!range.empty) return {
2353
- changes: [{
2354
- insert: open,
2355
- from: range.from
2356
- }, {
2357
- insert: close,
2358
- from: range.to
2359
- }],
2360
- effects: closeBracketEffect.of(range.to + open.length),
2361
- range: EditorSelection.range(range.anchor + open.length, range.head + open.length)
2362
- };
2363
- let next = nextChar(state.doc, range.head);
2364
- if (!next || /\s/.test(next) || closeBefore.indexOf(next) > -1) return {
2365
- changes: {
2366
- insert: open + close,
2367
- from: range.head
2368
- },
2369
- effects: closeBracketEffect.of(range.head + open.length),
2370
- range: EditorSelection.cursor(range.head + open.length)
2371
- };
2372
- return { range: dont = range };
2373
- });
2374
- return dont ? null : state.update(changes, {
2375
- scrollIntoView: true,
2376
- userEvent: "input.type"
2377
- });
2378
- }
2379
- function handleClose(state, _open, close) {
2380
- let dont = null, changes = state.changeByRange((range) => {
2381
- if (range.empty && nextChar(state.doc, range.head) == close) return {
2382
- changes: {
2383
- from: range.head,
2384
- to: range.head + close.length,
2385
- insert: close
2386
- },
2387
- range: EditorSelection.cursor(range.head + close.length)
2388
- };
2389
- return dont = { range };
2390
- });
2391
- return dont ? null : state.update(changes, {
2392
- scrollIntoView: true,
2393
- userEvent: "input.type"
2394
- });
2395
- }
2396
- function handleSame(state, token, allowTriple, config$1) {
2397
- let stringPrefixes = config$1.stringPrefixes || defaults.stringPrefixes;
2398
- let dont = null, changes = state.changeByRange((range) => {
2399
- if (!range.empty) return {
2400
- changes: [{
2401
- insert: token,
2402
- from: range.from
2403
- }, {
2404
- insert: token,
2405
- from: range.to
2406
- }],
2407
- effects: closeBracketEffect.of(range.to + token.length),
2408
- range: EditorSelection.range(range.anchor + token.length, range.head + token.length)
2409
- };
2410
- let pos = range.head, next = nextChar(state.doc, pos), start;
2411
- if (next == token) {
2412
- if (nodeStart(state, pos)) return {
2413
- changes: {
2414
- insert: token + token,
2415
- from: pos
2416
- },
2417
- effects: closeBracketEffect.of(pos + token.length),
2418
- range: EditorSelection.cursor(pos + token.length)
2419
- };
2420
- else if (closedBracketAt(state, pos)) {
2421
- let content = allowTriple && state.sliceDoc(pos, pos + token.length * 3) == token + token + token ? token + token + token : token;
2422
- return {
2423
- changes: {
2424
- from: pos,
2425
- to: pos + content.length,
2426
- insert: content
2427
- },
2428
- range: EditorSelection.cursor(pos + content.length)
2429
- };
2430
- }
2431
- } else if (allowTriple && state.sliceDoc(pos - 2 * token.length, pos) == token + token && (start = canStartStringAt(state, pos - 2 * token.length, stringPrefixes)) > -1 && nodeStart(state, start)) return {
2432
- changes: {
2433
- insert: token + token + token + token,
2434
- from: pos
2435
- },
2436
- effects: closeBracketEffect.of(pos + token.length),
2437
- range: EditorSelection.cursor(pos + token.length)
2438
- };
2439
- else if (state.charCategorizer(pos)(next) != CharCategory.Word) {
2440
- if (canStartStringAt(state, pos, stringPrefixes) > -1 && !probablyInString(state, pos, token, stringPrefixes)) return {
2441
- changes: {
2442
- insert: token + token,
2443
- from: pos
2444
- },
2445
- effects: closeBracketEffect.of(pos + token.length),
2446
- range: EditorSelection.cursor(pos + token.length)
2447
- };
2448
- }
2449
- return { range: dont = range };
2450
- });
2451
- return dont ? null : state.update(changes, {
2452
- scrollIntoView: true,
2453
- userEvent: "input.type"
2454
- });
2455
- }
2456
- function nodeStart(state, pos) {
2457
- let tree = syntaxTree(state).resolveInner(pos + 1);
2458
- return tree.parent && tree.from == pos;
2459
- }
2460
- function probablyInString(state, pos, quoteToken, prefixes) {
2461
- let node = syntaxTree(state).resolveInner(pos, -1);
2462
- let maxPrefix = prefixes.reduce((m, p) => Math.max(m, p.length), 0);
2463
- for (let i = 0; i < 5; i++) {
2464
- let start = state.sliceDoc(node.from, Math.min(node.to, node.from + quoteToken.length + maxPrefix));
2465
- let quotePos = start.indexOf(quoteToken);
2466
- if (!quotePos || quotePos > -1 && prefixes.indexOf(start.slice(0, quotePos)) > -1) {
2467
- let first = node.firstChild;
2468
- while (first && first.from == node.from && first.to - first.from > quoteToken.length + quotePos) {
2469
- if (state.sliceDoc(first.to - quoteToken.length, first.to) == quoteToken) return false;
2470
- first = first.firstChild;
2471
- }
2472
- return true;
2473
- }
2474
- let parent = node.to == pos && node.parent;
2475
- if (!parent) break;
2476
- node = parent;
2477
- }
2478
- return false;
2479
- }
2480
- function canStartStringAt(state, pos, prefixes) {
2481
- let charCat = state.charCategorizer(pos);
2482
- if (charCat(state.sliceDoc(pos - 1, pos)) != CharCategory.Word) return pos;
2483
- for (let prefix of prefixes) {
2484
- let start = pos - prefix.length;
2485
- if (state.sliceDoc(start, pos) == prefix && charCat(state.sliceDoc(start - 1, start)) != CharCategory.Word) return start;
2486
- }
2487
- return -1;
2488
- }
2489
- /**
2490
- Returns an extension that enables autocompletion.
2491
- */
2492
- function autocompletion(config$1 = {}) {
2493
- return [
2494
- commitCharacters,
2495
- completionState,
2496
- completionConfig.of(config$1),
2497
- completionPlugin,
2498
- completionKeymapExt,
2499
- baseTheme$2
2500
- ];
2501
- }
2502
- /**
2503
- Basic keybindings for autocompletion.
2504
-
2505
- - Ctrl-Space (and Alt-\` on macOS): [`startCompletion`](https://codemirror.net/6/docs/ref/#autocomplete.startCompletion)
2506
- - Escape: [`closeCompletion`](https://codemirror.net/6/docs/ref/#autocomplete.closeCompletion)
2507
- - ArrowDown: [`moveCompletionSelection`](https://codemirror.net/6/docs/ref/#autocomplete.moveCompletionSelection)`(true)`
2508
- - ArrowUp: [`moveCompletionSelection`](https://codemirror.net/6/docs/ref/#autocomplete.moveCompletionSelection)`(false)`
2509
- - PageDown: [`moveCompletionSelection`](https://codemirror.net/6/docs/ref/#autocomplete.moveCompletionSelection)`(true, "page")`
2510
- - PageDown: [`moveCompletionSelection`](https://codemirror.net/6/docs/ref/#autocomplete.moveCompletionSelection)`(true, "page")`
2511
- - Enter: [`acceptCompletion`](https://codemirror.net/6/docs/ref/#autocomplete.acceptCompletion)
2512
- */
2513
- const completionKeymap = [
2514
- {
2515
- key: "Ctrl-Space",
2516
- run: startCompletion
2517
- },
2518
- {
2519
- mac: "Alt-`",
2520
- run: startCompletion
2521
- },
2522
- {
2523
- key: "Escape",
2524
- run: closeCompletion
2525
- },
2526
- {
2527
- key: "ArrowDown",
2528
- run: /* @__PURE__ */ moveCompletionSelection(true)
2529
- },
2530
- {
2531
- key: "ArrowUp",
2532
- run: /* @__PURE__ */ moveCompletionSelection(false)
2533
- },
2534
- {
2535
- key: "PageDown",
2536
- run: /* @__PURE__ */ moveCompletionSelection(true, "page")
2537
- },
2538
- {
2539
- key: "PageUp",
2540
- run: /* @__PURE__ */ moveCompletionSelection(false, "page")
2541
- },
2542
- {
2543
- key: "Enter",
2544
- run: acceptCompletion
2545
- }
2546
- ];
2547
- const completionKeymapExt = /* @__PURE__ */ Prec.highest(/* @__PURE__ */ keymap.computeN([completionConfig], (state) => state.facet(completionConfig).defaultKeymap ? [completionKeymap] : []));
2548
-
2549
- //#endregion
2550
- //#region ../../node_modules/@codemirror/lint/dist/index.js
2551
- var SelectedDiagnostic = class {
2552
- constructor(from, to, diagnostic) {
2553
- this.from = from;
2554
- this.to = to;
2555
- this.diagnostic = diagnostic;
2556
- }
2557
- };
2558
- var LintState = class LintState {
2559
- constructor(diagnostics, panel, selected) {
2560
- this.diagnostics = diagnostics;
2561
- this.panel = panel;
2562
- this.selected = selected;
2563
- }
2564
- static init(diagnostics, panel, state) {
2565
- let diagnosticFilter = state.facet(lintConfig).markerFilter;
2566
- if (diagnosticFilter) diagnostics = diagnosticFilter(diagnostics, state);
2567
- let sorted = diagnostics.slice().sort((a, b) => a.from - b.from || a.to - b.to);
2568
- let deco = new RangeSetBuilder(), active = [], pos = 0;
2569
- for (let i = 0;;) {
2570
- let next = i == sorted.length ? null : sorted[i];
2571
- if (!next && !active.length) break;
2572
- let from, to;
2573
- if (active.length) {
2574
- from = pos;
2575
- to = active.reduce((p, d) => Math.min(p, d.to), next && next.from > from ? next.from : 1e8);
2576
- } else {
2577
- from = next.from;
2578
- to = next.to;
2579
- active.push(next);
2580
- i++;
2581
- }
2582
- while (i < sorted.length) {
2583
- let next$1 = sorted[i];
2584
- if (next$1.from == from && (next$1.to > next$1.from || next$1.to == from)) {
2585
- active.push(next$1);
2586
- i++;
2587
- to = Math.min(next$1.to, to);
2588
- } else {
2589
- to = Math.min(next$1.from, to);
2590
- break;
2591
- }
2592
- }
2593
- let sev = maxSeverity(active);
2594
- if (active.some((d) => d.from == d.to || d.from == d.to - 1 && state.doc.lineAt(d.from).to == d.from)) deco.add(from, from, Decoration.widget({
2595
- widget: new DiagnosticWidget(sev),
2596
- diagnostics: active.slice()
2597
- }));
2598
- else {
2599
- let markClass = active.reduce((c, d) => d.markClass ? c + " " + d.markClass : c, "");
2600
- deco.add(from, to, Decoration.mark({
2601
- class: "cm-lintRange cm-lintRange-" + sev + markClass,
2602
- diagnostics: active.slice(),
2603
- inclusiveEnd: active.some((a) => a.to > to)
2604
- }));
2605
- }
2606
- pos = to;
2607
- for (let i$1 = 0; i$1 < active.length; i$1++) if (active[i$1].to <= pos) active.splice(i$1--, 1);
2608
- }
2609
- let set = deco.finish();
2610
- return new LintState(set, panel, findDiagnostic(set));
2611
- }
2612
- };
2613
- function findDiagnostic(diagnostics, diagnostic = null, after = 0) {
2614
- let found = null;
2615
- diagnostics.between(after, 1e9, (from, to, { spec }) => {
2616
- if (diagnostic && spec.diagnostics.indexOf(diagnostic) < 0) return;
2617
- if (!found) found = new SelectedDiagnostic(from, to, diagnostic || spec.diagnostics[0]);
2618
- else if (spec.diagnostics.indexOf(found.diagnostic) < 0) return false;
2619
- else found = new SelectedDiagnostic(found.from, to, found.diagnostic);
2620
- });
2621
- return found;
2622
- }
2623
- function hideTooltip(tr, tooltip) {
2624
- let from = tooltip.pos, to = tooltip.end || from;
2625
- let result = tr.state.facet(lintConfig).hideOn(tr, from, to);
2626
- if (result != null) return result;
2627
- let line = tr.startState.doc.lineAt(tooltip.pos);
2628
- return !!(tr.effects.some((e) => e.is(setDiagnosticsEffect)) || tr.changes.touchesRange(line.from, Math.max(line.to, to)));
2629
- }
2630
- function maybeEnableLint(state, effects) {
2631
- return state.field(lintState, false) ? effects : effects.concat(StateEffect.appendConfig.of(lintExtensions));
2632
- }
2633
- /**
2634
- The state effect that updates the set of active diagnostics. Can
2635
- be useful when writing an extension that needs to track these.
2636
- */
2637
- const setDiagnosticsEffect = /* @__PURE__ */ StateEffect.define();
2638
- const togglePanel = /* @__PURE__ */ StateEffect.define();
2639
- const movePanelSelection = /* @__PURE__ */ StateEffect.define();
2640
- const lintState = /* @__PURE__ */ StateField.define({
2641
- create() {
2642
- return new LintState(Decoration.none, null, null);
2643
- },
2644
- update(value, tr) {
2645
- if (tr.docChanged && value.diagnostics.size) {
2646
- let mapped = value.diagnostics.map(tr.changes), selected = null, panel = value.panel;
2647
- if (value.selected) {
2648
- let selPos = tr.changes.mapPos(value.selected.from, 1);
2649
- selected = findDiagnostic(mapped, value.selected.diagnostic, selPos) || findDiagnostic(mapped, null, selPos);
2650
- }
2651
- if (!mapped.size && panel && tr.state.facet(lintConfig).autoPanel) panel = null;
2652
- value = new LintState(mapped, panel, selected);
2653
- }
2654
- for (let effect of tr.effects) if (effect.is(setDiagnosticsEffect)) {
2655
- let panel = !tr.state.facet(lintConfig).autoPanel ? value.panel : effect.value.length ? LintPanel.open : null;
2656
- value = LintState.init(effect.value, panel, tr.state);
2657
- } else if (effect.is(togglePanel)) value = new LintState(value.diagnostics, effect.value ? LintPanel.open : null, value.selected);
2658
- else if (effect.is(movePanelSelection)) value = new LintState(value.diagnostics, value.panel, effect.value);
2659
- return value;
2660
- },
2661
- provide: (f) => [showPanel.from(f, (val) => val.panel), EditorView.decorations.from(f, (s) => s.diagnostics)]
2662
- });
2663
- const activeMark = /* @__PURE__ */ Decoration.mark({ class: "cm-lintRange cm-lintRange-active" });
2664
- function lintTooltip(view, pos, side) {
2665
- let { diagnostics } = view.state.field(lintState);
2666
- let found, start = -1, end = -1;
2667
- diagnostics.between(pos - (side < 0 ? 1 : 0), pos + (side > 0 ? 1 : 0), (from, to, { spec }) => {
2668
- if (pos >= from && pos <= to && (from == to || (pos > from || side > 0) && (pos < to || side < 0))) {
2669
- found = spec.diagnostics;
2670
- start = from;
2671
- end = to;
2672
- return false;
2673
- }
2674
- });
2675
- let diagnosticFilter = view.state.facet(lintConfig).tooltipFilter;
2676
- if (found && diagnosticFilter) found = diagnosticFilter(found, view.state);
2677
- if (!found) return null;
2678
- return {
2679
- pos: start,
2680
- end,
2681
- above: view.state.doc.lineAt(start).to < end,
2682
- create() {
2683
- return { dom: diagnosticsTooltip(view, found) };
2684
- }
2685
- };
2686
- }
2687
- function diagnosticsTooltip(view, diagnostics) {
2688
- return crelt("ul", { class: "cm-tooltip-lint" }, diagnostics.map((d) => renderDiagnostic(view, d, false)));
2689
- }
2690
- /**
2691
- Command to open and focus the lint panel.
2692
- */
2693
- const openLintPanel = (view) => {
2694
- let field = view.state.field(lintState, false);
2695
- if (!field || !field.panel) view.dispatch({ effects: maybeEnableLint(view.state, [togglePanel.of(true)]) });
2696
- let panel = getPanel(view, LintPanel.open);
2697
- if (panel) panel.dom.querySelector(".cm-panel-lint ul").focus();
2698
- return true;
2699
- };
2700
- /**
2701
- Command to close the lint panel, when open.
2702
- */
2703
- const closeLintPanel = (view) => {
2704
- let field = view.state.field(lintState, false);
2705
- if (!field || !field.panel) return false;
2706
- view.dispatch({ effects: togglePanel.of(false) });
2707
- return true;
2708
- };
2709
- /**
2710
- Move the selection to the next diagnostic.
2711
- */
2712
- const nextDiagnostic = (view) => {
2713
- let field = view.state.field(lintState, false);
2714
- if (!field) return false;
2715
- let sel = view.state.selection.main, next = field.diagnostics.iter(sel.to + 1);
2716
- if (!next.value) {
2717
- next = field.diagnostics.iter(0);
2718
- if (!next.value || next.from == sel.from && next.to == sel.to) return false;
2719
- }
2720
- view.dispatch({
2721
- selection: {
2722
- anchor: next.from,
2723
- head: next.to
2724
- },
2725
- scrollIntoView: true
2726
- });
2727
- return true;
2728
- };
2729
- /**
2730
- A set of default key bindings for the lint functionality.
2731
-
2732
- - Ctrl-Shift-m (Cmd-Shift-m on macOS): [`openLintPanel`](https://codemirror.net/6/docs/ref/#lint.openLintPanel)
2733
- - F8: [`nextDiagnostic`](https://codemirror.net/6/docs/ref/#lint.nextDiagnostic)
2734
- */
2735
- const lintKeymap = [{
2736
- key: "Mod-Shift-m",
2737
- run: openLintPanel,
2738
- preventDefault: true
2739
- }, {
2740
- key: "F8",
2741
- run: nextDiagnostic
2742
- }];
2743
- const lintConfig = /* @__PURE__ */ Facet.define({ combine(input) {
2744
- return Object.assign({ sources: input.map((i) => i.source).filter((x) => x != null) }, combineConfig(input.map((i) => i.config), {
2745
- delay: 750,
2746
- markerFilter: null,
2747
- tooltipFilter: null,
2748
- needsRefresh: null,
2749
- hideOn: () => null
2750
- }, { needsRefresh: (a, b) => !a ? b : !b ? a : (u) => a(u) || b(u) }));
2751
- } });
2752
- function assignKeys(actions) {
2753
- let assigned = [];
2754
- if (actions) actions: for (let { name } of actions) {
2755
- for (let i = 0; i < name.length; i++) {
2756
- let ch = name[i];
2757
- if (/[a-zA-Z]/.test(ch) && !assigned.some((c) => c.toLowerCase() == ch.toLowerCase())) {
2758
- assigned.push(ch);
2759
- continue actions;
2760
- }
2761
- }
2762
- assigned.push("");
2763
- }
2764
- return assigned;
2765
- }
2766
- function renderDiagnostic(view, diagnostic, inPanel) {
2767
- var _a;
2768
- let keys = inPanel ? assignKeys(diagnostic.actions) : [];
2769
- return crelt("li", { class: "cm-diagnostic cm-diagnostic-" + diagnostic.severity }, crelt("span", { class: "cm-diagnosticText" }, diagnostic.renderMessage ? diagnostic.renderMessage(view) : diagnostic.message), (_a = diagnostic.actions) === null || _a === void 0 ? void 0 : _a.map((action, i) => {
2770
- let fired = false, click = (e) => {
2771
- e.preventDefault();
2772
- if (fired) return;
2773
- fired = true;
2774
- let found = findDiagnostic(view.state.field(lintState).diagnostics, diagnostic);
2775
- if (found) action.apply(view, found.from, found.to);
2776
- };
2777
- let { name } = action, keyIndex = keys[i] ? name.indexOf(keys[i]) : -1;
2778
- let nameElt = keyIndex < 0 ? name : [
2779
- name.slice(0, keyIndex),
2780
- crelt("u", name.slice(keyIndex, keyIndex + 1)),
2781
- name.slice(keyIndex + 1)
2782
- ];
2783
- return crelt("button", {
2784
- type: "button",
2785
- class: "cm-diagnosticAction",
2786
- onclick: click,
2787
- onmousedown: click,
2788
- "aria-label": ` Action: ${name}${keyIndex < 0 ? "" : ` (access key "${keys[i]})"`}.`
2789
- }, nameElt);
2790
- }), diagnostic.source && crelt("div", { class: "cm-diagnosticSource" }, diagnostic.source));
2791
- }
2792
- var DiagnosticWidget = class extends WidgetType {
2793
- constructor(sev) {
2794
- super();
2795
- this.sev = sev;
2796
- }
2797
- eq(other) {
2798
- return other.sev == this.sev;
2799
- }
2800
- toDOM() {
2801
- return crelt("span", { class: "cm-lintPoint cm-lintPoint-" + this.sev });
2802
- }
2803
- };
2804
- var PanelItem = class {
2805
- constructor(view, diagnostic) {
2806
- this.diagnostic = diagnostic;
2807
- this.id = "item_" + Math.floor(Math.random() * 4294967295).toString(16);
2808
- this.dom = renderDiagnostic(view, diagnostic, true);
2809
- this.dom.id = this.id;
2810
- this.dom.setAttribute("role", "option");
2811
- }
2812
- };
2813
- var LintPanel = class LintPanel {
2814
- constructor(view) {
2815
- this.view = view;
2816
- this.items = [];
2817
- let onkeydown = (event) => {
2818
- if (event.keyCode == 27) {
2819
- closeLintPanel(this.view);
2820
- this.view.focus();
2821
- } else if (event.keyCode == 38 || event.keyCode == 33) this.moveSelection((this.selectedIndex - 1 + this.items.length) % this.items.length);
2822
- else if (event.keyCode == 40 || event.keyCode == 34) this.moveSelection((this.selectedIndex + 1) % this.items.length);
2823
- else if (event.keyCode == 36) this.moveSelection(0);
2824
- else if (event.keyCode == 35) this.moveSelection(this.items.length - 1);
2825
- else if (event.keyCode == 13) this.view.focus();
2826
- else if (event.keyCode >= 65 && event.keyCode <= 90 && this.selectedIndex >= 0) {
2827
- let { diagnostic } = this.items[this.selectedIndex], keys = assignKeys(diagnostic.actions);
2828
- for (let i = 0; i < keys.length; i++) if (keys[i].toUpperCase().charCodeAt(0) == event.keyCode) {
2829
- let found = findDiagnostic(this.view.state.field(lintState).diagnostics, diagnostic);
2830
- if (found) diagnostic.actions[i].apply(view, found.from, found.to);
2831
- }
2832
- } else return;
2833
- event.preventDefault();
2834
- };
2835
- let onclick = (event) => {
2836
- for (let i = 0; i < this.items.length; i++) if (this.items[i].dom.contains(event.target)) this.moveSelection(i);
2837
- };
2838
- this.list = crelt("ul", {
2839
- tabIndex: 0,
2840
- role: "listbox",
2841
- "aria-label": this.view.state.phrase("Diagnostics"),
2842
- onkeydown,
2843
- onclick
2844
- });
2845
- this.dom = crelt("div", { class: "cm-panel-lint" }, this.list, crelt("button", {
2846
- type: "button",
2847
- name: "close",
2848
- "aria-label": this.view.state.phrase("close"),
2849
- onclick: () => closeLintPanel(this.view)
2850
- }, "×"));
2851
- this.update();
2852
- }
2853
- get selectedIndex() {
2854
- let selected = this.view.state.field(lintState).selected;
2855
- if (!selected) return -1;
2856
- for (let i = 0; i < this.items.length; i++) if (this.items[i].diagnostic == selected.diagnostic) return i;
2857
- return -1;
2858
- }
2859
- update() {
2860
- let { diagnostics, selected } = this.view.state.field(lintState);
2861
- let i = 0, needsSync = false, newSelectedItem = null;
2862
- let seen = /* @__PURE__ */ new Set();
2863
- diagnostics.between(0, this.view.state.doc.length, (_start, _end, { spec }) => {
2864
- for (let diagnostic of spec.diagnostics) {
2865
- if (seen.has(diagnostic)) continue;
2866
- seen.add(diagnostic);
2867
- let found = -1, item;
2868
- for (let j = i; j < this.items.length; j++) if (this.items[j].diagnostic == diagnostic) {
2869
- found = j;
2870
- break;
2871
- }
2872
- if (found < 0) {
2873
- item = new PanelItem(this.view, diagnostic);
2874
- this.items.splice(i, 0, item);
2875
- needsSync = true;
2876
- } else {
2877
- item = this.items[found];
2878
- if (found > i) {
2879
- this.items.splice(i, found - i);
2880
- needsSync = true;
2881
- }
2882
- }
2883
- if (selected && item.diagnostic == selected.diagnostic) {
2884
- if (!item.dom.hasAttribute("aria-selected")) {
2885
- item.dom.setAttribute("aria-selected", "true");
2886
- newSelectedItem = item;
2887
- }
2888
- } else if (item.dom.hasAttribute("aria-selected")) item.dom.removeAttribute("aria-selected");
2889
- i++;
2890
- }
2891
- });
2892
- while (i < this.items.length && !(this.items.length == 1 && this.items[0].diagnostic.from < 0)) {
2893
- needsSync = true;
2894
- this.items.pop();
2895
- }
2896
- if (this.items.length == 0) {
2897
- this.items.push(new PanelItem(this.view, {
2898
- from: -1,
2899
- to: -1,
2900
- severity: "info",
2901
- message: this.view.state.phrase("No diagnostics")
2902
- }));
2903
- needsSync = true;
2904
- }
2905
- if (newSelectedItem) {
2906
- this.list.setAttribute("aria-activedescendant", newSelectedItem.id);
2907
- this.view.requestMeasure({
2908
- key: this,
2909
- read: () => ({
2910
- sel: newSelectedItem.dom.getBoundingClientRect(),
2911
- panel: this.list.getBoundingClientRect()
2912
- }),
2913
- write: ({ sel, panel }) => {
2914
- let scaleY = panel.height / this.list.offsetHeight;
2915
- if (sel.top < panel.top) this.list.scrollTop -= (panel.top - sel.top) / scaleY;
2916
- else if (sel.bottom > panel.bottom) this.list.scrollTop += (sel.bottom - panel.bottom) / scaleY;
2917
- }
2918
- });
2919
- } else if (this.selectedIndex < 0) this.list.removeAttribute("aria-activedescendant");
2920
- if (needsSync) this.sync();
2921
- }
2922
- sync() {
2923
- let domPos = this.list.firstChild;
2924
- function rm() {
2925
- let prev = domPos;
2926
- domPos = prev.nextSibling;
2927
- prev.remove();
2928
- }
2929
- for (let item of this.items) if (item.dom.parentNode == this.list) {
2930
- while (domPos != item.dom) rm();
2931
- domPos = item.dom.nextSibling;
2932
- } else this.list.insertBefore(item.dom, domPos);
2933
- while (domPos) rm();
2934
- }
2935
- moveSelection(selectedIndex) {
2936
- if (this.selectedIndex < 0) return;
2937
- let field = this.view.state.field(lintState);
2938
- let selection = findDiagnostic(field.diagnostics, this.items[selectedIndex].diagnostic);
2939
- if (!selection) return;
2940
- this.view.dispatch({
2941
- selection: {
2942
- anchor: selection.from,
2943
- head: selection.to
2944
- },
2945
- scrollIntoView: true,
2946
- effects: movePanelSelection.of(selection)
2947
- });
2948
- }
2949
- static open(view) {
2950
- return new LintPanel(view);
2951
- }
2952
- };
2953
- function svg(content, attrs = `viewBox="0 0 40 40"`) {
2954
- return `url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" ${attrs}>${encodeURIComponent(content)}</svg>')`;
2955
- }
2956
- function underline(color) {
2957
- return svg(`<path d="m0 2.5 l2 -1.5 l1 0 l2 1.5 l1 0" stroke="${color}" fill="none" stroke-width=".7"/>`, `width="6" height="3"`);
2958
- }
2959
- const baseTheme$1 = /* @__PURE__ */ EditorView.baseTheme({
2960
- ".cm-diagnostic": {
2961
- padding: "3px 6px 3px 8px",
2962
- marginLeft: "-1px",
2963
- display: "block",
2964
- whiteSpace: "pre-wrap"
2965
- },
2966
- ".cm-diagnostic-error": { borderLeft: "5px solid #d11" },
2967
- ".cm-diagnostic-warning": { borderLeft: "5px solid orange" },
2968
- ".cm-diagnostic-info": { borderLeft: "5px solid #999" },
2969
- ".cm-diagnostic-hint": { borderLeft: "5px solid #66d" },
2970
- ".cm-diagnosticAction": {
2971
- font: "inherit",
2972
- border: "none",
2973
- padding: "2px 4px",
2974
- backgroundColor: "#444",
2975
- color: "white",
2976
- borderRadius: "3px",
2977
- marginLeft: "8px",
2978
- cursor: "pointer"
2979
- },
2980
- ".cm-diagnosticSource": {
2981
- fontSize: "70%",
2982
- opacity: .7
2983
- },
2984
- ".cm-lintRange": {
2985
- backgroundPosition: "left bottom",
2986
- backgroundRepeat: "repeat-x",
2987
- paddingBottom: "0.7px"
2988
- },
2989
- ".cm-lintRange-error": { backgroundImage: /* @__PURE__ */ underline("#d11") },
2990
- ".cm-lintRange-warning": { backgroundImage: /* @__PURE__ */ underline("orange") },
2991
- ".cm-lintRange-info": { backgroundImage: /* @__PURE__ */ underline("#999") },
2992
- ".cm-lintRange-hint": { backgroundImage: /* @__PURE__ */ underline("#66d") },
2993
- ".cm-lintRange-active": { backgroundColor: "#ffdd9980" },
2994
- ".cm-tooltip-lint": {
2995
- padding: 0,
2996
- margin: 0
2997
- },
2998
- ".cm-lintPoint": {
2999
- position: "relative",
3000
- "&:after": {
3001
- content: "\"\"",
3002
- position: "absolute",
3003
- bottom: 0,
3004
- left: "-2px",
3005
- borderLeft: "3px solid transparent",
3006
- borderRight: "3px solid transparent",
3007
- borderBottom: "4px solid #d11"
3008
- }
3009
- },
3010
- ".cm-lintPoint-warning": { "&:after": { borderBottomColor: "orange" } },
3011
- ".cm-lintPoint-info": { "&:after": { borderBottomColor: "#999" } },
3012
- ".cm-lintPoint-hint": { "&:after": { borderBottomColor: "#66d" } },
3013
- ".cm-panel.cm-panel-lint": {
3014
- position: "relative",
3015
- "& ul": {
3016
- maxHeight: "100px",
3017
- overflowY: "auto",
3018
- "& [aria-selected]": {
3019
- backgroundColor: "#ddd",
3020
- "& u": { textDecoration: "underline" }
3021
- },
3022
- "&:focus [aria-selected]": {
3023
- background_fallback: "#bdf",
3024
- backgroundColor: "Highlight",
3025
- color_fallback: "white",
3026
- color: "HighlightText"
3027
- },
3028
- "& u": { textDecoration: "none" },
3029
- padding: 0,
3030
- margin: 0
3031
- },
3032
- "& [name=close]": {
3033
- position: "absolute",
3034
- top: "0",
3035
- right: "2px",
3036
- background: "inherit",
3037
- border: "none",
3038
- font: "inherit",
3039
- padding: 0,
3040
- margin: 0
3041
- }
3042
- }
3043
- });
3044
- function severityWeight(sev) {
3045
- return sev == "error" ? 4 : sev == "warning" ? 3 : sev == "info" ? 2 : 1;
3046
- }
3047
- function maxSeverity(diagnostics) {
3048
- let sev = "hint", weight = 1;
3049
- for (let d of diagnostics) {
3050
- let w = severityWeight(d.severity);
3051
- if (w > weight) {
3052
- weight = w;
3053
- sev = d.severity;
3054
- }
3055
- }
3056
- return sev;
3057
- }
3058
- const lintExtensions = [
3059
- lintState,
3060
- /* @__PURE__ */ EditorView.decorations.compute([lintState], (state) => {
3061
- let { selected, panel } = state.field(lintState);
3062
- return !selected || !panel || selected.from == selected.to ? Decoration.none : Decoration.set([activeMark.range(selected.from, selected.to)]);
3063
- }),
3064
- /* @__PURE__ */ hoverTooltip(lintTooltip, { hideOn: hideTooltip }),
3065
- baseTheme$1
3066
- ];
3067
-
3068
- //#endregion
3069
11
  //#region lib/utils.ts
3070
12
  function stateWORDAt(state, pos) {
3071
13
  const { text, from, length } = state.doc.lineAt(pos);
@@ -3109,12 +51,17 @@ function eventHandlersWithClass(handlers) {
3109
51
 
3110
52
  //#endregion
3111
53
  //#region lib/hide/core.ts
3112
- const hideTheme = EditorView.theme({ ".cm-hidden-token": { fontSize: "0px" } });
54
+ const hideTheme = EditorView.theme({
55
+ ".cm-hidden-token": { fontSize: "0px" },
56
+ ".cm-transparent-token": { opacity: 0 }
57
+ });
3113
58
  const hideInlineDecoration = Decoration.mark({ class: "cm-hidden-token" });
59
+ const hideInlineKeepSpaceDecoration = Decoration.mark({ class: "cm-transparent-token" });
3114
60
  const hideBlockDecoration = Decoration.replace({ block: true });
3115
61
  const buildDecorations$1 = (state) => {
3116
62
  const decorations = [];
3117
63
  const specs = state.facet(hidableNodeFacet);
64
+ specs.map(checkSpec);
3118
65
  syntaxTree(state).iterate({ enter: (node) => {
3119
66
  if (state.selection.ranges.some((range) => rangeTouchesRange(node, range))) return;
3120
67
  for (const spec of specs) {
@@ -3136,11 +83,9 @@ const buildDecorations$1 = (state) => {
3136
83
  let names;
3137
84
  if (!Array.isArray(spec.subNodeNameToHide)) names = [spec.subNodeNameToHide];
3138
85
  else names = spec.subNodeNameToHide;
3139
- const cursor = node.node.cursor();
3140
- console.assert(cursor.firstChild(), "A hide node must have children");
3141
- do
3142
- if (names.includes(cursor.type.name)) decorations.push((spec.block ? hideBlockDecoration : hideInlineDecoration).range(cursor.from, cursor.to));
3143
- while (cursor.nextSibling());
86
+ node.node.cursor().iterate((node$1) => {
87
+ if (names.includes(node$1.type.name)) decorations.push((spec.block ? hideBlockDecoration : spec.keepSpace ? hideInlineKeepSpaceDecoration : hideInlineDecoration).range(node$1.from, node$1.to));
88
+ });
3144
89
  }
3145
90
  }
3146
91
  } });
@@ -3156,6 +101,9 @@ const hideExtension = StateField.define({
3156
101
  },
3157
102
  provide: (f) => [EditorView.decorations.from(f), hideTheme]
3158
103
  });
104
+ const checkSpec = (spec) => {
105
+ if (spec.block && spec.keepSpace) console.warn("Only inline hide nodes can maintain space currently, but `block` and `keepSpace` are set in:", spec);
106
+ };
3159
107
  const hidableNodeFacet = Facet.define({
3160
108
  combine(value) {
3161
109
  return [...value];
@@ -3173,7 +121,8 @@ const markdownTags = {
3173
121
  escapeMark: Tag.define(),
3174
122
  emoji: Tag.define(),
3175
123
  emojiMark: Tag.define(),
3176
- listMark: Tag.define()
124
+ listMark: Tag.define(),
125
+ dash: Tag.define()
3177
126
  };
3178
127
 
3179
128
  //#endregion
@@ -3226,7 +175,8 @@ const defaultHidableSpecs = [
3226
175
  },
3227
176
  {
3228
177
  nodeName: "Blockquote",
3229
- subNodeNameToHide: "QuoteMark"
178
+ subNodeNameToHide: "QuoteMark",
179
+ keepSpace: true
3230
180
  }
3231
181
  ];
3232
182
  const defaultHideExtensions = defaultHidableSpecs.map((spec) => hidableNodeFacet.of(spec));
@@ -3245,31 +195,102 @@ const escapeMarkdownSyntaxExtension = {
3245
195
  }]
3246
196
  };
3247
197
 
198
+ //#endregion
199
+ //#region lib/blockQuote.ts
200
+ var NestedBlockQuoteBorder = class extends WidgetType {
201
+ constructor(offset) {
202
+ super();
203
+ this.offset = offset;
204
+ }
205
+ toDOM() {
206
+ const span = document.createElement("span");
207
+ span.className = "cm-nested-blockquote-border";
208
+ span.style = `--blockquote-border-offset: ${this.offset.toString()}px`;
209
+ return span;
210
+ }
211
+ ignoreEvent(_event) {
212
+ return false;
213
+ }
214
+ };
215
+ function measureBlockQuotes(view) {
216
+ const lineFroms = [];
217
+ const nestedBorders = [];
218
+ syntaxTree(view.state).iterate({ enter(node) {
219
+ if (node.type.name != "Blockquote") return;
220
+ const startLine = view.state.doc.lineAt(node.from).number;
221
+ const endLine = view.state.doc.lineAt(node.to).number;
222
+ for (let i = startLine; i <= endLine; i++) {
223
+ const line = view.state.doc.line(i);
224
+ lineFroms.push(line.from);
225
+ }
226
+ node.node.cursor().iterate((child) => {
227
+ if (child.type.name !== "QuoteMark") return;
228
+ const line = view.state.doc.lineAt(child.from);
229
+ if (child.from == line.from) return;
230
+ const offset = (view.coordsAtPos(child.from)?.left ?? 0) - (view.coordsAtPos(line.from)?.left ?? 0);
231
+ nestedBorders.push({
232
+ pos: child.from,
233
+ offset
234
+ });
235
+ });
236
+ return false;
237
+ } });
238
+ return {
239
+ lineFroms,
240
+ nestedBorders
241
+ };
242
+ }
243
+ function buildDecorationsFromMeasure(_view, data) {
244
+ const decos = [];
245
+ for (const from of data.lineFroms) decos.push(Decoration.line({ attributes: { class: "cm-blockquote-line" } }).range(from));
246
+ for (const { pos, offset } of data.nestedBorders) decos.push(Decoration.widget({ widget: new NestedBlockQuoteBorder(offset) }).range(pos));
247
+ return RangeSet.of(decos, true);
248
+ }
249
+ const blockQuoteExtension = ViewPlugin.fromClass(class {
250
+ decorations = Decoration.none;
251
+ constructor(view) {
252
+ this.requestMeasure(view);
253
+ }
254
+ update(u) {
255
+ if (u.docChanged || u.viewportChanged) this.requestMeasure(u.view);
256
+ }
257
+ requestMeasure(view) {
258
+ view.requestMeasure({
259
+ read: (v) => measureBlockQuotes(v),
260
+ write: (data, v) => {
261
+ this.applyMeasure(data, v);
262
+ }
263
+ });
264
+ }
265
+ applyMeasure(data, view) {
266
+ this.decorations = buildDecorationsFromMeasure(view, data);
267
+ }
268
+ }, { decorations: (v) => v.decorations });
269
+
3248
270
  //#endregion
3249
271
  //#region lib/fold/core.ts
3250
272
  const buildDecorations = (state) => {
3251
273
  const decorations = [];
3252
274
  const specs = state.facet(foldableSyntaxFacet);
3253
275
  syntaxTree(state).iterate({ enter: (node) => {
3254
- if (selectionTouchesRange(state.selection.ranges, node)) return;
276
+ const selectionTouchesNodeRange = selectionTouchesRange(state.selection.ranges, node);
277
+ const lineage = [];
278
+ let node_ = node;
279
+ while (node_) {
280
+ lineage.push(node_.name);
281
+ node_ = node_.node.parent;
282
+ }
283
+ const path = lineage.reverse().join("/");
3255
284
  for (const spec of specs) {
3256
- const lineage = [];
3257
- let node_ = node;
3258
- while (node_) {
3259
- lineage.push(node_.name);
3260
- node_ = node_.node.parent;
3261
- }
3262
- const path = lineage.reverse().join("/");
3263
285
  if (spec.nodePath instanceof Function) {
3264
286
  if (!spec.nodePath(path)) continue;
3265
287
  } else if (spec.nodePath instanceof Array) {
3266
288
  if (!spec.nodePath.some((testPath) => path.endsWith(testPath))) continue;
3267
289
  } else if (!path.endsWith(spec.nodePath)) continue;
3268
- if (spec.unfoldZone) {
3269
- if (selectionTouchesRange(state.selection.ranges, spec.unfoldZone(state, node))) return;
3270
- }
3271
- if (spec.onFold) {
3272
- const res = spec.onFold(state, node);
290
+ const selectionTouchesRange_ = spec.unfoldZone ? selectionTouchesRange(state.selection.ranges, spec.unfoldZone(state, node)) : selectionTouchesNodeRange;
291
+ if (!spec.keepDecorationOnUnfold && selectionTouchesRange_) return;
292
+ if (spec.buildDecorations) {
293
+ const res = spec.buildDecorations(state, node, selectionTouchesRange_);
3273
294
  if (res instanceof Array) decorations.push(...res);
3274
295
  else if (res) decorations.push(res);
3275
296
  }
@@ -3277,7 +298,7 @@ const buildDecorations = (state) => {
3277
298
  } });
3278
299
  return Decoration.set(decorations, true);
3279
300
  };
3280
- const foldDecorationExtension = StateField.define({
301
+ const foldExtension = StateField.define({
3281
302
  create(state) {
3282
303
  return buildDecorations(state);
3283
304
  },
@@ -3287,7 +308,6 @@ const foldDecorationExtension = StateField.define({
3287
308
  },
3288
309
  provide: (f) => [EditorView.decorations.from(f)]
3289
310
  });
3290
- const foldExtension = [foldDecorationExtension];
3291
311
  const foldableSyntaxFacet = Facet.define({
3292
312
  combine(value) {
3293
313
  return [...value];
@@ -3299,7 +319,7 @@ const selectAllDecorationsOnSelectExtension = (widgetClass) => EditorView.domEve
3299
319
  if (ranges.length === 0 || ranges[0]?.anchor !== ranges[0]?.head) return;
3300
320
  const target = e.target;
3301
321
  const pos = view.posAtDOM(target);
3302
- view.state.field(foldDecorationExtension).between(pos, pos, (from, to) => {
322
+ view.state.field(foldExtension).between(pos, pos, (from, to) => {
3303
323
  setTimeout(() => {
3304
324
  view.dispatch({ selection: EditorSelection.single(to, from) });
3305
325
  }, 0);
@@ -3322,13 +342,55 @@ var BulletPoint = class extends WidgetType {
3322
342
  };
3323
343
  const bulletListExtension = foldableSyntaxFacet.of({
3324
344
  nodePath: "BulletList/ListItem/ListMark",
3325
- onFold: (_state, node) => {
345
+ buildDecorations: (_state, node) => {
3326
346
  const cursor = node.node.cursor();
3327
347
  if (cursor.nextSibling() && cursor.name === "Task") return;
3328
348
  return Decoration.replace({ widget: new BulletPoint() }).range(node.from, node.to);
3329
349
  }
3330
350
  });
3331
351
 
352
+ //#endregion
353
+ //#region lib/fold/dashes.ts
354
+ const dashMarkdownSyntaxExtension = {
355
+ defineNodes: [{
356
+ name: "Dash",
357
+ style: markdownTags.dash
358
+ }],
359
+ parseInline: [{
360
+ name: "Dash",
361
+ parse: (cx, next, pos) => {
362
+ if (next !== 45 || pos > 1 && cx.char(pos - 1) == 45) return -1;
363
+ let i;
364
+ for (i = pos; i < cx.end && cx.char(i) === 45; i++);
365
+ if (i - pos > 3) return -1;
366
+ return cx.addElement(cx.elt("Dash", pos, i));
367
+ },
368
+ before: "Emphasis"
369
+ }]
370
+ };
371
+ var DashWidget = class extends WidgetType {
372
+ constructor(dashCount) {
373
+ super();
374
+ this.dashCount = dashCount;
375
+ }
376
+ toDOM() {
377
+ const span = document.createElement("span");
378
+ span.className = "cm-dash";
379
+ if (this.dashCount === 2) span.innerHTML = "&#8211;";
380
+ else if (this.dashCount === 3) span.innerHTML = "&#8212;";
381
+ else span.innerHTML = "-".repeat(this.dashCount);
382
+ return span;
383
+ }
384
+ };
385
+ const dashExtension = foldableSyntaxFacet.of({
386
+ nodePath: "Dash",
387
+ buildDecorations: (_state, node) => {
388
+ const dashCount = node.to - node.from;
389
+ if (dashCount < 2 || dashCount > 3) return;
390
+ return Decoration.replace({ widget: new DashWidget(dashCount) }).range(node.from, node.to);
391
+ }
392
+ });
393
+
3332
394
  //#endregion
3333
395
  //#region lib/fold/emoji.ts
3334
396
  const emojiDelimiter = {
@@ -3369,7 +431,7 @@ var EmojiWidget = class extends WidgetType {
3369
431
  };
3370
432
  const emojiExtension = foldableSyntaxFacet.of({
3371
433
  nodePath: "Emoji",
3372
- onFold: (state, node) => {
434
+ buildDecorations: (state, node) => {
3373
435
  const emojiName = state.doc.sliceString(node.from + 1, node.to - 1);
3374
436
  const emoji_ = emoji.get(emojiName);
3375
437
  if (!emoji_) return;
@@ -3394,16 +456,19 @@ var HorizontalRuleWidget = class extends WidgetType {
3394
456
  dom.remove();
3395
457
  }
3396
458
  };
3397
- const horizontalRuleTheme = EditorView.theme({ ".cm-horizontal-rule-container": {
3398
- height: "1.4em",
3399
- display: "flow-root",
3400
- "align-items": "center",
3401
- padding: "0 2px 0 6px"
3402
- } });
459
+ const horizontalRuleTheme = EditorView.theme({
460
+ ".cm-horizontal-rule-container": {
461
+ height: "1.4em",
462
+ display: "flex",
463
+ "align-items": "center",
464
+ padding: "0 2px 0 6px"
465
+ },
466
+ ".cm-horizontal-rule-container hr": { width: "100%" }
467
+ });
3403
468
  const horizonalRuleExtension = [
3404
469
  foldableSyntaxFacet.of({
3405
470
  nodePath: "HorizontalRule",
3406
- onFold: (_state, node) => {
471
+ buildDecorations: (_state, node) => {
3407
472
  return Decoration.replace({
3408
473
  widget: new HorizontalRuleWidget(),
3409
474
  block: true,
@@ -3418,15 +483,17 @@ const horizonalRuleExtension = [
3418
483
  //#endregion
3419
484
  //#region lib/fold/image.ts
3420
485
  var ImageWidget = class extends WidgetType {
3421
- constructor(url) {
486
+ constructor(url, block) {
3422
487
  super();
3423
488
  this.url = url;
489
+ this.block = block;
3424
490
  }
3425
491
  toDOM() {
3426
- const span = document.createElement("span");
3427
- span.className = "cm-image";
3428
- if (this.url) span.innerHTML = `<img src="${this.url}" />`;
3429
- return span;
492
+ const elem = document.createElement(this.block ? "div" : "span");
493
+ elem.className = "cm-image";
494
+ if (this.block) elem.className += " cm-image-block";
495
+ elem.innerHTML = `<img src="${this.url}" />`;
496
+ return elem;
3430
497
  }
3431
498
  ignoreEvent(_event) {
3432
499
  return false;
@@ -3434,12 +501,25 @@ var ImageWidget = class extends WidgetType {
3434
501
  };
3435
502
  const imageExtension = [foldableSyntaxFacet.of({
3436
503
  nodePath: "Image",
3437
- onFold: (state, node) => {
504
+ keepDecorationOnUnfold: true,
505
+ buildDecorations: (state, node, selectionTouchesRange$1) => {
3438
506
  let imageUrl;
3439
507
  iterChildren(node.node.cursor(), (node$1) => {
3440
508
  if (node$1.name === "URL") imageUrl = state.doc.sliceString(node$1.from, node$1.to);
3441
509
  });
3442
- if (imageUrl) return Decoration.replace({ widget: new ImageWidget(imageUrl) }).range(node.from, node.to);
510
+ if (imageUrl) {
511
+ const line = state.doc.lineAt(node.from);
512
+ const block = node.from == line.from && node.to == line.to;
513
+ const widget = new ImageWidget(imageUrl, block);
514
+ if (selectionTouchesRange$1) return Decoration.widget({
515
+ widget,
516
+ block
517
+ }).range(node.to, node.to);
518
+ else return Decoration.replace({
519
+ widget,
520
+ block
521
+ }).range(node.from, node.to);
522
+ }
3443
523
  }
3444
524
  }), selectAllDecorationsOnSelectExtension("cm-image")];
3445
525
 
@@ -3464,7 +544,7 @@ var Checkbox = class extends WidgetType {
3464
544
  };
3465
545
  const taskExtension = [foldableSyntaxFacet.of({
3466
546
  nodePath: "BulletList/ListItem/Task/TaskMarker",
3467
- onFold: (state, node) => {
547
+ buildDecorations: (state, node) => {
3468
548
  const value = state.doc.sliceString(node.from + 1, node.to - 1).toLowerCase() === "x";
3469
549
  return Decoration.replace({ widget: new Checkbox(value) }).range(node.from - 2, node.to);
3470
550
  },
@@ -3484,23 +564,39 @@ const taskExtension = [foldableSyntaxFacet.of({
3484
564
  } } }))];
3485
565
 
3486
566
  //#endregion
3487
- //#region lib/fold/blockQuote.ts
3488
- var BlockQuoteVerticalLine = class extends WidgetType {
3489
- toDOM() {
3490
- const span = document.createElement("span");
3491
- span.className = "cm-blockquote-vertical-line";
3492
- return span;
3493
- }
3494
- ignoreEvent(_event) {
3495
- return false;
567
+ //#region lib/fold/index.ts
568
+ const defaultFoldableSyntaxExtensions = [
569
+ blockQuoteExtension,
570
+ bulletListExtension,
571
+ taskExtension,
572
+ imageExtension,
573
+ emojiExtension,
574
+ horizonalRuleExtension,
575
+ dashExtension
576
+ ];
577
+
578
+ //#endregion
579
+ //#region lib/revealBlockOnArrow.ts
580
+ const maybeReveal = (decorationField, view, direction) => {
581
+ const decorations = view.state.field(decorationField);
582
+ const cursorAt = view.state.selection.main.head;
583
+ for (let iter = decorations.iter(); iter.value; iter.next()) if (direction === "down" && cursorAt == iter.from - 1) {
584
+ view.dispatch({ selection: EditorSelection.single(iter.from) });
585
+ return true;
586
+ } else if (direction === "up" && cursorAt == iter.to + 1) {
587
+ view.dispatch({ selection: EditorSelection.single(iter.to) });
588
+ return true;
3496
589
  }
590
+ return false;
3497
591
  };
3498
- const blockQuoteExtension = foldableSyntaxFacet.of({
3499
- nodePath: (nodePath) => nodePath.endsWith("Blockquote/QuoteMark") || nodePath.endsWith("Blockquote/Paragraph/QuoteMark"),
3500
- onFold: (_state, node) => {
3501
- return Decoration.replace({ widget: new BlockQuoteVerticalLine() }).range(node.from, node.to);
3502
- }
3503
- });
592
+ const revealBlockOnArrowExtension_ = (decorationField) => keymap.of([{
593
+ key: "ArrowUp",
594
+ run: (view) => maybeReveal(decorationField, view, "up")
595
+ }, {
596
+ key: "ArrowDown",
597
+ run: (view) => maybeReveal(decorationField, view, "down")
598
+ }]);
599
+ const revealBlockOnArrowExtension = [hideExtension, foldExtension].map(revealBlockOnArrowExtension_);
3504
600
 
3505
601
  //#endregion
3506
602
  //#region lib/syntaxHighlighting.ts
@@ -3596,15 +692,29 @@ const baseTheme = EditorView.theme({
3596
692
  color: "var(--pm-muted-color)",
3597
693
  margin: "0 0.2em"
3598
694
  },
3599
- ".cm-blockquote-vertical-line": {
3600
- display: "inline-block",
3601
- width: "4px",
3602
- marginRight: "4px",
3603
- marginLeft: "4px",
3604
- height: "1.4em",
3605
- verticalAlign: "bottom",
3606
- backgroundColor: "var(--pm-blockquote-vertical-line-background-color)"
3607
- }
695
+ ".cm-blockquote-line": {
696
+ position: "relative",
697
+ "&:before": {
698
+ content: "\"\"",
699
+ display: "block",
700
+ borderLeft: "solid 0.3em var(--pm-blockquote-vertical-line-background-color)",
701
+ position: "absolute",
702
+ top: "0px",
703
+ bottom: "0px",
704
+ insetInlineStart: "6px",
705
+ zIndex: -10
706
+ }
707
+ },
708
+ ".cm-nested-blockquote-border": {
709
+ display: "block",
710
+ borderLeft: "solid 0.3em var(--pm-blockquote-vertical-line-background-color)",
711
+ position: "absolute",
712
+ top: "0px",
713
+ bottom: "0px",
714
+ insetInlineStart: "calc(6px + var(--blockquote-border-offset))",
715
+ zIndex: -10
716
+ },
717
+ ".cm-image-block": { paddingLeft: "6px" }
3608
718
  });
3609
719
  const generalSyntaxHighlights = syntaxHighlighting(HighlightStyle.define([
3610
720
  {
@@ -3767,6 +877,70 @@ const clickLinkExtension = [
3767
877
  clickRawUrlExtension
3768
878
  ];
3769
879
 
880
+ //#endregion
881
+ //#region lib/softIndentExtension.ts
882
+ const softIndentPattern = /^(> )*(\s*)?(([-*+]?|\d[.)])\s)?(\[.\]\s)?/;
883
+ const softIndentRefresh = Annotation.define();
884
+ const softIndentExtension = ViewPlugin.fromClass(class {
885
+ decorations = Decoration.none;
886
+ constructor(view) {
887
+ this.requestMeasure(view);
888
+ }
889
+ update(u) {
890
+ if (u.docChanged || u.viewportChanged || u.selectionSet) this.requestMeasure(u.view);
891
+ if (u.transactions.some((tr) => tr.annotation(softIndentRefresh))) this.requestMeasure(u.view);
892
+ }
893
+ requestMeasure(view) {
894
+ view.requestMeasure({
895
+ read: (view$1) => this.measureIndents(view$1),
896
+ write: (indents, view$1) => {
897
+ this.applyIndents(indents, view$1);
898
+ }
899
+ });
900
+ }
901
+ measureIndents(view) {
902
+ const indents = [];
903
+ for (const { from, to } of view.visibleRanges) {
904
+ const start = view.state.doc.lineAt(from);
905
+ const end = view.state.doc.lineAt(to);
906
+ for (let i = start.number; i <= end.number; i++) {
907
+ const line = view.state.doc.line(i);
908
+ const text = view.state.sliceDoc(line.from, line.to);
909
+ const matches = softIndentPattern.exec(text);
910
+ if (!matches) continue;
911
+ const nonContent = matches[0];
912
+ const indentWidth = (view.coordsAtPos(line.from + nonContent.length)?.left ?? 0) - (view.coordsAtPos(line.from)?.left ?? 0);
913
+ if (!indentWidth) continue;
914
+ indents.push({
915
+ line,
916
+ indentWidth
917
+ });
918
+ }
919
+ }
920
+ return indents;
921
+ }
922
+ buildDecorations(indents) {
923
+ const builder = new RangeSetBuilder();
924
+ for (const { line, indentWidth } of indents) {
925
+ const deco = Decoration.line({ attributes: { style: `padding-inline-start: ${(indentWidth + 6).toString()}px; text-indent: -${indentWidth.toString()}px;` } });
926
+ builder.add(line.from, line.from, deco);
927
+ }
928
+ return builder.finish();
929
+ }
930
+ applyIndents(indents, view) {
931
+ const newDecos = this.buildDecorations(indents);
932
+ let changed = false;
933
+ for (const { from, to } of view.visibleRanges) if (!RangeSet.eq([this.decorations], [newDecos], from, to)) {
934
+ changed = true;
935
+ break;
936
+ }
937
+ if (changed) queueMicrotask(() => {
938
+ view.dispatch({ annotations: [softIndentRefresh.of(true)] });
939
+ });
940
+ this.decorations = newDecos;
941
+ }
942
+ }, { decorations: (v) => v.decorations });
943
+
3770
944
  //#endregion
3771
945
  //#region lib/codeFenceExtension.ts
3772
946
  const codeBlockDecorations = (view) => {
@@ -3887,17 +1061,17 @@ const codeFenceTheme = EditorView.theme({
3887
1061
  const prosemarkMarkdownSyntaxExtensions = [
3888
1062
  additionalMarkdownSyntaxTags,
3889
1063
  escapeMarkdownSyntaxExtension,
3890
- emojiMarkdownSyntaxExtension
3891
- ];
3892
- const defaultFoldableSyntaxExtensions = [
3893
- blockQuoteExtension,
3894
- bulletListExtension,
3895
- taskExtension,
3896
- imageExtension,
3897
- emojiExtension,
3898
- horizonalRuleExtension
1064
+ emojiMarkdownSyntaxExtension,
1065
+ dashMarkdownSyntaxExtension
3899
1066
  ];
3900
1067
  const prosemarkBasicSetup = () => [
1068
+ defaultHideExtensions,
1069
+ defaultFoldableSyntaxExtensions,
1070
+ revealBlockOnArrowExtension,
1071
+ clickLinkExtension,
1072
+ defaultClickLinkHandler,
1073
+ softIndentExtension,
1074
+ codeBlockDecorationsExtension,
3901
1075
  history(),
3902
1076
  dropCursor(),
3903
1077
  indentOnInput(),
@@ -3915,11 +1089,7 @@ const prosemarkBasicSetup = () => [
3915
1089
  indentWithTab
3916
1090
  ]),
3917
1091
  foldGutter(),
3918
- EditorView.lineWrapping,
3919
- defaultHideExtensions,
3920
- defaultFoldableSyntaxExtensions,
3921
- clickLinkExtension,
3922
- codeBlockDecorationsExtension
1092
+ EditorView.lineWrapping
3923
1093
  ];
3924
1094
  const prosemarkBaseThemeSetup = () => [
3925
1095
  baseSyntaxHighlights,
@@ -3927,7 +1097,7 @@ const prosemarkBaseThemeSetup = () => [
3927
1097
  baseTheme,
3928
1098
  codeFenceTheme
3929
1099
  ];
3930
- const prosemarkLightThemeSetup = () => lightTheme;
1100
+ const prosemarkLightThemeSetup = () => [prosemarkBaseThemeSetup(), lightTheme];
3931
1101
 
3932
1102
  //#endregion
3933
- export { additionalMarkdownSyntaxTags, baseSyntaxHighlights, baseTheme, blockQuoteExtension, bulletListExtension, clickLinkExtension, clickLinkHandler, codeBlockDecorationsExtension, codeFenceTheme, defaultClickLinkHandler, defaultFoldableSyntaxExtensions, defaultHideExtensions, emojiExtension, emojiMarkdownSyntaxExtension, escapeMarkdownSyntaxExtension, eventHandlersWithClass, foldDecorationExtension, foldableSyntaxFacet, generalSyntaxHighlights, horizonalRuleExtension, imageExtension, lightTheme, markdownTags, prosemarkBaseThemeSetup, prosemarkBasicSetup, prosemarkLightThemeSetup, prosemarkMarkdownSyntaxExtensions, selectAllDecorationsOnSelectExtension, taskExtension };
1103
+ export { additionalMarkdownSyntaxTags, baseSyntaxHighlights, baseTheme, blockQuoteExtension, bulletListExtension, clickLinkExtension, clickLinkHandler, codeBlockDecorationsExtension, codeFenceTheme, dashExtension, dashMarkdownSyntaxExtension, defaultClickLinkHandler, defaultFoldableSyntaxExtensions, defaultHideExtensions, emojiExtension, emojiMarkdownSyntaxExtension, escapeMarkdownSyntaxExtension, eventHandlersWithClass, foldExtension, foldableSyntaxFacet, generalSyntaxHighlights, hideExtension, horizonalRuleExtension, imageExtension, lightTheme, markdownTags, prosemarkBaseThemeSetup, prosemarkBasicSetup, prosemarkLightThemeSetup, prosemarkMarkdownSyntaxExtensions, revealBlockOnArrowExtension, selectAllDecorationsOnSelectExtension, softIndentExtension, taskExtension };