symfony-expression-editor 0.1.0

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.
package/dist/index.cjs ADDED
@@ -0,0 +1,1886 @@
1
+ 'use strict';
2
+
3
+ var view = require('@codemirror/view');
4
+ var state = require('@codemirror/state');
5
+ var language = require('@codemirror/language');
6
+ var commands = require('@codemirror/commands');
7
+ var autocomplete = require('@codemirror/autocomplete');
8
+ var codemirrorLangEl = require('@valtzu/codemirror-lang-el');
9
+ var highlight = require('@lezer/highlight');
10
+
11
+ function crelt() {
12
+ var elt = arguments[0];
13
+ if (typeof elt == "string") elt = document.createElement(elt);
14
+ var i = 1, next = arguments[1];
15
+ if (next && typeof next == "object" && next.nodeType == null && !Array.isArray(next)) {
16
+ for (var name in next) if (Object.prototype.hasOwnProperty.call(next, name)) {
17
+ var value = next[name];
18
+ if (typeof value == "string") elt.setAttribute(name, value);
19
+ else if (value != null) elt[name] = value;
20
+ }
21
+ i++;
22
+ }
23
+ for (; i < arguments.length; i++) add(elt, arguments[i]);
24
+ return elt;
25
+ }
26
+ function add(elt, child) {
27
+ if (typeof child == "string") {
28
+ elt.appendChild(document.createTextNode(child));
29
+ } else if (child == null) ; else if (child.nodeType != null) {
30
+ elt.appendChild(child);
31
+ } else if (Array.isArray(child)) {
32
+ for (var i = 0; i < child.length; i++) add(elt, child[i]);
33
+ } else {
34
+ throw new RangeError("Unsupported child node: " + child);
35
+ }
36
+ }
37
+
38
+ const basicNormalize = typeof String.prototype.normalize == "function" ? (x) => x.normalize("NFKD") : (x) => x;
39
+ class SearchCursor {
40
+ /**
41
+ Create a text cursor. The query is the search string, `from` to
42
+ `to` provides the region to search.
43
+
44
+ When `normalize` is given, it will be called, on both the query
45
+ string and the content it is matched against, before comparing.
46
+ You can, for example, create a case-insensitive search by
47
+ passing `s => s.toLowerCase()`.
48
+
49
+ Text is always normalized with
50
+ [`.normalize("NFKD")`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize)
51
+ (when supported).
52
+ */
53
+ constructor(text, query, from = 0, to = text.length, normalize, test) {
54
+ this.test = test;
55
+ this.value = { from: 0, to: 0 };
56
+ this.done = false;
57
+ this.matches = [];
58
+ this.buffer = "";
59
+ this.bufferPos = 0;
60
+ this.iter = text.iterRange(from, to);
61
+ this.bufferStart = from;
62
+ this.normalize = normalize ? (x) => normalize(basicNormalize(x)) : basicNormalize;
63
+ this.query = this.normalize(query);
64
+ }
65
+ peek() {
66
+ if (this.bufferPos == this.buffer.length) {
67
+ this.bufferStart += this.buffer.length;
68
+ this.iter.next();
69
+ if (this.iter.done)
70
+ return -1;
71
+ this.bufferPos = 0;
72
+ this.buffer = this.iter.value;
73
+ }
74
+ return state.codePointAt(this.buffer, this.bufferPos);
75
+ }
76
+ /**
77
+ Look for the next match. Updates the iterator's
78
+ [`value`](https://codemirror.net/6/docs/ref/#search.SearchCursor.value) and
79
+ [`done`](https://codemirror.net/6/docs/ref/#search.SearchCursor.done) properties. Should be called
80
+ at least once before using the cursor.
81
+ */
82
+ next() {
83
+ while (this.matches.length)
84
+ this.matches.pop();
85
+ return this.nextOverlapping();
86
+ }
87
+ /**
88
+ The `next` method will ignore matches that partially overlap a
89
+ previous match. This method behaves like `next`, but includes
90
+ such matches.
91
+ */
92
+ nextOverlapping() {
93
+ for (; ; ) {
94
+ let next = this.peek();
95
+ if (next < 0) {
96
+ this.done = true;
97
+ return this;
98
+ }
99
+ let str = state.fromCodePoint(next), start = this.bufferStart + this.bufferPos;
100
+ this.bufferPos += state.codePointSize(next);
101
+ let norm = this.normalize(str);
102
+ if (norm.length)
103
+ for (let i = 0, pos = start; ; i++) {
104
+ let code = norm.charCodeAt(i);
105
+ let match = this.match(code, pos, this.bufferPos + this.bufferStart);
106
+ if (i == norm.length - 1) {
107
+ if (match) {
108
+ this.value = match;
109
+ return this;
110
+ }
111
+ break;
112
+ }
113
+ if (pos == start && i < str.length && str.charCodeAt(i) == code)
114
+ pos++;
115
+ }
116
+ }
117
+ }
118
+ match(code, pos, end) {
119
+ let match = null;
120
+ for (let i = 0; i < this.matches.length; i += 2) {
121
+ let index = this.matches[i], keep = false;
122
+ if (this.query.charCodeAt(index) == code) {
123
+ if (index == this.query.length - 1) {
124
+ match = { from: this.matches[i + 1], to: end };
125
+ } else {
126
+ this.matches[i]++;
127
+ keep = true;
128
+ }
129
+ }
130
+ if (!keep) {
131
+ this.matches.splice(i, 2);
132
+ i -= 2;
133
+ }
134
+ }
135
+ if (this.query.charCodeAt(0) == code) {
136
+ if (this.query.length == 1)
137
+ match = { from: pos, to: end };
138
+ else
139
+ this.matches.push(1, pos);
140
+ }
141
+ if (match && this.test && !this.test(match.from, match.to, this.buffer, this.bufferStart))
142
+ match = null;
143
+ return match;
144
+ }
145
+ }
146
+ if (typeof Symbol != "undefined")
147
+ SearchCursor.prototype[Symbol.iterator] = function() {
148
+ return this;
149
+ };
150
+ const empty = { from: -1, to: -1, match: /* @__PURE__ */ /.*/.exec("") };
151
+ const baseFlags = "gm" + (/x/.unicode == null ? "" : "u");
152
+ class RegExpCursor {
153
+ /**
154
+ Create a cursor that will search the given range in the given
155
+ document. `query` should be the raw pattern (as you'd pass it to
156
+ `new RegExp`).
157
+ */
158
+ constructor(text, query, options, from = 0, to = text.length) {
159
+ this.text = text;
160
+ this.to = to;
161
+ this.curLine = "";
162
+ this.done = false;
163
+ this.value = empty;
164
+ if (/\\[sWDnr]|\n|\r|\[\^/.test(query))
165
+ return new MultilineRegExpCursor(text, query, options, from, to);
166
+ this.re = new RegExp(query, baseFlags + ((options === null || options === void 0 ? void 0 : options.ignoreCase) ? "i" : ""));
167
+ this.test = options === null || options === void 0 ? void 0 : options.test;
168
+ this.iter = text.iter();
169
+ let startLine = text.lineAt(from);
170
+ this.curLineStart = startLine.from;
171
+ this.matchPos = toCharEnd(text, from);
172
+ this.getLine(this.curLineStart);
173
+ }
174
+ getLine(skip) {
175
+ this.iter.next(skip);
176
+ if (this.iter.lineBreak) {
177
+ this.curLine = "";
178
+ } else {
179
+ this.curLine = this.iter.value;
180
+ if (this.curLineStart + this.curLine.length > this.to)
181
+ this.curLine = this.curLine.slice(0, this.to - this.curLineStart);
182
+ this.iter.next();
183
+ }
184
+ }
185
+ nextLine() {
186
+ this.curLineStart = this.curLineStart + this.curLine.length + 1;
187
+ if (this.curLineStart > this.to)
188
+ this.curLine = "";
189
+ else
190
+ this.getLine(0);
191
+ }
192
+ /**
193
+ Move to the next match, if there is one.
194
+ */
195
+ next() {
196
+ for (let off = this.matchPos - this.curLineStart; ; ) {
197
+ this.re.lastIndex = off;
198
+ let match = this.matchPos <= this.to && this.re.exec(this.curLine);
199
+ if (match) {
200
+ let from = this.curLineStart + match.index, to = from + match[0].length;
201
+ this.matchPos = toCharEnd(this.text, to + (from == to ? 1 : 0));
202
+ if (from == this.curLineStart + this.curLine.length)
203
+ this.nextLine();
204
+ if ((from < to || from > this.value.to) && (!this.test || this.test(from, to, match))) {
205
+ this.value = { from, to, match };
206
+ return this;
207
+ }
208
+ off = this.matchPos - this.curLineStart;
209
+ } else if (this.curLineStart + this.curLine.length < this.to) {
210
+ this.nextLine();
211
+ off = 0;
212
+ } else {
213
+ this.done = true;
214
+ return this;
215
+ }
216
+ }
217
+ }
218
+ }
219
+ const flattened = /* @__PURE__ */ new WeakMap();
220
+ class FlattenedDoc {
221
+ constructor(from, text) {
222
+ this.from = from;
223
+ this.text = text;
224
+ }
225
+ get to() {
226
+ return this.from + this.text.length;
227
+ }
228
+ static get(doc, from, to) {
229
+ let cached = flattened.get(doc);
230
+ if (!cached || cached.from >= to || cached.to <= from) {
231
+ let flat = new FlattenedDoc(from, doc.sliceString(from, to));
232
+ flattened.set(doc, flat);
233
+ return flat;
234
+ }
235
+ if (cached.from == from && cached.to == to)
236
+ return cached;
237
+ let { text, from: cachedFrom } = cached;
238
+ if (cachedFrom > from) {
239
+ text = doc.sliceString(from, cachedFrom) + text;
240
+ cachedFrom = from;
241
+ }
242
+ if (cached.to < to)
243
+ text += doc.sliceString(cached.to, to);
244
+ flattened.set(doc, new FlattenedDoc(cachedFrom, text));
245
+ return new FlattenedDoc(from, text.slice(from - cachedFrom, to - cachedFrom));
246
+ }
247
+ }
248
+ class MultilineRegExpCursor {
249
+ constructor(text, query, options, from, to) {
250
+ this.text = text;
251
+ this.to = to;
252
+ this.done = false;
253
+ this.value = empty;
254
+ this.matchPos = toCharEnd(text, from);
255
+ this.re = new RegExp(query, baseFlags + ((options === null || options === void 0 ? void 0 : options.ignoreCase) ? "i" : ""));
256
+ this.test = options === null || options === void 0 ? void 0 : options.test;
257
+ this.flat = FlattenedDoc.get(text, from, this.chunkEnd(
258
+ from + 5e3
259
+ /* Chunk.Base */
260
+ ));
261
+ }
262
+ chunkEnd(pos) {
263
+ return pos >= this.to ? this.to : this.text.lineAt(pos).to;
264
+ }
265
+ next() {
266
+ for (; ; ) {
267
+ let off = this.re.lastIndex = this.matchPos - this.flat.from;
268
+ let match = this.re.exec(this.flat.text);
269
+ if (match && !match[0] && match.index == off) {
270
+ this.re.lastIndex = off + 1;
271
+ match = this.re.exec(this.flat.text);
272
+ }
273
+ if (match) {
274
+ let from = this.flat.from + match.index, to = from + match[0].length;
275
+ if ((this.flat.to >= this.to || match.index + match[0].length <= this.flat.text.length - 10) && (!this.test || this.test(from, to, match))) {
276
+ this.value = { from, to, match };
277
+ this.matchPos = toCharEnd(this.text, to + (from == to ? 1 : 0));
278
+ return this;
279
+ }
280
+ }
281
+ if (this.flat.to == this.to) {
282
+ this.done = true;
283
+ return this;
284
+ }
285
+ this.flat = FlattenedDoc.get(this.text, this.flat.from, this.chunkEnd(this.flat.from + this.flat.text.length * 2));
286
+ }
287
+ }
288
+ }
289
+ if (typeof Symbol != "undefined") {
290
+ RegExpCursor.prototype[Symbol.iterator] = MultilineRegExpCursor.prototype[Symbol.iterator] = function() {
291
+ return this;
292
+ };
293
+ }
294
+ function validRegExp(source) {
295
+ try {
296
+ new RegExp(source, baseFlags);
297
+ return true;
298
+ } catch (_a) {
299
+ return false;
300
+ }
301
+ }
302
+ function toCharEnd(text, pos) {
303
+ if (pos >= text.length)
304
+ return pos;
305
+ let line = text.lineAt(pos), next;
306
+ while (pos < line.to && (next = line.text.charCodeAt(pos - line.from)) >= 56320 && next < 57344)
307
+ pos++;
308
+ return pos;
309
+ }
310
+ function createLineDialog(view$1) {
311
+ let line = String(view$1.state.doc.lineAt(view$1.state.selection.main.head).number);
312
+ let input = crelt("input", { class: "cm-textfield", name: "line", value: line });
313
+ let dom = crelt("form", {
314
+ class: "cm-gotoLine",
315
+ onkeydown: (event) => {
316
+ if (event.keyCode == 27) {
317
+ event.preventDefault();
318
+ view$1.dispatch({ effects: dialogEffect.of(false) });
319
+ view$1.focus();
320
+ } else if (event.keyCode == 13) {
321
+ event.preventDefault();
322
+ go();
323
+ }
324
+ },
325
+ onsubmit: (event) => {
326
+ event.preventDefault();
327
+ go();
328
+ }
329
+ }, crelt("label", view$1.state.phrase("Go to line"), ": ", input), " ", crelt("button", { class: "cm-button", type: "submit" }, view$1.state.phrase("go")), crelt("button", {
330
+ name: "close",
331
+ onclick: () => {
332
+ view$1.dispatch({ effects: dialogEffect.of(false) });
333
+ view$1.focus();
334
+ },
335
+ "aria-label": view$1.state.phrase("close"),
336
+ type: "button"
337
+ }, ["\xD7"]));
338
+ function go() {
339
+ let match = /^([+-])?(\d+)?(:\d+)?(%)?$/.exec(input.value);
340
+ if (!match)
341
+ return;
342
+ let { state: state$1 } = view$1, startLine = state$1.doc.lineAt(state$1.selection.main.head);
343
+ let [, sign, ln, cl, percent] = match;
344
+ let col = cl ? +cl.slice(1) : 0;
345
+ let line2 = ln ? +ln : startLine.number;
346
+ if (ln && percent) {
347
+ let pc = line2 / 100;
348
+ if (sign)
349
+ pc = pc * (sign == "-" ? -1 : 1) + startLine.number / state$1.doc.lines;
350
+ line2 = Math.round(state$1.doc.lines * pc);
351
+ } else if (ln && sign) {
352
+ line2 = line2 * (sign == "-" ? -1 : 1) + startLine.number;
353
+ }
354
+ let docLine = state$1.doc.line(Math.max(1, Math.min(state$1.doc.lines, line2)));
355
+ let selection = state.EditorSelection.cursor(docLine.from + Math.max(0, Math.min(col, docLine.length)));
356
+ view$1.dispatch({
357
+ effects: [dialogEffect.of(false), view.EditorView.scrollIntoView(selection.from, { y: "center" })],
358
+ selection
359
+ });
360
+ view$1.focus();
361
+ }
362
+ return { dom };
363
+ }
364
+ const dialogEffect = /* @__PURE__ */ state.StateEffect.define();
365
+ const dialogField = /* @__PURE__ */ state.StateField.define({
366
+ create() {
367
+ return true;
368
+ },
369
+ update(value, tr) {
370
+ for (let e of tr.effects)
371
+ if (e.is(dialogEffect))
372
+ value = e.value;
373
+ return value;
374
+ },
375
+ provide: (f) => view.showPanel.from(f, (val) => val ? createLineDialog : null)
376
+ });
377
+ const gotoLine = (view$1) => {
378
+ let panel = view.getPanel(view$1, createLineDialog);
379
+ if (!panel) {
380
+ let effects = [dialogEffect.of(true)];
381
+ if (view$1.state.field(dialogField, false) == null)
382
+ effects.push(state.StateEffect.appendConfig.of([dialogField, baseTheme$1]));
383
+ view$1.dispatch({ effects });
384
+ panel = view.getPanel(view$1, createLineDialog);
385
+ }
386
+ if (panel)
387
+ panel.dom.querySelector("input").select();
388
+ return true;
389
+ };
390
+ const baseTheme$1 = /* @__PURE__ */ view.EditorView.baseTheme({
391
+ ".cm-panel.cm-gotoLine": {
392
+ padding: "2px 6px 4px",
393
+ position: "relative",
394
+ "& label": { fontSize: "80%" },
395
+ "& [name=close]": {
396
+ position: "absolute",
397
+ top: "0",
398
+ bottom: "0",
399
+ right: "4px",
400
+ backgroundColor: "inherit",
401
+ border: "none",
402
+ font: "inherit",
403
+ padding: "0"
404
+ }
405
+ }
406
+ });
407
+ const defaultHighlightOptions = {
408
+ highlightWordAroundCursor: false,
409
+ minSelectionLength: 1,
410
+ maxMatches: 100,
411
+ wholeWords: false
412
+ };
413
+ const highlightConfig = /* @__PURE__ */ state.Facet.define({
414
+ combine(options) {
415
+ return state.combineConfig(options, defaultHighlightOptions, {
416
+ highlightWordAroundCursor: (a, b) => a || b,
417
+ minSelectionLength: Math.min,
418
+ maxMatches: Math.min
419
+ });
420
+ }
421
+ });
422
+ function highlightSelectionMatches(options) {
423
+ let ext = [defaultTheme, matchHighlighter];
424
+ return ext;
425
+ }
426
+ const matchDeco = /* @__PURE__ */ view.Decoration.mark({ class: "cm-selectionMatch" });
427
+ const mainMatchDeco = /* @__PURE__ */ view.Decoration.mark({ class: "cm-selectionMatch cm-selectionMatch-main" });
428
+ function insideWordBoundaries(check, state$1, from, to) {
429
+ return (from == 0 || check(state$1.sliceDoc(from - 1, from)) != state.CharCategory.Word) && (to == state$1.doc.length || check(state$1.sliceDoc(to, to + 1)) != state.CharCategory.Word);
430
+ }
431
+ function insideWord(check, state$1, from, to) {
432
+ return check(state$1.sliceDoc(from, from + 1)) == state.CharCategory.Word && check(state$1.sliceDoc(to - 1, to)) == state.CharCategory.Word;
433
+ }
434
+ const matchHighlighter = /* @__PURE__ */ view.ViewPlugin.fromClass(class {
435
+ constructor(view) {
436
+ this.decorations = this.getDeco(view);
437
+ }
438
+ update(update) {
439
+ if (update.selectionSet || update.docChanged || update.viewportChanged)
440
+ this.decorations = this.getDeco(update.view);
441
+ }
442
+ getDeco(view$1) {
443
+ let conf = view$1.state.facet(highlightConfig);
444
+ let { state } = view$1, sel = state.selection;
445
+ if (sel.ranges.length > 1)
446
+ return view.Decoration.none;
447
+ let range = sel.main, query, check = null;
448
+ if (range.empty) {
449
+ if (!conf.highlightWordAroundCursor)
450
+ return view.Decoration.none;
451
+ let word = state.wordAt(range.head);
452
+ if (!word)
453
+ return view.Decoration.none;
454
+ check = state.charCategorizer(range.head);
455
+ query = state.sliceDoc(word.from, word.to);
456
+ } else {
457
+ let len = range.to - range.from;
458
+ if (len < conf.minSelectionLength || len > 200)
459
+ return view.Decoration.none;
460
+ if (conf.wholeWords) {
461
+ query = state.sliceDoc(range.from, range.to);
462
+ check = state.charCategorizer(range.head);
463
+ if (!(insideWordBoundaries(check, state, range.from, range.to) && insideWord(check, state, range.from, range.to)))
464
+ return view.Decoration.none;
465
+ } else {
466
+ query = state.sliceDoc(range.from, range.to);
467
+ if (!query)
468
+ return view.Decoration.none;
469
+ }
470
+ }
471
+ let deco = [];
472
+ for (let part of view$1.visibleRanges) {
473
+ let cursor = new SearchCursor(state.doc, query, part.from, part.to);
474
+ while (!cursor.next().done) {
475
+ let { from, to } = cursor.value;
476
+ if (!check || insideWordBoundaries(check, state, from, to)) {
477
+ if (range.empty && from <= range.from && to >= range.to)
478
+ deco.push(mainMatchDeco.range(from, to));
479
+ else if (from >= range.to || to <= range.from)
480
+ deco.push(matchDeco.range(from, to));
481
+ if (deco.length > conf.maxMatches)
482
+ return view.Decoration.none;
483
+ }
484
+ }
485
+ }
486
+ return view.Decoration.set(deco);
487
+ }
488
+ }, {
489
+ decorations: (v) => v.decorations
490
+ });
491
+ const defaultTheme = /* @__PURE__ */ view.EditorView.baseTheme({
492
+ ".cm-selectionMatch": { backgroundColor: "#99ff7780" },
493
+ ".cm-searchMatch .cm-selectionMatch": { backgroundColor: "transparent" }
494
+ });
495
+ const selectWord = ({ state: state$1, dispatch }) => {
496
+ let { selection } = state$1;
497
+ let newSel = state.EditorSelection.create(selection.ranges.map((range) => state$1.wordAt(range.head) || state.EditorSelection.cursor(range.head)), selection.mainIndex);
498
+ if (newSel.eq(selection))
499
+ return false;
500
+ dispatch(state$1.update({ selection: newSel }));
501
+ return true;
502
+ };
503
+ function findNextOccurrence(state, query) {
504
+ let { main, ranges } = state.selection;
505
+ let word = state.wordAt(main.head), fullWord = word && word.from == main.from && word.to == main.to;
506
+ for (let cycled = false, cursor = new SearchCursor(state.doc, query, ranges[ranges.length - 1].to); ; ) {
507
+ cursor.next();
508
+ if (cursor.done) {
509
+ if (cycled)
510
+ return null;
511
+ cursor = new SearchCursor(state.doc, query, 0, Math.max(0, ranges[ranges.length - 1].from - 1));
512
+ cycled = true;
513
+ } else {
514
+ if (cycled && ranges.some((r) => r.from == cursor.value.from))
515
+ continue;
516
+ if (fullWord) {
517
+ let word2 = state.wordAt(cursor.value.from);
518
+ if (!word2 || word2.from != cursor.value.from || word2.to != cursor.value.to)
519
+ continue;
520
+ }
521
+ return cursor.value;
522
+ }
523
+ }
524
+ }
525
+ const selectNextOccurrence = ({ state: state$1, dispatch }) => {
526
+ let { ranges } = state$1.selection;
527
+ if (ranges.some((sel) => sel.from === sel.to))
528
+ return selectWord({ state: state$1, dispatch });
529
+ let searchedText = state$1.sliceDoc(ranges[0].from, ranges[0].to);
530
+ if (state$1.selection.ranges.some((r) => state$1.sliceDoc(r.from, r.to) != searchedText))
531
+ return false;
532
+ let range = findNextOccurrence(state$1, searchedText);
533
+ if (!range)
534
+ return false;
535
+ dispatch(state$1.update({
536
+ selection: state$1.selection.addRange(state.EditorSelection.range(range.from, range.to), false),
537
+ effects: view.EditorView.scrollIntoView(range.to)
538
+ }));
539
+ return true;
540
+ };
541
+ const searchConfigFacet = /* @__PURE__ */ state.Facet.define({
542
+ combine(configs) {
543
+ return state.combineConfig(configs, {
544
+ top: false,
545
+ caseSensitive: false,
546
+ literal: false,
547
+ regexp: false,
548
+ wholeWord: false,
549
+ createPanel: (view) => new SearchPanel(view),
550
+ scrollToMatch: (range) => view.EditorView.scrollIntoView(range)
551
+ });
552
+ }
553
+ });
554
+ class SearchQuery {
555
+ /**
556
+ Create a query object.
557
+ */
558
+ constructor(config) {
559
+ this.search = config.search;
560
+ this.caseSensitive = !!config.caseSensitive;
561
+ this.literal = !!config.literal;
562
+ this.regexp = !!config.regexp;
563
+ this.replace = config.replace || "";
564
+ this.valid = !!this.search && (!this.regexp || validRegExp(this.search));
565
+ this.unquoted = this.unquote(this.search);
566
+ this.wholeWord = !!config.wholeWord;
567
+ }
568
+ /**
569
+ @internal
570
+ */
571
+ unquote(text) {
572
+ return this.literal ? text : text.replace(/\\([nrt\\])/g, (_, ch) => ch == "n" ? "\n" : ch == "r" ? "\r" : ch == "t" ? " " : "\\");
573
+ }
574
+ /**
575
+ Compare this query to another query.
576
+ */
577
+ eq(other) {
578
+ return this.search == other.search && this.replace == other.replace && this.caseSensitive == other.caseSensitive && this.regexp == other.regexp && this.wholeWord == other.wholeWord;
579
+ }
580
+ /**
581
+ @internal
582
+ */
583
+ create() {
584
+ return this.regexp ? new RegExpQuery(this) : new StringQuery(this);
585
+ }
586
+ /**
587
+ Get a search cursor for this query, searching through the given
588
+ range in the given state.
589
+ */
590
+ getCursor(state$1, from = 0, to) {
591
+ let st = state$1.doc ? state$1 : state.EditorState.create({ doc: state$1 });
592
+ if (to == null)
593
+ to = st.doc.length;
594
+ return this.regexp ? regexpCursor(this, st, from, to) : stringCursor(this, st, from, to);
595
+ }
596
+ }
597
+ class QueryType {
598
+ constructor(spec) {
599
+ this.spec = spec;
600
+ }
601
+ }
602
+ function stringCursor(spec, state, from, to) {
603
+ 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);
604
+ }
605
+ function stringWordTest(doc, categorizer) {
606
+ return (from, to, buf, bufPos) => {
607
+ if (bufPos > from || bufPos + buf.length < to) {
608
+ bufPos = Math.max(0, from - 2);
609
+ buf = doc.sliceString(bufPos, Math.min(doc.length, to + 2));
610
+ }
611
+ return (categorizer(charBefore(buf, from - bufPos)) != state.CharCategory.Word || categorizer(charAfter(buf, from - bufPos)) != state.CharCategory.Word) && (categorizer(charAfter(buf, to - bufPos)) != state.CharCategory.Word || categorizer(charBefore(buf, to - bufPos)) != state.CharCategory.Word);
612
+ };
613
+ }
614
+ class StringQuery extends QueryType {
615
+ constructor(spec) {
616
+ super(spec);
617
+ }
618
+ nextMatch(state, curFrom, curTo) {
619
+ let cursor = stringCursor(this.spec, state, curTo, state.doc.length).nextOverlapping();
620
+ if (cursor.done) {
621
+ let end = Math.min(state.doc.length, curFrom + this.spec.unquoted.length);
622
+ cursor = stringCursor(this.spec, state, 0, end).nextOverlapping();
623
+ }
624
+ return cursor.done || cursor.value.from == curFrom && cursor.value.to == curTo ? null : cursor.value;
625
+ }
626
+ // Searching in reverse is, rather than implementing an inverted search
627
+ // cursor, done by scanning chunk after chunk forward.
628
+ prevMatchInRange(state, from, to) {
629
+ for (let pos = to; ; ) {
630
+ let start = Math.max(from, pos - 1e4 - this.spec.unquoted.length);
631
+ let cursor = stringCursor(this.spec, state, start, pos), range = null;
632
+ while (!cursor.nextOverlapping().done)
633
+ range = cursor.value;
634
+ if (range)
635
+ return range;
636
+ if (start == from)
637
+ return null;
638
+ pos -= 1e4;
639
+ }
640
+ }
641
+ prevMatch(state, curFrom, curTo) {
642
+ let found = this.prevMatchInRange(state, 0, curFrom);
643
+ if (!found)
644
+ found = this.prevMatchInRange(state, Math.max(0, curTo - this.spec.unquoted.length), state.doc.length);
645
+ return found && (found.from != curFrom || found.to != curTo) ? found : null;
646
+ }
647
+ getReplacement(_result) {
648
+ return this.spec.unquote(this.spec.replace);
649
+ }
650
+ matchAll(state, limit) {
651
+ let cursor = stringCursor(this.spec, state, 0, state.doc.length), ranges = [];
652
+ while (!cursor.next().done) {
653
+ if (ranges.length >= limit)
654
+ return null;
655
+ ranges.push(cursor.value);
656
+ }
657
+ return ranges;
658
+ }
659
+ highlight(state, from, to, add) {
660
+ let cursor = stringCursor(this.spec, state, Math.max(0, from - this.spec.unquoted.length), Math.min(to + this.spec.unquoted.length, state.doc.length));
661
+ while (!cursor.next().done)
662
+ add(cursor.value.from, cursor.value.to);
663
+ }
664
+ }
665
+ function regexpCursor(spec, state, from, to) {
666
+ return new RegExpCursor(state.doc, spec.search, {
667
+ ignoreCase: !spec.caseSensitive,
668
+ test: spec.wholeWord ? regexpWordTest(state.charCategorizer(state.selection.main.head)) : void 0
669
+ }, from, to);
670
+ }
671
+ function charBefore(str, index) {
672
+ return str.slice(state.findClusterBreak(str, index, false), index);
673
+ }
674
+ function charAfter(str, index) {
675
+ return str.slice(index, state.findClusterBreak(str, index));
676
+ }
677
+ function regexpWordTest(categorizer) {
678
+ return (_from, _to, match) => !match[0].length || (categorizer(charBefore(match.input, match.index)) != state.CharCategory.Word || categorizer(charAfter(match.input, match.index)) != state.CharCategory.Word) && (categorizer(charAfter(match.input, match.index + match[0].length)) != state.CharCategory.Word || categorizer(charBefore(match.input, match.index + match[0].length)) != state.CharCategory.Word);
679
+ }
680
+ class RegExpQuery extends QueryType {
681
+ nextMatch(state, curFrom, curTo) {
682
+ let cursor = regexpCursor(this.spec, state, curTo, state.doc.length).next();
683
+ if (cursor.done)
684
+ cursor = regexpCursor(this.spec, state, 0, curFrom).next();
685
+ return cursor.done ? null : cursor.value;
686
+ }
687
+ prevMatchInRange(state, from, to) {
688
+ for (let size = 1; ; size++) {
689
+ let start = Math.max(
690
+ from,
691
+ to - size * 1e4
692
+ /* FindPrev.ChunkSize */
693
+ );
694
+ let cursor = regexpCursor(this.spec, state, start, to), range = null;
695
+ while (!cursor.next().done)
696
+ range = cursor.value;
697
+ if (range && (start == from || range.from > start + 10))
698
+ return range;
699
+ if (start == from)
700
+ return null;
701
+ }
702
+ }
703
+ prevMatch(state, curFrom, curTo) {
704
+ return this.prevMatchInRange(state, 0, curFrom) || this.prevMatchInRange(state, curTo, state.doc.length);
705
+ }
706
+ getReplacement(result) {
707
+ return this.spec.unquote(this.spec.replace).replace(/\$([$&]|\d+)/g, (m, i) => {
708
+ if (i == "&")
709
+ return result.match[0];
710
+ if (i == "$")
711
+ return "$";
712
+ for (let l = i.length; l > 0; l--) {
713
+ let n = +i.slice(0, l);
714
+ if (n > 0 && n < result.match.length)
715
+ return result.match[n] + i.slice(l);
716
+ }
717
+ return m;
718
+ });
719
+ }
720
+ matchAll(state, limit) {
721
+ let cursor = regexpCursor(this.spec, state, 0, state.doc.length), ranges = [];
722
+ while (!cursor.next().done) {
723
+ if (ranges.length >= limit)
724
+ return null;
725
+ ranges.push(cursor.value);
726
+ }
727
+ return ranges;
728
+ }
729
+ highlight(state, from, to, add) {
730
+ let cursor = regexpCursor(this.spec, state, Math.max(
731
+ 0,
732
+ from - 250
733
+ /* RegExp.HighlightMargin */
734
+ ), Math.min(to + 250, state.doc.length));
735
+ while (!cursor.next().done)
736
+ add(cursor.value.from, cursor.value.to);
737
+ }
738
+ }
739
+ const setSearchQuery = /* @__PURE__ */ state.StateEffect.define();
740
+ const togglePanel$1 = /* @__PURE__ */ state.StateEffect.define();
741
+ const searchState = /* @__PURE__ */ state.StateField.define({
742
+ create(state) {
743
+ return new SearchState(defaultQuery(state).create(), null);
744
+ },
745
+ update(value, tr) {
746
+ for (let effect of tr.effects) {
747
+ if (effect.is(setSearchQuery))
748
+ value = new SearchState(effect.value.create(), value.panel);
749
+ else if (effect.is(togglePanel$1))
750
+ value = new SearchState(value.query, effect.value ? createSearchPanel : null);
751
+ }
752
+ return value;
753
+ },
754
+ provide: (f) => view.showPanel.from(f, (val) => val.panel)
755
+ });
756
+ class SearchState {
757
+ constructor(query, panel) {
758
+ this.query = query;
759
+ this.panel = panel;
760
+ }
761
+ }
762
+ const matchMark = /* @__PURE__ */ view.Decoration.mark({ class: "cm-searchMatch" }), selectedMatchMark = /* @__PURE__ */ view.Decoration.mark({ class: "cm-searchMatch cm-searchMatch-selected" });
763
+ const searchHighlighter = /* @__PURE__ */ view.ViewPlugin.fromClass(class {
764
+ constructor(view) {
765
+ this.view = view;
766
+ this.decorations = this.highlight(view.state.field(searchState));
767
+ }
768
+ update(update) {
769
+ let state = update.state.field(searchState);
770
+ if (state != update.startState.field(searchState) || update.docChanged || update.selectionSet || update.viewportChanged)
771
+ this.decorations = this.highlight(state);
772
+ }
773
+ highlight({ query, panel }) {
774
+ if (!panel || !query.spec.valid)
775
+ return view.Decoration.none;
776
+ let { view: view$1 } = this;
777
+ let builder = new state.RangeSetBuilder();
778
+ for (let i = 0, ranges = view$1.visibleRanges, l = ranges.length; i < l; i++) {
779
+ let { from, to } = ranges[i];
780
+ while (i < l - 1 && to > ranges[i + 1].from - 2 * 250)
781
+ to = ranges[++i].to;
782
+ query.highlight(view$1.state, from, to, (from2, to2) => {
783
+ let selected = view$1.state.selection.ranges.some((r) => r.from == from2 && r.to == to2);
784
+ builder.add(from2, to2, selected ? selectedMatchMark : matchMark);
785
+ });
786
+ }
787
+ return builder.finish();
788
+ }
789
+ }, {
790
+ decorations: (v) => v.decorations
791
+ });
792
+ function searchCommand(f) {
793
+ return (view) => {
794
+ let state = view.state.field(searchState, false);
795
+ return state && state.query.spec.valid ? f(view, state) : openSearchPanel(view);
796
+ };
797
+ }
798
+ const findNext = /* @__PURE__ */ searchCommand((view, { query }) => {
799
+ let { to } = view.state.selection.main;
800
+ let next = query.nextMatch(view.state, to, to);
801
+ if (!next)
802
+ return false;
803
+ let selection = state.EditorSelection.single(next.from, next.to);
804
+ let config = view.state.facet(searchConfigFacet);
805
+ view.dispatch({
806
+ selection,
807
+ effects: [announceMatch(view, next), config.scrollToMatch(selection.main, view)],
808
+ userEvent: "select.search"
809
+ });
810
+ selectSearchInput(view);
811
+ return true;
812
+ });
813
+ const findPrevious = /* @__PURE__ */ searchCommand((view, { query }) => {
814
+ let { state: state$1 } = view, { from } = state$1.selection.main;
815
+ let prev = query.prevMatch(state$1, from, from);
816
+ if (!prev)
817
+ return false;
818
+ let selection = state.EditorSelection.single(prev.from, prev.to);
819
+ let config = view.state.facet(searchConfigFacet);
820
+ view.dispatch({
821
+ selection,
822
+ effects: [announceMatch(view, prev), config.scrollToMatch(selection.main, view)],
823
+ userEvent: "select.search"
824
+ });
825
+ selectSearchInput(view);
826
+ return true;
827
+ });
828
+ const selectMatches = /* @__PURE__ */ searchCommand((view, { query }) => {
829
+ let ranges = query.matchAll(view.state, 1e3);
830
+ if (!ranges || !ranges.length)
831
+ return false;
832
+ view.dispatch({
833
+ selection: state.EditorSelection.create(ranges.map((r) => state.EditorSelection.range(r.from, r.to))),
834
+ userEvent: "select.search.matches"
835
+ });
836
+ return true;
837
+ });
838
+ const selectSelectionMatches = ({ state: state$1, dispatch }) => {
839
+ let sel = state$1.selection;
840
+ if (sel.ranges.length > 1 || sel.main.empty)
841
+ return false;
842
+ let { from, to } = sel.main;
843
+ let ranges = [], main = 0;
844
+ for (let cur = new SearchCursor(state$1.doc, state$1.sliceDoc(from, to)); !cur.next().done; ) {
845
+ if (ranges.length > 1e3)
846
+ return false;
847
+ if (cur.value.from == from)
848
+ main = ranges.length;
849
+ ranges.push(state.EditorSelection.range(cur.value.from, cur.value.to));
850
+ }
851
+ dispatch(state$1.update({
852
+ selection: state.EditorSelection.create(ranges, main),
853
+ userEvent: "select.search.matches"
854
+ }));
855
+ return true;
856
+ };
857
+ const replaceNext = /* @__PURE__ */ searchCommand((view$1, { query }) => {
858
+ let { state: state$1 } = view$1, { from, to } = state$1.selection.main;
859
+ if (state$1.readOnly)
860
+ return false;
861
+ let match = query.nextMatch(state$1, from, from);
862
+ if (!match)
863
+ return false;
864
+ let next = match;
865
+ let changes = [], selection, replacement;
866
+ let effects = [];
867
+ if (next.from == from && next.to == to) {
868
+ replacement = state$1.toText(query.getReplacement(next));
869
+ changes.push({ from: next.from, to: next.to, insert: replacement });
870
+ next = query.nextMatch(state$1, next.from, next.to);
871
+ effects.push(view.EditorView.announce.of(state$1.phrase("replaced match on line $", state$1.doc.lineAt(from).number) + "."));
872
+ }
873
+ if (next) {
874
+ let off = changes.length == 0 || changes[0].from >= match.to ? 0 : match.to - match.from - replacement.length;
875
+ selection = state.EditorSelection.single(next.from - off, next.to - off);
876
+ effects.push(announceMatch(view$1, next));
877
+ effects.push(state$1.facet(searchConfigFacet).scrollToMatch(selection.main, view$1));
878
+ }
879
+ view$1.dispatch({
880
+ changes,
881
+ selection,
882
+ effects,
883
+ userEvent: "input.replace"
884
+ });
885
+ return true;
886
+ });
887
+ const replaceAll = /* @__PURE__ */ searchCommand((view$1, { query }) => {
888
+ if (view$1.state.readOnly)
889
+ return false;
890
+ let changes = query.matchAll(view$1.state, 1e9).map((match) => {
891
+ let { from, to } = match;
892
+ return { from, to, insert: query.getReplacement(match) };
893
+ });
894
+ if (!changes.length)
895
+ return false;
896
+ let announceText = view$1.state.phrase("replaced $ matches", changes.length) + ".";
897
+ view$1.dispatch({
898
+ changes,
899
+ effects: view.EditorView.announce.of(announceText),
900
+ userEvent: "input.replace.all"
901
+ });
902
+ return true;
903
+ });
904
+ function createSearchPanel(view) {
905
+ return view.state.facet(searchConfigFacet).createPanel(view);
906
+ }
907
+ function defaultQuery(state, fallback) {
908
+ var _a, _b, _c, _d, _e;
909
+ let sel = state.selection.main;
910
+ let selText = sel.empty || sel.to > sel.from + 100 ? "" : state.sliceDoc(sel.from, sel.to);
911
+ if (fallback && !selText)
912
+ return fallback;
913
+ let config = state.facet(searchConfigFacet);
914
+ return new SearchQuery({
915
+ search: ((_a = fallback === null || fallback === void 0 ? void 0 : fallback.literal) !== null && _a !== void 0 ? _a : config.literal) ? selText : selText.replace(/\n/g, "\\n"),
916
+ caseSensitive: (_b = fallback === null || fallback === void 0 ? void 0 : fallback.caseSensitive) !== null && _b !== void 0 ? _b : config.caseSensitive,
917
+ literal: (_c = fallback === null || fallback === void 0 ? void 0 : fallback.literal) !== null && _c !== void 0 ? _c : config.literal,
918
+ regexp: (_d = fallback === null || fallback === void 0 ? void 0 : fallback.regexp) !== null && _d !== void 0 ? _d : config.regexp,
919
+ wholeWord: (_e = fallback === null || fallback === void 0 ? void 0 : fallback.wholeWord) !== null && _e !== void 0 ? _e : config.wholeWord
920
+ });
921
+ }
922
+ function getSearchInput(view$1) {
923
+ let panel = view.getPanel(view$1, createSearchPanel);
924
+ return panel && panel.dom.querySelector("[main-field]");
925
+ }
926
+ function selectSearchInput(view) {
927
+ let input = getSearchInput(view);
928
+ if (input && input == view.root.activeElement)
929
+ input.select();
930
+ }
931
+ const openSearchPanel = (view) => {
932
+ let state$1 = view.state.field(searchState, false);
933
+ if (state$1 && state$1.panel) {
934
+ let searchInput = getSearchInput(view);
935
+ if (searchInput && searchInput != view.root.activeElement) {
936
+ let query = defaultQuery(view.state, state$1.query.spec);
937
+ if (query.valid)
938
+ view.dispatch({ effects: setSearchQuery.of(query) });
939
+ searchInput.focus();
940
+ searchInput.select();
941
+ }
942
+ } else {
943
+ view.dispatch({ effects: [
944
+ togglePanel$1.of(true),
945
+ state$1 ? setSearchQuery.of(defaultQuery(view.state, state$1.query.spec)) : state.StateEffect.appendConfig.of(searchExtensions)
946
+ ] });
947
+ }
948
+ return true;
949
+ };
950
+ const closeSearchPanel = (view$1) => {
951
+ let state = view$1.state.field(searchState, false);
952
+ if (!state || !state.panel)
953
+ return false;
954
+ let panel = view.getPanel(view$1, createSearchPanel);
955
+ if (panel && panel.dom.contains(view$1.root.activeElement))
956
+ view$1.focus();
957
+ view$1.dispatch({ effects: togglePanel$1.of(false) });
958
+ return true;
959
+ };
960
+ const searchKeymap = [
961
+ { key: "Mod-f", run: openSearchPanel, scope: "editor search-panel" },
962
+ { key: "F3", run: findNext, shift: findPrevious, scope: "editor search-panel", preventDefault: true },
963
+ { key: "Mod-g", run: findNext, shift: findPrevious, scope: "editor search-panel", preventDefault: true },
964
+ { key: "Escape", run: closeSearchPanel, scope: "editor search-panel" },
965
+ { key: "Mod-Shift-l", run: selectSelectionMatches },
966
+ { key: "Mod-Alt-g", run: gotoLine },
967
+ { key: "Mod-d", run: selectNextOccurrence, preventDefault: true }
968
+ ];
969
+ class SearchPanel {
970
+ constructor(view) {
971
+ this.view = view;
972
+ let query = this.query = view.state.field(searchState).query.spec;
973
+ this.commit = this.commit.bind(this);
974
+ this.searchField = crelt("input", {
975
+ value: query.search,
976
+ placeholder: phrase(view, "Find"),
977
+ "aria-label": phrase(view, "Find"),
978
+ class: "cm-textfield",
979
+ name: "search",
980
+ form: "",
981
+ "main-field": "true",
982
+ onchange: this.commit,
983
+ onkeyup: this.commit
984
+ });
985
+ this.replaceField = crelt("input", {
986
+ value: query.replace,
987
+ placeholder: phrase(view, "Replace"),
988
+ "aria-label": phrase(view, "Replace"),
989
+ class: "cm-textfield",
990
+ name: "replace",
991
+ form: "",
992
+ onchange: this.commit,
993
+ onkeyup: this.commit
994
+ });
995
+ this.caseField = crelt("input", {
996
+ type: "checkbox",
997
+ name: "case",
998
+ form: "",
999
+ checked: query.caseSensitive,
1000
+ onchange: this.commit
1001
+ });
1002
+ this.reField = crelt("input", {
1003
+ type: "checkbox",
1004
+ name: "re",
1005
+ form: "",
1006
+ checked: query.regexp,
1007
+ onchange: this.commit
1008
+ });
1009
+ this.wordField = crelt("input", {
1010
+ type: "checkbox",
1011
+ name: "word",
1012
+ form: "",
1013
+ checked: query.wholeWord,
1014
+ onchange: this.commit
1015
+ });
1016
+ function button(name, onclick, content) {
1017
+ return crelt("button", { class: "cm-button", name, onclick, type: "button" }, content);
1018
+ }
1019
+ this.dom = crelt("div", { onkeydown: (e) => this.keydown(e), class: "cm-search" }, [
1020
+ this.searchField,
1021
+ button("next", () => findNext(view), [phrase(view, "next")]),
1022
+ button("prev", () => findPrevious(view), [phrase(view, "previous")]),
1023
+ button("select", () => selectMatches(view), [phrase(view, "all")]),
1024
+ crelt("label", null, [this.caseField, phrase(view, "match case")]),
1025
+ crelt("label", null, [this.reField, phrase(view, "regexp")]),
1026
+ crelt("label", null, [this.wordField, phrase(view, "by word")]),
1027
+ ...view.state.readOnly ? [] : [
1028
+ crelt("br"),
1029
+ this.replaceField,
1030
+ button("replace", () => replaceNext(view), [phrase(view, "replace")]),
1031
+ button("replaceAll", () => replaceAll(view), [phrase(view, "replace all")])
1032
+ ],
1033
+ crelt("button", {
1034
+ name: "close",
1035
+ onclick: () => closeSearchPanel(view),
1036
+ "aria-label": phrase(view, "close"),
1037
+ type: "button"
1038
+ }, ["\xD7"])
1039
+ ]);
1040
+ }
1041
+ commit() {
1042
+ let query = new SearchQuery({
1043
+ search: this.searchField.value,
1044
+ caseSensitive: this.caseField.checked,
1045
+ regexp: this.reField.checked,
1046
+ wholeWord: this.wordField.checked,
1047
+ replace: this.replaceField.value
1048
+ });
1049
+ if (!query.eq(this.query)) {
1050
+ this.query = query;
1051
+ this.view.dispatch({ effects: setSearchQuery.of(query) });
1052
+ }
1053
+ }
1054
+ keydown(e) {
1055
+ if (view.runScopeHandlers(this.view, e, "search-panel")) {
1056
+ e.preventDefault();
1057
+ } else if (e.keyCode == 13 && e.target == this.searchField) {
1058
+ e.preventDefault();
1059
+ (e.shiftKey ? findPrevious : findNext)(this.view);
1060
+ } else if (e.keyCode == 13 && e.target == this.replaceField) {
1061
+ e.preventDefault();
1062
+ replaceNext(this.view);
1063
+ }
1064
+ }
1065
+ update(update) {
1066
+ for (let tr of update.transactions)
1067
+ for (let effect of tr.effects) {
1068
+ if (effect.is(setSearchQuery) && !effect.value.eq(this.query))
1069
+ this.setQuery(effect.value);
1070
+ }
1071
+ }
1072
+ setQuery(query) {
1073
+ this.query = query;
1074
+ this.searchField.value = query.search;
1075
+ this.replaceField.value = query.replace;
1076
+ this.caseField.checked = query.caseSensitive;
1077
+ this.reField.checked = query.regexp;
1078
+ this.wordField.checked = query.wholeWord;
1079
+ }
1080
+ mount() {
1081
+ this.searchField.select();
1082
+ }
1083
+ get pos() {
1084
+ return 80;
1085
+ }
1086
+ get top() {
1087
+ return this.view.state.facet(searchConfigFacet).top;
1088
+ }
1089
+ }
1090
+ function phrase(view, phrase2) {
1091
+ return view.state.phrase(phrase2);
1092
+ }
1093
+ const AnnounceMargin = 30;
1094
+ const Break = /[\s\.,:;?!]/;
1095
+ function announceMatch(view$1, { from, to }) {
1096
+ let line = view$1.state.doc.lineAt(from), lineEnd = view$1.state.doc.lineAt(to).to;
1097
+ let start = Math.max(line.from, from - AnnounceMargin), end = Math.min(lineEnd, to + AnnounceMargin);
1098
+ let text = view$1.state.sliceDoc(start, end);
1099
+ if (start != line.from) {
1100
+ for (let i = 0; i < AnnounceMargin; i++)
1101
+ if (!Break.test(text[i + 1]) && Break.test(text[i])) {
1102
+ text = text.slice(i);
1103
+ break;
1104
+ }
1105
+ }
1106
+ if (end != lineEnd) {
1107
+ for (let i = text.length - 1; i > text.length - AnnounceMargin; i--)
1108
+ if (!Break.test(text[i - 1]) && Break.test(text[i])) {
1109
+ text = text.slice(0, i);
1110
+ break;
1111
+ }
1112
+ }
1113
+ return view.EditorView.announce.of(`${view$1.state.phrase("current match")}. ${text} ${view$1.state.phrase("on line")} ${line.number}.`);
1114
+ }
1115
+ const baseTheme$2 = /* @__PURE__ */ view.EditorView.baseTheme({
1116
+ ".cm-panel.cm-search": {
1117
+ padding: "2px 6px 4px",
1118
+ position: "relative",
1119
+ "& [name=close]": {
1120
+ position: "absolute",
1121
+ top: "0",
1122
+ right: "4px",
1123
+ backgroundColor: "inherit",
1124
+ border: "none",
1125
+ font: "inherit",
1126
+ padding: 0,
1127
+ margin: 0
1128
+ },
1129
+ "& input, & button, & label": {
1130
+ margin: ".2em .6em .2em 0"
1131
+ },
1132
+ "& input[type=checkbox]": {
1133
+ marginRight: ".2em"
1134
+ },
1135
+ "& label": {
1136
+ fontSize: "80%",
1137
+ whiteSpace: "pre"
1138
+ }
1139
+ },
1140
+ "&light .cm-searchMatch": { backgroundColor: "#ffff0054" },
1141
+ "&dark .cm-searchMatch": { backgroundColor: "#00ffff8a" },
1142
+ "&light .cm-searchMatch-selected": { backgroundColor: "#ff6a0054" },
1143
+ "&dark .cm-searchMatch-selected": { backgroundColor: "#ff00ff8a" }
1144
+ });
1145
+ const searchExtensions = [
1146
+ searchState,
1147
+ /* @__PURE__ */ state.Prec.low(searchHighlighter),
1148
+ baseTheme$2
1149
+ ];
1150
+
1151
+ class SelectedDiagnostic {
1152
+ constructor(from, to, diagnostic) {
1153
+ this.from = from;
1154
+ this.to = to;
1155
+ this.diagnostic = diagnostic;
1156
+ }
1157
+ }
1158
+ class LintState {
1159
+ constructor(diagnostics, panel, selected) {
1160
+ this.diagnostics = diagnostics;
1161
+ this.panel = panel;
1162
+ this.selected = selected;
1163
+ }
1164
+ static init(diagnostics, panel, state$1) {
1165
+ let markedDiagnostics = diagnostics;
1166
+ let diagnosticFilter = state$1.facet(lintConfig).markerFilter;
1167
+ if (diagnosticFilter)
1168
+ markedDiagnostics = diagnosticFilter(markedDiagnostics, state$1);
1169
+ let sorted = diagnostics.slice().sort((a, b) => a.from - b.from || a.to - b.to);
1170
+ let deco = new state.RangeSetBuilder(), active = [], pos = 0;
1171
+ for (let i = 0; ; ) {
1172
+ let next = i == sorted.length ? null : sorted[i];
1173
+ if (!next && !active.length)
1174
+ break;
1175
+ let from, to;
1176
+ if (active.length) {
1177
+ from = pos;
1178
+ to = active.reduce((p, d) => Math.min(p, d.to), next && next.from > from ? next.from : 1e8);
1179
+ } else {
1180
+ from = next.from;
1181
+ to = next.to;
1182
+ active.push(next);
1183
+ i++;
1184
+ }
1185
+ while (i < sorted.length) {
1186
+ let next2 = sorted[i];
1187
+ if (next2.from == from && (next2.to > next2.from || next2.to == from)) {
1188
+ active.push(next2);
1189
+ i++;
1190
+ to = Math.min(next2.to, to);
1191
+ } else {
1192
+ to = Math.min(next2.from, to);
1193
+ break;
1194
+ }
1195
+ }
1196
+ let sev = maxSeverity(active);
1197
+ if (active.some((d) => d.from == d.to || d.from == d.to - 1 && state$1.doc.lineAt(d.from).to == d.from)) {
1198
+ deco.add(from, from, view.Decoration.widget({
1199
+ widget: new DiagnosticWidget(sev),
1200
+ diagnostics: active.slice()
1201
+ }));
1202
+ } else {
1203
+ let markClass = active.reduce((c, d) => d.markClass ? c + " " + d.markClass : c, "");
1204
+ deco.add(from, to, view.Decoration.mark({
1205
+ class: "cm-lintRange cm-lintRange-" + sev + markClass,
1206
+ diagnostics: active.slice(),
1207
+ inclusiveEnd: active.some((a) => a.to > to)
1208
+ }));
1209
+ }
1210
+ pos = to;
1211
+ for (let i2 = 0; i2 < active.length; i2++)
1212
+ if (active[i2].to <= pos)
1213
+ active.splice(i2--, 1);
1214
+ }
1215
+ let set = deco.finish();
1216
+ return new LintState(set, panel, findDiagnostic(set));
1217
+ }
1218
+ }
1219
+ function findDiagnostic(diagnostics, diagnostic = null, after = 0) {
1220
+ let found = null;
1221
+ diagnostics.between(after, 1e9, (from, to, { spec }) => {
1222
+ if (diagnostic && spec.diagnostics.indexOf(diagnostic) < 0)
1223
+ return;
1224
+ if (!found)
1225
+ found = new SelectedDiagnostic(from, to, diagnostic || spec.diagnostics[0]);
1226
+ else if (spec.diagnostics.indexOf(found.diagnostic) < 0)
1227
+ return false;
1228
+ else
1229
+ found = new SelectedDiagnostic(found.from, to, found.diagnostic);
1230
+ });
1231
+ return found;
1232
+ }
1233
+ function hideTooltip(tr, tooltip) {
1234
+ let from = tooltip.pos, to = tooltip.end || from;
1235
+ let result = tr.state.facet(lintConfig).hideOn(tr, from, to);
1236
+ if (result != null)
1237
+ return result;
1238
+ let line = tr.startState.doc.lineAt(tooltip.pos);
1239
+ return !!(tr.effects.some((e) => e.is(setDiagnosticsEffect)) || tr.changes.touchesRange(line.from, Math.max(line.to, to)));
1240
+ }
1241
+ function maybeEnableLint(state$1, effects) {
1242
+ return state$1.field(lintState, false) ? effects : effects.concat(state.StateEffect.appendConfig.of(lintExtensions));
1243
+ }
1244
+ const setDiagnosticsEffect = /* @__PURE__ */ state.StateEffect.define();
1245
+ const togglePanel = /* @__PURE__ */ state.StateEffect.define();
1246
+ const movePanelSelection = /* @__PURE__ */ state.StateEffect.define();
1247
+ const lintState = /* @__PURE__ */ state.StateField.define({
1248
+ create() {
1249
+ return new LintState(view.Decoration.none, null, null);
1250
+ },
1251
+ update(value, tr) {
1252
+ if (tr.docChanged && value.diagnostics.size) {
1253
+ let mapped = value.diagnostics.map(tr.changes), selected = null, panel = value.panel;
1254
+ if (value.selected) {
1255
+ let selPos = tr.changes.mapPos(value.selected.from, 1);
1256
+ selected = findDiagnostic(mapped, value.selected.diagnostic, selPos) || findDiagnostic(mapped, null, selPos);
1257
+ }
1258
+ if (!mapped.size && panel && tr.state.facet(lintConfig).autoPanel)
1259
+ panel = null;
1260
+ value = new LintState(mapped, panel, selected);
1261
+ }
1262
+ for (let effect of tr.effects) {
1263
+ if (effect.is(setDiagnosticsEffect)) {
1264
+ let panel = !tr.state.facet(lintConfig).autoPanel ? value.panel : effect.value.length ? LintPanel.open : null;
1265
+ value = LintState.init(effect.value, panel, tr.state);
1266
+ } else if (effect.is(togglePanel)) {
1267
+ value = new LintState(value.diagnostics, effect.value ? LintPanel.open : null, value.selected);
1268
+ } else if (effect.is(movePanelSelection)) {
1269
+ value = new LintState(value.diagnostics, value.panel, effect.value);
1270
+ }
1271
+ }
1272
+ return value;
1273
+ },
1274
+ provide: (f) => [
1275
+ view.showPanel.from(f, (val) => val.panel),
1276
+ view.EditorView.decorations.from(f, (s) => s.diagnostics)
1277
+ ]
1278
+ });
1279
+ const activeMark = /* @__PURE__ */ view.Decoration.mark({ class: "cm-lintRange cm-lintRange-active" });
1280
+ function lintTooltip(view, pos, side) {
1281
+ let { diagnostics } = view.state.field(lintState);
1282
+ let found, start = -1, end = -1;
1283
+ diagnostics.between(pos - (side < 0 ? 1 : 0), pos + (side > 0 ? 1 : 0), (from, to, { spec }) => {
1284
+ if (pos >= from && pos <= to && (from == to || (pos > from || side > 0) && (pos < to || side < 0))) {
1285
+ found = spec.diagnostics;
1286
+ start = from;
1287
+ end = to;
1288
+ return false;
1289
+ }
1290
+ });
1291
+ let diagnosticFilter = view.state.facet(lintConfig).tooltipFilter;
1292
+ if (found && diagnosticFilter)
1293
+ found = diagnosticFilter(found, view.state);
1294
+ if (!found)
1295
+ return null;
1296
+ return {
1297
+ pos: start,
1298
+ end,
1299
+ above: view.state.doc.lineAt(start).to < end,
1300
+ create() {
1301
+ return { dom: diagnosticsTooltip(view, found) };
1302
+ }
1303
+ };
1304
+ }
1305
+ function diagnosticsTooltip(view, diagnostics) {
1306
+ return crelt("ul", { class: "cm-tooltip-lint" }, diagnostics.map((d) => renderDiagnostic(view, d, false)));
1307
+ }
1308
+ const openLintPanel = (view$1) => {
1309
+ let field = view$1.state.field(lintState, false);
1310
+ if (!field || !field.panel)
1311
+ view$1.dispatch({ effects: maybeEnableLint(view$1.state, [togglePanel.of(true)]) });
1312
+ let panel = view.getPanel(view$1, LintPanel.open);
1313
+ if (panel)
1314
+ panel.dom.querySelector(".cm-panel-lint ul").focus();
1315
+ return true;
1316
+ };
1317
+ const closeLintPanel = (view) => {
1318
+ let field = view.state.field(lintState, false);
1319
+ if (!field || !field.panel)
1320
+ return false;
1321
+ view.dispatch({ effects: togglePanel.of(false) });
1322
+ return true;
1323
+ };
1324
+ const nextDiagnostic = (view) => {
1325
+ let field = view.state.field(lintState, false);
1326
+ if (!field)
1327
+ return false;
1328
+ let sel = view.state.selection.main, next = field.diagnostics.iter(sel.to + 1);
1329
+ if (!next.value) {
1330
+ next = field.diagnostics.iter(0);
1331
+ if (!next.value || next.from == sel.from && next.to == sel.to)
1332
+ return false;
1333
+ }
1334
+ view.dispatch({ selection: { anchor: next.from, head: next.to }, scrollIntoView: true });
1335
+ return true;
1336
+ };
1337
+ const lintKeymap = [
1338
+ { key: "Mod-Shift-m", run: openLintPanel, preventDefault: true },
1339
+ { key: "F8", run: nextDiagnostic }
1340
+ ];
1341
+ const lintConfig = /* @__PURE__ */ state.Facet.define({
1342
+ combine(input) {
1343
+ return Object.assign({ sources: input.map((i) => i.source).filter((x) => x != null) }, state.combineConfig(input.map((i) => i.config), {
1344
+ delay: 750,
1345
+ markerFilter: null,
1346
+ tooltipFilter: null,
1347
+ needsRefresh: null,
1348
+ hideOn: () => null
1349
+ }, {
1350
+ needsRefresh: (a, b) => !a ? b : !b ? a : (u) => a(u) || b(u)
1351
+ }));
1352
+ }
1353
+ });
1354
+ function assignKeys(actions) {
1355
+ let assigned = [];
1356
+ if (actions)
1357
+ actions: for (let { name } of actions) {
1358
+ for (let i = 0; i < name.length; i++) {
1359
+ let ch = name[i];
1360
+ if (/[a-zA-Z]/.test(ch) && !assigned.some((c) => c.toLowerCase() == ch.toLowerCase())) {
1361
+ assigned.push(ch);
1362
+ continue actions;
1363
+ }
1364
+ }
1365
+ assigned.push("");
1366
+ }
1367
+ return assigned;
1368
+ }
1369
+ function renderDiagnostic(view, diagnostic, inPanel) {
1370
+ var _a;
1371
+ let keys = inPanel ? assignKeys(diagnostic.actions) : [];
1372
+ 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) => {
1373
+ let fired = false, click = (e) => {
1374
+ e.preventDefault();
1375
+ if (fired)
1376
+ return;
1377
+ fired = true;
1378
+ let found = findDiagnostic(view.state.field(lintState).diagnostics, diagnostic);
1379
+ if (found)
1380
+ action.apply(view, found.from, found.to);
1381
+ };
1382
+ let { name } = action, keyIndex = keys[i] ? name.indexOf(keys[i]) : -1;
1383
+ let nameElt = keyIndex < 0 ? name : [
1384
+ name.slice(0, keyIndex),
1385
+ crelt("u", name.slice(keyIndex, keyIndex + 1)),
1386
+ name.slice(keyIndex + 1)
1387
+ ];
1388
+ return crelt("button", {
1389
+ type: "button",
1390
+ class: "cm-diagnosticAction",
1391
+ onclick: click,
1392
+ onmousedown: click,
1393
+ "aria-label": ` Action: ${name}${keyIndex < 0 ? "" : ` (access key "${keys[i]})"`}.`
1394
+ }, nameElt);
1395
+ }), diagnostic.source && crelt("div", { class: "cm-diagnosticSource" }, diagnostic.source));
1396
+ }
1397
+ class DiagnosticWidget extends view.WidgetType {
1398
+ constructor(sev) {
1399
+ super();
1400
+ this.sev = sev;
1401
+ }
1402
+ eq(other) {
1403
+ return other.sev == this.sev;
1404
+ }
1405
+ toDOM() {
1406
+ return crelt("span", { class: "cm-lintPoint cm-lintPoint-" + this.sev });
1407
+ }
1408
+ }
1409
+ class PanelItem {
1410
+ constructor(view, diagnostic) {
1411
+ this.diagnostic = diagnostic;
1412
+ this.id = "item_" + Math.floor(Math.random() * 4294967295).toString(16);
1413
+ this.dom = renderDiagnostic(view, diagnostic, true);
1414
+ this.dom.id = this.id;
1415
+ this.dom.setAttribute("role", "option");
1416
+ }
1417
+ }
1418
+ class LintPanel {
1419
+ constructor(view) {
1420
+ this.view = view;
1421
+ this.items = [];
1422
+ let onkeydown = (event) => {
1423
+ if (event.keyCode == 27) {
1424
+ closeLintPanel(this.view);
1425
+ this.view.focus();
1426
+ } else if (event.keyCode == 38 || event.keyCode == 33) {
1427
+ this.moveSelection((this.selectedIndex - 1 + this.items.length) % this.items.length);
1428
+ } else if (event.keyCode == 40 || event.keyCode == 34) {
1429
+ this.moveSelection((this.selectedIndex + 1) % this.items.length);
1430
+ } else if (event.keyCode == 36) {
1431
+ this.moveSelection(0);
1432
+ } else if (event.keyCode == 35) {
1433
+ this.moveSelection(this.items.length - 1);
1434
+ } else if (event.keyCode == 13) {
1435
+ this.view.focus();
1436
+ } else if (event.keyCode >= 65 && event.keyCode <= 90 && this.selectedIndex >= 0) {
1437
+ let { diagnostic } = this.items[this.selectedIndex], keys = assignKeys(diagnostic.actions);
1438
+ for (let i = 0; i < keys.length; i++)
1439
+ if (keys[i].toUpperCase().charCodeAt(0) == event.keyCode) {
1440
+ let found = findDiagnostic(this.view.state.field(lintState).diagnostics, diagnostic);
1441
+ if (found)
1442
+ diagnostic.actions[i].apply(view, found.from, found.to);
1443
+ }
1444
+ } else {
1445
+ return;
1446
+ }
1447
+ event.preventDefault();
1448
+ };
1449
+ let onclick = (event) => {
1450
+ for (let i = 0; i < this.items.length; i++) {
1451
+ if (this.items[i].dom.contains(event.target))
1452
+ this.moveSelection(i);
1453
+ }
1454
+ };
1455
+ this.list = crelt("ul", {
1456
+ tabIndex: 0,
1457
+ role: "listbox",
1458
+ "aria-label": this.view.state.phrase("Diagnostics"),
1459
+ onkeydown,
1460
+ onclick
1461
+ });
1462
+ this.dom = crelt("div", { class: "cm-panel-lint" }, this.list, crelt("button", {
1463
+ type: "button",
1464
+ name: "close",
1465
+ "aria-label": this.view.state.phrase("close"),
1466
+ onclick: () => closeLintPanel(this.view)
1467
+ }, "\xD7"));
1468
+ this.update();
1469
+ }
1470
+ get selectedIndex() {
1471
+ let selected = this.view.state.field(lintState).selected;
1472
+ if (!selected)
1473
+ return -1;
1474
+ for (let i = 0; i < this.items.length; i++)
1475
+ if (this.items[i].diagnostic == selected.diagnostic)
1476
+ return i;
1477
+ return -1;
1478
+ }
1479
+ update() {
1480
+ let { diagnostics, selected } = this.view.state.field(lintState);
1481
+ let i = 0, needsSync = false, newSelectedItem = null;
1482
+ let seen = /* @__PURE__ */ new Set();
1483
+ diagnostics.between(0, this.view.state.doc.length, (_start, _end, { spec }) => {
1484
+ for (let diagnostic of spec.diagnostics) {
1485
+ if (seen.has(diagnostic))
1486
+ continue;
1487
+ seen.add(diagnostic);
1488
+ let found = -1, item;
1489
+ for (let j = i; j < this.items.length; j++)
1490
+ if (this.items[j].diagnostic == diagnostic) {
1491
+ found = j;
1492
+ break;
1493
+ }
1494
+ if (found < 0) {
1495
+ item = new PanelItem(this.view, diagnostic);
1496
+ this.items.splice(i, 0, item);
1497
+ needsSync = true;
1498
+ } else {
1499
+ item = this.items[found];
1500
+ if (found > i) {
1501
+ this.items.splice(i, found - i);
1502
+ needsSync = true;
1503
+ }
1504
+ }
1505
+ if (selected && item.diagnostic == selected.diagnostic) {
1506
+ if (!item.dom.hasAttribute("aria-selected")) {
1507
+ item.dom.setAttribute("aria-selected", "true");
1508
+ newSelectedItem = item;
1509
+ }
1510
+ } else if (item.dom.hasAttribute("aria-selected")) {
1511
+ item.dom.removeAttribute("aria-selected");
1512
+ }
1513
+ i++;
1514
+ }
1515
+ });
1516
+ while (i < this.items.length && !(this.items.length == 1 && this.items[0].diagnostic.from < 0)) {
1517
+ needsSync = true;
1518
+ this.items.pop();
1519
+ }
1520
+ if (this.items.length == 0) {
1521
+ this.items.push(new PanelItem(this.view, {
1522
+ from: -1,
1523
+ to: -1,
1524
+ severity: "info",
1525
+ message: this.view.state.phrase("No diagnostics")
1526
+ }));
1527
+ needsSync = true;
1528
+ }
1529
+ if (newSelectedItem) {
1530
+ this.list.setAttribute("aria-activedescendant", newSelectedItem.id);
1531
+ this.view.requestMeasure({
1532
+ key: this,
1533
+ read: () => ({ sel: newSelectedItem.dom.getBoundingClientRect(), panel: this.list.getBoundingClientRect() }),
1534
+ write: ({ sel, panel }) => {
1535
+ let scaleY = panel.height / this.list.offsetHeight;
1536
+ if (sel.top < panel.top)
1537
+ this.list.scrollTop -= (panel.top - sel.top) / scaleY;
1538
+ else if (sel.bottom > panel.bottom)
1539
+ this.list.scrollTop += (sel.bottom - panel.bottom) / scaleY;
1540
+ }
1541
+ });
1542
+ } else if (this.selectedIndex < 0) {
1543
+ this.list.removeAttribute("aria-activedescendant");
1544
+ }
1545
+ if (needsSync)
1546
+ this.sync();
1547
+ }
1548
+ sync() {
1549
+ let domPos = this.list.firstChild;
1550
+ function rm() {
1551
+ let prev = domPos;
1552
+ domPos = prev.nextSibling;
1553
+ prev.remove();
1554
+ }
1555
+ for (let item of this.items) {
1556
+ if (item.dom.parentNode == this.list) {
1557
+ while (domPos != item.dom)
1558
+ rm();
1559
+ domPos = item.dom.nextSibling;
1560
+ } else {
1561
+ this.list.insertBefore(item.dom, domPos);
1562
+ }
1563
+ }
1564
+ while (domPos)
1565
+ rm();
1566
+ }
1567
+ moveSelection(selectedIndex) {
1568
+ if (this.selectedIndex < 0)
1569
+ return;
1570
+ let field = this.view.state.field(lintState);
1571
+ let selection = findDiagnostic(field.diagnostics, this.items[selectedIndex].diagnostic);
1572
+ if (!selection)
1573
+ return;
1574
+ this.view.dispatch({
1575
+ selection: { anchor: selection.from, head: selection.to },
1576
+ scrollIntoView: true,
1577
+ effects: movePanelSelection.of(selection)
1578
+ });
1579
+ }
1580
+ static open(view) {
1581
+ return new LintPanel(view);
1582
+ }
1583
+ }
1584
+ function svg(content, attrs = `viewBox="0 0 40 40"`) {
1585
+ return `url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" ${attrs}>${encodeURIComponent(content)}</svg>')`;
1586
+ }
1587
+ function underline(color) {
1588
+ 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"`);
1589
+ }
1590
+ const baseTheme = /* @__PURE__ */ view.EditorView.baseTheme({
1591
+ ".cm-diagnostic": {
1592
+ padding: "3px 6px 3px 8px",
1593
+ marginLeft: "-1px",
1594
+ display: "block",
1595
+ whiteSpace: "pre-wrap"
1596
+ },
1597
+ ".cm-diagnostic-error": { borderLeft: "5px solid #d11" },
1598
+ ".cm-diagnostic-warning": { borderLeft: "5px solid orange" },
1599
+ ".cm-diagnostic-info": { borderLeft: "5px solid #999" },
1600
+ ".cm-diagnostic-hint": { borderLeft: "5px solid #66d" },
1601
+ ".cm-diagnosticAction": {
1602
+ font: "inherit",
1603
+ border: "none",
1604
+ padding: "2px 4px",
1605
+ backgroundColor: "#444",
1606
+ color: "white",
1607
+ borderRadius: "3px",
1608
+ marginLeft: "8px",
1609
+ cursor: "pointer"
1610
+ },
1611
+ ".cm-diagnosticSource": {
1612
+ fontSize: "70%",
1613
+ opacity: 0.7
1614
+ },
1615
+ ".cm-lintRange": {
1616
+ backgroundPosition: "left bottom",
1617
+ backgroundRepeat: "repeat-x",
1618
+ paddingBottom: "0.7px"
1619
+ },
1620
+ ".cm-lintRange-error": { backgroundImage: /* @__PURE__ */ underline("#d11") },
1621
+ ".cm-lintRange-warning": { backgroundImage: /* @__PURE__ */ underline("orange") },
1622
+ ".cm-lintRange-info": { backgroundImage: /* @__PURE__ */ underline("#999") },
1623
+ ".cm-lintRange-hint": { backgroundImage: /* @__PURE__ */ underline("#66d") },
1624
+ ".cm-lintRange-active": { backgroundColor: "#ffdd9980" },
1625
+ ".cm-tooltip-lint": {
1626
+ padding: 0,
1627
+ margin: 0
1628
+ },
1629
+ ".cm-lintPoint": {
1630
+ position: "relative",
1631
+ "&:after": {
1632
+ content: '""',
1633
+ position: "absolute",
1634
+ bottom: 0,
1635
+ left: "-2px",
1636
+ borderLeft: "3px solid transparent",
1637
+ borderRight: "3px solid transparent",
1638
+ borderBottom: "4px solid #d11"
1639
+ }
1640
+ },
1641
+ ".cm-lintPoint-warning": {
1642
+ "&:after": { borderBottomColor: "orange" }
1643
+ },
1644
+ ".cm-lintPoint-info": {
1645
+ "&:after": { borderBottomColor: "#999" }
1646
+ },
1647
+ ".cm-lintPoint-hint": {
1648
+ "&:after": { borderBottomColor: "#66d" }
1649
+ },
1650
+ ".cm-panel.cm-panel-lint": {
1651
+ position: "relative",
1652
+ "& ul": {
1653
+ maxHeight: "100px",
1654
+ overflowY: "auto",
1655
+ "& [aria-selected]": {
1656
+ backgroundColor: "#ddd",
1657
+ "& u": { textDecoration: "underline" }
1658
+ },
1659
+ "&:focus [aria-selected]": {
1660
+ background_fallback: "#bdf",
1661
+ backgroundColor: "Highlight",
1662
+ color_fallback: "white",
1663
+ color: "HighlightText"
1664
+ },
1665
+ "& u": { textDecoration: "none" },
1666
+ padding: 0,
1667
+ margin: 0
1668
+ },
1669
+ "& [name=close]": {
1670
+ position: "absolute",
1671
+ top: "0",
1672
+ right: "2px",
1673
+ background: "inherit",
1674
+ border: "none",
1675
+ font: "inherit",
1676
+ padding: 0,
1677
+ margin: 0
1678
+ }
1679
+ }
1680
+ });
1681
+ function severityWeight(sev) {
1682
+ return sev == "error" ? 4 : sev == "warning" ? 3 : sev == "info" ? 2 : 1;
1683
+ }
1684
+ function maxSeverity(diagnostics) {
1685
+ let sev = "hint", weight = 1;
1686
+ for (let d of diagnostics) {
1687
+ let w = severityWeight(d.severity);
1688
+ if (w > weight) {
1689
+ weight = w;
1690
+ sev = d.severity;
1691
+ }
1692
+ }
1693
+ return sev;
1694
+ }
1695
+ const lintExtensions = [
1696
+ lintState,
1697
+ /* @__PURE__ */ view.EditorView.decorations.compute([lintState], (state) => {
1698
+ let { selected, panel } = state.field(lintState);
1699
+ return !selected || !panel || selected.from == selected.to ? view.Decoration.none : view.Decoration.set([
1700
+ activeMark.range(selected.from, selected.to)
1701
+ ]);
1702
+ }),
1703
+ /* @__PURE__ */ view.hoverTooltip(lintTooltip, { hideOn: hideTooltip }),
1704
+ baseTheme
1705
+ ];
1706
+
1707
+ const basicSetup = ({ useLineNumbers }) => [
1708
+ useLineNumbers ? [view.lineNumbers()] : [],
1709
+ view.highlightActiveLineGutter(),
1710
+ view.highlightSpecialChars(),
1711
+ commands.history(),
1712
+ useLineNumbers ? [language.foldGutter()] : [],
1713
+ view.drawSelection(),
1714
+ view.dropCursor(),
1715
+ state.EditorState.allowMultipleSelections.of(false),
1716
+ language.indentOnInput(),
1717
+ language.syntaxHighlighting(language.defaultHighlightStyle, { fallback: true }),
1718
+ language.bracketMatching(),
1719
+ autocomplete.closeBrackets(),
1720
+ autocomplete.autocompletion(),
1721
+ view.rectangularSelection(),
1722
+ view.crosshairCursor(),
1723
+ view.highlightActiveLine(),
1724
+ highlightSelectionMatches(),
1725
+ view.keymap.of([
1726
+ ...autocomplete.closeBracketsKeymap,
1727
+ ...commands.defaultKeymap,
1728
+ ...searchKeymap,
1729
+ ...commands.historyKeymap,
1730
+ ...language.foldKeymap,
1731
+ ...autocomplete.completionKeymap,
1732
+ ...lintKeymap
1733
+ ])
1734
+ ];
1735
+
1736
+ const bootstrap = view.EditorView.theme({
1737
+ "&": {
1738
+ backgroundColor: "var(--bs-body-bg)",
1739
+ color: "var(--bs-body-color)",
1740
+ border: "var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important",
1741
+ "--bs-border-width": "1px",
1742
+ borderRadius: "var(--bs-border-radius)",
1743
+ fontSize: "0.8125rem !important",
1744
+ overflow: "hidden",
1745
+ transition: "border-color .15s ease-in-out, box-shadow .15s ease-in-out",
1746
+ resize: "vertical"
1747
+ },
1748
+ ".cm-line": {
1749
+ padding: "0 0.75rem"
1750
+ },
1751
+ ".cm-content": {
1752
+ padding: "0.375rem 0"
1753
+ },
1754
+ "&:not(.cm-focused) .cm-selectionBackground, &:not(.cm-focused) .cm-activeLine, &:not(.cm-focused) .cm-activeLineGutter": {
1755
+ backgroundColor: "transparent"
1756
+ },
1757
+ "&.cm-focused": {
1758
+ color: "var(--bs-body-color)",
1759
+ backgroundColor: "var(--bs-body-bg)",
1760
+ outline: 0,
1761
+ boxShadow: "0 0 0 .25rem rgba(13, 110, 253, .25)"
1762
+ },
1763
+ ".cm-scroller": {
1764
+ overflow: "auto",
1765
+ height: "100%",
1766
+ lineHeight: 1.85,
1767
+ flexGrow: 1
1768
+ },
1769
+ ".cm-gutters": {
1770
+ height: "100% !important",
1771
+ color: "var(--bs-tertiary-color)",
1772
+ backgroundColor: "var(--bs-tertiary-bg)"
1773
+ }
1774
+ });
1775
+
1776
+ const darkTheme = view.EditorView.theme({
1777
+ "&.cm-focused .cm-selectionBackground": {
1778
+ backgroundColor: "#214181 !important"
1779
+ },
1780
+ "&.cm-focused": {
1781
+ borderColor: "#86b7fe !important",
1782
+ boxShadow: "0 0 0 .25rem rgba(13, 110, 253, .25)"
1783
+ },
1784
+ ".cm-activeLine, .cm-activeLineGutter": {
1785
+ backgroundColor: "rgba(255, 255, 255, .05)"
1786
+ }
1787
+ }, { dark: true });
1788
+ const darkHighlight = language.HighlightStyle.define([
1789
+ { tag: highlight.tags.keyword, color: "#d26f15" },
1790
+ { tag: highlight.tags.number, color: "#6aa8cc" },
1791
+ { tag: highlight.tags.variableName, color: "#9675a7", fontWeight: "bold" },
1792
+ { tag: highlight.tags.function(highlight.tags.variableName), color: "#fcc46c" },
1793
+ { tag: highlight.tags.function(highlight.tags.propertyName), color: "#fcc46c" },
1794
+ { tag: highlight.tags.typeName, color: "#C792EA" },
1795
+ { tag: highlight.tags.string, color: "#85a75b" },
1796
+ { tag: highlight.tags.comment, color: "#808080" },
1797
+ { tag: highlight.tags.operator, color: "#EEEEEE" }
1798
+ ]);
1799
+ var dark = [
1800
+ bootstrap,
1801
+ darkTheme,
1802
+ language.syntaxHighlighting(darkHighlight)
1803
+ ];
1804
+
1805
+ const lightTheme = view.EditorView.theme({
1806
+ "&.cm-focused .cm-selectionBackground": {
1807
+ backgroundColor: "#cce5ff !important"
1808
+ // Light blue selection when focused
1809
+ },
1810
+ "&.cm-focused": {
1811
+ borderColor: "#86b7fe !important",
1812
+ boxShadow: "0 0 0 .25rem rgba(0, 123, 255, .25)"
1813
+ },
1814
+ ".cm-activeLine, .cm-activeLineGutter": {
1815
+ backgroundColor: "rgba(0, 0, 0, .05)"
1816
+ }
1817
+ }, { dark: false });
1818
+ const lightHighlight = language.HighlightStyle.define([
1819
+ { tag: highlight.tags.keyword, color: "#0033a0", fontWeight: "bold" },
1820
+ // Dark blue keywords
1821
+ { tag: highlight.tags.atom, color: "#d35400" },
1822
+ // Orange atoms
1823
+ { tag: highlight.tags.number, color: "#d32f2f" },
1824
+ // Red numbers
1825
+ { tag: highlight.tags.definition(highlight.tags.name), color: "#007acc" },
1826
+ // Blue function/class names
1827
+ { tag: highlight.tags.variableName, color: "#2d2d2d" },
1828
+ // Dark text for variables
1829
+ { tag: highlight.tags.typeName, color: "#0056b3" },
1830
+ // Blue types
1831
+ { tag: highlight.tags.string, color: "#388e3c" },
1832
+ // Green strings
1833
+ { tag: highlight.tags.comment, color: "#757575", fontStyle: "italic" },
1834
+ // Gray italic comments
1835
+ { tag: highlight.tags.operator, color: "#6a1b9a" }
1836
+ // Purple operators
1837
+ ]);
1838
+ var light = [
1839
+ bootstrap,
1840
+ lightTheme,
1841
+ language.syntaxHighlighting(lightHighlight)
1842
+ ];
1843
+
1844
+ const getTheme = (el) => ({ light, dark })[el.dataset.bsTheme || "light"];
1845
+ const mutationObserver = new MutationObserver(
1846
+ (changes) => changes.map((mutation) => mutation.target).filter((mutationTarget) => mutationTarget instanceof HTMLElement).map((themeContainer) => ({ transaction: { effects: themes.get(themeContainer).reconfigure(getTheme(themeContainer)) }, editors: editors.get(themeContainer) })).forEach(({ transaction, editors: editors2 }) => editors2.forEach((editor) => editor.dispatch(transaction)))
1847
+ );
1848
+ const themes = /* @__PURE__ */ new WeakMap();
1849
+ const editors = /* @__PURE__ */ new WeakMap();
1850
+ class ExpressionEditor extends HTMLTextAreaElement {
1851
+ constructor() {
1852
+ super();
1853
+ const themeContainer = this.closest("[data-bs-theme]") || document.documentElement;
1854
+ if (!themes.has(themeContainer)) {
1855
+ mutationObserver.observe(themeContainer, { attributes: true, attributeFilter: ["data-bs-theme"] });
1856
+ themes.set(themeContainer, new state.Compartment());
1857
+ editors.set(themeContainer, /* @__PURE__ */ new Set());
1858
+ }
1859
+ this.dom = document.createElement("div");
1860
+ this.editorView = new view.EditorView({
1861
+ extensions: [
1862
+ view.EditorView.editorAttributes.of({ style: `min-height: calc(0.75rem + ${this.rows} * 0.8125 * 1.85rem + 2px); height: calc(0.75rem + ${this.rows} * 0.8125 * 1.85rem + 2px)` }),
1863
+ basicSetup({
1864
+ useLineNumbers: JSON.parse(this.dataset.lineNumbers || "false")
1865
+ }),
1866
+ themes.get(themeContainer).of(getTheme(themeContainer)),
1867
+ view.keymap.of([...commands.defaultKeymap, { key: "Tab", run: autocomplete.acceptCompletion }]),
1868
+ codemirrorLangEl.expressionlanguage(JSON.parse(this.dataset.config || "{}"), [getTheme(themeContainer)]),
1869
+ view.EditorView.updateListener.of((e) => {
1870
+ if (e.docChanged) {
1871
+ this.value = e.state.doc.toString();
1872
+ }
1873
+ })
1874
+ ],
1875
+ parent: this.dom,
1876
+ doc: this.value.trim()
1877
+ });
1878
+ editors.get(themeContainer).add(this.editorView);
1879
+ this.replaceWith(this.dom);
1880
+ this.hidden = true;
1881
+ this.dom.appendChild(this);
1882
+ }
1883
+ }
1884
+ customElements.define("expression-editor", ExpressionEditor, { extends: "textarea" });
1885
+
1886
+ exports.ExpressionEditor = ExpressionEditor;