@codemirror/lint 6.8.2 → 6.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## 6.8.4 (2024-11-28)
2
+
3
+ ### Bug fixes
4
+
5
+ Don't create overlapping decorations when diagnostics overlap.
6
+
7
+ Fix an issue where block widgets could cause the lint gutter to show diagnostics multiple times.
8
+
9
+ ## 6.8.3 (2024-11-21)
10
+
11
+ ### Bug fixes
12
+
13
+ Fix an issue that prevented tooltips in the lint gutter from being displayed.
14
+
1
15
  ## 6.8.2 (2024-09-24)
2
16
 
3
17
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -17,34 +17,76 @@ class LintState {
17
17
  this.panel = panel;
18
18
  this.selected = selected;
19
19
  }
20
- static init(diagnostics, panel, state) {
20
+ static init(diagnostics, panel, state$1) {
21
21
  // Filter the list of diagnostics for which to create markers
22
22
  let markedDiagnostics = diagnostics;
23
- let diagnosticFilter = state.facet(lintConfig).markerFilter;
23
+ let diagnosticFilter = state$1.facet(lintConfig).markerFilter;
24
24
  if (diagnosticFilter)
25
- markedDiagnostics = diagnosticFilter(markedDiagnostics, state);
26
- let ranges = view.Decoration.set(markedDiagnostics.map((d) => {
27
- // For zero-length ranges or ranges covering only a line break, create a widget
28
- return d.from == d.to || (d.from == d.to - 1 && state.doc.lineAt(d.from).to == d.from)
29
- ? view.Decoration.widget({
30
- widget: new DiagnosticWidget(d),
31
- diagnostic: d
32
- }).range(d.from)
33
- : view.Decoration.mark({
34
- attributes: { class: "cm-lintRange cm-lintRange-" + d.severity + (d.markClass ? " " + d.markClass : "") },
35
- diagnostic: d
36
- }).range(d.from, d.to);
37
- }), true);
38
- return new LintState(ranges, panel, findDiagnostic(ranges));
25
+ markedDiagnostics = diagnosticFilter(markedDiagnostics, state$1);
26
+ let sorted = diagnostics.slice().sort((a, b) => a.from - b.from || a.to - b.to);
27
+ let deco = new state.RangeSetBuilder(), active = [], pos = 0;
28
+ for (let i = 0;;) {
29
+ let next = i == sorted.length ? null : sorted[i];
30
+ if (!next && !active.length)
31
+ break;
32
+ let from, to;
33
+ if (active.length) {
34
+ from = pos;
35
+ to = active.reduce((p, d) => Math.min(p, d.to), next && next.from > from ? next.from : 1e8);
36
+ }
37
+ else {
38
+ from = next.from;
39
+ to = next.to;
40
+ active.push(next);
41
+ i++;
42
+ }
43
+ while (i < sorted.length) {
44
+ let next = sorted[i];
45
+ if (next.from == from && (next.to > next.from || next.to == from)) {
46
+ active.push(next);
47
+ i++;
48
+ to = Math.min(next.to, to);
49
+ }
50
+ else {
51
+ to = Math.min(next.from, to);
52
+ break;
53
+ }
54
+ }
55
+ let sev = maxSeverity(active);
56
+ if (active.some(d => d.from == d.to || (d.from == d.to - 1 && state$1.doc.lineAt(d.from).to == d.from))) {
57
+ deco.add(from, from, view.Decoration.widget({
58
+ widget: new DiagnosticWidget(sev),
59
+ diagnostics: active.slice()
60
+ }));
61
+ }
62
+ else {
63
+ let markClass = active.reduce((c, d) => d.markClass ? c + " " + d.markClass : c, "");
64
+ deco.add(from, to, view.Decoration.mark({
65
+ class: "cm-lintRange cm-lintRange-" + sev + markClass,
66
+ diagnostics: active.slice(),
67
+ inclusiveEnd: active.some(a => a.to > to)
68
+ }));
69
+ }
70
+ pos = to;
71
+ for (let i = 0; i < active.length; i++)
72
+ if (active[i].to <= pos)
73
+ active.splice(i--, 1);
74
+ }
75
+ let set = deco.finish();
76
+ return new LintState(set, panel, findDiagnostic(set));
39
77
  }
40
78
  }
