@coze-editor/extension-lint 0.1.0-alpha.0fd19e

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.
@@ -0,0 +1,1011 @@
1
+ // src/index.ts
2
+ import elt from "crelt";
3
+ import {
4
+ EditorView,
5
+ ViewPlugin,
6
+ Decoration as Decoration2,
7
+ WidgetType,
8
+ logException,
9
+ hoverTooltip,
10
+ showTooltip,
11
+ gutter,
12
+ GutterMarker,
13
+ showPanel,
14
+ getPanel
15
+ } from "@codemirror/view";
16
+ import {
17
+ StateEffect,
18
+ StateField,
19
+ Facet,
20
+ combineConfig,
21
+ RangeSet
22
+ } from "@codemirror/state";
23
+
24
+ // src/merge-decorations.ts
25
+ import { Decoration } from "@codemirror/view";
26
+ var splitDiagnosticBySeverity = (decorations) => {
27
+ const diagnosticMap = /* @__PURE__ */ new Map();
28
+ const decoration = decorations.iter();
29
+ while (decoration.value) {
30
+ const { severity } = decoration.value.spec.diagnostic;
31
+ if (!diagnosticMap.has(severity)) {
32
+ diagnosticMap.set(severity, []);
33
+ }
34
+ diagnosticMap.get(severity).push(decoration.value.spec.diagnostic);
35
+ decoration.next();
36
+ }
37
+ return diagnosticMap;
38
+ };
39
+ var createDecoration = (from, to, severity, diagnostics) => {
40
+ const last = diagnostics[diagnostics.length - 1];
41
+ return Decoration.mark({
42
+ attributes: {
43
+ class: `cm-lintRange cm-lintRange-${severity}${last.markClass ? ` ${last.markClass}` : ""}`
44
+ },
45
+ sourceDiagnostics: diagnostics
46
+ }).range(from, to);
47
+ };
48
+ var mergeDecoration = (diagnostics) => {
49
+ const cloned = diagnostics.map((e) => ({
50
+ from: e.from,
51
+ to: e.to
52
+ }));
53
+ const merged = [];
54
+ cloned.sort((a, b) => a.from - b.from);
55
+ let current = cloned[0];
56
+ let currentSourceDiagnostics = [diagnostics[0]];
57
+ for (let i = 1; i < cloned.length; i++) {
58
+ const next = cloned[i];
59
+ if (next.from <= current.to) {
60
+ currentSourceDiagnostics.push(diagnostics[i]);
61
+ current.to = Math.max(current.to, next.to);
62
+ } else {
63
+ merged.push({ ...current, sourceDiagnostics: currentSourceDiagnostics });
64
+ current = next;
65
+ currentSourceDiagnostics = [diagnostics[i]];
66
+ }
67
+ }
68
+ merged.push({ ...current, sourceDiagnostics: currentSourceDiagnostics });
69
+ return merged;
70
+ };
71
+ var mergeDecorations = (decorations) => {
72
+ try {
73
+ const diagnosticMap = splitDiagnosticBySeverity(decorations);
74
+ const merge = [];
75
+ diagnosticMap.forEach((value, key) => {
76
+ mergeDecoration(value).forEach((diagnostic) => {
77
+ if (typeof diagnostic.from !== "number" || typeof diagnostic.to !== "number") {
78
+ return;
79
+ }
80
+ if (diagnostic.from <= diagnostic.to) {
81
+ merge.push(
82
+ createDecoration(
83
+ diagnostic.from,
84
+ diagnostic.to,
85
+ key,
86
+ diagnostic.sourceDiagnostics
87
+ )
88
+ );
89
+ }
90
+ });
91
+ });
92
+ merge.sort((a, b) => {
93
+ if (a.from !== b.from) {
94
+ return a.from - b.from;
95
+ }
96
+ return a.value.startSide - b.value.startSide;
97
+ });
98
+ return Decoration.set(merge);
99
+ } catch (e) {
100
+ console.error("FlowLangSDK: linter mergeDecorations error", e);
101
+ return decorations;
102
+ }
103
+ };
104
+
105
+ // src/index.ts
106
+ var SelectedDiagnostic = class {
107
+ constructor(from, to, diagnostic) {
108
+ this.from = from;
109
+ this.to = to;
110
+ this.diagnostic = diagnostic;
111
+ }
112
+ };
113
+ var LintState = class _LintState {
114
+ constructor(diagnostics, panel, selected) {
115
+ this.diagnostics = diagnostics;
116
+ this.panel = panel;
117
+ this.selected = selected;
118
+ }
119
+ static init(diagnostics, panel, state) {
120
+ let markedDiagnostics = diagnostics;
121
+ const diagnosticFilter = state.facet(lintConfig).markerFilter;
122
+ if (diagnosticFilter) {
123
+ markedDiagnostics = diagnosticFilter(markedDiagnostics, state);
124
+ }
125
+ const ranges = Decoration2.set(
126
+ markedDiagnostics.map((d) => {
127
+ if (d.from == d.to || d.from == d.to - 1 && state.doc.lineAt(d.from).to == d.from) {
128
+ return Decoration2.widget({
129
+ widget: new DiagnosticWidget(d),
130
+ diagnostic: d
131
+ }).range(d.from);
132
+ }
133
+ return Decoration2.mark({
134
+ attributes: {
135
+ class: `cm-lintRange cm-lintRange-${d.severity}${d.markClass ? ` ${d.markClass}` : ""}`
136
+ },
137
+ diagnostic: d
138
+ }).range(d.from, d.to);
139
+ }),
140
+ true
141
+ );
142
+ return new _LintState(ranges, panel, findDiagnostic(ranges));
143
+ }
144
+ };
145
+ function findDiagnostic(diagnostics, diagnostic = null, after = 0) {
146
+ let found = null;
147
+ diagnostics.between(after, 1e9, (from, to, { spec }) => {
148
+ if (diagnostic && spec.diagnostic != diagnostic) {
149
+ return;
150
+ }
151
+ found = new SelectedDiagnostic(from, to, spec.diagnostic);
152
+ return false;
153
+ });
154
+ return found;
155
+ }
156
+ function hideTooltip(tr, tooltip) {
157
+ const from = tooltip.pos, to = tooltip.end || from;
158
+ const result = tr.state.facet(lintConfig).hideOn(tr, from, to);
159
+ if (result != null) {
160
+ return result;
161
+ }
162
+ const line = tr.startState.doc.lineAt(tooltip.pos);
163
+ return !!(tr.effects.some((e) => e.is(setDiagnosticsEffect)) || tr.changes.touchesRange(line.from, Math.max(line.to, to)));
164
+ }
165
+ function maybeEnableLint(state, effects) {
166
+ return state.field(lintState, false) ? effects : effects.concat(StateEffect.appendConfig.of(lintExtensions));
167
+ }
168
+ function setDiagnostics(state, diagnostics) {
169
+ return {
170
+ effects: maybeEnableLint(state, [setDiagnosticsEffect.of(diagnostics)])
171
+ };
172
+ }
173
+ var setDiagnosticsEffect = StateEffect.define();
174
+ var togglePanel = StateEffect.define();
175
+ var movePanelSelection = StateEffect.define();
176
+ var lintState = StateField.define({
177
+ create() {
178
+ return new LintState(Decoration2.none, null, null);
179
+ },
180
+ update(value, tr) {
181
+ if (tr.docChanged && value.diagnostics.size) {
182
+ const mapped = value.diagnostics.map(tr.changes);
183
+ let selected = null, { panel } = value;
184
+ if (value.selected) {
185
+ const selPos = tr.changes.mapPos(value.selected.from, 1);
186
+ selected = findDiagnostic(mapped, value.selected.diagnostic, selPos) || findDiagnostic(mapped, null, selPos);
187
+ }
188
+ if (!mapped.size && panel && tr.state.facet(lintConfig).autoPanel) {
189
+ panel = null;
190
+ }
191
+ value = new LintState(mapped, panel, selected);
192
+ }
193
+ for (const effect of tr.effects) {
194
+ if (effect.is(setDiagnosticsEffect)) {
195
+ const panel = !tr.state.facet(lintConfig).autoPanel ? value.panel : effect.value.length ? LintPanel.open : null;
196
+ value = LintState.init(effect.value, panel, tr.state);
197
+ } else if (effect.is(togglePanel)) {
198
+ value = new LintState(
199
+ value.diagnostics,
200
+ effect.value ? LintPanel.open : null,
201
+ value.selected
202
+ );
203
+ } else if (effect.is(movePanelSelection)) {
204
+ value = new LintState(value.diagnostics, value.panel, effect.value);
205
+ }
206
+ }
207
+ return value;
208
+ },
209
+ provide: (f) => [
210
+ showPanel.from(f, (val) => val.panel),
211
+ // mergeDecorations 中合并了相同 severity 的 diagnostic 在编辑器中的下划线样式,以前会重复绘制下划线,现在只会绘制一次。
212
+ EditorView.decorations.from(f, (s) => mergeDecorations(s.diagnostics))
213
+ ]
214
+ });
215
+ function diagnosticCount(state) {
216
+ const lint = state.field(lintState, false);
217
+ return lint ? lint.diagnostics.size : 0;
218
+ }
219
+ var activeMark = Decoration2.mark({
220
+ class: "cm-lintRange cm-lintRange-active"
221
+ });
222
+ function lintTooltip(view, pos, side) {
223
+ const { diagnostics } = view.state.field(lintState);
224
+ let found = [], stackStart = 2e8, stackEnd = 0;
225
+ diagnostics.between(
226
+ pos - (side < 0 ? 1 : 0),
227
+ pos + (side > 0 ? 1 : 0),
228
+ (from, to, { spec }) => {
229
+ if (pos >= from && pos <= to && (from == to || (pos > from || side > 0) && (pos < to || side < 0))) {
230
+ found.push(spec.diagnostic);
231
+ stackStart = Math.min(from, stackStart);
232
+ stackEnd = Math.max(to, stackEnd);
233
+ }
234
+ }
235
+ );
236
+ const diagnosticFilter = view.state.facet(lintConfig).tooltipFilter;
237
+ if (diagnosticFilter) {
238
+ found = diagnosticFilter(found, view.state);
239
+ }
240
+ if (!found.length) {
241
+ return null;
242
+ }
243
+ return {
244
+ pos: stackStart,
245
+ end: stackEnd,
246
+ above: view.state.doc.lineAt(stackStart).to < stackEnd,
247
+ create() {
248
+ return { dom: diagnosticsTooltip(view, found) };
249
+ }
250
+ };
251
+ }
252
+ function diagnosticsTooltip(view, diagnostics) {
253
+ return elt(
254
+ "ul",
255
+ { class: "cm-tooltip-lint" },
256
+ diagnostics.map((d) => renderDiagnostic(view, d, false))
257
+ );
258
+ }
259
+ var openLintPanel = (view) => {
260
+ const field = view.state.field(lintState, false);
261
+ if (!field || !field.panel) {
262
+ view.dispatch({
263
+ effects: maybeEnableLint(view.state, [togglePanel.of(true)])
264
+ });
265
+ }
266
+ const panel = getPanel(view, LintPanel.open);
267
+ if (panel) {
268
+ panel.dom.querySelector(".cm-panel-lint ul").focus();
269
+ }
270
+ return true;
271
+ };
272
+ var closeLintPanel = (view) => {
273
+ const field = view.state.field(lintState, false);
274
+ if (!field || !field.panel) {
275
+ return false;
276
+ }
277
+ view.dispatch({ effects: togglePanel.of(false) });
278
+ return true;
279
+ };
280
+ var nextDiagnostic = (view) => {
281
+ const field = view.state.field(lintState, false);
282
+ if (!field) {
283
+ return false;
284
+ }
285
+ const sel = view.state.selection.main;
286
+ let next = field.diagnostics.iter(sel.to + 1);
287
+ if (!next.value) {
288
+ next = field.diagnostics.iter(0);
289
+ if (!next.value || next.from == sel.from && next.to == sel.to) {
290
+ return false;
291
+ }
292
+ }
293
+ view.dispatch({
294
+ selection: { anchor: next.from, head: next.to },
295
+ scrollIntoView: true
296
+ });
297
+ return true;
298
+ };
299
+ var previousDiagnostic = (view) => {
300
+ const { state } = view, field = state.field(lintState, false);
301
+ if (!field) {
302
+ return false;
303
+ }
304
+ const sel = state.selection.main;
305
+ let prevFrom, prevTo, lastFrom, lastTo;
306
+ field.diagnostics.between(0, state.doc.length, (from, to) => {
307
+ if (to < sel.to && (prevFrom == null || prevFrom < from)) {
308
+ prevFrom = from;
309
+ prevTo = to;
310
+ }
311
+ if (lastFrom == null || from > lastFrom) {
312
+ lastFrom = from;
313
+ lastTo = to;
314
+ }
315
+ });
316
+ if (lastFrom == null || prevFrom == null && lastFrom == sel.from) {
317
+ return false;
318
+ }
319
+ view.dispatch({
320
+ selection: { anchor: prevFrom ?? lastFrom, head: prevTo ?? lastTo },
321
+ scrollIntoView: true
322
+ });
323
+ return true;
324
+ };
325
+ var lintKeymap = [
326
+ { key: "Mod-Shift-m", run: openLintPanel, preventDefault: true },
327
+ { key: "F8", run: nextDiagnostic }
328
+ ];
329
+ var lintPlugin = ViewPlugin.fromClass(
330
+ class {
331
+ constructor(view) {
332
+ this.view = view;
333
+ const { delay } = view.state.facet(lintConfig);
334
+ this.lintTime = Date.now() + delay;
335
+ this.run = this.run.bind(this);
336
+ this.timeout = window.setTimeout(this.run, delay);
337
+ }
338
+ lintTime;
339
+ timeout = -1;
340
+ set = true;
341
+ run() {
342
+ clearTimeout(this.timeout);
343
+ const now = Date.now();
344
+ if (now < this.lintTime - 10) {
345
+ this.timeout = window.setTimeout(this.run, this.lintTime - now);
346
+ } else {
347
+ this.set = false;
348
+ const { state } = this.view, { sources } = state.facet(lintConfig);
349
+ if (sources.length) {
350
+ Promise.all(
351
+ sources.map((source) => Promise.resolve(source(this.view)))
352
+ ).then(
353
+ (annotations) => {
354
+ const all = annotations.reduce((a, b) => a.concat(b));
355
+ if (this.view.state.doc == state.doc) {
356
+ this.view.dispatch(setDiagnostics(this.view.state, all));
357
+ }
358
+ },
359
+ (error) => {
360
+ logException(this.view.state, error);
361
+ }
362
+ );
363
+ }
364
+ }
365
+ }
366
+ update(update) {
367
+ const config = update.state.facet(lintConfig);
368
+ if (update.docChanged || config != update.startState.facet(lintConfig) || config.needsRefresh && config.needsRefresh(update)) {
369
+ this.lintTime = Date.now() + config.delay;
370
+ if (!this.set) {
371
+ this.set = true;
372
+ this.timeout = window.setTimeout(this.run, config.delay);
373
+ }
374
+ }
375
+ }
376
+ force() {
377
+ if (this.set) {
378
+ this.lintTime = Date.now();
379
+ this.run();
380
+ }
381
+ }
382
+ destroy() {
383
+ clearTimeout(this.timeout);
384
+ }
385
+ }
386
+ );
387
+ var lintConfig = Facet.define({
388
+ combine(input) {
389
+ return {
390
+ sources: input.map((i) => i.source).filter((x) => x != null),
391
+ ...combineConfig(
392
+ input.map((i) => i.config),
393
+ {
394
+ delay: 750,
395
+ markerFilter: null,
396
+ tooltipFilter: null,
397
+ needsRefresh: null,
398
+ hideOn: () => null
399
+ },
400
+ {
401
+ needsRefresh: (a, b) => !a ? b : !b ? a : (u) => a(u) || b(u)
402
+ }
403
+ )
404
+ };
405
+ }
406
+ });
407
+ function linter(source, config = {}) {
408
+ return [lintConfig.of({ source, config }), lintPlugin, lintExtensions];
409
+ }
410
+ function forceLinting(view) {
411
+ const plugin = view.plugin(lintPlugin);
412
+ if (plugin) {
413
+ plugin.force();
414
+ }
415
+ }
416
+ function assignKeys(actions) {
417
+ const assigned = [];
418
+ if (actions) {
419
+ actions: for (const { name } of actions) {
420
+ for (let i = 0; i < name.length; i++) {
421
+ const ch = name[i];
422
+ if (/[a-zA-Z]/.test(ch) && !assigned.some((c) => c.toLowerCase() == ch.toLowerCase())) {
423
+ assigned.push(ch);
424
+ continue actions;
425
+ }
426
+ }
427
+ assigned.push("");
428
+ }
429
+ }
430
+ return assigned;
431
+ }
432
+ function renderDiagnostic(view, diagnostic, inPanel) {
433
+ var _a;
434
+ const keys = inPanel ? assignKeys(diagnostic.actions) : [];
435
+ return elt(
436
+ "li",
437
+ { class: `cm-diagnostic cm-diagnostic-${diagnostic.severity}` },
438
+ elt(
439
+ "span",
440
+ { class: "cm-diagnosticText" },
441
+ diagnostic.renderMessage ? diagnostic.renderMessage(view) : diagnostic.message
442
+ ),
443
+ (_a = diagnostic.actions) == null ? void 0 : _a.map((action, i) => {
444
+ let fired = false;
445
+ const click = (e) => {
446
+ e.preventDefault();
447
+ if (fired) {
448
+ return;
449
+ }
450
+ fired = true;
451
+ const found = findDiagnostic(
452
+ view.state.field(lintState).diagnostics,
453
+ diagnostic
454
+ );
455
+ if (found) {
456
+ action.apply(view, found.from, found.to);
457
+ }
458
+ };
459
+ const { name } = action, keyIndex = keys[i] ? name.indexOf(keys[i]) : -1;
460
+ const nameElt = keyIndex < 0 ? name : [
461
+ name.slice(0, keyIndex),
462
+ elt("u", name.slice(keyIndex, keyIndex + 1)),
463
+ name.slice(keyIndex + 1)
464
+ ];
465
+ return elt(
466
+ "button",
467
+ {
468
+ type: "button",
469
+ class: "cm-diagnosticAction",
470
+ onclick: click,
471
+ onmousedown: click,
472
+ "aria-label": ` Action: ${name}${keyIndex < 0 ? "" : ` (access key "${keys[i]})"`}.`
473
+ },
474
+ nameElt
475
+ );
476
+ }),
477
+ diagnostic.source && elt("div", { class: "cm-diagnosticSource" }, diagnostic.source)
478
+ );
479
+ }
480
+ var DiagnosticWidget = class extends WidgetType {
481
+ constructor(diagnostic) {
482
+ super();
483
+ this.diagnostic = diagnostic;
484
+ }
485
+ eq(other) {
486
+ return other.diagnostic == this.diagnostic;
487
+ }
488
+ toDOM() {
489
+ return elt("span", {
490
+ class: `cm-lintPoint cm-lintPoint-${this.diagnostic.severity}`
491
+ });
492
+ }
493
+ };
494
+ var PanelItem = class {
495
+ constructor(view, diagnostic) {
496
+ this.diagnostic = diagnostic;
497
+ this.dom = renderDiagnostic(view, diagnostic, true);
498
+ this.dom.id = this.id;
499
+ this.dom.setAttribute("role", "option");
500
+ }
501
+ id = `item_${Math.floor(Math.random() * 4294967295).toString(16)}`;
502
+ dom;
503
+ };
504
+ var LintPanel = class _LintPanel {
505
+ constructor(view) {
506
+ this.view = view;
507
+ const onkeydown = (event) => {
508
+ if (event.keyCode == 27) {
509
+ closeLintPanel(this.view);
510
+ this.view.focus();
511
+ } else if (event.keyCode == 38 || event.keyCode == 33) {
512
+ this.moveSelection(
513
+ (this.selectedIndex - 1 + this.items.length) % this.items.length
514
+ );
515
+ } else if (event.keyCode == 40 || event.keyCode == 34) {
516
+ this.moveSelection((this.selectedIndex + 1) % this.items.length);
517
+ } else if (event.keyCode == 36) {
518
+ this.moveSelection(0);
519
+ } else if (event.keyCode == 35) {
520
+ this.moveSelection(this.items.length - 1);
521
+ } else if (event.keyCode == 13) {
522
+ this.view.focus();
523
+ } else if (event.keyCode >= 65 && event.keyCode <= 90 && this.selectedIndex >= 0) {
524
+ const { diagnostic } = this.items[this.selectedIndex], keys = assignKeys(diagnostic.actions);
525
+ for (let i = 0; i < keys.length; i++) {
526
+ if (keys[i].toUpperCase().charCodeAt(0) == event.keyCode) {
527
+ const found = findDiagnostic(
528
+ this.view.state.field(lintState).diagnostics,
529
+ diagnostic
530
+ );
531
+ if (found) {
532
+ diagnostic.actions[i].apply(view, found.from, found.to);
533
+ }
534
+ }
535
+ }
536
+ } else {
537
+ return;
538
+ }
539
+ event.preventDefault();
540
+ };
541
+ const onclick = (event) => {
542
+ for (let i = 0; i < this.items.length; i++) {
543
+ if (this.items[i].dom.contains(event.target)) {
544
+ this.moveSelection(i);
545
+ }
546
+ }
547
+ };
548
+ this.list = elt("ul", {
549
+ tabIndex: 0,
550
+ role: "listbox",
551
+ "aria-label": this.view.state.phrase("Diagnostics"),
552
+ onkeydown,
553
+ onclick
554
+ });
555
+ this.dom = elt(
556
+ "div",
557
+ { class: "cm-panel-lint" },
558
+ this.list,
559
+ elt(
560
+ "button",
561
+ {
562
+ type: "button",
563
+ name: "close",
564
+ "aria-label": this.view.state.phrase("close"),
565
+ onclick: () => closeLintPanel(this.view)
566
+ },
567
+ "\xD7"
568
+ )
569
+ );
570
+ this.update();
571
+ }
572
+ items = [];
573
+ dom;
574
+ list;
575
+ get selectedIndex() {
576
+ const { selected } = this.view.state.field(lintState);
577
+ if (!selected) {
578
+ return -1;
579
+ }
580
+ for (let i = 0; i < this.items.length; i++) {
581
+ if (this.items[i].diagnostic == selected.diagnostic) {
582
+ return i;
583
+ }
584
+ }
585
+ return -1;
586
+ }
587
+ update() {
588
+ const { diagnostics, selected } = this.view.state.field(lintState);
589
+ let i = 0, needsSync = false, newSelectedItem = null;
590
+ diagnostics.between(
591
+ 0,
592
+ this.view.state.doc.length,
593
+ (_start, _end, { spec }) => {
594
+ let found = -1, item;
595
+ for (let j = i; j < this.items.length; j++) {
596
+ if (this.items[j].diagnostic == spec.diagnostic) {
597
+ found = j;
598
+ break;
599
+ }
600
+ }
601
+ if (found < 0) {
602
+ item = new PanelItem(this.view, spec.diagnostic);
603
+ this.items.splice(i, 0, item);
604
+ needsSync = true;
605
+ } else {
606
+ item = this.items[found];
607
+ if (found > i) {
608
+ this.items.splice(i, found - i);
609
+ needsSync = true;
610
+ }
611
+ }
612
+ if (selected && item.diagnostic == selected.diagnostic) {
613
+ if (!item.dom.hasAttribute("aria-selected")) {
614
+ item.dom.setAttribute("aria-selected", "true");
615
+ newSelectedItem = item;
616
+ }
617
+ } else if (item.dom.hasAttribute("aria-selected")) {
618
+ item.dom.removeAttribute("aria-selected");
619
+ }
620
+ i++;
621
+ }
622
+ );
623
+ while (i < this.items.length && !(this.items.length == 1 && this.items[0].diagnostic.from < 0)) {
624
+ needsSync = true;
625
+ this.items.pop();
626
+ }
627
+ if (this.items.length == 0) {
628
+ this.items.push(
629
+ new PanelItem(this.view, {
630
+ from: -1,
631
+ to: -1,
632
+ severity: "info",
633
+ message: this.view.state.phrase("No diagnostics")
634
+ })
635
+ );
636
+ needsSync = true;
637
+ }
638
+ if (newSelectedItem) {
639
+ this.list.setAttribute("aria-activedescendant", newSelectedItem.id);
640
+ this.view.requestMeasure({
641
+ key: this,
642
+ read: () => ({
643
+ sel: newSelectedItem.dom.getBoundingClientRect(),
644
+ panel: this.list.getBoundingClientRect()
645
+ }),
646
+ write: ({ sel, panel }) => {
647
+ const scaleY = panel.height / this.list.offsetHeight;
648
+ if (sel.top < panel.top) {
649
+ this.list.scrollTop -= (panel.top - sel.top) / scaleY;
650
+ } else if (sel.bottom > panel.bottom) {
651
+ this.list.scrollTop += (sel.bottom - panel.bottom) / scaleY;
652
+ }
653
+ }
654
+ });
655
+ } else if (this.selectedIndex < 0) {
656
+ this.list.removeAttribute("aria-activedescendant");
657
+ }
658
+ if (needsSync) {
659
+ this.sync();
660
+ }
661
+ }
662
+ sync() {
663
+ let domPos = this.list.firstChild;
664
+ function rm() {
665
+ const prev = domPos;
666
+ domPos = prev.nextSibling;
667
+ prev.remove();
668
+ }
669
+ for (const item of this.items) {
670
+ if (item.dom.parentNode == this.list) {
671
+ while (domPos != item.dom) {
672
+ rm();
673
+ }
674
+ domPos = item.dom.nextSibling;
675
+ } else {
676
+ this.list.insertBefore(item.dom, domPos);
677
+ }
678
+ }
679
+ while (domPos) {
680
+ rm();
681
+ }
682
+ }
683
+ moveSelection(selectedIndex) {
684
+ if (this.selectedIndex < 0) {
685
+ return;
686
+ }
687
+ const field = this.view.state.field(lintState);
688
+ const selection = findDiagnostic(
689
+ field.diagnostics,
690
+ this.items[selectedIndex].diagnostic
691
+ );
692
+ if (!selection) {
693
+ return;
694
+ }
695
+ this.view.dispatch({
696
+ selection: { anchor: selection.from, head: selection.to },
697
+ scrollIntoView: true,
698
+ effects: movePanelSelection.of(selection)
699
+ });
700
+ }
701
+ static open(view) {
702
+ return new _LintPanel(view);
703
+ }
704
+ };
705
+ function svg(content, attrs = 'viewBox="0 0 40 40"') {
706
+ return `url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" ${attrs}>${encodeURIComponent(content)}</svg>')`;
707
+ }
708
+ function underline(color) {
709
+ return svg(
710
+ `<path d="m0 2.5 l2 -1.5 l1 0 l2 1.5 l1 0" stroke="${color}" fill="none" stroke-width=".7"/>`,
711
+ 'width="6" height="3"'
712
+ );
713
+ }
714
+ var baseTheme = EditorView.baseTheme({
715
+ ".cm-diagnostic": {
716
+ padding: "3px 6px 3px 8px",
717
+ marginLeft: "-1px",
718
+ display: "block",
719
+ whiteSpace: "pre-wrap"
720
+ },
721
+ ".cm-diagnostic-error": { borderLeft: "5px solid #d11" },
722
+ ".cm-diagnostic-warning": { borderLeft: "5px solid orange" },
723
+ ".cm-diagnostic-info": { borderLeft: "5px solid #999" },
724
+ ".cm-diagnostic-hint": { borderLeft: "5px solid #66d" },
725
+ ".cm-diagnosticAction": {
726
+ font: "inherit",
727
+ border: "none",
728
+ padding: "2px 4px",
729
+ backgroundColor: "#444",
730
+ color: "white",
731
+ borderRadius: "3px",
732
+ marginLeft: "8px",
733
+ cursor: "pointer"
734
+ },
735
+ ".cm-diagnosticSource": {
736
+ fontSize: "70%",
737
+ opacity: 0.7
738
+ },
739
+ ".cm-lintRange": {
740
+ backgroundPosition: "left bottom",
741
+ backgroundRepeat: "repeat-x",
742
+ paddingBottom: "0.7px"
743
+ },
744
+ ".cm-lintRange-error": { backgroundImage: underline("#d11") },
745
+ ".cm-lintRange-warning": { backgroundImage: underline("orange") },
746
+ ".cm-lintRange-info": { backgroundImage: underline("#999") },
747
+ ".cm-lintRange-hint": { backgroundImage: underline("#66d") },
748
+ ".cm-lintRange-active": { backgroundColor: "#ffdd9980" },
749
+ ".cm-tooltip-lint": {
750
+ padding: 0,
751
+ margin: 0
752
+ },
753
+ ".cm-lintPoint": {
754
+ position: "relative",
755
+ "&:after": {
756
+ content: '""',
757
+ position: "absolute",
758
+ bottom: 0,
759
+ left: "-2px",
760
+ borderLeft: "3px solid transparent",
761
+ borderRight: "3px solid transparent",
762
+ borderBottom: "4px solid #d11"
763
+ }
764
+ },
765
+ ".cm-lintPoint-warning": {
766
+ "&:after": { borderBottomColor: "orange" }
767
+ },
768
+ ".cm-lintPoint-info": {
769
+ "&:after": { borderBottomColor: "#999" }
770
+ },
771
+ ".cm-lintPoint-hint": {
772
+ "&:after": { borderBottomColor: "#66d" }
773
+ },
774
+ ".cm-panel.cm-panel-lint": {
775
+ position: "relative",
776
+ "& ul": {
777
+ maxHeight: "100px",
778
+ overflowY: "auto",
779
+ "& [aria-selected]": {
780
+ backgroundColor: "#ddd",
781
+ "& u": { textDecoration: "underline" }
782
+ },
783
+ "&:focus [aria-selected]": {
784
+ background_fallback: "#bdf",
785
+ backgroundColor: "Highlight",
786
+ color_fallback: "white",
787
+ color: "HighlightText"
788
+ },
789
+ "& u": { textDecoration: "none" },
790
+ padding: 0,
791
+ margin: 0
792
+ },
793
+ "& [name=close]": {
794
+ position: "absolute",
795
+ top: "0",
796
+ right: "2px",
797
+ background: "inherit",
798
+ border: "none",
799
+ font: "inherit",
800
+ padding: 0,
801
+ margin: 0
802
+ }
803
+ }
804
+ });
805
+ function severityWeight(sev) {
806
+ return sev == "error" ? 4 : sev == "warning" ? 3 : sev == "info" ? 2 : 1;
807
+ }
808
+ var LintGutterMarker = class extends GutterMarker {
809
+ constructor(diagnostics) {
810
+ super();
811
+ this.diagnostics = diagnostics;
812
+ this.severity = diagnostics.reduce(
813
+ (max, d) => severityWeight(max) < severityWeight(d.severity) ? d.severity : max,
814
+ "hint"
815
+ );
816
+ }
817
+ severity;
818
+ toDOM(view) {
819
+ const elt2 = document.createElement("div");
820
+ elt2.className = `cm-lint-marker cm-lint-marker-${this.severity}`;
821
+ let { diagnostics } = this;
822
+ const diagnosticsFilter = view.state.facet(lintGutterConfig).tooltipFilter;
823
+ if (diagnosticsFilter) {
824
+ diagnostics = diagnosticsFilter(diagnostics, view.state);
825
+ }
826
+ if (diagnostics.length) {
827
+ elt2.onmouseover = () => gutterMarkerMouseOver(view, elt2, diagnostics);
828
+ }
829
+ return elt2;
830
+ }
831
+ };
832
+ function trackHoverOn(view, marker) {
833
+ const mousemove = (event) => {
834
+ const rect = marker.getBoundingClientRect();
835
+ if (event.clientX > rect.left - 10 /* Margin */ && event.clientX < rect.right + 10 /* Margin */ && event.clientY > rect.top - 10 /* Margin */ && event.clientY < rect.bottom + 10 /* Margin */) {
836
+ return;
837
+ }
838
+ for (let target = event.target; target; target = target.parentNode) {
839
+ if (target.nodeType == 1 && target.classList.contains("cm-tooltip-lint")) {
840
+ return;
841
+ }
842
+ }
843
+ window.removeEventListener("mousemove", mousemove);
844
+ if (view.state.field(lintGutterTooltip)) {
845
+ view.dispatch({ effects: setLintGutterTooltip.of(null) });
846
+ }
847
+ };
848
+ window.addEventListener("mousemove", mousemove);
849
+ }
850
+ function gutterMarkerMouseOver(view, marker, diagnostics) {
851
+ function hovered() {
852
+ const line = view.elementAtHeight(
853
+ marker.getBoundingClientRect().top + 5 - view.documentTop
854
+ );
855
+ const linePos = view.coordsAtPos(line.from);
856
+ if (linePos) {
857
+ view.dispatch({
858
+ effects: setLintGutterTooltip.of({
859
+ pos: line.from,
860
+ above: false,
861
+ create() {
862
+ return {
863
+ dom: diagnosticsTooltip(view, diagnostics),
864
+ getCoords: () => marker.getBoundingClientRect()
865
+ };
866
+ }
867
+ })
868
+ });
869
+ }
870
+ marker.onmouseout = marker.onmousemove = null;
871
+ trackHoverOn(view, marker);
872
+ }
873
+ const { hoverTime } = view.state.facet(lintGutterConfig);
874
+ let hoverTimeout = setTimeout(hovered, hoverTime);
875
+ marker.onmouseout = () => {
876
+ clearTimeout(hoverTimeout);
877
+ marker.onmouseout = marker.onmousemove = null;
878
+ };
879
+ marker.onmousemove = () => {
880
+ clearTimeout(hoverTimeout);
881
+ hoverTimeout = setTimeout(hovered, hoverTime);
882
+ };
883
+ }
884
+ function markersForDiagnostics(doc, diagnostics) {
885
+ const byLine = /* @__PURE__ */ Object.create(null);
886
+ for (const diagnostic of diagnostics) {
887
+ const line = doc.lineAt(diagnostic.from);
888
+ (byLine[line.from] || (byLine[line.from] = [])).push(diagnostic);
889
+ }
890
+ const markers = [];
891
+ for (const line in byLine) {
892
+ markers.push(new LintGutterMarker(byLine[line]).range(+line));
893
+ }
894
+ return RangeSet.of(markers, true);
895
+ }
896
+ var lintGutterExtension = gutter({
897
+ class: "cm-gutter-lint",
898
+ markers: (view) => view.state.field(lintGutterMarkers)
899
+ });
900
+ var lintGutterMarkers = StateField.define({
901
+ create() {
902
+ return RangeSet.empty;
903
+ },
904
+ update(markers, tr) {
905
+ markers = markers.map(tr.changes);
906
+ const diagnosticFilter = tr.state.facet(lintGutterConfig).markerFilter;
907
+ for (const effect of tr.effects) {
908
+ if (effect.is(setDiagnosticsEffect)) {
909
+ let diagnostics = effect.value;
910
+ if (diagnosticFilter) {
911
+ diagnostics = diagnosticFilter(diagnostics || [], tr.state);
912
+ }
913
+ markers = markersForDiagnostics(tr.state.doc, diagnostics.slice(0));
914
+ }
915
+ }
916
+ return markers;
917
+ }
918
+ });
919
+ var setLintGutterTooltip = StateEffect.define();
920
+ var lintGutterTooltip = StateField.define({
921
+ create() {
922
+ return null;
923
+ },
924
+ update(tooltip, tr) {
925
+ if (tooltip && tr.docChanged) {
926
+ tooltip = hideTooltip(tr, tooltip) ? null : { ...tooltip, pos: tr.changes.mapPos(tooltip.pos) };
927
+ }
928
+ return tr.effects.reduce(
929
+ (t, e) => e.is(setLintGutterTooltip) ? e.value : t,
930
+ tooltip
931
+ );
932
+ },
933
+ provide: (field) => showTooltip.from(field)
934
+ });
935
+ var lintGutterTheme = EditorView.baseTheme({
936
+ ".cm-gutter-lint": {
937
+ width: "1.4em",
938
+ "& .cm-gutterElement": {
939
+ padding: ".2em"
940
+ }
941
+ },
942
+ ".cm-lint-marker": {
943
+ width: "1em",
944
+ height: "1em"
945
+ },
946
+ ".cm-lint-marker-info": {
947
+ content: svg(
948
+ '<path fill="#aaf" stroke="#77e" stroke-width="6" stroke-linejoin="round" d="M5 5L35 5L35 35L5 35Z"/>'
949
+ )
950
+ },
951
+ ".cm-lint-marker-warning": {
952
+ content: svg(
953
+ '<path fill="#fe8" stroke="#fd7" stroke-width="6" stroke-linejoin="round" d="M20 6L37 35L3 35Z"/>'
954
+ )
955
+ },
956
+ ".cm-lint-marker-error": {
957
+ content: svg(
958
+ '<circle cx="20" cy="20" r="15" fill="#f87" stroke="#f43" stroke-width="6"/>'
959
+ )
960
+ }
961
+ });
962
+ var lintExtensions = [
963
+ lintState,
964
+ EditorView.decorations.compute([lintState], (state) => {
965
+ const { selected, panel } = state.field(lintState);
966
+ return !selected || !panel || selected.from == selected.to ? Decoration2.none : Decoration2.set([activeMark.range(selected.from, selected.to)]);
967
+ }),
968
+ hoverTooltip(lintTooltip, { hideOn: hideTooltip }),
969
+ baseTheme
970
+ ];
971
+ var lintGutterConfig = Facet.define({
972
+ combine(configs) {
973
+ return combineConfig(configs, {
974
+ hoverTime: 300 /* Time */,
975
+ markerFilter: null,
976
+ tooltipFilter: null
977
+ });
978
+ }
979
+ });
980
+ function lintGutter(config = {}) {
981
+ return [
982
+ lintGutterConfig.of(config),
983
+ lintGutterMarkers,
984
+ lintGutterExtension,
985
+ lintGutterTheme,
986
+ lintGutterTooltip
987
+ ];
988
+ }
989
+ function forEachDiagnostic(state, f) {
990
+ const lState = state.field(lintState, false);
991
+ if (lState && lState.diagnostics.size) {
992
+ for (let iter = RangeSet.iter([lState.diagnostics]); iter.value; iter.next()) {
993
+ f(iter.value.spec.diagnostic, iter.from, iter.to);
994
+ }
995
+ }
996
+ }
997
+ export {
998
+ closeLintPanel,
999
+ diagnosticCount,
1000
+ forEachDiagnostic,
1001
+ forceLinting,
1002
+ lintGutter,
1003
+ lintKeymap,
1004
+ linter,
1005
+ nextDiagnostic,
1006
+ openLintPanel,
1007
+ previousDiagnostic,
1008
+ setDiagnostics,
1009
+ setDiagnosticsEffect
1010
+ };
1011
+ //# sourceMappingURL=index.js.map