@codemirror/lint
Advanced tools
Comparing version 0.19.2 to 0.19.3
@@ -0,1 +1,9 @@ | ||
## 0.19.3 (2021-11-09) | ||
### New features | ||
Export a function `lintGutter` which returns an extension that installs a gutter marking lines with diagnostics. | ||
The package now exports the effect used to update the diagnostics (`setDiagnosticsEffect`). | ||
## 0.19.2 (2021-09-29) | ||
@@ -2,0 +10,0 @@ |
@@ -0,3 +1,4 @@ | ||
import * as _codemirror_state from '@codemirror/state'; | ||
import { EditorState, TransactionSpec, Extension } from '@codemirror/state'; | ||
import { EditorView, Command, KeyBinding } from '@codemirror/view'; | ||
import { EditorState, TransactionSpec, Extension } from '@codemirror/state'; | ||
@@ -55,6 +56,12 @@ /** | ||
Returns a transaction spec which updates the current set of | ||
diagnostics. | ||
diagnostics, and enables the lint extension if if wasn't already | ||
active. | ||
*/ | ||
declare function setDiagnostics(state: EditorState, diagnostics: readonly Diagnostic[]): TransactionSpec; | ||
/** | ||
The state effect that updates the set of active diagnostics. Can | ||
be useful when writing an extension that needs to track these. | ||
*/ | ||
declare const setDiagnosticsEffect: _codemirror_state.StateEffectType<readonly Diagnostic[]>; | ||
/** | ||
Returns the number of active lint diagnostics in the given state. | ||
@@ -100,3 +107,9 @@ */ | ||
declare function forceLinting(view: EditorView): void; | ||
/** | ||
Returns an extension that installs a gutter showing markers for | ||
each line that has diagnostics, which can be hovered over to see | ||
the diagnostics. | ||
*/ | ||
declare function lintGutter(): Extension; | ||
export { Action, Diagnostic, closeLintPanel, diagnosticCount, forceLinting, lintKeymap, linter, nextDiagnostic, openLintPanel, setDiagnostics }; | ||
export { Action, Diagnostic, closeLintPanel, diagnosticCount, forceLinting, lintGutter, lintKeymap, linter, nextDiagnostic, openLintPanel, setDiagnostics, setDiagnosticsEffect }; |
import { Decoration, EditorView, ViewPlugin, logException, WidgetType } from '@codemirror/view'; | ||
import { StateEffect, StateField, Facet } from '@codemirror/state'; | ||
import { hoverTooltip } from '@codemirror/tooltip'; | ||
import { hoverTooltip, showTooltip } from '@codemirror/tooltip'; | ||
import { showPanel, getPanel } from '@codemirror/panel'; | ||
import { gutter, GutterMarker } from '@codemirror/gutter'; | ||
import { RangeSet } from '@codemirror/rangeset'; | ||
import elt from 'crelt'; | ||
@@ -46,5 +48,5 @@ | ||
} | ||
function maybeEnableLint(state, effects, getState) { | ||
function maybeEnableLint(state, effects) { | ||
return state.field(lintState, false) ? effects : effects.concat(StateEffect.appendConfig.of([ | ||
lintState.init(getState), | ||
lintState, | ||
EditorView.decorations.compute([lintState], state => { | ||
@@ -62,9 +64,14 @@ let { selected, panel } = state.field(lintState); | ||
Returns a transaction spec which updates the current set of | ||
diagnostics. | ||
diagnostics, and enables the lint extension if if wasn't already | ||
active. | ||
*/ | ||
function setDiagnostics(state, diagnostics) { | ||
return { | ||
effects: maybeEnableLint(state, [setDiagnosticsEffect.of(diagnostics)], () => LintState.init(diagnostics, null, state)) | ||
effects: maybeEnableLint(state, [setDiagnosticsEffect.of(diagnostics)]) | ||
}; | ||
} | ||
/** | ||
The state effect that updates the set of active diagnostics. Can | ||
be useful when writing an extension that needs to track these. | ||
*/ | ||
const setDiagnosticsEffect = /*@__PURE__*/StateEffect.define(); | ||
@@ -128,6 +135,9 @@ const togglePanel = /*@__PURE__*/StateEffect.define(); | ||
create() { | ||
return { dom: elt("ul", { class: "cm-tooltip-lint" }, found.map(d => renderDiagnostic(view, d, false))) }; | ||
return { dom: diagnosticsTooltip(view, found) }; | ||
} | ||
}; | ||
} | ||
function diagnosticsTooltip(view, diagnostics) { | ||
return elt("ul", { class: "cm-tooltip-lint" }, diagnostics.map(d => renderDiagnostic(view, d, false))); | ||
} | ||
/** | ||
@@ -139,3 +149,3 @@ Command to open and focus the lint panel. | ||
if (!field || !field.panel) | ||
view.dispatch({ effects: maybeEnableLint(view.state, [togglePanel.of(true)], () => LintState.init([], LintPanel.open, view.state)) }); | ||
view.dispatch({ effects: maybeEnableLint(view.state, [togglePanel.of(true)]) }); | ||
let panel = getPanel(view, LintPanel.open); | ||
@@ -477,9 +487,7 @@ if (panel) | ||
} | ||
function svg(content, attrs = `viewBox="0 0 40 40"`) { | ||
return `url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" ${attrs}>${encodeURIComponent(content)}</svg>')`; | ||
} | ||
function underline(color) { | ||
if (typeof btoa != "function") | ||
return "none"; | ||
let svg = `<svg xmlns="http://www.w3.org/2000/svg" width="6" height="3"> | ||
<path d="m0 3 l2 -2 l1 0 l2 2 l1 0" stroke="${color}" fill="none" stroke-width=".7"/> | ||
</svg>`; | ||
return `url('data:image/svg+xml;base64,${btoa(svg)}')`; | ||
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"`); | ||
} | ||
@@ -511,3 +519,4 @@ const baseTheme = /*@__PURE__*/EditorView.baseTheme({ | ||
backgroundPosition: "left bottom", | ||
backgroundRepeat: "repeat-x" | ||
backgroundRepeat: "repeat-x", | ||
paddingBottom: "0.7px", | ||
}, | ||
@@ -571,3 +580,135 @@ ".cm-lintRange-error": { backgroundImage: /*@__PURE__*/underline("#d11") }, | ||
}); | ||
class LintGutterMarker extends GutterMarker { | ||
constructor(diagnostics) { | ||
super(); | ||
this.diagnostics = diagnostics; | ||
this.severity = diagnostics.reduce((max, d) => { | ||
let s = d.severity; | ||
return s == "error" || s == "warning" && max == "info" ? s : max; | ||
}, "info"); | ||
} | ||
toDOM(view) { | ||
let elt = document.createElement("div"); | ||
elt.className = "cm-lint-marker cm-lint-marker-" + this.severity; | ||
elt.onmouseover = () => gutterMarkerMouseOver(view, elt, this.diagnostics); | ||
return elt; | ||
} | ||
} | ||
function trackHoverOn(view, marker) { | ||
let mousemove = (event) => { | ||
let rect = marker.getBoundingClientRect(); | ||
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 */) | ||
return; | ||
for (let target = event.target; target; target = target.parentNode) { | ||
if (target.nodeType == 1 && target.classList.contains("cm-tooltip-lint")) | ||
return; | ||
} | ||
window.removeEventListener("mousemove", mousemove); | ||
if (view.state.field(lintGutterTooltip)) | ||
view.dispatch({ effects: setLintGutterTooltip.of(null) }); | ||
}; | ||
window.addEventListener("mousemove", mousemove); | ||
} | ||
function gutterMarkerMouseOver(view, marker, diagnostics) { | ||
function hovered() { | ||
let line = view.visualLineAtHeight(marker.getBoundingClientRect().top + 5); | ||
const linePos = view.coordsAtPos(line.from), markerRect = marker.getBoundingClientRect(); | ||
if (linePos) { | ||
view.dispatch({ effects: setLintGutterTooltip.of({ | ||
pos: line.from, | ||
above: false, | ||
create() { | ||
return { | ||
dom: diagnosticsTooltip(view, diagnostics), | ||
offset: { x: markerRect.left - linePos.left, y: 0 } | ||
}; | ||
} | ||
}) }); | ||
} | ||
marker.onmouseout = marker.onmousemove = null; | ||
trackHoverOn(view, marker); | ||
} | ||
let hoverTimeout = setTimeout(hovered, 600 /* Time */); | ||
marker.onmouseout = () => { | ||
clearTimeout(hoverTimeout); | ||
marker.onmouseout = marker.onmousemove = null; | ||
}; | ||
marker.onmousemove = () => { | ||
clearTimeout(hoverTimeout); | ||
hoverTimeout = setTimeout(hovered, 600 /* Time */); | ||
}; | ||
} | ||
function markersForDiagnostics(doc, diagnostics) { | ||
let byLine = Object.create(null); | ||
for (let diagnostic of diagnostics) { | ||
let line = doc.lineAt(diagnostic.from); | ||
(byLine[line.from] || (byLine[line.from] = [])).push(diagnostic); | ||
} | ||
let markers = []; | ||
for (let line in byLine) { | ||
markers.push(new LintGutterMarker(byLine[line]).range(+line)); | ||
} | ||
return RangeSet.of(markers, true); | ||
} | ||
const lintGutterExtension = /*@__PURE__*/gutter({ | ||
class: "cm-gutter-lint", | ||
markers: view => view.state.field(lintGutterMarkers), | ||
}); | ||
const lintGutterMarkers = /*@__PURE__*/StateField.define({ | ||
create() { | ||
return RangeSet.empty; | ||
}, | ||
update(markers, tr) { | ||
markers = markers.map(tr.changes); | ||
for (let effect of tr.effects) | ||
if (effect.is(setDiagnosticsEffect)) { | ||
markers = markersForDiagnostics(tr.state.doc, effect.value); | ||
} | ||
return markers; | ||
} | ||
}); | ||
const setLintGutterTooltip = /*@__PURE__*/StateEffect.define(); | ||
const lintGutterTooltip = /*@__PURE__*/StateField.define({ | ||
create() { return null; }, | ||
update(tooltip, tr) { | ||
if (tooltip && tr.docChanged) | ||
tooltip = Object.assign(Object.assign({}, tooltip), { pos: tr.changes.mapPos(tooltip.pos) }); | ||
return tr.effects.reduce((t, e) => e.is(setLintGutterTooltip) ? e.value : t, tooltip); | ||
}, | ||
provide: field => showTooltip.from(field) | ||
}); | ||
const lintGutterTheme = /*@__PURE__*/EditorView.baseTheme({ | ||
".cm-gutter-lint": { | ||
width: "1.4em", | ||
"& .cm-gutterElement": { | ||
padding: "0 .2em", | ||
display: "flex", | ||
flexDirection: "column", | ||
justifyContent: "center", | ||
} | ||
}, | ||
".cm-lint-marker": { | ||
width: "1em", | ||
height: "1em", | ||
}, | ||
".cm-lint-marker-info": { | ||
content: /*@__PURE__*/svg(`<path fill="#aaf" stroke="#77e" stroke-width="6" stroke-linejoin="round" d="M5 5L35 5L35 35L5 35Z"/>`) | ||
}, | ||
".cm-lint-marker-warning": { | ||
content: /*@__PURE__*/svg(`<path fill="#fe8" stroke="#fd7" stroke-width="6" stroke-linejoin="round" d="M20 6L37 35L3 35Z"/>`), | ||
}, | ||
".cm-lint-marker-error:before": { | ||
content: /*@__PURE__*/svg(`<circle cx="20" cy="20" r="15" fill="#f87" stroke="#f43" stroke-width="6"/>`) | ||
}, | ||
}); | ||
/** | ||
Returns an extension that installs a gutter showing markers for | ||
each line that has diagnostics, which can be hovered over to see | ||
the diagnostics. | ||
*/ | ||
function lintGutter() { | ||
return [lintGutterMarkers, lintGutterExtension, lintGutterTheme, lintGutterTooltip]; | ||
} | ||
export { closeLintPanel, diagnosticCount, forceLinting, lintKeymap, linter, nextDiagnostic, openLintPanel, setDiagnostics }; | ||
export { closeLintPanel, diagnosticCount, forceLinting, lintGutter, lintKeymap, linter, nextDiagnostic, openLintPanel, setDiagnostics, setDiagnosticsEffect }; |
{ | ||
"name": "@codemirror/lint", | ||
"version": "0.19.2", | ||
"version": "0.19.3", | ||
"description": "Linting support for the CodeMirror code editor", | ||
@@ -29,5 +29,7 @@ "scripts": { | ||
"dependencies": { | ||
"@codemirror/gutter": "^0.19.4", | ||
"@codemirror/panel": "^0.19.0", | ||
"@codemirror/state": "^0.19.0", | ||
"@codemirror/tooltip": "^0.19.0", | ||
"@codemirror/rangeset": "^0.19.1", | ||
"@codemirror/state": "^0.19.4", | ||
"@codemirror/tooltip": "^0.19.5", | ||
"@codemirror/view": "^0.19.0", | ||
@@ -34,0 +36,0 @@ "crelt": "^1.0.5" |
Sorry, the diff of this file is not supported yet
63423
1531
7
+ Added@codemirror/gutter@^0.19.4
+ Added@codemirror/rangeset@^0.19.1
+ Added@codemirror/gutter@0.19.9(transitive)
Updated@codemirror/state@^0.19.4
Updated@codemirror/tooltip@^0.19.5