41
79
  function findDiagnostic(diagnostics, diagnostic = null, after = 0) {
42
80
  let found = null;
43
81
  diagnostics.between(after, 1e9, (from, to, { spec }) => {
44
- if (diagnostic && spec.diagnostic != diagnostic)
82
+ if (diagnostic && spec.diagnostics.indexOf(diagnostic) < 0)
45
83
  return;
46
- found = new SelectedDiagnostic(from, to, spec.diagnostic);
47
- return false;
84
+ if (!found)
85
+ found = new SelectedDiagnostic(from, to, diagnostic || spec.diagnostics[0]);
86
+ else if (spec.diagnostics.indexOf(found.diagnostic) < 0)
87
+ return false;
88
+ else
89
+ found = new SelectedDiagnostic(found.from, to, found.diagnostic);
48
90
  });
49
91
  return found;
50
92
  }
@@ -118,24 +160,25 @@ function diagnosticCount(state) {
118
160
  const activeMark = view.Decoration.mark({ class: "cm-lintRange cm-lintRange-active" });
119
161
  function lintTooltip(view, pos, side) {
120
162
  let { diagnostics } = view.state.field(lintState);
121
- let found = [], stackStart = 2e8, stackEnd = 0;
163
+ let found, start = -1, end = -1;
122
164
  diagnostics.between(pos - (side < 0 ? 1 : 0), pos + (side > 0 ? 1 : 0), (from, to, { spec }) => {
123
165
  if (pos >= from && pos <= to &&
124
166
  (from == to || ((pos > from || side > 0) && (pos < to || side < 0)))) {
125
- found.push(spec.diagnostic);
126
- stackStart = Math.min(from, stackStart);
127
- stackEnd = Math.max(to, stackEnd);
167
+ found = spec.diagnostics;
168
+ start = from;
169
+ end = to;
170
+ return false;
128
171
  }
129
172
  });
130
173
  let diagnosticFilter = view.state.facet(lintConfig).tooltipFilter;
131
- if (diagnosticFilter)
174
+ if (found && diagnosticFilter)
132
175
  found = diagnosticFilter(found, view.state);
133
- if (!found.length)
176
+ if (!found)
134
177
  return null;
135
178
  return {
136
- pos: stackStart,
137
- end: stackEnd,
138
- above: view.state.doc.lineAt(stackStart).to < stackEnd,
179
+ pos: start,
180
+ end: end,
181
+ above: view.state.doc.lineAt(start).to < end,
139
182
  create() {
140
183
  return { dom: diagnosticsTooltip(view, found) };
141
184
  }
@@ -272,7 +315,7 @@ function batchResults(promises, sink, error) {
272
315
  if (collected.length == promises.length)
273
316
  sink(collected);
274
317
  else
275
- setTimeout(() => sink(collected), 200);
318
+ timeout = setTimeout(() => sink(collected), 200);
276
319
  }, error);
277
320
  }
278
321
  const lintConfig = state.Facet.define({
@@ -352,13 +395,13 @@ function renderDiagnostic(view, diagnostic, inPanel) {
352
395
  }), diagnostic.source && elt("div", { class: "cm-diagnosticSource" }, diagnostic.source));
353
396
  }
