@codemirror/lint 0.20.1 → 6.0.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/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ ## 6.0.0 (2022-06-08)
2
+
3
+ ### Breaking changes
4
+
5
+ Update dependencies to 6.0.0
6
+
7
+ ## 0.20.3 (2022-05-25)
8
+
9
+ ### New features
10
+
11
+ Diagnostic objects may now have a `renderMessage` method to render their message to the DOM.
12
+
13
+ ## 0.20.2 (2022-05-02)
14
+
15
+ ### New features
16
+
17
+ The package now exports the `LintSource` function type.
18
+
19
+ The new `markerFilter` and `tooltipFilter` options to `linter` and `lintGutter` allow more control over which diagnostics are visible and which have tooltips.
20
+
1
21
  ## 0.20.1 (2022-04-22)
2
22
 
3
23
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -24,7 +24,12 @@ class LintState {
24
24
  this.selected = selected;
25
25
  }
26
26
  static init(diagnostics, panel, state) {
27
- let ranges = view.Decoration.set(diagnostics.map((d) => {
27
+ // Filter the list of diagnostics for which to create markers
28
+ let markedDiagnostics = diagnostics;
29
+ let diagnosticFilter = state.facet(lintConfig).markerFilter;
30
+ if (diagnosticFilter)
31
+ markedDiagnostics = diagnosticFilter(markedDiagnostics);
32
+ let ranges = view.Decoration.set(markedDiagnostics.map((d) => {
28
33
  // For zero-length ranges or ranges covering only a line break, create a widget
29
34
  return d.from == d.to || (d.from == d.to - 1 && state.doc.lineAt(d.from).to == d.from)
30
35
  ? view.Decoration.widget({
@@ -130,6 +135,9 @@ function lintTooltip(view, pos, side) {
130
135
  stackEnd = Math.max(to, stackEnd);
131
136
  }
132
137
  });
138
+ let diagnosticFilter = view.state.facet(lintConfig).tooltipFilter;
139
+ if (diagnosticFilter)
140
+ found = diagnosticFilter(found);
133
141
  if (!found.length)
134
142
  return null;
135
143
  return {
@@ -197,7 +205,7 @@ const lintPlugin = view.ViewPlugin.fromClass(class {
197
205
  this.view = view;
198
206
  this.timeout = -1;
199
207
  this.set = true;
200
- let { delay } = view.state.facet(lintSource);
208
+ let { delay } = view.state.facet(lintConfig);
201
209
  this.lintTime = Date.now() + delay;
202
210
  this.run = this.run.bind(this);
203
211
  this.timeout = setTimeout(this.run, delay);
@@ -209,7 +217,7 @@ const lintPlugin = view.ViewPlugin.fromClass(class {
209
217
  }
210
218
  else {
211
219
  this.set = false;
212
- let { state } = this.view, { sources } = state.facet(lintSource);
220
+ let { state } = this.view, { sources } = state.facet(lintConfig);
213
221
  Promise.all(sources.map(source => Promise.resolve(source(this.view)))).then(annotations => {
214
222
  let all = annotations.reduce((a, b) => a.concat(b));
215
223
  if (this.view.state.doc == state.doc)
@@ -218,12 +226,12 @@ const lintPlugin = view.ViewPlugin.fromClass(class {
218
226
  }
219
227
  }
220
228
  update(update) {
221
- let source = update.state.facet(lintSource);
222
- if (update.docChanged || source != update.startState.facet(lintSource)) {
223
- this.lintTime = Date.now() + source.delay;
229
+ let config = update.state.facet(lintConfig);
230
+ if (update.docChanged || config != update.startState.facet(lintConfig)) {
231
+ this.lintTime = Date.now() + config.delay;
224
232
  if (!this.set) {
225
233
  this.set = true;
226
- this.timeout = setTimeout(this.run, source.delay);
234
+ this.timeout = setTimeout(this.run, config.delay);
227
235
  }
228
236
  }
229
237
  }
@@ -237,9 +245,13 @@ const lintPlugin = view.ViewPlugin.fromClass(class {
237
245
  clearTimeout(this.timeout);
238
246
  }
239
247
  });
240
- const lintSource = state.Facet.define({
248
+ const lintConfig = state.Facet.define({
241
249
  combine(input) {
242
- return { sources: input.map(i => i.source), delay: input.length ? Math.max(...input.map(i => i.delay)) : 750 };
250
+ return Object.assign({ sources: input.map(i => i.source) }, state.combineConfig(input.map(i => i.config), {
251
+ delay: 750,
252
+ markerFilter: null,
253
+ tooltipFilter: null
254
+ }));
243
255
  },
244
256
  enables: lintPlugin
245
257
  });
@@ -249,8 +261,7 @@ enables linting with that source. It will be called whenever the
249
261
  editor is idle (after its content changed).
250
262
  */
251
263
  function linter(source, config = {}) {
252
- var _a;
253
- return lintSource.of({ source, delay: (_a = config.delay) !== null && _a !== void 0 ? _a : 750 });
264
+ return lintConfig.of({ source, config });
254
265
  }
255
266
  /**
256
267
  Forces any linters [configured](https://codemirror.net/6/docs/ref/#lint.linter) to run when the
@@ -279,7 +290,7 @@ function assignKeys(actions) {
279
290
  function renderDiagnostic(view, diagnostic, inPanel) {
280
291
  var _a;
281
292
  let keys = inPanel ? assignKeys(diagnostic.actions) : [];
282
- return elt__default["default"]("li", { class: "cm-diagnostic cm-diagnostic-" + diagnostic.severity }, elt__default["default"]("span", { class: "cm-diagnosticText" }, diagnostic.message), (_a = diagnostic.actions) === null || _a === void 0 ? void 0 : _a.map((action, i) => {
293
+ return elt__default["default"]("li", { class: "cm-diagnostic cm-diagnostic-" + diagnostic.severity }, elt__default["default"]("span", { class: "cm-diagnosticText" }, diagnostic.renderMessage ? diagnostic.renderMessage() : diagnostic.message), (_a = diagnostic.actions) === null || _a === void 0 ? void 0 : _a.map((action, i) => {
283
294
  let click = (e) => {
284
295
  e.preventDefault();
285
296
  let found = findDiagnostic(view.state.field(lintState).diagnostics, diagnostic);
@@ -588,7 +599,12 @@ class LintGutterMarker extends view.GutterMarker {
588
599
  toDOM(view) {
589
600
  let elt = document.createElement("div");
590
601
  elt.className = "cm-lint-marker cm-lint-marker-" + this.severity;
591
- elt.onmouseover = () => gutterMarkerMouseOver(view, elt, this.diagnostics);
602
+ let diagnostics = this.diagnostics;
603
+ let diagnosticsFilter = view.state.facet(lintGutterConfig).tooltipFilter;
604
+ if (diagnosticsFilter)
605
+ diagnostics = diagnosticsFilter(diagnostics);
606
+ if (diagnostics.length)
607
+ elt.onmouseover = () => gutterMarkerMouseOver(view, elt, diagnostics);
592
608
  return elt;
593
609
  }
594
610
  }
@@ -660,10 +676,15 @@ const lintGutterMarkers = state.StateField.define({
660
676
  },
661
677
  update(markers, tr) {
662
678
  markers = markers.map(tr.changes);
663
- for (let effect of tr.effects)
679
+ let diagnosticFilter = tr.state.facet(lintGutterConfig).markerFilter;
680
+ for (let effect of tr.effects) {
664
681
  if (effect.is(setDiagnosticsEffect)) {
665
- markers = markersForDiagnostics(tr.state.doc, effect.value);
682
+ let diagnostics = effect.value;
683
+ if (diagnosticFilter)
684
+ diagnostics = diagnosticFilter(diagnostics || []);
685
+ markers = markersForDiagnostics(tr.state.doc, diagnostics.slice(0));
666
686
  }
687
+ }
667
688
  return markers;
668
689
  }
669
690
  });
@@ -702,6 +723,8 @@ const lintGutterConfig = state.Facet.define({
702
723
  combine(configs) {
703
724
  return state.combineConfig(configs, {
704
725
  hoverTime: 300 /* Time */,
726
+ markerFilter: null,
727
+ tooltipFilter: null
705
728
  });
706
729
  }
707
730
  });
package/dist/index.d.ts CHANGED
@@ -31,6 +31,11 @@ interface Diagnostic {
31
31
  */
32
32
  message: string;
33
33
  /**
34
+ An optional custom rendering function that displays the message
35
+ as a DOM node.
36
+ */
37
+ renderMessage?: () => Node;
38
+ /**
34
39
  An optional array of actions that can be taken on this
35
40
  diagnostic.
36
41
  */
@@ -51,11 +56,39 @@ interface Action {
51
56
  */
52
57
  apply: (view: EditorView, from: number, to: number) => void;
53
58
  }
59
+ declare type DiagnosticFilter = (diagnostics: readonly Diagnostic[]) => Diagnostic[];
60
+ interface LintConfig {
61
+ /**
62
+ Time to wait (in milliseconds) after a change before running
63
+ the linter. Defaults to 750ms.
64
+ */
65
+ delay?: number;
66
+ /**
67
+ Optional filter to determine which diagnostics produce markers
68
+ in the content.
69
+ */
70
+ markerFilter?: null | DiagnosticFilter;
71
+ /**
72
+ Filter applied to a set of diagnostics shown in a tooltip. No
73
+ tooltip will appear if the empty set is returned.
74
+ */
75
+ tooltipFilter?: null | DiagnosticFilter;
76
+ }
54
77
  interface LintGutterConfig {
55
78
  /**
56
79
  The delay before showing a tooltip when hovering over a lint gutter marker.
57
80
  */
58
81
  hoverTime?: number;
82
+ /**
83
+ Optional filter determining which diagnostics show a marker in
84
+ the gutter.
85
+ */
86
+ markerFilter?: null | DiagnosticFilter;
87
+ /**
88
+ Optional filter for diagnostics displayed in a tooltip, which
89
+ can also be used to prevent a tooltip appearing.
90
+ */
91
+ tooltipFilter?: null | DiagnosticFilter;
59
92
  }
60
93
  /**
61
94
  Returns a transaction spec which updates the current set of
@@ -91,19 +124,16 @@ A set of default key bindings for the lint functionality.
91
124
  - F8: [`nextDiagnostic`](https://codemirror.net/6/docs/ref/#lint.nextDiagnostic)
92
125
  */
93
126
  declare const lintKeymap: readonly KeyBinding[];
127
+ /**
128
+ The type of a function that produces diagnostics.
129
+ */
94
130
  declare type LintSource = (view: EditorView) => readonly Diagnostic[] | Promise<readonly Diagnostic[]>;
95
131
  /**
96
132
  Given a diagnostic source, this function returns an extension that
97
133
  enables linting with that source. It will be called whenever the
98
134
  editor is idle (after its content changed).
99
135
  */
100
- declare function linter(source: LintSource, config?: {
101
- /**
102
- Time to wait (in milliseconds) after a change before running
103
- the linter. Defaults to 750ms.
104
- */
105
- delay?: number;
106
- }): Extension;
136
+ declare function linter(source: LintSource, config?: LintConfig): Extension;
107
137
  /**
108
138
  Forces any linters [configured](https://codemirror.net/6/docs/ref/#lint.linter) to run when the
109
139
  editor is idle to run right away.
@@ -116,4 +146,4 @@ the diagnostics.
116
146
  */
117
147
  declare function lintGutter(config?: LintGutterConfig): Extension;
118
148
 
119
- export { Action, Diagnostic, closeLintPanel, diagnosticCount, forceLinting, lintGutter, lintKeymap, linter, nextDiagnostic, openLintPanel, setDiagnostics, setDiagnosticsEffect };
149
+ export { Action, Diagnostic, LintSource, closeLintPanel, diagnosticCount, forceLinting, lintGutter, lintKeymap, linter, nextDiagnostic, openLintPanel, setDiagnostics, setDiagnosticsEffect };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Decoration, showPanel, EditorView, ViewPlugin, hoverTooltip, logException, gutter, showTooltip, getPanel, WidgetType, GutterMarker } from '@codemirror/view';
2
- import { StateEffect, StateField, Facet, RangeSet, combineConfig } from '@codemirror/state';
2
+ import { StateEffect, StateField, Facet, combineConfig, RangeSet } from '@codemirror/state';
3
3
  import elt from 'crelt';
4
4
 
5
5
  class SelectedDiagnostic {
@@ -16,7 +16,12 @@ class LintState {
16
16
  this.selected = selected;
17
17
  }
18
18
  static init(diagnostics, panel, state) {
19
- let ranges = Decoration.set(diagnostics.map((d) => {
19
+ // Filter the list of diagnostics for which to create markers
20
+ let markedDiagnostics = diagnostics;
21
+ let diagnosticFilter = state.facet(lintConfig).markerFilter;
22
+ if (diagnosticFilter)
23
+ markedDiagnostics = diagnosticFilter(markedDiagnostics);
24
+ let ranges = Decoration.set(markedDiagnostics.map((d) => {
20
25
  // For zero-length ranges or ranges covering only a line break, create a widget
21
26
  return d.from == d.to || (d.from == d.to - 1 && state.doc.lineAt(d.from).to == d.from)
22
27
  ? Decoration.widget({
@@ -122,6 +127,9 @@ function lintTooltip(view, pos, side) {
122
127
  stackEnd = Math.max(to, stackEnd);
123
128
  }
124
129
  });
130
+ let diagnosticFilter = view.state.facet(lintConfig).tooltipFilter;
131
+ if (diagnosticFilter)
132
+ found = diagnosticFilter(found);
125
133
  if (!found.length)
126
134
  return null;
127
135
  return {
@@ -189,7 +197,7 @@ const lintPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
189
197
  this.view = view;
190
198
  this.timeout = -1;
191
199
  this.set = true;
192
- let { delay } = view.state.facet(lintSource);
200
+ let { delay } = view.state.facet(lintConfig);
193
201
  this.lintTime = Date.now() + delay;
194
202
  this.run = this.run.bind(this);
195
203
  this.timeout = setTimeout(this.run, delay);
@@ -201,7 +209,7 @@ const lintPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
201
209
  }
202
210
  else {
203
211
  this.set = false;
204
- let { state } = this.view, { sources } = state.facet(lintSource);
212
+ let { state } = this.view, { sources } = state.facet(lintConfig);
205
213
  Promise.all(sources.map(source => Promise.resolve(source(this.view)))).then(annotations => {
206
214
  let all = annotations.reduce((a, b) => a.concat(b));
207
215
  if (this.view.state.doc == state.doc)
@@ -210,12 +218,12 @@ const lintPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
210
218
  }
211
219
  }
212
220
  update(update) {
213
- let source = update.state.facet(lintSource);
214
- if (update.docChanged || source != update.startState.facet(lintSource)) {
215
- this.lintTime = Date.now() + source.delay;
221
+ let config = update.state.facet(lintConfig);
222
+ if (update.docChanged || config != update.startState.facet(lintConfig)) {
223
+ this.lintTime = Date.now() + config.delay;
216
224
  if (!this.set) {
217
225
  this.set = true;
218
- this.timeout = setTimeout(this.run, source.delay);
226
+ this.timeout = setTimeout(this.run, config.delay);
219
227
  }
220
228
  }
221
229
  }
@@ -229,9 +237,13 @@ const lintPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
229
237
  clearTimeout(this.timeout);
230
238
  }
231
239
  });
232
- const lintSource = /*@__PURE__*/Facet.define({
240
+ const lintConfig = /*@__PURE__*/Facet.define({
233
241
  combine(input) {
234
- return { sources: input.map(i => i.source), delay: input.length ? Math.max(...input.map(i => i.delay)) : 750 };
242
+ return Object.assign({ sources: input.map(i => i.source) }, combineConfig(input.map(i => i.config), {
243
+ delay: 750,
244
+ markerFilter: null,
245
+ tooltipFilter: null
246
+ }));
235
247
  },
236
248
  enables: lintPlugin
237
249
  });
@@ -241,8 +253,7 @@ enables linting with that source. It will be called whenever the
241
253
  editor is idle (after its content changed).
242
254
  */
243
255
  function linter(source, config = {}) {
244
- var _a;
245
- return lintSource.of({ source, delay: (_a = config.delay) !== null && _a !== void 0 ? _a : 750 });
256
+ return lintConfig.of({ source, config });
246
257
  }
247
258
  /**
248
259
  Forces any linters [configured](https://codemirror.net/6/docs/ref/#lint.linter) to run when the
@@ -271,7 +282,7 @@ function assignKeys(actions) {
271
282
  function renderDiagnostic(view, diagnostic, inPanel) {
272
283
  var _a;
273
284
  let keys = inPanel ? assignKeys(diagnostic.actions) : [];
274
- return elt("li", { class: "cm-diagnostic cm-diagnostic-" + diagnostic.severity }, elt("span", { class: "cm-diagnosticText" }, diagnostic.message), (_a = diagnostic.actions) === null || _a === void 0 ? void 0 : _a.map((action, i) => {
285
+ return elt("li", { class: "cm-diagnostic cm-diagnostic-" + diagnostic.severity }, elt("span", { class: "cm-diagnosticText" }, diagnostic.renderMessage ? diagnostic.renderMessage() : diagnostic.message), (_a = diagnostic.actions) === null || _a === void 0 ? void 0 : _a.map((action, i) => {
275
286
  let click = (e) => {
276
287
  e.preventDefault();
277
288
  let found = findDiagnostic(view.state.field(lintState).diagnostics, diagnostic);
@@ -580,7 +591,12 @@ class LintGutterMarker extends GutterMarker {
580
591
  toDOM(view) {
581
592
  let elt = document.createElement("div");
582
593
  elt.className = "cm-lint-marker cm-lint-marker-" + this.severity;
583
- elt.onmouseover = () => gutterMarkerMouseOver(view, elt, this.diagnostics);
594
+ let diagnostics = this.diagnostics;
595
+ let diagnosticsFilter = view.state.facet(lintGutterConfig).tooltipFilter;
596
+ if (diagnosticsFilter)
597
+ diagnostics = diagnosticsFilter(diagnostics);
598
+ if (diagnostics.length)
599
+ elt.onmouseover = () => gutterMarkerMouseOver(view, elt, diagnostics);
584
600
  return elt;
585
601
  }
586
602
  }
@@ -652,10 +668,15 @@ const lintGutterMarkers = /*@__PURE__*/StateField.define({
652
668
  },
653
669
  update(markers, tr) {
654
670
  markers = markers.map(tr.changes);
655
- for (let effect of tr.effects)
671
+ let diagnosticFilter = tr.state.facet(lintGutterConfig).markerFilter;
672
+ for (let effect of tr.effects) {
656
673
  if (effect.is(setDiagnosticsEffect)) {
657
- markers = markersForDiagnostics(tr.state.doc, effect.value);
674
+ let diagnostics = effect.value;
675
+ if (diagnosticFilter)
676
+ diagnostics = diagnosticFilter(diagnostics || []);
677
+ markers = markersForDiagnostics(tr.state.doc, diagnostics.slice(0));
658
678
  }
679
+ }
659
680
  return markers;
660
681
  }
661
682
  });
@@ -694,6 +715,8 @@ const lintGutterConfig = /*@__PURE__*/Facet.define({
694
715
  combine(configs) {
695
716
  return combineConfig(configs, {
696
717
  hoverTime: 300 /* Time */,
718
+ markerFilter: null,
719
+ tooltipFilter: null
697
720
  });
698
721
  }
699
722
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/lint",
3
- "version": "0.20.1",
3
+ "version": "6.0.0",
4
4
  "description": "Linting support for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",
@@ -26,8 +26,8 @@
26
26
  "sideEffects": false,
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
- "@codemirror/state": "^0.20.0",
30
- "@codemirror/view": "^0.20.2",
29
+ "@codemirror/state": "^6.0.0",
30
+ "@codemirror/view": "^6.0.0",
31
31
  "crelt": "^1.0.5"
32
32
  },
33
33
  "devDependencies": {