354
397
  class DiagnosticWidget extends view.WidgetType {
355
- constructor(diagnostic) {
398
+ constructor(sev) {
356
399
  super();
357
- this.diagnostic = diagnostic;
400
+ this.sev = sev;
358
401
  }
359
- eq(other) { return other.diagnostic == this.diagnostic; }
402
+ eq(other) { return other.sev == this.sev; }
360
403
  toDOM() {
361
- return elt("span", { class: "cm-lintPoint cm-lintPoint-" + this.diagnostic.severity });
404
+ return elt("span", { class: "cm-lintPoint cm-lintPoint-" + this.sev });
362
405
  }
363
406
  }
364
407
  class PanelItem {
@@ -441,35 +484,41 @@ class LintPanel {
441
484
  update() {
442
485
  let { diagnostics, selected } = this.view.state.field(lintState);
443
486
  let i = 0, needsSync = false, newSelectedItem = null;
487
+ let seen = new Set();
444
488
  diagnostics.between(0, this.view.state.doc.length, (_start, _end, { spec }) => {
445
- let found = -1, item;
446
- for (let j = i; j < this.items.length; j++)
447
- if (this.items[j].diagnostic == spec.diagnostic) {
448
- found = j;
449
- break;
450
- }
451
- if (found < 0) {
452
- item = new PanelItem(this.view, spec.diagnostic);
453
- this.items.splice(i, 0, item);
454
- needsSync = true;
455
- }
456
- else {
457
- item = this.items[found];
458
- if (found > i) {
459
- this.items.splice(i, found - i);
489
+ for (let diagnostic of spec.diagnostics) {
490
+ if (seen.has(diagnostic))
491
+ continue;
492
+ seen.add(diagnostic);
493
+ let found = -1, item;
494
+ for (let j = i; j < this.items.length; j++)
495
+ if (this.items[j].diagnostic == diagnostic) {
496
+ found = j;
497
+ break;
498
+ }
499
+ if (found < 0) {
500
+ item = new PanelItem(this.view, diagnostic);
501
+ this.items.splice(i, 0, item);
460
502
  needsSync = true;
461
503
  }
462
- }
463
- if (selected && item.diagnostic == selected.diagnostic) {
464
- if (!item.dom.hasAttribute("aria-selected")) {
465
- item.dom.setAttribute("aria-selected", "true");
466
- newSelectedItem = item;
504
+ else {
505
+ item = this.items[found];
506
+ if (found > i) {
507
+ this.items.splice(i, found - i);
508
+ needsSync = true;
509
+ }
467
510
  }
511
+ if (selected && item.diagnostic == selected.diagnostic) {
512
+ if (!item.dom.hasAttribute("aria-selected")) {
513
+ item.dom.setAttribute("aria-selected", "true");
514
+ newSelectedItem = item;
515
+ }
516
+ }
517
+ else if (item.dom.hasAttribute("aria-selected")) {
518
+ item.dom.removeAttribute("aria-selected");
519
+ }
520
+ i++;
468
521
  }
469
- else if (item.dom.hasAttribute("aria-selected")) {
470
- item.dom.removeAttribute("aria-selected");
471
- }
472
- i++;
473
522
  });
474
523
  while (i < this.items.length && !(this.items.length == 1 && this.items[0].diagnostic.from < 0)) {
475
524
  needsSync = true;
@@ -638,11 +687,22 @@ const baseTheme = view.EditorView.baseTheme({
638
687
  function severityWeight(sev) {
639
688
  return sev == "error" ? 4 : sev == "warning" ? 3 : sev == "info" ? 2 : 1;
640
689
  }
690
+ function maxSeverity(diagnostics) {
691
+ let sev = "hint", weight = 1;
692
+ for (let d of diagnostics) {
693
+ let w = severityWeight(d.severity);
694
+ if (w > weight) {
695
+ weight = w;
696
+ sev = d.severity;
697
+ }
698
+ }
699
+ return sev;
700
+ }
641
701
  class LintGutterMarker extends view.GutterMarker {
642
702
  constructor(diagnostics) {
643
703
  super();
644
704
  this.diagnostics = diagnostics;
645
- this.severity = diagnostics.reduce((max, d) => severityWeight(max) < severityWeight(d.severity) ? d.severity : max, "hint");
705
+ this.severity = maxSeverity(diagnostics);
646
706
  }
647
707
  toDOM(view) {
648
708
  let elt = document.createElement("div");
@@ -680,6 +740,7 @@ function gutterMarkerMouseOver(view, marker, diagnostics) {
680
740
  view.dispatch({ effects: setLintGutterTooltip.of({
681
741
  pos: line.from,
682
742
  above: false,
743
+ clip: false,
683
744
  create() {
684
745
  return {
685
746
  dom: diagnosticsTooltip(view, diagnostics),
@@ -720,7 +781,8 @@ const lintGutterExtension = view.gutter({
720
781
  widgetMarker: (view, widget, block) => {
721
782
  let diagnostics = [];
722
783
  view.state.field(lintGutterMarkers).between(block.from, block.to, (from, to, value) => {
723
- diagnostics.push(...value.diagnostics);
784
+ if (from > block.from && from < block.to)
785
+ diagnostics.push(...value.diagnostics);
724
786
  });
725
787
  return diagnostics.length ? new LintGutterMarker(diagnostics) : null;
726
788
  }
@@ -811,9 +873,25 @@ arguments hold the diagnostic's current position.
811
873
  */
812
874
  function forEachDiagnostic(state$1, f) {
813
875
  let lState = state$1.field(lintState, false);
814
- if (lState && lState.diagnostics.size)
815
- for (let iter = state.RangeSet.iter([lState.diagnostics]); iter.value; iter.next())
816
- f(iter.value.spec.diagnostic, iter.from, iter.to);
876
+ if (lState && lState.diagnostics.size) {
877
+ let pending = [], pendingStart = [], lastEnd = -1;
878
+ for (let iter = state.RangeSet.iter([lState.diagnostics]);; iter.next()) {
879
+ for (let i = 0; i < pending.length; i++)
880
+ if (!iter.value || iter.value.spec.diagnostics.indexOf(pending[i]) < 0) {
881
+ f(pending[i], pendingStart[i], lastEnd);
882
+ pending.splice(i, 1);
883
+ pendingStart.splice(i--, 1);
884
+ }
885
+ if (!iter.value)
886
+ break;
887
+ for (let d of iter.value.spec.diagnostics)
888
+ if (pending.indexOf(d) < 0) {
889
+ pending.push(d);
890
+ pendingStart.push(iter.from);
891
+ }
892
+ lastEnd = iter.to;
893
+ }
894
+ }
817
895
  }
818
896
 
819
897
  exports.closeLintPanel = closeLintPanel;
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Decoration, showPanel, EditorView, ViewPlugin, gutter, showTooltip, hoverTooltip, getPanel, logException, WidgetType, GutterMarker } from '@codemirror/view';
2
- import { StateEffect, StateField, Facet, combineConfig, RangeSet } from '@codemirror/state';
2
+ import { StateEffect, StateField, Facet, combineConfig, RangeSet, RangeSetBuilder } from '@codemirror/state';
3
3
  import elt from 'crelt';
4
4
 
5
5
  class SelectedDiagnostic {
@@ -21,28 +21,70 @@ class LintState {
21
21
  let diagnosticFilter = state.facet(lintConfig).markerFilter;
22
22
  if (diagnosticFilter)
23
23
  markedDiagnostics = diagnosticFilter(markedDiagnostics, state);
24
- let ranges = Decoration.set(markedDiagnostics.map((d) => {
25
- // For zero-length ranges or ranges covering only a line break, create a widget
26
- return d.from == d.to || (d.from == d.to - 1 && state.doc.lineAt(d.from).to == d.from)
27
- ? Decoration.widget({
28
- widget: new DiagnosticWidget(d),
29
- diagnostic: d
30
- }).range(d.from)
31
- : Decoration.mark({
32
- attributes: { class: "cm-lintRange cm-lintRange-" + d.severity + (d.markClass ? " " + d.markClass : "") },
33
- diagnostic: d
34
- }).range(d.from, d.to);
35
- }), true);
36
- return new LintState(ranges, panel, findDiagnostic(ranges));
24
+ let sorted = diagnostics.slice().sort((a, b) => a.from - b.from || a.to - b.to);
25
+ let deco = new RangeSetBuilder(), active = [], pos = 0;
26
+ for (let i = 0;;) {
27
+ let next = i == sorted.length ? null : sorted[i];
28
+ if (!next && !active.length)
29
+ break;
30
+ let from, to;
31
+ if (active.length) {
32
+ from = pos;
33
+ to = active.reduce((p, d) => Math.min(p, d.to), next && next.from > from ? next.from : 1e8);
34
+ }
35
+ else {
36
+ from = next.from;
37
+ to = next.to;
38
+ active.push(next);
39
+ i++;
40
+ }
41
+ while (i < sorted.length) {
42
+ let next = sorted[i];
43
+ if (next.from == from && (next.to > next.from || next.to == from)) {
44
+ active.push(next);
45
+ i++;
46
+ to = Math.min(next.to, to);
47
+ }
48
+ else {
49
+ to = Math.min(next.from, to);
50
+ break;
51
+ }
52
+ }
53
+ let sev = maxSeverity(active);
54
+ if (active.some(d => d.from == d.to || (d.from == d.to - 1 && state.doc.lineAt(d.from).to == d.from))) {
55
+ deco.add(from, from, Decoration.widget({
56
+ widget: new DiagnosticWidget(sev),
57
+ diagnostics: active.slice()
58
+ }));
59
+ }
60
+ else {
61
+ let markClass = active.reduce((c, d) => d.markClass ? c + " " + d.markClass : c, "");
62
+ deco.add(from, to, Decoration.mark({
63
+ class: "cm-lintRange cm-lintRange-" + sev + markClass,
64
+ diagnostics: active.slice(),
65
+ inclusiveEnd: active.some(a => a.to > to)
66
+ }));
67
+ }
68
+ pos = to;
69
+ for (let i = 0; i < active.length; i++)
70
+ if (active[i].to <= pos)
71
+ active.splice(i--, 1);
72
+ }
73
+ let set = deco.finish();
74
+ return new LintState(set, panel, findDiagnostic(set));
37
75
  }
38
76
  }
39
77
  function findDiagnostic(diagnostics, diagnostic = null, after = 0) {
40
78
  let found = null;
41
79
  diagnostics.between(after, 1e9, (from, to, { spec }) => {
42
- if (diagnostic && spec.diagnostic != diagnostic)
80
+ if (diagnostic && spec.diagnostics.indexOf(diagnostic) < 0)
43
81
  return;
44
- found = new SelectedDiagnostic(from, to, spec.diagnostic);
45
- return false;
82
+ if (!found)
83
+ found = new SelectedDiagnostic(from, to, diagnostic || spec.diagnostics[0]);
84
+ else if (spec.diagnostics.indexOf(found.diagnostic) < 0)
85
+ return false;
86
+ else
87
+ found = new SelectedDiagnostic(found.from, to, found.diagnostic);
46
88
  });
47
89
  return found;
48
90
  }
@@ -116,24 +158,25 @@ function diagnosticCount(state) {
116
158
  const activeMark = /*@__PURE__*/Decoration.mark({ class: "cm-lintRange cm-lintRange-active" });
117
159
  function lintTooltip(view, pos, side) {
118
160
  let { diagnostics } = view.state.field(lintState);
119
- let found = [], stackStart = 2e8, stackEnd = 0;
161
+ let found, start = -1, end = -1;
120
162
  diagnostics.between(pos - (side < 0 ? 1 : 0), pos + (side > 0 ? 1 : 0), (from, to, { spec }) => {
121
163
  if (pos >= from && pos <= to &&
122
164
  (from == to || ((pos > from || side > 0) && (pos < to || side < 0)))) {
123
- found.push(spec.diagnostic);
124
- stackStart = Math.min(from, stackStart);
125
- stackEnd = Math.max(to, stackEnd);
165
+ found = spec.diagnostics;
166
+ start = from;
167
+ end = to;
168
+ return false;
126
169
  }
127
170
  });
128
171
  let diagnosticFilter = view.state.facet(lintConfig).tooltipFilter;
129
- if (diagnosticFilter)
172
+ if (found && diagnosticFilter)
130
173
  found = diagnosticFilter(found, view.state);
131
- if (!found.length)
174
+ if (!found)
132
175
  return null;
133
176
  return {
134
- pos: stackStart,
135
- end: stackEnd,
136
- above: view.state.doc.lineAt(stackStart).to < stackEnd,
177
+ pos: start,
178
+ end: end,
179
+ above: view.state.doc.lineAt(start).to < end,
137
180
  create() {
138
181
  return { dom: diagnosticsTooltip(view, found) };
139
182
  }
@@ -270,7 +313,7 @@ function batchResults(promises, sink, error) {
270
313
  if (collected.length == promises.length)
271
314
  sink(collected);
272
315
  else
273
- setTimeout(() => sink(collected), 200);
316
+ timeout = setTimeout(() => sink(collected), 200);
274
317
  }, error);
275
318
  }
276
319
  const lintConfig = /*@__PURE__*/Facet.define({
@@ -350,13 +393,13 @@ function renderDiagnostic(view, diagnostic, inPanel) {
350
393
  }), diagnostic.source && elt("div", { class: "cm-diagnosticSource" }, diagnostic.source));
351
394
  }
352
395
  class DiagnosticWidget extends WidgetType {
353
- constructor(diagnostic) {
396
+ constructor(sev) {
354
397
  super();
355
- this.diagnostic = diagnostic;
398
+ this.sev = sev;
356
399
  }
357
- eq(other) { return other.diagnostic == this.diagnostic; }
400
+ eq(other) { return other.sev == this.sev; }
358
401
  toDOM() {
359
- return elt("span", { class: "cm-lintPoint cm-lintPoint-" + this.diagnostic.severity });
402
+ return elt("span", { class: "cm-lintPoint cm-lintPoint-" + this.sev });
360
403
  }
361
404
  }
362
405
  class PanelItem {
@@ -439,35 +482,41 @@ class LintPanel {
439
482
  update() {
440
483
  let { diagnostics, selected } = this.view.state.field(lintState);
441
484
  let i = 0, needsSync = false, newSelectedItem = null;
485
+ let seen = new Set();
442
486
  diagnostics.between(0, this.view.state.doc.length, (_start, _end, { spec }) => {
443
- let found = -1, item;
444
- for (let j = i; j < this.items.length; j++)
445
- if (this.items[j].diagnostic == spec.diagnostic) {
446
- found = j;
447
- break;
448
- }
449
- if (found < 0) {
450
- item = new PanelItem(this.view, spec.diagnostic);
451
- this.items.splice(i, 0, item);
452
- needsSync = true;
453
- }
454
- else {
455
- item = this.items[found];
456
- if (found > i) {
457
- this.items.splice(i, found - i);
487
+ for (let diagnostic of spec.diagnostics) {
488
+ if (seen.has(diagnostic))
489
+ continue;
490
+ seen.add(diagnostic);
491
+ let found = -1, item;
492
+ for (let j = i; j < this.items.length; j++)
493
+ if (this.items[j].diagnostic == diagnostic) {
494
+ found = j;
495
+ break;
496
+ }
497
+ if (found < 0) {
498
+ item = new PanelItem(this.view, diagnostic);
499
+ this.items.splice(i, 0, item);
458
500
  needsSync = true;
459
501
  }
460
- }
461
- if (selected && item.diagnostic == selected.diagnostic) {
462
- if (!item.dom.hasAttribute("aria-selected")) {
463
- item.dom.setAttribute("aria-selected", "true");
464
- newSelectedItem = item;
502
+ else {
503
+ item = this.items[found];
504
+ if (found > i) {
505
+ this.items.splice(i, found - i);
506
+ needsSync = true;
507
+ }
465
508
  }
509
+ if (selected && item.diagnostic == selected.diagnostic) {
510
+ if (!item.dom.hasAttribute("aria-selected")) {
511
+ item.dom.setAttribute("aria-selected", "true");
512
+ newSelectedItem = item;
513
+ }
514
+ }
515
+ else if (item.dom.hasAttribute("aria-selected")) {
516
+ item.dom.removeAttribute("aria-selected");
517
+ }
518
+ i++;
466
519
  }
467
- else if (item.dom.hasAttribute("aria-selected")) {
468
- item.dom.removeAttribute("aria-selected");
469
- }
470
- i++;
471
520
  });
472
521
  while (i < this.items.length && !(this.items.length == 1 && this.items[0].diagnostic.from < 0)) {
473
522
  needsSync = true;
@@ -636,11 +685,22 @@ const baseTheme = /*@__PURE__*/EditorView.baseTheme({
636
685
  function severityWeight(sev) {
637
686
  return sev == "error" ? 4 : sev == "warning" ? 3 : sev == "info" ? 2 : 1;
638
687
  }
688
+ function maxSeverity(diagnostics) {
689
+ let sev = "hint", weight = 1;
690
+ for (let d of diagnostics) {
691
+ let w = severityWeight(d.severity);
692
+ if (w > weight) {
693
+ weight = w;
694
+ sev = d.severity;
695
+ }
696
+ }
697
+ return sev;
698
+ }
639
699
  class LintGutterMarker extends GutterMarker {
640
700
  constructor(diagnostics) {
641
701
  super();
642
702
  this.diagnostics = diagnostics;
643
- this.severity = diagnostics.reduce((max, d) => severityWeight(max) < severityWeight(d.severity) ? d.severity : max, "hint");
703
+ this.severity = maxSeverity(diagnostics);
644
704
  }
645
705
  toDOM(view) {
646
706
  let elt = document.createElement("div");
@@ -678,6 +738,7 @@ function gutterMarkerMouseOver(view, marker, diagnostics) {
678
738
  view.dispatch({ effects: setLintGutterTooltip.of({
679
739
  pos: line.from,
680
740
  above: false,
741
+ clip: false,
681
742
  create() {
682
743
  return {
683
744
  dom: diagnosticsTooltip(view, diagnostics),
@@ -718,7 +779,8 @@ const lintGutterExtension = /*@__PURE__*/gutter({
718
779
  widgetMarker: (view, widget, block) => {
719
780
  let diagnostics = [];
720
781
  view.state.field(lintGutterMarkers).between(block.from, block.to, (from, to, value) => {
721
- diagnostics.push(...value.diagnostics);
782
+ if (from > block.from && from < block.to)
783
+ diagnostics.push(...value.diagnostics);
722
784
  });
723
785
  return diagnostics.length ? new LintGutterMarker(diagnostics) : null;
724
786
  }
@@ -809,9 +871,25 @@ arguments hold the diagnostic's current position.
809
871
  */
810
872
  function forEachDiagnostic(state, f) {
811
873
  let lState = state.field(lintState, false);
812
- if (lState && lState.diagnostics.size)
813
- for (let iter = RangeSet.iter([lState.diagnostics]); iter.value; iter.next())
814
- f(iter.value.spec.diagnostic, iter.from, iter.to);
874
+ if (lState && lState.diagnostics.size) {
875
+ let pending = [], pendingStart = [], lastEnd = -1;
876
+ for (let iter = RangeSet.iter([lState.diagnostics]);; iter.next()) {
877
+ for (let i = 0; i < pending.length; i++)
878
+ if (!iter.value || iter.value.spec.diagnostics.indexOf(pending[i]) < 0) {
879
+ f(pending[i], pendingStart[i], lastEnd);
880
+ pending.splice(i, 1);
881
+ pendingStart.splice(i--, 1);
882
+ }
883
+ if (!iter.value)
884
+ break;
885
+ for (let d of iter.value.spec.diagnostics)
886
+ if (pending.indexOf(d) < 0) {
887
+ pending.push(d);
888
+ pendingStart.push(iter.from);
889
+ }
890
+ lastEnd = iter.to;
891
+ }
892
+ }
815
893
  }
816
894
 
817
895
  export { closeLintPanel, diagnosticCount, forEachDiagnostic, forceLinting, lintGutter, lintKeymap, linter, nextDiagnostic, openLintPanel, previousDiagnostic, setDiagnostics, setDiagnosticsEffect };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/lint",
3
- "version": "6.8.2",
3
+ "version": "6.8.4",
4
4
  "description": "Linting support for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",
@@ -27,7 +27,7 @@
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
29
  "@codemirror/state": "^6.0.0",
30
- "@codemirror/view": "^6.0.0",
30
+ "@codemirror/view": "^6.35.0",
31
31
  "crelt": "^1.0.5"
32
32
  },
33
33
  "devDependencies": {