Premier commit déjà bien avancé
This commit is contained in:
16
frontend/node_modules/@codemirror/lint/.github/workflows/dispatch.yml
generated
vendored
Normal file
16
frontend/node_modules/@codemirror/lint/.github/workflows/dispatch.yml
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
name: Trigger CI
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Dispatch to main repo
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Emit repository_dispatch
|
||||
uses: mvasigh/dispatch-action@main
|
||||
with:
|
||||
# You should create a personal access token and store it in your repository
|
||||
token: ${{ secrets.DISPATCH_AUTH }}
|
||||
repo: dev
|
||||
owner: codemirror
|
||||
event_type: push
|
||||
300
frontend/node_modules/@codemirror/lint/CHANGELOG.md
generated
vendored
Normal file
300
frontend/node_modules/@codemirror/lint/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,300 @@
|
||||
## 6.9.2 (2025-11-03)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fix an infinite loop that would occur when a diagnostic pointed beyond the end of the document.
|
||||
|
||||
## 6.9.1 (2025-10-23)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Properly display diagnostics that just cover multiple newlines as widgets.
|
||||
|
||||
## 6.9.0 (2025-10-02)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Multiple configurations to `linter` will now be merged without raising an error.
|
||||
|
||||
### New features
|
||||
|
||||
The new `markClass` option to actions makes it possible to style action buttons.
|
||||
|
||||
## 6.8.5 (2025-03-26)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fix a regression (since 6.8.4) that broke the `markerFilter` option.
|
||||
|
||||
## 6.8.4 (2024-11-28)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Don't create overlapping decorations when diagnostics overlap.
|
||||
|
||||
Fix an issue where block widgets could cause the lint gutter to show diagnostics multiple times.
|
||||
|
||||
## 6.8.3 (2024-11-21)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fix an issue that prevented tooltips in the lint gutter from being displayed.
|
||||
|
||||
## 6.8.2 (2024-09-24)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Show lint markers for code replaced by a block widget.
|
||||
|
||||
When multiple linters are installed, start displaying results from ones that return quickly even if others are slow to return.
|
||||
|
||||
## 6.8.1 (2024-06-19)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Make lint markers non-inclusive again, since having them that way causes more issues than it solves.
|
||||
|
||||
## 6.8.0 (2024-05-23)
|
||||
|
||||
### New features
|
||||
|
||||
The new `autoPanel` option can be used to make the panel automatically appear when diagnostics are added and close when no diagnostics are left.
|
||||
|
||||
## 6.7.1 (2024-05-15)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Don't perform an additional superfluous timed lint run after `forceLinting` has been called.
|
||||
|
||||
## 6.7.0 (2024-04-30)
|
||||
|
||||
### New features
|
||||
|
||||
The `renderMessage` function is now called with the editor view as first argument.
|
||||
|
||||
## 6.6.0 (2024-04-29)
|
||||
|
||||
### New features
|
||||
|
||||
The new `hideOn` configuration option can be used to control in what circumstances lint tooltips get hidden by state changes.
|
||||
|
||||
## 6.5.0 (2024-01-30)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Make lint mark decorations inclusive, so that they are applied even if the marked content is replaced by a widget decoration.
|
||||
|
||||
### New features
|
||||
|
||||
`linter` can now be called with null as source to only provide a configuration.
|
||||
|
||||
`markerFilter` and `tooltipFilter` function now get passed the current editor state.
|
||||
|
||||
## 6.4.2 (2023-09-14)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Make sure scrolling diagnostic into view in the panel works when the editor is scaled.
|
||||
|
||||
## 6.4.1 (2023-08-26)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fix a crash that could occur when a view was reconfigured in a way that removed the lint extension.
|
||||
|
||||
## 6.4.0 (2023-07-03)
|
||||
|
||||
### New features
|
||||
|
||||
Diagnostics can now use `"hint"` as a severity level.
|
||||
|
||||
Diagnostics can now set a `markClass` property to add an additional CSS class to the text marked by the diagnostic.
|
||||
|
||||
## 6.3.0 (2023-06-23)
|
||||
|
||||
### New features
|
||||
|
||||
A new `previousDiagnostic` command can be used to move back through the active diagnostics.
|
||||
|
||||
## 6.2.2 (2023-06-05)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Make sure lint gutter tooltips are properly closed when the content of their line changes.
|
||||
|
||||
## 6.2.1 (2023-04-13)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
The `linter` function now eagerly includes all lint-related extensions, rather than appending them to the configuration as-needed, so that turning off linting by clearing the compartment that contains it works properly.
|
||||
|
||||
## 6.2.0 (2023-02-27)
|
||||
|
||||
### New features
|
||||
|
||||
The new `needsRefresh` option to `linter` makes it possible to cause linting to be recalculated for non-document state changes.
|
||||
|
||||
## 6.1.1 (2023-02-15)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Give lint action buttons a pointer cursor style.
|
||||
|
||||
Fix a bug that caused diagnostic action callbacks to be called twice when their button was clicked.
|
||||
|
||||
## 6.1.0 (2022-11-15)
|
||||
|
||||
### New features
|
||||
|
||||
The new `forEachDiagnostic` function can be used to iterate over the diagnostics in an editor state.
|
||||
|
||||
## 6.0.0 (2022-06-08)
|
||||
|
||||
### Breaking changes
|
||||
|
||||
Update dependencies to 6.0.0
|
||||
|
||||
## 0.20.3 (2022-05-25)
|
||||
|
||||
### New features
|
||||
|
||||
Diagnostic objects may now have a `renderMessage` method to render their message to the DOM.
|
||||
|
||||
## 0.20.2 (2022-05-02)
|
||||
|
||||
### New features
|
||||
|
||||
The package now exports the `LintSource` function type.
|
||||
|
||||
The new `markerFilter` and `tooltipFilter` options to `linter` and `lintGutter` allow more control over which diagnostics are visible and which have tooltips.
|
||||
|
||||
## 0.20.1 (2022-04-22)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Hide lint tooltips when the document is changed.
|
||||
|
||||
## 0.20.0 (2022-04-20)
|
||||
|
||||
### Breaking changes
|
||||
|
||||
Update dependencies to 0.20.0
|
||||
|
||||
## 0.19.6 (2022-03-04)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fix a bug where hovering over the icons in the lint gutter would sometimes fail to show a tooltip or show the tooltip for another line.
|
||||
|
||||
## 0.19.5 (2022-02-25)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Make sure the lint gutter tooltips are positioned under their icon, even when the line is wrapped.
|
||||
|
||||
## 0.19.4 (2022-02-25)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fix an issue where an outdated marker could stick around on the lint gutter after all diagnostics were removed.
|
||||
|
||||
### New features
|
||||
|
||||
Add a `hoverTime` option to the lint gutter. Change default hover time to 300
|
||||
|
||||
## 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)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fix a bug where reconfiguring the lint source didn't restart linting.
|
||||
|
||||
## 0.19.1 (2021-09-17)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Prevent decorations that cover just a line break from being invisible by showing a widget instead of range for them.
|
||||
|
||||
### New features
|
||||
|
||||
The `diagnosticCount` method can now be used to determine whether there are active diagnostics.
|
||||
|
||||
## 0.19.0 (2021-08-11)
|
||||
|
||||
### Breaking changes
|
||||
|
||||
Update dependencies to 0.19.0
|
||||
|
||||
## 0.18.6 (2021-08-08)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fix a crash in the key handler of the lint panel when no diagnostics are available.
|
||||
|
||||
## 0.18.5 (2021-08-07)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fix an issue that caused `openLintPanel` to not actually open the panel when ran before the editor had any lint state loaded.
|
||||
|
||||
### New features
|
||||
|
||||
The package now exports a `forceLinting` function that forces pending lint queries to run immediately.
|
||||
|
||||
## 0.18.4 (2021-06-07)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Multiple `linter` extensions can now be added to an editor without disrupting each other.
|
||||
|
||||
Fix poor layout on lint tooltips due to changes in @codemirror/tooltip.
|
||||
|
||||
## 0.18.3 (2021-05-10)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fix a regression where using `setDiagnostics` when linting hadn't been abled yet ignored the first set of diagnostics.
|
||||
|
||||
## 0.18.2 (2021-04-16)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Newlines in line messages are now shown as line breaks to the user.
|
||||
|
||||
### New features
|
||||
|
||||
You can now pass a delay option to `linter` to configure how long it waits before calling the linter.
|
||||
|
||||
## 0.18.1 (2021-03-15)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Adjust to current @codemirror/panel and @codemirror/tooltip interfaces.
|
||||
|
||||
## 0.18.0 (2021-03-03)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Make sure action access keys are discoverable for screen reader users.
|
||||
|
||||
Selection in the lint panel should now be properly visible to screen readers.
|
||||
|
||||
## 0.17.1 (2021-01-06)
|
||||
|
||||
### New features
|
||||
|
||||
The package now also exports a CommonJS module.
|
||||
|
||||
## 0.17.0 (2020-12-29)
|
||||
|
||||
### Breaking changes
|
||||
|
||||
First numbered release.
|
||||
|
||||
21
frontend/node_modules/@codemirror/lint/LICENSE
generated
vendored
Normal file
21
frontend/node_modules/@codemirror/lint/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (C) 2018-2021 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
18
frontend/node_modules/@codemirror/lint/README.md
generated
vendored
Normal file
18
frontend/node_modules/@codemirror/lint/README.md
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
# @codemirror/lint [](https://www.npmjs.org/package/@codemirror/lint)
|
||||
|
||||
[ [**WEBSITE**](https://codemirror.net/) | [**DOCS**](https://codemirror.net/docs/ref/#lint) | [**ISSUES**](https://github.com/codemirror/dev/issues) | [**FORUM**](https://discuss.codemirror.net/c/next/) | [**CHANGELOG**](https://github.com/codemirror/lint/blob/main/CHANGELOG.md) ]
|
||||
|
||||
This package implements linting support for the
|
||||
[CodeMirror](https://codemirror.net/) code editor.
|
||||
|
||||
The [project page](https://codemirror.net/) has more information, a
|
||||
number of [examples](https://codemirror.net/examples/) and the
|
||||
[documentation](https://codemirror.net/docs/).
|
||||
|
||||
This code is released under an
|
||||
[MIT license](https://github.com/codemirror/lint/tree/main/LICENSE).
|
||||
|
||||
We aim to be an inclusive, welcoming community. To make that explicit,
|
||||
we have a [code of
|
||||
conduct](http://contributor-covenant.org/version/1/1/0/) that applies
|
||||
to communication around the project.
|
||||
950
frontend/node_modules/@codemirror/lint/dist/index.cjs
generated
vendored
Normal file
950
frontend/node_modules/@codemirror/lint/dist/index.cjs
generated
vendored
Normal file
@ -0,0 +1,950 @@
|
||||
'use strict';
|
||||
|
||||
var view = require('@codemirror/view');
|
||||
var state = require('@codemirror/state');
|
||||
var elt = require('crelt');
|
||||
|
||||
class SelectedDiagnostic {
|
||||
constructor(from, to, diagnostic) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.diagnostic = diagnostic;
|
||||
}
|
||||
}
|
||||
class LintState {
|
||||
constructor(diagnostics, panel, selected) {
|
||||
this.diagnostics = diagnostics;
|
||||
this.panel = panel;
|
||||
this.selected = selected;
|
||||
}
|
||||
static init(diagnostics, panel, state$1) {
|
||||
// Filter the list of diagnostics for which to create markers
|
||||
let diagnosticFilter = state$1.facet(lintConfig).markerFilter;
|
||||
if (diagnosticFilter)
|
||||
diagnostics = diagnosticFilter(diagnostics, state$1);
|
||||
let sorted = diagnostics.slice().sort((a, b) => a.from - b.from || a.to - b.to);
|
||||
let deco = new state.RangeSetBuilder(), active = [], pos = 0;
|
||||
let scan = state$1.doc.iter(), scanPos = 0, docLen = state$1.doc.length;
|
||||
for (let i = 0;;) {
|
||||
let next = i == sorted.length ? null : sorted[i];
|
||||
if (!next && !active.length)
|
||||
break;
|
||||
let from, to;
|
||||
if (active.length) {
|
||||
from = pos;
|
||||
to = active.reduce((p, d) => Math.min(p, d.to), next && next.from > from ? next.from : 1e8);
|
||||
}
|
||||
else {
|
||||
from = next.from;
|
||||
if (from > docLen)
|
||||
break;
|
||||
to = next.to;
|
||||
active.push(next);
|
||||
i++;
|
||||
}
|
||||
while (i < sorted.length) {
|
||||
let next = sorted[i];
|
||||
if (next.from == from && (next.to > next.from || next.to == from)) {
|
||||
active.push(next);
|
||||
i++;
|
||||
to = Math.min(next.to, to);
|
||||
}
|
||||
else {
|
||||
to = Math.min(next.from, to);
|
||||
break;
|
||||
}
|
||||
}
|
||||
to = Math.min(to, docLen);
|
||||
let widget = false;
|
||||
if (active.some(d => d.from == from && (d.to == to || to == docLen))) {
|
||||
widget = from == to;
|
||||
if (!widget && to - from < 10) {
|
||||
let behind = from - (scanPos + scan.value.length);
|
||||
if (behind > 0) {
|
||||
scan.next(behind);
|
||||
scanPos = from;
|
||||
}
|
||||
for (let check = from;;) {
|
||||
if (check >= to) {
|
||||
widget = true;
|
||||
break;
|
||||
}
|
||||
if (!scan.lineBreak && scanPos + scan.value.length > check)
|
||||
break;
|
||||
check = scanPos + scan.value.length;
|
||||
scanPos += scan.value.length;
|
||||
scan.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
let sev = maxSeverity(active);
|
||||
if (widget) {
|
||||
deco.add(from, from, view.Decoration.widget({
|
||||
widget: new DiagnosticWidget(sev),
|
||||
diagnostics: active.slice()
|
||||
}));
|
||||
}
|
||||
else {
|
||||
let markClass = active.reduce((c, d) => d.markClass ? c + " " + d.markClass : c, "");
|
||||
deco.add(from, to, view.Decoration.mark({
|
||||
class: "cm-lintRange cm-lintRange-" + sev + markClass,
|
||||
diagnostics: active.slice(),
|
||||
inclusiveEnd: active.some(a => a.to > to)
|
||||
}));
|
||||
}
|
||||
pos = to;
|
||||
if (pos == docLen)
|
||||
break;
|
||||
for (let i = 0; i < active.length; i++)
|
||||
if (active[i].to <= pos)
|
||||
active.splice(i--, 1);
|
||||
}
|
||||
let set = deco.finish();
|
||||
return new LintState(set, panel, findDiagnostic(set));
|
||||
}
|
||||
}
|
||||
function findDiagnostic(diagnostics, diagnostic = null, after = 0) {
|
||||
let found = null;
|
||||
diagnostics.between(after, 1e9, (from, to, { spec }) => {
|
||||
if (diagnostic && spec.diagnostics.indexOf(diagnostic) < 0)
|
||||
return;
|
||||
if (!found)
|
||||
found = new SelectedDiagnostic(from, to, diagnostic || spec.diagnostics[0]);
|
||||
else if (spec.diagnostics.indexOf(found.diagnostic) < 0)
|
||||
return false;
|
||||
else
|
||||
found = new SelectedDiagnostic(found.from, to, found.diagnostic);
|
||||
});
|
||||
return found;
|
||||
}
|
||||
function hideTooltip(tr, tooltip) {
|
||||
let from = tooltip.pos, to = tooltip.end || from;
|
||||
let result = tr.state.facet(lintConfig).hideOn(tr, from, to);
|
||||
if (result != null)
|
||||
return result;
|
||||
let line = tr.startState.doc.lineAt(tooltip.pos);
|
||||
return !!(tr.effects.some(e => e.is(setDiagnosticsEffect)) || tr.changes.touchesRange(line.from, Math.max(line.to, to)));
|
||||
}
|
||||
function maybeEnableLint(state$1, effects) {
|
||||
return state$1.field(lintState, false) ? effects : effects.concat(state.StateEffect.appendConfig.of(lintExtensions));
|
||||
}
|
||||
/**
|
||||
Returns a transaction spec which updates the current set of
|
||||
diagnostics, and enables the lint extension if if wasn't already
|
||||
active.
|
||||
*/
|
||||
function setDiagnostics(state, diagnostics) {
|
||||
return {
|
||||
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 = state.StateEffect.define();
|
||||
const togglePanel = state.StateEffect.define();
|
||||
const movePanelSelection = state.StateEffect.define();
|
||||
const lintState = state.StateField.define({
|
||||
create() {
|
||||
return new LintState(view.Decoration.none, null, null);
|
||||
},
|
||||
update(value, tr) {
|
||||
if (tr.docChanged && value.diagnostics.size) {
|
||||
let mapped = value.diagnostics.map(tr.changes), selected = null, panel = value.panel;
|
||||
if (value.selected) {
|
||||
let selPos = tr.changes.mapPos(value.selected.from, 1);
|
||||
selected = findDiagnostic(mapped, value.selected.diagnostic, selPos) || findDiagnostic(mapped, null, selPos);
|
||||
}
|
||||
if (!mapped.size && panel && tr.state.facet(lintConfig).autoPanel)
|
||||
panel = null;
|
||||
value = new LintState(mapped, panel, selected);
|
||||
}
|
||||
for (let effect of tr.effects) {
|
||||
if (effect.is(setDiagnosticsEffect)) {
|
||||
let panel = !tr.state.facet(lintConfig).autoPanel ? value.panel : effect.value.length ? LintPanel.open : null;
|
||||
value = LintState.init(effect.value, panel, tr.state);
|
||||
}
|
||||
else if (effect.is(togglePanel)) {
|
||||
value = new LintState(value.diagnostics, effect.value ? LintPanel.open : null, value.selected);
|
||||
}
|
||||
else if (effect.is(movePanelSelection)) {
|
||||
value = new LintState(value.diagnostics, value.panel, effect.value);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
},
|
||||
provide: f => [view.showPanel.from(f, val => val.panel),
|
||||
view.EditorView.decorations.from(f, s => s.diagnostics)]
|
||||
});
|
||||
/**
|
||||
Returns the number of active lint diagnostics in the given state.
|
||||
*/
|
||||
function diagnosticCount(state) {
|
||||
let lint = state.field(lintState, false);
|
||||
return lint ? lint.diagnostics.size : 0;
|
||||
}
|
||||
const activeMark = view.Decoration.mark({ class: "cm-lintRange cm-lintRange-active" });
|
||||
function lintTooltip(view, pos, side) {
|
||||
let { diagnostics } = view.state.field(lintState);
|
||||
let found, start = -1, end = -1;
|
||||
diagnostics.between(pos - (side < 0 ? 1 : 0), pos + (side > 0 ? 1 : 0), (from, to, { spec }) => {
|
||||
if (pos >= from && pos <= to &&
|
||||
(from == to || ((pos > from || side > 0) && (pos < to || side < 0)))) {
|
||||
found = spec.diagnostics;
|
||||
start = from;
|
||||
end = to;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
let diagnosticFilter = view.state.facet(lintConfig).tooltipFilter;
|
||||
if (found && diagnosticFilter)
|
||||
found = diagnosticFilter(found, view.state);
|
||||
if (!found)
|
||||
return null;
|
||||
return {
|
||||
pos: start,
|
||||
end: end,
|
||||
above: view.state.doc.lineAt(start).to < end,
|
||||
create() {
|
||||
return { dom: diagnosticsTooltip(view, found) };
|
||||
}
|
||||
};
|
||||
}
|
||||
function diagnosticsTooltip(view, diagnostics) {
|
||||
return elt("ul", { class: "cm-tooltip-lint" }, diagnostics.map(d => renderDiagnostic(view, d, false)));
|
||||
}
|
||||
/**
|
||||
Command to open and focus the lint panel.
|
||||
*/
|
||||
const openLintPanel = (view$1) => {
|
||||
let field = view$1.state.field(lintState, false);
|
||||
if (!field || !field.panel)
|
||||
view$1.dispatch({ effects: maybeEnableLint(view$1.state, [togglePanel.of(true)]) });
|
||||
let panel = view.getPanel(view$1, LintPanel.open);
|
||||
if (panel)
|
||||
panel.dom.querySelector(".cm-panel-lint ul").focus();
|
||||
return true;
|
||||
};
|
||||
/**
|
||||
Command to close the lint panel, when open.
|
||||
*/
|
||||
const closeLintPanel = (view) => {
|
||||
let field = view.state.field(lintState, false);
|
||||
if (!field || !field.panel)
|
||||
return false;
|
||||
view.dispatch({ effects: togglePanel.of(false) });
|
||||
return true;
|
||||
};
|
||||
/**
|
||||
Move the selection to the next diagnostic.
|
||||
*/
|
||||
const nextDiagnostic = (view) => {
|
||||
let field = view.state.field(lintState, false);
|
||||
if (!field)
|
||||
return false;
|
||||
let sel = view.state.selection.main, next = field.diagnostics.iter(sel.to + 1);
|
||||
if (!next.value) {
|
||||
next = field.diagnostics.iter(0);
|
||||
if (!next.value || next.from == sel.from && next.to == sel.to)
|
||||
return false;
|
||||
}
|
||||
view.dispatch({ selection: { anchor: next.from, head: next.to }, scrollIntoView: true });
|
||||
return true;
|
||||
};
|
||||
/**
|
||||
Move the selection to the previous diagnostic.
|
||||
*/
|
||||
const previousDiagnostic = (view) => {
|
||||
let { state } = view, field = state.field(lintState, false);
|
||||
if (!field)
|
||||
return false;
|
||||
let sel = state.selection.main;
|
||||
let prevFrom, prevTo, lastFrom, lastTo;
|
||||
field.diagnostics.between(0, state.doc.length, (from, to) => {
|
||||
if (to < sel.to && (prevFrom == null || prevFrom < from)) {
|
||||
prevFrom = from;
|
||||
prevTo = to;
|
||||
}
|
||||
if (lastFrom == null || from > lastFrom) {
|
||||
lastFrom = from;
|
||||
lastTo = to;
|
||||
}
|
||||
});
|
||||
if (lastFrom == null || prevFrom == null && lastFrom == sel.from)
|
||||
return false;
|
||||
view.dispatch({ selection: { anchor: prevFrom !== null && prevFrom !== void 0 ? prevFrom : lastFrom, head: prevTo !== null && prevTo !== void 0 ? prevTo : lastTo }, scrollIntoView: true });
|
||||
return true;
|
||||
};
|
||||
/**
|
||||
A set of default key bindings for the lint functionality.
|
||||
|
||||
- Ctrl-Shift-m (Cmd-Shift-m on macOS): [`openLintPanel`](https://codemirror.net/6/docs/ref/#lint.openLintPanel)
|
||||
- F8: [`nextDiagnostic`](https://codemirror.net/6/docs/ref/#lint.nextDiagnostic)
|
||||
*/
|
||||
const lintKeymap = [
|
||||
{ key: "Mod-Shift-m", run: openLintPanel, preventDefault: true },
|
||||
{ key: "F8", run: nextDiagnostic }
|
||||
];
|
||||
const lintPlugin = view.ViewPlugin.fromClass(class {
|
||||
constructor(view) {
|
||||
this.view = view;
|
||||
this.timeout = -1;
|
||||
this.set = true;
|
||||
let { delay } = view.state.facet(lintConfig);
|
||||
this.lintTime = Date.now() + delay;
|
||||
this.run = this.run.bind(this);
|
||||
this.timeout = setTimeout(this.run, delay);
|
||||
}
|
||||
run() {
|
||||
clearTimeout(this.timeout);
|
||||
let now = Date.now();
|
||||
if (now < this.lintTime - 10) {
|
||||
this.timeout = setTimeout(this.run, this.lintTime - now);
|
||||
}
|
||||
else {
|
||||
this.set = false;
|
||||
let { state } = this.view, { sources } = state.facet(lintConfig);
|
||||
if (sources.length)
|
||||
batchResults(sources.map(s => Promise.resolve(s(this.view))), annotations => {
|
||||
if (this.view.state.doc == state.doc)
|
||||
this.view.dispatch(setDiagnostics(this.view.state, annotations.reduce((a, b) => a.concat(b))));
|
||||
}, error => { view.logException(this.view.state, error); });
|
||||
}
|
||||
}
|
||||
update(update) {
|
||||
let config = update.state.facet(lintConfig);
|
||||
if (update.docChanged || config != update.startState.facet(lintConfig) ||
|
||||
config.needsRefresh && config.needsRefresh(update)) {
|
||||
this.lintTime = Date.now() + config.delay;
|
||||
if (!this.set) {
|
||||
this.set = true;
|
||||
this.timeout = setTimeout(this.run, config.delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
force() {
|
||||
if (this.set) {
|
||||
this.lintTime = Date.now();
|
||||
this.run();
|
||||
}
|
||||
}
|
||||
destroy() {
|
||||
clearTimeout(this.timeout);
|
||||
}
|
||||
});
|
||||
function batchResults(promises, sink, error) {
|
||||
let collected = [], timeout = -1;
|
||||
for (let p of promises)
|
||||
p.then(value => {
|
||||
collected.push(value);
|
||||
clearTimeout(timeout);
|
||||
if (collected.length == promises.length)
|
||||
sink(collected);
|
||||
else
|
||||
timeout = setTimeout(() => sink(collected), 200);
|
||||
}, error);
|
||||
}
|
||||
const lintConfig = state.Facet.define({
|
||||
combine(input) {
|
||||
return {
|
||||
sources: input.map(i => i.source).filter(x => x != null),
|
||||
...state.combineConfig(input.map(i => i.config), {
|
||||
delay: 750,
|
||||
markerFilter: null,
|
||||
tooltipFilter: null,
|
||||
needsRefresh: null,
|
||||
hideOn: () => null,
|
||||
}, {
|
||||
delay: Math.max,
|
||||
markerFilter: combineFilter,
|
||||
tooltipFilter: combineFilter,
|
||||
needsRefresh: (a, b) => !a ? b : !b ? a : u => a(u) || b(u),
|
||||
hideOn: (a, b) => !a ? b : !b ? a : (t, x, y) => a(t, x, y) || b(t, x, y),
|
||||
autoPanel: (a, b) => a || b
|
||||
})
|
||||
};
|
||||
}
|
||||
});
|
||||
function combineFilter(a, b) {
|
||||
return !a ? b : !b ? a : (d, s) => b(a(d, s), s);
|
||||
}
|
||||
/**
|
||||
Given a diagnostic source, this function returns an extension that
|
||||
enables linting with that source. It will be called whenever the
|
||||
editor is idle (after its content changed).
|
||||
|
||||
Note that settings given here will apply to all linters active in
|
||||
the editor. If `null` is given as source, this only configures the
|
||||
lint extension.
|
||||
*/
|
||||
function linter(source, config = {}) {
|
||||
return [
|
||||
lintConfig.of({ source, config }),
|
||||
lintPlugin,
|
||||
lintExtensions
|
||||
];
|
||||
}
|
||||
/**
|
||||
Forces any linters [configured](https://codemirror.net/6/docs/ref/#lint.linter) to run when the
|
||||
editor is idle to run right away.
|
||||
*/
|
||||
function forceLinting(view) {
|
||||
let plugin = view.plugin(lintPlugin);
|
||||
if (plugin)
|
||||
plugin.force();
|
||||
}
|
||||
function assignKeys(actions) {
|
||||
let assigned = [];
|
||||
if (actions)
|
||||
actions: for (let { name } of actions) {
|
||||
for (let i = 0; i < name.length; i++) {
|
||||
let ch = name[i];
|
||||
if (/[a-zA-Z]/.test(ch) && !assigned.some(c => c.toLowerCase() == ch.toLowerCase())) {
|
||||
assigned.push(ch);
|
||||
continue actions;
|
||||
}
|
||||
}
|
||||
assigned.push("");
|
||||
}
|
||||
return assigned;
|
||||
}
|
||||
function renderDiagnostic(view, diagnostic, inPanel) {
|
||||
var _a;
|
||||
let keys = inPanel ? assignKeys(diagnostic.actions) : [];
|
||||
return elt("li", { class: "cm-diagnostic cm-diagnostic-" + diagnostic.severity }, elt("span", { class: "cm-diagnosticText" }, diagnostic.renderMessage ? diagnostic.renderMessage(view) : diagnostic.message), (_a = diagnostic.actions) === null || _a === void 0 ? void 0 : _a.map((action, i) => {
|
||||
let fired = false, click = (e) => {
|
||||
e.preventDefault();
|
||||
if (fired)
|
||||
return;
|
||||
fired = true;
|
||||
let found = findDiagnostic(view.state.field(lintState).diagnostics, diagnostic);
|
||||
if (found)
|
||||
action.apply(view, found.from, found.to);
|
||||
};
|
||||
let { name } = action, keyIndex = keys[i] ? name.indexOf(keys[i]) : -1;
|
||||
let nameElt = keyIndex < 0 ? name : [name.slice(0, keyIndex),
|
||||
elt("u", name.slice(keyIndex, keyIndex + 1)),
|
||||
name.slice(keyIndex + 1)];
|
||||
let markClass = action.markClass ? " " + action.markClass : "";
|
||||
return elt("button", {
|
||||
type: "button",
|
||||
class: "cm-diagnosticAction" + markClass,
|
||||
onclick: click,
|
||||
onmousedown: click,
|
||||
"aria-label": ` Action: ${name}${keyIndex < 0 ? "" : ` (access key "${keys[i]})"`}.`
|
||||
}, nameElt);
|
||||
}), diagnostic.source && elt("div", { class: "cm-diagnosticSource" }, diagnostic.source));
|
||||
}
|
||||
class DiagnosticWidget extends view.WidgetType {
|
||||
constructor(sev) {
|
||||
super();
|
||||
this.sev = sev;
|
||||
}
|
||||
eq(other) { return other.sev == this.sev; }
|
||||
toDOM() {
|
||||
return elt("span", { class: "cm-lintPoint cm-lintPoint-" + this.sev });
|
||||
}
|
||||
}
|
||||
class PanelItem {
|
||||
constructor(view, diagnostic) {
|
||||
this.diagnostic = diagnostic;
|
||||
this.id = "item_" + Math.floor(Math.random() * 0xffffffff).toString(16);
|
||||
this.dom = renderDiagnostic(view, diagnostic, true);
|
||||
this.dom.id = this.id;
|
||||
this.dom.setAttribute("role", "option");
|
||||
}
|
||||
}
|
||||
class LintPanel {
|
||||
constructor(view) {
|
||||
this.view = view;
|
||||
this.items = [];
|
||||
let onkeydown = (event) => {
|
||||
if (event.keyCode == 27) { // Escape
|
||||
closeLintPanel(this.view);
|
||||
this.view.focus();
|
||||
}
|
||||
else if (event.keyCode == 38 || event.keyCode == 33) { // ArrowUp, PageUp
|
||||
this.moveSelection((this.selectedIndex - 1 + this.items.length) % this.items.length);
|
||||
}
|
||||
else if (event.keyCode == 40 || event.keyCode == 34) { // ArrowDown, PageDown
|
||||
this.moveSelection((this.selectedIndex + 1) % this.items.length);
|
||||
}
|
||||
else if (event.keyCode == 36) { // Home
|
||||
this.moveSelection(0);
|
||||
}
|
||||
else if (event.keyCode == 35) { // End
|
||||
this.moveSelection(this.items.length - 1);
|
||||
}
|
||||
else if (event.keyCode == 13) { // Enter
|
||||
this.view.focus();
|
||||
}
|
||||
else if (event.keyCode >= 65 && event.keyCode <= 90 && this.selectedIndex >= 0) { // A-Z
|
||||
let { diagnostic } = this.items[this.selectedIndex], keys = assignKeys(diagnostic.actions);
|
||||
for (let i = 0; i < keys.length; i++)
|
||||
if (keys[i].toUpperCase().charCodeAt(0) == event.keyCode) {
|
||||
let found = findDiagnostic(this.view.state.field(lintState).diagnostics, diagnostic);
|
||||
if (found)
|
||||
diagnostic.actions[i].apply(view, found.from, found.to);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
};
|
||||
let onclick = (event) => {
|
||||
for (let i = 0; i < this.items.length; i++) {
|
||||
if (this.items[i].dom.contains(event.target))
|
||||
this.moveSelection(i);
|
||||
}
|
||||
};
|
||||
this.list = elt("ul", {
|
||||
tabIndex: 0,
|
||||
role: "listbox",
|
||||
"aria-label": this.view.state.phrase("Diagnostics"),
|
||||
onkeydown,
|
||||
onclick
|
||||
});
|
||||
this.dom = elt("div", { class: "cm-panel-lint" }, this.list, elt("button", {
|
||||
type: "button",
|
||||
name: "close",
|
||||
"aria-label": this.view.state.phrase("close"),
|
||||
onclick: () => closeLintPanel(this.view)
|
||||
}, "×"));
|
||||
this.update();
|
||||
}
|
||||
get selectedIndex() {
|
||||
let selected = this.view.state.field(lintState).selected;
|
||||
if (!selected)
|
||||
return -1;
|
||||
for (let i = 0; i < this.items.length; i++)
|
||||
if (this.items[i].diagnostic == selected.diagnostic)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
update() {
|
||||
let { diagnostics, selected } = this.view.state.field(lintState);
|
||||
let i = 0, needsSync = false, newSelectedItem = null;
|
||||
let seen = new Set();
|
||||
diagnostics.between(0, this.view.state.doc.length, (_start, _end, { spec }) => {
|
||||
for (let diagnostic of spec.diagnostics) {
|
||||
if (seen.has(diagnostic))
|
||||
continue;
|
||||
seen.add(diagnostic);
|
||||
let found = -1, item;
|
||||
for (let j = i; j < this.items.length; j++)
|
||||
if (this.items[j].diagnostic == diagnostic) {
|
||||
found = j;
|
||||
break;
|
||||
}
|
||||
if (found < 0) {
|
||||
item = new PanelItem(this.view, diagnostic);
|
||||
this.items.splice(i, 0, item);
|
||||
needsSync = true;
|
||||
}
|
||||
else {
|
||||
item = this.items[found];
|
||||
if (found > i) {
|
||||
this.items.splice(i, found - i);
|
||||
needsSync = true;
|
||||
}
|
||||
}
|
||||
if (selected && item.diagnostic == selected.diagnostic) {
|
||||
if (!item.dom.hasAttribute("aria-selected")) {
|
||||
item.dom.setAttribute("aria-selected", "true");
|
||||
newSelectedItem = item;
|
||||
}
|
||||
}
|
||||
else if (item.dom.hasAttribute("aria-selected")) {
|
||||
item.dom.removeAttribute("aria-selected");
|
||||
}
|
||||
i++;
|
||||
}
|
||||
});
|
||||
while (i < this.items.length && !(this.items.length == 1 && this.items[0].diagnostic.from < 0)) {
|
||||
needsSync = true;
|
||||
this.items.pop();
|
||||
}
|
||||
if (this.items.length == 0) {
|
||||
this.items.push(new PanelItem(this.view, {
|
||||
from: -1, to: -1,
|
||||
severity: "info",
|
||||
message: this.view.state.phrase("No diagnostics")
|
||||
}));
|
||||
needsSync = true;
|
||||
}
|
||||
if (newSelectedItem) {
|
||||
this.list.setAttribute("aria-activedescendant", newSelectedItem.id);
|
||||
this.view.requestMeasure({
|
||||
key: this,
|
||||
read: () => ({ sel: newSelectedItem.dom.getBoundingClientRect(), panel: this.list.getBoundingClientRect() }),
|
||||
write: ({ sel, panel }) => {
|
||||
let scaleY = panel.height / this.list.offsetHeight;
|
||||
if (sel.top < panel.top)
|
||||
this.list.scrollTop -= (panel.top - sel.top) / scaleY;
|
||||
else if (sel.bottom > panel.bottom)
|
||||
this.list.scrollTop += (sel.bottom - panel.bottom) / scaleY;
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (this.selectedIndex < 0) {
|
||||
this.list.removeAttribute("aria-activedescendant");
|
||||
}
|
||||
if (needsSync)
|
||||
this.sync();
|
||||
}
|
||||
sync() {
|
||||
let domPos = this.list.firstChild;
|
||||
function rm() {
|
||||
let prev = domPos;
|
||||
domPos = prev.nextSibling;
|
||||
prev.remove();
|
||||
}
|
||||
for (let item of this.items) {
|
||||
if (item.dom.parentNode == this.list) {
|
||||
while (domPos != item.dom)
|
||||
rm();
|
||||
domPos = item.dom.nextSibling;
|
||||
}
|
||||
else {
|
||||
this.list.insertBefore(item.dom, domPos);
|
||||
}
|
||||
}
|
||||
while (domPos)
|
||||
rm();
|
||||
}
|
||||
moveSelection(selectedIndex) {
|
||||
if (this.selectedIndex < 0)
|
||||
return;
|
||||
let field = this.view.state.field(lintState);
|
||||
let selection = findDiagnostic(field.diagnostics, this.items[selectedIndex].diagnostic);
|
||||
if (!selection)
|
||||
return;
|
||||
this.view.dispatch({
|
||||
selection: { anchor: selection.from, head: selection.to },
|
||||
scrollIntoView: true,
|
||||
effects: movePanelSelection.of(selection)
|
||||
});
|
||||
}
|
||||
static open(view) { return new LintPanel(view); }
|
||||
}
|
||||
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) {
|
||||
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"`);
|
||||
}
|
||||
const baseTheme = view.EditorView.baseTheme({
|
||||
".cm-diagnostic": {
|
||||
padding: "3px 6px 3px 8px",
|
||||
marginLeft: "-1px",
|
||||
display: "block",
|
||||
whiteSpace: "pre-wrap"
|
||||
},
|
||||
".cm-diagnostic-error": { borderLeft: "5px solid #d11" },
|
||||
".cm-diagnostic-warning": { borderLeft: "5px solid orange" },
|
||||
".cm-diagnostic-info": { borderLeft: "5px solid #999" },
|
||||
".cm-diagnostic-hint": { borderLeft: "5px solid #66d" },
|
||||
".cm-diagnosticAction": {
|
||||
font: "inherit",
|
||||
border: "none",
|
||||
padding: "2px 4px",
|
||||
backgroundColor: "#444",
|
||||
color: "white",
|
||||
borderRadius: "3px",
|
||||
marginLeft: "8px",
|
||||
cursor: "pointer"
|
||||
},
|
||||
".cm-diagnosticSource": {
|
||||
fontSize: "70%",
|
||||
opacity: .7
|
||||
},
|
||||
".cm-lintRange": {
|
||||
backgroundPosition: "left bottom",
|
||||
backgroundRepeat: "repeat-x",
|
||||
paddingBottom: "0.7px",
|
||||
},
|
||||
".cm-lintRange-error": { backgroundImage: underline("#d11") },
|
||||
".cm-lintRange-warning": { backgroundImage: underline("orange") },
|
||||
".cm-lintRange-info": { backgroundImage: underline("#999") },
|
||||
".cm-lintRange-hint": { backgroundImage: underline("#66d") },
|
||||
".cm-lintRange-active": { backgroundColor: "#ffdd9980" },
|
||||
".cm-tooltip-lint": {
|
||||
padding: 0,
|
||||
margin: 0
|
||||
},
|
||||
".cm-lintPoint": {
|
||||
position: "relative",
|
||||
"&:after": {
|
||||
content: '""',
|
||||
position: "absolute",
|
||||
bottom: 0,
|
||||
left: "-2px",
|
||||
borderLeft: "3px solid transparent",
|
||||
borderRight: "3px solid transparent",
|
||||
borderBottom: "4px solid #d11"
|
||||
}
|
||||
},
|
||||
".cm-lintPoint-warning": {
|
||||
"&:after": { borderBottomColor: "orange" }
|
||||
},
|
||||
".cm-lintPoint-info": {
|
||||
"&:after": { borderBottomColor: "#999" }
|
||||
},
|
||||
".cm-lintPoint-hint": {
|
||||
"&:after": { borderBottomColor: "#66d" }
|
||||
},
|
||||
".cm-panel.cm-panel-lint": {
|
||||
position: "relative",
|
||||
"& ul": {
|
||||
maxHeight: "100px",
|
||||
overflowY: "auto",
|
||||
"& [aria-selected]": {
|
||||
backgroundColor: "#ddd",
|
||||
"& u": { textDecoration: "underline" }
|
||||
},
|
||||
"&:focus [aria-selected]": {
|
||||
background_fallback: "#bdf",
|
||||
backgroundColor: "Highlight",
|
||||
color_fallback: "white",
|
||||
color: "HighlightText"
|
||||
},
|
||||
"& u": { textDecoration: "none" },
|
||||
padding: 0,
|
||||
margin: 0
|
||||
},
|
||||
"& [name=close]": {
|
||||
position: "absolute",
|
||||
top: "0",
|
||||
right: "2px",
|
||||
background: "inherit",
|
||||
border: "none",
|
||||
font: "inherit",
|
||||
padding: 0,
|
||||
margin: 0
|
||||
}
|
||||
}
|
||||
});
|
||||
function severityWeight(sev) {
|
||||
return sev == "error" ? 4 : sev == "warning" ? 3 : sev == "info" ? 2 : 1;
|
||||
}
|
||||
function maxSeverity(diagnostics) {
|
||||
let sev = "hint", weight = 1;
|
||||
for (let d of diagnostics) {
|
||||
let w = severityWeight(d.severity);
|
||||
if (w > weight) {
|
||||
weight = w;
|
||||
sev = d.severity;
|
||||
}
|
||||
}
|
||||
return sev;
|
||||
}
|
||||
class LintGutterMarker extends view.GutterMarker {
|
||||
constructor(diagnostics) {
|
||||
super();
|
||||
this.diagnostics = diagnostics;
|
||||
this.severity = maxSeverity(diagnostics);
|
||||
}
|
||||
toDOM(view) {
|
||||
let elt = document.createElement("div");
|
||||
elt.className = "cm-lint-marker cm-lint-marker-" + this.severity;
|
||||
let diagnostics = this.diagnostics;
|
||||
let diagnosticsFilter = view.state.facet(lintGutterConfig).tooltipFilter;
|
||||
if (diagnosticsFilter)
|
||||
diagnostics = diagnosticsFilter(diagnostics, view.state);
|
||||
if (diagnostics.length)
|
||||
elt.onmouseover = () => gutterMarkerMouseOver(view, elt, diagnostics);
|
||||
return elt;
|
||||
}
|
||||
}
|
||||
function trackHoverOn(view, marker) {
|
||||
let mousemove = (event) => {
|
||||
let rect = marker.getBoundingClientRect();
|
||||
if (event.clientX > rect.left - 10 /* Hover.Margin */ && event.clientX < rect.right + 10 /* Hover.Margin */ &&
|
||||
event.clientY > rect.top - 10 /* Hover.Margin */ && event.clientY < rect.bottom + 10 /* Hover.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.elementAtHeight(marker.getBoundingClientRect().top + 5 - view.documentTop);
|
||||
const linePos = view.coordsAtPos(line.from);
|
||||
if (linePos) {
|
||||
view.dispatch({ effects: setLintGutterTooltip.of({
|
||||
pos: line.from,
|
||||
above: false,
|
||||
clip: false,
|
||||
create() {
|
||||
return {
|
||||
dom: diagnosticsTooltip(view, diagnostics),
|
||||
getCoords: () => marker.getBoundingClientRect()
|
||||
};
|
||||
}
|
||||
}) });
|
||||
}
|
||||
marker.onmouseout = marker.onmousemove = null;
|
||||
trackHoverOn(view, marker);
|
||||
}
|
||||
let { hoverTime } = view.state.facet(lintGutterConfig);
|
||||
let hoverTimeout = setTimeout(hovered, hoverTime);
|
||||
marker.onmouseout = () => {
|
||||
clearTimeout(hoverTimeout);
|
||||
marker.onmouseout = marker.onmousemove = null;
|
||||
};
|
||||
marker.onmousemove = () => {
|
||||
clearTimeout(hoverTimeout);
|
||||
hoverTimeout = setTimeout(hovered, hoverTime);
|
||||
};
|
||||
}
|
||||
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 state.RangeSet.of(markers, true);
|
||||
}
|
||||
const lintGutterExtension = view.gutter({
|
||||
class: "cm-gutter-lint",
|
||||
markers: view => view.state.field(lintGutterMarkers),
|
||||
widgetMarker: (view, widget, block) => {
|
||||
let diagnostics = [];
|
||||
view.state.field(lintGutterMarkers).between(block.from, block.to, (from, to, value) => {
|
||||
if (from > block.from && from < block.to)
|
||||
diagnostics.push(...value.diagnostics);
|
||||
});
|
||||
return diagnostics.length ? new LintGutterMarker(diagnostics) : null;
|
||||
}
|
||||
});
|
||||
const lintGutterMarkers = state.StateField.define({
|
||||
create() {
|
||||
return state.RangeSet.empty;
|
||||
},
|
||||
update(markers, tr) {
|
||||
markers = markers.map(tr.changes);
|
||||
let diagnosticFilter = tr.state.facet(lintGutterConfig).markerFilter;
|
||||
for (let effect of tr.effects) {
|
||||
if (effect.is(setDiagnosticsEffect)) {
|
||||
let diagnostics = effect.value;
|
||||
if (diagnosticFilter)
|
||||
diagnostics = diagnosticFilter(diagnostics || [], tr.state);
|
||||
markers = markersForDiagnostics(tr.state.doc, diagnostics.slice(0));
|
||||
}
|
||||
}
|
||||
return markers;
|
||||
}
|
||||
});
|
||||
const setLintGutterTooltip = state.StateEffect.define();
|
||||
const lintGutterTooltip = state.StateField.define({
|
||||
create() { return null; },
|
||||
update(tooltip, tr) {
|
||||
if (tooltip && tr.docChanged)
|
||||
tooltip = hideTooltip(tr, tooltip) ? null : { ...tooltip, pos: tr.changes.mapPos(tooltip.pos) };
|
||||
return tr.effects.reduce((t, e) => e.is(setLintGutterTooltip) ? e.value : t, tooltip);
|
||||
},
|
||||
provide: field => view.showTooltip.from(field)
|
||||
});
|
||||
const lintGutterTheme = view.EditorView.baseTheme({
|
||||
".cm-gutter-lint": {
|
||||
width: "1.4em",
|
||||
"& .cm-gutterElement": {
|
||||
padding: ".2em"
|
||||
}
|
||||
},
|
||||
".cm-lint-marker": {
|
||||
width: "1em",
|
||||
height: "1em"
|
||||
},
|
||||
".cm-lint-marker-info": {
|
||||
content: svg(`<path fill="#aaf" stroke="#77e" stroke-width="6" stroke-linejoin="round" d="M5 5L35 5L35 35L5 35Z"/>`)
|
||||
},
|
||||
".cm-lint-marker-warning": {
|
||||
content: svg(`<path fill="#fe8" stroke="#fd7" stroke-width="6" stroke-linejoin="round" d="M20 6L37 35L3 35Z"/>`),
|
||||
},
|
||||
".cm-lint-marker-error": {
|
||||
content: svg(`<circle cx="20" cy="20" r="15" fill="#f87" stroke="#f43" stroke-width="6"/>`)
|
||||
},
|
||||
});
|
||||
const lintExtensions = [
|
||||
lintState,
|
||||
view.EditorView.decorations.compute([lintState], state => {
|
||||
let { selected, panel } = state.field(lintState);
|
||||
return !selected || !panel || selected.from == selected.to ? view.Decoration.none : view.Decoration.set([
|
||||
activeMark.range(selected.from, selected.to)
|
||||
]);
|
||||
}),
|
||||
view.hoverTooltip(lintTooltip, { hideOn: hideTooltip }),
|
||||
baseTheme
|
||||
];
|
||||
const lintGutterConfig = state.Facet.define({
|
||||
combine(configs) {
|
||||
return state.combineConfig(configs, {
|
||||
hoverTime: 300 /* Hover.Time */,
|
||||
markerFilter: null,
|
||||
tooltipFilter: null
|
||||
});
|
||||
}
|
||||
});
|
||||
/**
|
||||
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(config = {}) {
|
||||
return [lintGutterConfig.of(config), lintGutterMarkers, lintGutterExtension, lintGutterTheme, lintGutterTooltip];
|
||||
}
|
||||
/**
|
||||
Iterate over the marked diagnostics for the given editor state,
|
||||
calling `f` for each of them. Note that, if the document changed
|
||||
since the diagnostics were created, the `Diagnostic` object will
|
||||
hold the original outdated position, whereas the `to` and `from`
|
||||
arguments hold the diagnostic's current position.
|
||||
*/
|
||||
function forEachDiagnostic(state$1, f) {
|
||||
let lState = state$1.field(lintState, false);
|
||||
if (lState && lState.diagnostics.size) {
|
||||
let pending = [], pendingStart = [], lastEnd = -1;
|
||||
for (let iter = state.RangeSet.iter([lState.diagnostics]);; iter.next()) {
|
||||
for (let i = 0; i < pending.length; i++)
|
||||
if (!iter.value || iter.value.spec.diagnostics.indexOf(pending[i]) < 0) {
|
||||
f(pending[i], pendingStart[i], lastEnd);
|
||||
pending.splice(i, 1);
|
||||
pendingStart.splice(i--, 1);
|
||||
}
|
||||
if (!iter.value)
|
||||
break;
|
||||
for (let d of iter.value.spec.diagnostics)
|
||||
if (pending.indexOf(d) < 0) {
|
||||
pending.push(d);
|
||||
pendingStart.push(iter.from);
|
||||
}
|
||||
lastEnd = iter.to;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exports.closeLintPanel = closeLintPanel;
|
||||
exports.diagnosticCount = diagnosticCount;
|
||||
exports.forEachDiagnostic = forEachDiagnostic;
|
||||
exports.forceLinting = forceLinting;
|
||||
exports.lintGutter = lintGutter;
|
||||
exports.lintKeymap = lintKeymap;
|
||||
exports.linter = linter;
|
||||
exports.nextDiagnostic = nextDiagnostic;
|
||||
exports.openLintPanel = openLintPanel;
|
||||
exports.previousDiagnostic = previousDiagnostic;
|
||||
exports.setDiagnostics = setDiagnostics;
|
||||
exports.setDiagnosticsEffect = setDiagnosticsEffect;
|
||||
195
frontend/node_modules/@codemirror/lint/dist/index.d.cts
generated
vendored
Normal file
195
frontend/node_modules/@codemirror/lint/dist/index.d.cts
generated
vendored
Normal file
@ -0,0 +1,195 @@
|
||||
import * as _codemirror_state from '@codemirror/state';
|
||||
import { EditorState, TransactionSpec, Extension, Transaction } from '@codemirror/state';
|
||||
import { EditorView, Command, KeyBinding, ViewUpdate } from '@codemirror/view';
|
||||
|
||||
type Severity = "hint" | "info" | "warning" | "error";
|
||||
/**
|
||||
Describes a problem or hint for a piece of code.
|
||||
*/
|
||||
interface Diagnostic {
|
||||
/**
|
||||
The start position of the relevant text.
|
||||
*/
|
||||
from: number;
|
||||
/**
|
||||
The end position. May be equal to `from`, though actually
|
||||
covering text is preferable.
|
||||
*/
|
||||
to: number;
|
||||
/**
|
||||
The severity of the problem. This will influence how it is
|
||||
displayed.
|
||||
*/
|
||||
severity: Severity;
|
||||
/**
|
||||
When given, add an extra CSS class to parts of the code that
|
||||
this diagnostic applies to.
|
||||
*/
|
||||
markClass?: string;
|
||||
/**
|
||||
An optional source string indicating where the diagnostic is
|
||||
coming from. You can put the name of your linter here, if
|
||||
applicable.
|
||||
*/
|
||||
source?: string;
|
||||
/**
|
||||
The message associated with this diagnostic.
|
||||
*/
|
||||
message: string;
|
||||
/**
|
||||
An optional custom rendering function that displays the message
|
||||
as a DOM node.
|
||||
*/
|
||||
renderMessage?: (view: EditorView) => Node;
|
||||
/**
|
||||
An optional array of actions that can be taken on this
|
||||
diagnostic.
|
||||
*/
|
||||
actions?: readonly Action[];
|
||||
}
|
||||
/**
|
||||
An action associated with a diagnostic.
|
||||
*/
|
||||
interface Action {
|
||||
/**
|
||||
The label to show to the user. Should be relatively short.
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
When given, add an extra CSS class to the action button.
|
||||
*/
|
||||
markClass?: string;
|
||||
/**
|
||||
The function to call when the user activates this action. Is
|
||||
given the diagnostic's _current_ position, which may have
|
||||
changed since the creation of the diagnostic, due to editing.
|
||||
*/
|
||||
apply: (view: EditorView, from: number, to: number) => void;
|
||||
}
|
||||
type DiagnosticFilter = (diagnostics: readonly Diagnostic[], state: EditorState) => Diagnostic[];
|
||||
interface LintConfig {
|
||||
/**
|
||||
Time to wait (in milliseconds) after a change before running
|
||||
the linter. Defaults to 750ms.
|
||||
*/
|
||||
delay?: number;
|
||||
/**
|
||||
Optional predicate that can be used to indicate when diagnostics
|
||||
need to be recomputed. Linting is always re-done on document
|
||||
changes.
|
||||
*/
|
||||
needsRefresh?: null | ((update: ViewUpdate) => boolean);
|
||||
/**
|
||||
Optional filter to determine which diagnostics produce markers
|
||||
in the content.
|
||||
*/
|
||||
markerFilter?: null | DiagnosticFilter;
|
||||
/**
|
||||
Filter applied to a set of diagnostics shown in a tooltip. No
|
||||
tooltip will appear if the empty set is returned.
|
||||
*/
|
||||
tooltipFilter?: null | DiagnosticFilter;
|
||||
/**
|
||||
Can be used to control what kind of transactions cause lint
|
||||
hover tooltips associated with the given document range to be
|
||||
hidden. By default any transactions that changes the line
|
||||
around the range will hide it. Returning null falls back to this
|
||||
behavior.
|
||||
*/
|
||||
hideOn?: (tr: Transaction, from: number, to: number) => boolean | null;
|
||||
/**
|
||||
When enabled (defaults to off), this will cause the lint panel
|
||||
to automatically open when diagnostics are found, and close when
|
||||
all diagnostics are resolved or removed.
|
||||
*/
|
||||
autoPanel?: boolean;
|
||||
}
|
||||
interface LintGutterConfig {
|
||||
/**
|
||||
The delay before showing a tooltip when hovering over a lint gutter marker.
|
||||
*/
|
||||
hoverTime?: number;
|
||||
/**
|
||||
Optional filter determining which diagnostics show a marker in
|
||||
the gutter.
|
||||
*/
|
||||
markerFilter?: null | DiagnosticFilter;
|
||||
/**
|
||||
Optional filter for diagnostics displayed in a tooltip, which
|
||||
can also be used to prevent a tooltip appearing.
|
||||
*/
|
||||
tooltipFilter?: null | DiagnosticFilter;
|
||||
}
|
||||
/**
|
||||
Returns a transaction spec which updates the current set of
|
||||
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.
|
||||
*/
|
||||
declare function diagnosticCount(state: EditorState): number;
|
||||
/**
|
||||
Command to open and focus the lint panel.
|
||||
*/
|
||||
declare const openLintPanel: Command;
|
||||
/**
|
||||
Command to close the lint panel, when open.
|
||||
*/
|
||||
declare const closeLintPanel: Command;
|
||||
/**
|
||||
Move the selection to the next diagnostic.
|
||||
*/
|
||||
declare const nextDiagnostic: Command;
|
||||
/**
|
||||
Move the selection to the previous diagnostic.
|
||||
*/
|
||||
declare const previousDiagnostic: Command;
|
||||
/**
|
||||
A set of default key bindings for the lint functionality.
|
||||
|
||||
- Ctrl-Shift-m (Cmd-Shift-m on macOS): [`openLintPanel`](https://codemirror.net/6/docs/ref/#lint.openLintPanel)
|
||||
- F8: [`nextDiagnostic`](https://codemirror.net/6/docs/ref/#lint.nextDiagnostic)
|
||||
*/
|
||||
declare const lintKeymap: readonly KeyBinding[];
|
||||
/**
|
||||
The type of a function that produces diagnostics.
|
||||
*/
|
||||
type LintSource = (view: EditorView) => readonly Diagnostic[] | Promise<readonly Diagnostic[]>;
|
||||
/**
|
||||
Given a diagnostic source, this function returns an extension that
|
||||
enables linting with that source. It will be called whenever the
|
||||
editor is idle (after its content changed).
|
||||
|
||||
Note that settings given here will apply to all linters active in
|
||||
the editor. If `null` is given as source, this only configures the
|
||||
lint extension.
|
||||
*/
|
||||
declare function linter(source: LintSource | null, config?: LintConfig): Extension;
|
||||
/**
|
||||
Forces any linters [configured](https://codemirror.net/6/docs/ref/#lint.linter) to run when the
|
||||
editor is idle to run right away.
|
||||
*/
|
||||
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(config?: LintGutterConfig): Extension;
|
||||
/**
|
||||
Iterate over the marked diagnostics for the given editor state,
|
||||
calling `f` for each of them. Note that, if the document changed
|
||||
since the diagnostics were created, the `Diagnostic` object will
|
||||
hold the original outdated position, whereas the `to` and `from`
|
||||
arguments hold the diagnostic's current position.
|
||||
*/
|
||||
declare function forEachDiagnostic(state: EditorState, f: (d: Diagnostic, from: number, to: number) => void): void;
|
||||
|
||||
export { type Action, type Diagnostic, type LintSource, closeLintPanel, diagnosticCount, forEachDiagnostic, forceLinting, lintGutter, lintKeymap, linter, nextDiagnostic, openLintPanel, previousDiagnostic, setDiagnostics, setDiagnosticsEffect };
|
||||
195
frontend/node_modules/@codemirror/lint/dist/index.d.ts
generated
vendored
Normal file
195
frontend/node_modules/@codemirror/lint/dist/index.d.ts
generated
vendored
Normal file
@ -0,0 +1,195 @@
|
||||
import * as _codemirror_state from '@codemirror/state';
|
||||
import { EditorState, TransactionSpec, Extension, Transaction } from '@codemirror/state';
|
||||
import { EditorView, Command, KeyBinding, ViewUpdate } from '@codemirror/view';
|
||||
|
||||
type Severity = "hint" | "info" | "warning" | "error";
|
||||
/**
|
||||
Describes a problem or hint for a piece of code.
|
||||
*/
|
||||
interface Diagnostic {
|
||||
/**
|
||||
The start position of the relevant text.
|
||||
*/
|
||||
from: number;
|
||||
/**
|
||||
The end position. May be equal to `from`, though actually
|
||||
covering text is preferable.
|
||||
*/
|
||||
to: number;
|
||||
/**
|
||||
The severity of the problem. This will influence how it is
|
||||
displayed.
|
||||
*/
|
||||
severity: Severity;
|
||||
/**
|
||||
When given, add an extra CSS class to parts of the code that
|
||||
this diagnostic applies to.
|
||||
*/
|
||||
markClass?: string;
|
||||
/**
|
||||
An optional source string indicating where the diagnostic is
|
||||
coming from. You can put the name of your linter here, if
|
||||
applicable.
|
||||
*/
|
||||
source?: string;
|
||||
/**
|
||||
The message associated with this diagnostic.
|
||||
*/
|
||||
message: string;
|
||||
/**
|
||||
An optional custom rendering function that displays the message
|
||||
as a DOM node.
|
||||
*/
|
||||
renderMessage?: (view: EditorView) => Node;
|
||||
/**
|
||||
An optional array of actions that can be taken on this
|
||||
diagnostic.
|
||||
*/
|
||||
actions?: readonly Action[];
|
||||
}
|
||||
/**
|
||||
An action associated with a diagnostic.
|
||||
*/
|
||||
interface Action {
|
||||
/**
|
||||
The label to show to the user. Should be relatively short.
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
When given, add an extra CSS class to the action button.
|
||||
*/
|
||||
markClass?: string;
|
||||
/**
|
||||
The function to call when the user activates this action. Is
|
||||
given the diagnostic's _current_ position, which may have
|
||||
changed since the creation of the diagnostic, due to editing.
|
||||
*/
|
||||
apply: (view: EditorView, from: number, to: number) => void;
|
||||
}
|
||||
type DiagnosticFilter = (diagnostics: readonly Diagnostic[], state: EditorState) => Diagnostic[];
|
||||
interface LintConfig {
|
||||
/**
|
||||
Time to wait (in milliseconds) after a change before running
|
||||
the linter. Defaults to 750ms.
|
||||
*/
|
||||
delay?: number;
|
||||
/**
|
||||
Optional predicate that can be used to indicate when diagnostics
|
||||
need to be recomputed. Linting is always re-done on document
|
||||
changes.
|
||||
*/
|
||||
needsRefresh?: null | ((update: ViewUpdate) => boolean);
|
||||
/**
|
||||
Optional filter to determine which diagnostics produce markers
|
||||
in the content.
|
||||
*/
|
||||
markerFilter?: null | DiagnosticFilter;
|
||||
/**
|
||||
Filter applied to a set of diagnostics shown in a tooltip. No
|
||||
tooltip will appear if the empty set is returned.
|
||||
*/
|
||||
tooltipFilter?: null | DiagnosticFilter;
|
||||
/**
|
||||
Can be used to control what kind of transactions cause lint
|
||||
hover tooltips associated with the given document range to be
|
||||
hidden. By default any transactions that changes the line
|
||||
around the range will hide it. Returning null falls back to this
|
||||
behavior.
|
||||
*/
|
||||
hideOn?: (tr: Transaction, from: number, to: number) => boolean | null;
|
||||
/**
|
||||
When enabled (defaults to off), this will cause the lint panel
|
||||
to automatically open when diagnostics are found, and close when
|
||||
all diagnostics are resolved or removed.
|
||||
*/
|
||||
autoPanel?: boolean;
|
||||
}
|
||||
interface LintGutterConfig {
|
||||
/**
|
||||
The delay before showing a tooltip when hovering over a lint gutter marker.
|
||||
*/
|
||||
hoverTime?: number;
|
||||
/**
|
||||
Optional filter determining which diagnostics show a marker in
|
||||
the gutter.
|
||||
*/
|
||||
markerFilter?: null | DiagnosticFilter;
|
||||
/**
|
||||
Optional filter for diagnostics displayed in a tooltip, which
|
||||
can also be used to prevent a tooltip appearing.
|
||||
*/
|
||||
tooltipFilter?: null | DiagnosticFilter;
|
||||
}
|
||||
/**
|
||||
Returns a transaction spec which updates the current set of
|
||||
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.
|
||||
*/
|
||||
declare function diagnosticCount(state: EditorState): number;
|
||||
/**
|
||||
Command to open and focus the lint panel.
|
||||
*/
|
||||
declare const openLintPanel: Command;
|
||||
/**
|
||||
Command to close the lint panel, when open.
|
||||
*/
|
||||
declare const closeLintPanel: Command;
|
||||
/**
|
||||
Move the selection to the next diagnostic.
|
||||
*/
|
||||
declare const nextDiagnostic: Command;
|
||||
/**
|
||||
Move the selection to the previous diagnostic.
|
||||
*/
|
||||
declare const previousDiagnostic: Command;
|
||||
/**
|
||||
A set of default key bindings for the lint functionality.
|
||||
|
||||
- Ctrl-Shift-m (Cmd-Shift-m on macOS): [`openLintPanel`](https://codemirror.net/6/docs/ref/#lint.openLintPanel)
|
||||
- F8: [`nextDiagnostic`](https://codemirror.net/6/docs/ref/#lint.nextDiagnostic)
|
||||
*/
|
||||
declare const lintKeymap: readonly KeyBinding[];
|
||||
/**
|
||||
The type of a function that produces diagnostics.
|
||||
*/
|
||||
type LintSource = (view: EditorView) => readonly Diagnostic[] | Promise<readonly Diagnostic[]>;
|
||||
/**
|
||||
Given a diagnostic source, this function returns an extension that
|
||||
enables linting with that source. It will be called whenever the
|
||||
editor is idle (after its content changed).
|
||||
|
||||
Note that settings given here will apply to all linters active in
|
||||
the editor. If `null` is given as source, this only configures the
|
||||
lint extension.
|
||||
*/
|
||||
declare function linter(source: LintSource | null, config?: LintConfig): Extension;
|
||||
/**
|
||||
Forces any linters [configured](https://codemirror.net/6/docs/ref/#lint.linter) to run when the
|
||||
editor is idle to run right away.
|
||||
*/
|
||||
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(config?: LintGutterConfig): Extension;
|
||||
/**
|
||||
Iterate over the marked diagnostics for the given editor state,
|
||||
calling `f` for each of them. Note that, if the document changed
|
||||
since the diagnostics were created, the `Diagnostic` object will
|
||||
hold the original outdated position, whereas the `to` and `from`
|
||||
arguments hold the diagnostic's current position.
|
||||
*/
|
||||
declare function forEachDiagnostic(state: EditorState, f: (d: Diagnostic, from: number, to: number) => void): void;
|
||||
|
||||
export { type Action, type Diagnostic, type LintSource, closeLintPanel, diagnosticCount, forEachDiagnostic, forceLinting, lintGutter, lintKeymap, linter, nextDiagnostic, openLintPanel, previousDiagnostic, setDiagnostics, setDiagnosticsEffect };
|
||||
937
frontend/node_modules/@codemirror/lint/dist/index.js
generated
vendored
Normal file
937
frontend/node_modules/@codemirror/lint/dist/index.js
generated
vendored
Normal file
@ -0,0 +1,937 @@
|
||||
import { Decoration, showPanel, EditorView, ViewPlugin, gutter, showTooltip, hoverTooltip, getPanel, logException, WidgetType, GutterMarker } from '@codemirror/view';
|
||||
import { StateEffect, StateField, Facet, combineConfig, RangeSet, RangeSetBuilder } from '@codemirror/state';
|
||||
import elt from 'crelt';
|
||||
|
||||
class SelectedDiagnostic {
|
||||
constructor(from, to, diagnostic) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.diagnostic = diagnostic;
|
||||
}
|
||||
}
|
||||
class LintState {
|
||||
constructor(diagnostics, panel, selected) {
|
||||
this.diagnostics = diagnostics;
|
||||
this.panel = panel;
|
||||
this.selected = selected;
|
||||
}
|
||||
static init(diagnostics, panel, state) {
|
||||
// Filter the list of diagnostics for which to create markers
|
||||
let diagnosticFilter = state.facet(lintConfig).markerFilter;
|
||||
if (diagnosticFilter)
|
||||
diagnostics = diagnosticFilter(diagnostics, state);
|
||||
let sorted = diagnostics.slice().sort((a, b) => a.from - b.from || a.to - b.to);
|
||||
let deco = new RangeSetBuilder(), active = [], pos = 0;
|
||||
let scan = state.doc.iter(), scanPos = 0, docLen = state.doc.length;
|
||||
for (let i = 0;;) {
|
||||
let next = i == sorted.length ? null : sorted[i];
|
||||
if (!next && !active.length)
|
||||
break;
|
||||
let from, to;
|
||||
if (active.length) {
|
||||
from = pos;
|
||||
to = active.reduce((p, d) => Math.min(p, d.to), next && next.from > from ? next.from : 1e8);
|
||||
}
|
||||
else {
|
||||
from = next.from;
|
||||
if (from > docLen)
|
||||
break;
|
||||
to = next.to;
|
||||
active.push(next);
|
||||
i++;
|
||||
}
|
||||
while (i < sorted.length) {
|
||||
let next = sorted[i];
|
||||
if (next.from == from && (next.to > next.from || next.to == from)) {
|
||||
active.push(next);
|
||||
i++;
|
||||
to = Math.min(next.to, to);
|
||||
}
|
||||
else {
|
||||
to = Math.min(next.from, to);
|
||||
break;
|
||||
}
|
||||
}
|
||||
to = Math.min(to, docLen);
|
||||
let widget = false;
|
||||
if (active.some(d => d.from == from && (d.to == to || to == docLen))) {
|
||||
widget = from == to;
|
||||
if (!widget && to - from < 10) {
|
||||
let behind = from - (scanPos + scan.value.length);
|
||||
if (behind > 0) {
|
||||
scan.next(behind);
|
||||
scanPos = from;
|
||||
}
|
||||
for (let check = from;;) {
|
||||
if (check >= to) {
|
||||
widget = true;
|
||||
break;
|
||||
}
|
||||
if (!scan.lineBreak && scanPos + scan.value.length > check)
|
||||
break;
|
||||
check = scanPos + scan.value.length;
|
||||
scanPos += scan.value.length;
|
||||
scan.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
let sev = maxSeverity(active);
|
||||
if (widget) {
|
||||
deco.add(from, from, Decoration.widget({
|
||||
widget: new DiagnosticWidget(sev),
|
||||
diagnostics: active.slice()
|
||||
}));
|
||||
}
|
||||
else {
|
||||
let markClass = active.reduce((c, d) => d.markClass ? c + " " + d.markClass : c, "");
|
||||
deco.add(from, to, Decoration.mark({
|
||||
class: "cm-lintRange cm-lintRange-" + sev + markClass,
|
||||
diagnostics: active.slice(),
|
||||
inclusiveEnd: active.some(a => a.to > to)
|
||||
}));
|
||||
}
|
||||
pos = to;
|
||||
if (pos == docLen)
|
||||
break;
|
||||
for (let i = 0; i < active.length; i++)
|
||||
if (active[i].to <= pos)
|
||||
active.splice(i--, 1);
|
||||
}
|
||||
let set = deco.finish();
|
||||
return new LintState(set, panel, findDiagnostic(set));
|
||||
}
|
||||
}
|
||||
function findDiagnostic(diagnostics, diagnostic = null, after = 0) {
|
||||
let found = null;
|
||||
diagnostics.between(after, 1e9, (from, to, { spec }) => {
|
||||
if (diagnostic && spec.diagnostics.indexOf(diagnostic) < 0)
|
||||
return;
|
||||
if (!found)
|
||||
found = new SelectedDiagnostic(from, to, diagnostic || spec.diagnostics[0]);
|
||||
else if (spec.diagnostics.indexOf(found.diagnostic) < 0)
|
||||
return false;
|
||||
else
|
||||
found = new SelectedDiagnostic(found.from, to, found.diagnostic);
|
||||
});
|
||||
return found;
|
||||
}
|
||||
function hideTooltip(tr, tooltip) {
|
||||
let from = tooltip.pos, to = tooltip.end || from;
|
||||
let result = tr.state.facet(lintConfig).hideOn(tr, from, to);
|
||||
if (result != null)
|
||||
return result;
|
||||
let line = tr.startState.doc.lineAt(tooltip.pos);
|
||||
return !!(tr.effects.some(e => e.is(setDiagnosticsEffect)) || tr.changes.touchesRange(line.from, Math.max(line.to, to)));
|
||||
}
|
||||
function maybeEnableLint(state, effects) {
|
||||
return state.field(lintState, false) ? effects : effects.concat(StateEffect.appendConfig.of(lintExtensions));
|
||||
}
|
||||
/**
|
||||
Returns a transaction spec which updates the current set of
|
||||
diagnostics, and enables the lint extension if if wasn't already
|
||||
active.
|
||||
*/
|
||||
function setDiagnostics(state, diagnostics) {
|
||||
return {
|
||||
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();
|
||||
const togglePanel = /*@__PURE__*/StateEffect.define();
|
||||
const movePanelSelection = /*@__PURE__*/StateEffect.define();
|
||||
const lintState = /*@__PURE__*/StateField.define({
|
||||
create() {
|
||||
return new LintState(Decoration.none, null, null);
|
||||
},
|
||||
update(value, tr) {
|
||||
if (tr.docChanged && value.diagnostics.size) {
|
||||
let mapped = value.diagnostics.map(tr.changes), selected = null, panel = value.panel;
|
||||
if (value.selected) {
|
||||
let selPos = tr.changes.mapPos(value.selected.from, 1);
|
||||
selected = findDiagnostic(mapped, value.selected.diagnostic, selPos) || findDiagnostic(mapped, null, selPos);
|
||||
}
|
||||
if (!mapped.size && panel && tr.state.facet(lintConfig).autoPanel)
|
||||
panel = null;
|
||||
value = new LintState(mapped, panel, selected);
|
||||
}
|
||||
for (let effect of tr.effects) {
|
||||
if (effect.is(setDiagnosticsEffect)) {
|
||||
let panel = !tr.state.facet(lintConfig).autoPanel ? value.panel : effect.value.length ? LintPanel.open : null;
|
||||
value = LintState.init(effect.value, panel, tr.state);
|
||||
}
|
||||
else if (effect.is(togglePanel)) {
|
||||
value = new LintState(value.diagnostics, effect.value ? LintPanel.open : null, value.selected);
|
||||
}
|
||||
else if (effect.is(movePanelSelection)) {
|
||||
value = new LintState(value.diagnostics, value.panel, effect.value);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
},
|
||||
provide: f => [showPanel.from(f, val => val.panel),
|
||||
EditorView.decorations.from(f, s => s.diagnostics)]
|
||||
});
|
||||
/**
|
||||
Returns the number of active lint diagnostics in the given state.
|
||||
*/
|
||||
function diagnosticCount(state) {
|
||||
let lint = state.field(lintState, false);
|
||||
return lint ? lint.diagnostics.size : 0;
|
||||
}
|
||||
const activeMark = /*@__PURE__*/Decoration.mark({ class: "cm-lintRange cm-lintRange-active" });
|
||||
function lintTooltip(view, pos, side) {
|
||||
let { diagnostics } = view.state.field(lintState);
|
||||
let found, start = -1, end = -1;
|
||||
diagnostics.between(pos - (side < 0 ? 1 : 0), pos + (side > 0 ? 1 : 0), (from, to, { spec }) => {
|
||||
if (pos >= from && pos <= to &&
|
||||
(from == to || ((pos > from || side > 0) && (pos < to || side < 0)))) {
|
||||
found = spec.diagnostics;
|
||||
start = from;
|
||||
end = to;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
let diagnosticFilter = view.state.facet(lintConfig).tooltipFilter;
|
||||
if (found && diagnosticFilter)
|
||||
found = diagnosticFilter(found, view.state);
|
||||
if (!found)
|
||||
return null;
|
||||
return {
|
||||
pos: start,
|
||||
end: end,
|
||||
above: view.state.doc.lineAt(start).to < end,
|
||||
create() {
|
||||
return { dom: diagnosticsTooltip(view, found) };
|
||||
}
|
||||
};
|
||||
}
|
||||
function diagnosticsTooltip(view, diagnostics) {
|
||||
return elt("ul", { class: "cm-tooltip-lint" }, diagnostics.map(d => renderDiagnostic(view, d, false)));
|
||||
}
|
||||
/**
|
||||
Command to open and focus the lint panel.
|
||||
*/
|
||||
const openLintPanel = (view) => {
|
||||
let field = view.state.field(lintState, false);
|
||||
if (!field || !field.panel)
|
||||
view.dispatch({ effects: maybeEnableLint(view.state, [togglePanel.of(true)]) });
|
||||
let panel = getPanel(view, LintPanel.open);
|
||||
if (panel)
|
||||
panel.dom.querySelector(".cm-panel-lint ul").focus();
|
||||
return true;
|
||||
};
|
||||
/**
|
||||
Command to close the lint panel, when open.
|
||||
*/
|
||||
const closeLintPanel = (view) => {
|
||||
let field = view.state.field(lintState, false);
|
||||
if (!field || !field.panel)
|
||||
return false;
|
||||
view.dispatch({ effects: togglePanel.of(false) });
|
||||
return true;
|
||||
};
|
||||
/**
|
||||
Move the selection to the next diagnostic.
|
||||
*/
|
||||
const nextDiagnostic = (view) => {
|
||||
let field = view.state.field(lintState, false);
|
||||
if (!field)
|
||||
return false;
|
||||
let sel = view.state.selection.main, next = field.diagnostics.iter(sel.to + 1);
|
||||
if (!next.value) {
|
||||
next = field.diagnostics.iter(0);
|
||||
if (!next.value || next.from == sel.from && next.to == sel.to)
|
||||
return false;
|
||||
}
|
||||
view.dispatch({ selection: { anchor: next.from, head: next.to }, scrollIntoView: true });
|
||||
return true;
|
||||
};
|
||||
/**
|
||||
Move the selection to the previous diagnostic.
|
||||
*/
|
||||
const previousDiagnostic = (view) => {
|
||||
let { state } = view, field = state.field(lintState, false);
|
||||
if (!field)
|
||||
return false;
|
||||
let sel = state.selection.main;
|
||||
let prevFrom, prevTo, lastFrom, lastTo;
|
||||
field.diagnostics.between(0, state.doc.length, (from, to) => {
|
||||
if (to < sel.to && (prevFrom == null || prevFrom < from)) {
|
||||
prevFrom = from;
|
||||
prevTo = to;
|
||||
}
|
||||
if (lastFrom == null || from > lastFrom) {
|
||||
lastFrom = from;
|
||||
lastTo = to;
|
||||
}
|
||||
});
|
||||
if (lastFrom == null || prevFrom == null && lastFrom == sel.from)
|
||||
return false;
|
||||
view.dispatch({ selection: { anchor: prevFrom !== null && prevFrom !== void 0 ? prevFrom : lastFrom, head: prevTo !== null && prevTo !== void 0 ? prevTo : lastTo }, scrollIntoView: true });
|
||||
return true;
|
||||
};
|
||||
/**
|
||||
A set of default key bindings for the lint functionality.
|
||||
|
||||
- Ctrl-Shift-m (Cmd-Shift-m on macOS): [`openLintPanel`](https://codemirror.net/6/docs/ref/#lint.openLintPanel)
|
||||
- F8: [`nextDiagnostic`](https://codemirror.net/6/docs/ref/#lint.nextDiagnostic)
|
||||
*/
|
||||
const lintKeymap = [
|
||||
{ key: "Mod-Shift-m", run: openLintPanel, preventDefault: true },
|
||||
{ key: "F8", run: nextDiagnostic }
|
||||
];
|
||||
const lintPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
|
||||
constructor(view) {
|
||||
this.view = view;
|
||||
this.timeout = -1;
|
||||
this.set = true;
|
||||
let { delay } = view.state.facet(lintConfig);
|
||||
this.lintTime = Date.now() + delay;
|
||||
this.run = this.run.bind(this);
|
||||
this.timeout = setTimeout(this.run, delay);
|
||||
}
|
||||
run() {
|
||||
clearTimeout(this.timeout);
|
||||
let now = Date.now();
|
||||
if (now < this.lintTime - 10) {
|
||||
this.timeout = setTimeout(this.run, this.lintTime - now);
|
||||
}
|
||||
else {
|
||||
this.set = false;
|
||||
let { state } = this.view, { sources } = state.facet(lintConfig);
|
||||
if (sources.length)
|
||||
batchResults(sources.map(s => Promise.resolve(s(this.view))), annotations => {
|
||||
if (this.view.state.doc == state.doc)
|
||||
this.view.dispatch(setDiagnostics(this.view.state, annotations.reduce((a, b) => a.concat(b))));
|
||||
}, error => { logException(this.view.state, error); });
|
||||
}
|
||||
}
|
||||
update(update) {
|
||||
let config = update.state.facet(lintConfig);
|
||||
if (update.docChanged || config != update.startState.facet(lintConfig) ||
|
||||
config.needsRefresh && config.needsRefresh(update)) {
|
||||
this.lintTime = Date.now() + config.delay;
|
||||
if (!this.set) {
|
||||
this.set = true;
|
||||
this.timeout = setTimeout(this.run, config.delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
force() {
|
||||
if (this.set) {
|
||||
this.lintTime = Date.now();
|
||||
this.run();
|
||||
}
|
||||
}
|
||||
destroy() {
|
||||
clearTimeout(this.timeout);
|
||||
}
|
||||
});
|
||||
function batchResults(promises, sink, error) {
|
||||
let collected = [], timeout = -1;
|
||||
for (let p of promises)
|
||||
p.then(value => {
|
||||
collected.push(value);
|
||||
clearTimeout(timeout);
|
||||
if (collected.length == promises.length)
|
||||
sink(collected);
|
||||
else
|
||||
timeout = setTimeout(() => sink(collected), 200);
|
||||
}, error);
|
||||
}
|
||||
const lintConfig = /*@__PURE__*/Facet.define({
|
||||
combine(input) {
|
||||
return {
|
||||
sources: input.map(i => i.source).filter(x => x != null),
|
||||
...combineConfig(input.map(i => i.config), {
|
||||
delay: 750,
|
||||
markerFilter: null,
|
||||
tooltipFilter: null,
|
||||
needsRefresh: null,
|
||||
hideOn: () => null,
|
||||
}, {
|
||||
delay: Math.max,
|
||||
markerFilter: combineFilter,
|
||||
tooltipFilter: combineFilter,
|
||||
needsRefresh: (a, b) => !a ? b : !b ? a : u => a(u) || b(u),
|
||||
hideOn: (a, b) => !a ? b : !b ? a : (t, x, y) => a(t, x, y) || b(t, x, y),
|
||||
autoPanel: (a, b) => a || b
|
||||
})
|
||||
};
|
||||
}
|
||||
});
|
||||
function combineFilter(a, b) {
|
||||
return !a ? b : !b ? a : (d, s) => b(a(d, s), s);
|
||||
}
|
||||
/**
|
||||
Given a diagnostic source, this function returns an extension that
|
||||
enables linting with that source. It will be called whenever the
|
||||
editor is idle (after its content changed).
|
||||
|
||||
Note that settings given here will apply to all linters active in
|
||||
the editor. If `null` is given as source, this only configures the
|
||||
lint extension.
|
||||
*/
|
||||
function linter(source, config = {}) {
|
||||
return [
|
||||
lintConfig.of({ source, config }),
|
||||
lintPlugin,
|
||||
lintExtensions
|
||||
];
|
||||
}
|
||||
/**
|
||||
Forces any linters [configured](https://codemirror.net/6/docs/ref/#lint.linter) to run when the
|
||||
editor is idle to run right away.
|
||||
*/
|
||||
function forceLinting(view) {
|
||||
let plugin = view.plugin(lintPlugin);
|
||||
if (plugin)
|
||||
plugin.force();
|
||||
}
|
||||
function assignKeys(actions) {
|
||||
let assigned = [];
|
||||
if (actions)
|
||||
actions: for (let { name } of actions) {
|
||||
for (let i = 0; i < name.length; i++) {
|
||||
let ch = name[i];
|
||||
if (/[a-zA-Z]/.test(ch) && !assigned.some(c => c.toLowerCase() == ch.toLowerCase())) {
|
||||
assigned.push(ch);
|
||||
continue actions;
|
||||
}
|
||||
}
|
||||
assigned.push("");
|
||||
}
|
||||
return assigned;
|
||||
}
|
||||
function renderDiagnostic(view, diagnostic, inPanel) {
|
||||
var _a;
|
||||
let keys = inPanel ? assignKeys(diagnostic.actions) : [];
|
||||
return elt("li", { class: "cm-diagnostic cm-diagnostic-" + diagnostic.severity }, elt("span", { class: "cm-diagnosticText" }, diagnostic.renderMessage ? diagnostic.renderMessage(view) : diagnostic.message), (_a = diagnostic.actions) === null || _a === void 0 ? void 0 : _a.map((action, i) => {
|
||||
let fired = false, click = (e) => {
|
||||
e.preventDefault();
|
||||
if (fired)
|
||||
return;
|
||||
fired = true;
|
||||
let found = findDiagnostic(view.state.field(lintState).diagnostics, diagnostic);
|
||||
if (found)
|
||||
action.apply(view, found.from, found.to);
|
||||
};
|
||||
let { name } = action, keyIndex = keys[i] ? name.indexOf(keys[i]) : -1;
|
||||
let nameElt = keyIndex < 0 ? name : [name.slice(0, keyIndex),
|
||||
elt("u", name.slice(keyIndex, keyIndex + 1)),
|
||||
name.slice(keyIndex + 1)];
|
||||
let markClass = action.markClass ? " " + action.markClass : "";
|
||||
return elt("button", {
|
||||
type: "button",
|
||||
class: "cm-diagnosticAction" + markClass,
|
||||
onclick: click,
|
||||
onmousedown: click,
|
||||
"aria-label": ` Action: ${name}${keyIndex < 0 ? "" : ` (access key "${keys[i]})"`}.`
|
||||
}, nameElt);
|
||||
}), diagnostic.source && elt("div", { class: "cm-diagnosticSource" }, diagnostic.source));
|
||||
}
|
||||
class DiagnosticWidget extends WidgetType {
|
||||
constructor(sev) {
|
||||
super();
|
||||
this.sev = sev;
|
||||
}
|
||||
eq(other) { return other.sev == this.sev; }
|
||||
toDOM() {
|
||||
return elt("span", { class: "cm-lintPoint cm-lintPoint-" + this.sev });
|
||||
}
|
||||
}
|
||||
class PanelItem {
|
||||
constructor(view, diagnostic) {
|
||||
this.diagnostic = diagnostic;
|
||||
this.id = "item_" + Math.floor(Math.random() * 0xffffffff).toString(16);
|
||||
this.dom = renderDiagnostic(view, diagnostic, true);
|
||||
this.dom.id = this.id;
|
||||
this.dom.setAttribute("role", "option");
|
||||
}
|
||||
}
|
||||
class LintPanel {
|
||||
constructor(view) {
|
||||
this.view = view;
|
||||
this.items = [];
|
||||
let onkeydown = (event) => {
|
||||
if (event.keyCode == 27) { // Escape
|
||||
closeLintPanel(this.view);
|
||||
this.view.focus();
|
||||
}
|
||||
else if (event.keyCode == 38 || event.keyCode == 33) { // ArrowUp, PageUp
|
||||
this.moveSelection((this.selectedIndex - 1 + this.items.length) % this.items.length);
|
||||
}
|
||||
else if (event.keyCode == 40 || event.keyCode == 34) { // ArrowDown, PageDown
|
||||
this.moveSelection((this.selectedIndex + 1) % this.items.length);
|
||||
}
|
||||
else if (event.keyCode == 36) { // Home
|
||||
this.moveSelection(0);
|
||||
}
|
||||
else if (event.keyCode == 35) { // End
|
||||
this.moveSelection(this.items.length - 1);
|
||||
}
|
||||
else if (event.keyCode == 13) { // Enter
|
||||
this.view.focus();
|
||||
}
|
||||
else if (event.keyCode >= 65 && event.keyCode <= 90 && this.selectedIndex >= 0) { // A-Z
|
||||
let { diagnostic } = this.items[this.selectedIndex], keys = assignKeys(diagnostic.actions);
|
||||
for (let i = 0; i < keys.length; i++)
|
||||
if (keys[i].toUpperCase().charCodeAt(0) == event.keyCode) {
|
||||
let found = findDiagnostic(this.view.state.field(lintState).diagnostics, diagnostic);
|
||||
if (found)
|
||||
diagnostic.actions[i].apply(view, found.from, found.to);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
};
|
||||
let onclick = (event) => {
|
||||
for (let i = 0; i < this.items.length; i++) {
|
||||
if (this.items[i].dom.contains(event.target))
|
||||
this.moveSelection(i);
|
||||
}
|
||||
};
|
||||
this.list = elt("ul", {
|
||||
tabIndex: 0,
|
||||
role: "listbox",
|
||||
"aria-label": this.view.state.phrase("Diagnostics"),
|
||||
onkeydown,
|
||||
onclick
|
||||
});
|
||||
this.dom = elt("div", { class: "cm-panel-lint" }, this.list, elt("button", {
|
||||
type: "button",
|
||||
name: "close",
|
||||
"aria-label": this.view.state.phrase("close"),
|
||||
onclick: () => closeLintPanel(this.view)
|
||||
}, "×"));
|
||||
this.update();
|
||||
}
|
||||
get selectedIndex() {
|
||||
let selected = this.view.state.field(lintState).selected;
|
||||
if (!selected)
|
||||
return -1;
|
||||
for (let i = 0; i < this.items.length; i++)
|
||||
if (this.items[i].diagnostic == selected.diagnostic)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
update() {
|
||||
let { diagnostics, selected } = this.view.state.field(lintState);
|
||||
let i = 0, needsSync = false, newSelectedItem = null;
|
||||
let seen = new Set();
|
||||
diagnostics.between(0, this.view.state.doc.length, (_start, _end, { spec }) => {
|
||||
for (let diagnostic of spec.diagnostics) {
|
||||
if (seen.has(diagnostic))
|
||||
continue;
|
||||
seen.add(diagnostic);
|
||||
let found = -1, item;
|
||||
for (let j = i; j < this.items.length; j++)
|
||||
if (this.items[j].diagnostic == diagnostic) {
|
||||
found = j;
|
||||
break;
|
||||
}
|
||||
if (found < 0) {
|
||||
item = new PanelItem(this.view, diagnostic);
|
||||
this.items.splice(i, 0, item);
|
||||
needsSync = true;
|
||||
}
|
||||
else {
|
||||
item = this.items[found];
|
||||
if (found > i) {
|
||||
this.items.splice(i, found - i);
|
||||
needsSync = true;
|
||||
}
|
||||
}
|
||||
if (selected && item.diagnostic == selected.diagnostic) {
|
||||
if (!item.dom.hasAttribute("aria-selected")) {
|
||||
item.dom.setAttribute("aria-selected", "true");
|
||||
newSelectedItem = item;
|
||||
}
|
||||
}
|
||||
else if (item.dom.hasAttribute("aria-selected")) {
|
||||
item.dom.removeAttribute("aria-selected");
|
||||
}
|
||||
i++;
|
||||
}
|
||||
});
|
||||
while (i < this.items.length && !(this.items.length == 1 && this.items[0].diagnostic.from < 0)) {
|
||||
needsSync = true;
|
||||
this.items.pop();
|
||||
}
|
||||
if (this.items.length == 0) {
|
||||
this.items.push(new PanelItem(this.view, {
|
||||
from: -1, to: -1,
|
||||
severity: "info",
|
||||
message: this.view.state.phrase("No diagnostics")
|
||||
}));
|
||||
needsSync = true;
|
||||
}
|
||||
if (newSelectedItem) {
|
||||
this.list.setAttribute("aria-activedescendant", newSelectedItem.id);
|
||||
this.view.requestMeasure({
|
||||
key: this,
|
||||
read: () => ({ sel: newSelectedItem.dom.getBoundingClientRect(), panel: this.list.getBoundingClientRect() }),
|
||||
write: ({ sel, panel }) => {
|
||||
let scaleY = panel.height / this.list.offsetHeight;
|
||||
if (sel.top < panel.top)
|
||||
this.list.scrollTop -= (panel.top - sel.top) / scaleY;
|
||||
else if (sel.bottom > panel.bottom)
|
||||
this.list.scrollTop += (sel.bottom - panel.bottom) / scaleY;
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (this.selectedIndex < 0) {
|
||||
this.list.removeAttribute("aria-activedescendant");
|
||||
}
|
||||
if (needsSync)
|
||||
this.sync();
|
||||
}
|
||||
sync() {
|
||||
let domPos = this.list.firstChild;
|
||||
function rm() {
|
||||
let prev = domPos;
|
||||
domPos = prev.nextSibling;
|
||||
prev.remove();
|
||||
}
|
||||
for (let item of this.items) {
|
||||
if (item.dom.parentNode == this.list) {
|
||||
while (domPos != item.dom)
|
||||
rm();
|
||||
domPos = item.dom.nextSibling;
|
||||
}
|
||||
else {
|
||||
this.list.insertBefore(item.dom, domPos);
|
||||
}
|
||||
}
|
||||
while (domPos)
|
||||
rm();
|
||||
}
|
||||
moveSelection(selectedIndex) {
|
||||
if (this.selectedIndex < 0)
|
||||
return;
|
||||
let field = this.view.state.field(lintState);
|
||||
let selection = findDiagnostic(field.diagnostics, this.items[selectedIndex].diagnostic);
|
||||
if (!selection)
|
||||
return;
|
||||
this.view.dispatch({
|
||||
selection: { anchor: selection.from, head: selection.to },
|
||||
scrollIntoView: true,
|
||||
effects: movePanelSelection.of(selection)
|
||||
});
|
||||
}
|
||||
static open(view) { return new LintPanel(view); }
|
||||
}
|
||||
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) {
|
||||
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"`);
|
||||
}
|
||||
const baseTheme = /*@__PURE__*/EditorView.baseTheme({
|
||||
".cm-diagnostic": {
|
||||
padding: "3px 6px 3px 8px",
|
||||
marginLeft: "-1px",
|
||||
display: "block",
|
||||
whiteSpace: "pre-wrap"
|
||||
},
|
||||
".cm-diagnostic-error": { borderLeft: "5px solid #d11" },
|
||||
".cm-diagnostic-warning": { borderLeft: "5px solid orange" },
|
||||
".cm-diagnostic-info": { borderLeft: "5px solid #999" },
|
||||
".cm-diagnostic-hint": { borderLeft: "5px solid #66d" },
|
||||
".cm-diagnosticAction": {
|
||||
font: "inherit",
|
||||
border: "none",
|
||||
padding: "2px 4px",
|
||||
backgroundColor: "#444",
|
||||
color: "white",
|
||||
borderRadius: "3px",
|
||||
marginLeft: "8px",
|
||||
cursor: "pointer"
|
||||
},
|
||||
".cm-diagnosticSource": {
|
||||
fontSize: "70%",
|
||||
opacity: .7
|
||||
},
|
||||
".cm-lintRange": {
|
||||
backgroundPosition: "left bottom",
|
||||
backgroundRepeat: "repeat-x",
|
||||
paddingBottom: "0.7px",
|
||||
},
|
||||
".cm-lintRange-error": { backgroundImage: /*@__PURE__*/underline("#d11") },
|
||||
".cm-lintRange-warning": { backgroundImage: /*@__PURE__*/underline("orange") },
|
||||
".cm-lintRange-info": { backgroundImage: /*@__PURE__*/underline("#999") },
|
||||
".cm-lintRange-hint": { backgroundImage: /*@__PURE__*/underline("#66d") },
|
||||
".cm-lintRange-active": { backgroundColor: "#ffdd9980" },
|
||||
".cm-tooltip-lint": {
|
||||
padding: 0,
|
||||
margin: 0
|
||||
},
|
||||
".cm-lintPoint": {
|
||||
position: "relative",
|
||||
"&:after": {
|
||||
content: '""',
|
||||
position: "absolute",
|
||||
bottom: 0,
|
||||
left: "-2px",
|
||||
borderLeft: "3px solid transparent",
|
||||
borderRight: "3px solid transparent",
|
||||
borderBottom: "4px solid #d11"
|
||||
}
|
||||
},
|
||||
".cm-lintPoint-warning": {
|
||||
"&:after": { borderBottomColor: "orange" }
|
||||
},
|
||||
".cm-lintPoint-info": {
|
||||
"&:after": { borderBottomColor: "#999" }
|
||||
},
|
||||
".cm-lintPoint-hint": {
|
||||
"&:after": { borderBottomColor: "#66d" }
|
||||
},
|
||||
".cm-panel.cm-panel-lint": {
|
||||
position: "relative",
|
||||
"& ul": {
|
||||
maxHeight: "100px",
|
||||
overflowY: "auto",
|
||||
"& [aria-selected]": {
|
||||
backgroundColor: "#ddd",
|
||||
"& u": { textDecoration: "underline" }
|
||||
},
|
||||
"&:focus [aria-selected]": {
|
||||
background_fallback: "#bdf",
|
||||
backgroundColor: "Highlight",
|
||||
color_fallback: "white",
|
||||
color: "HighlightText"
|
||||
},
|
||||
"& u": { textDecoration: "none" },
|
||||
padding: 0,
|
||||
margin: 0
|
||||
},
|
||||
"& [name=close]": {
|
||||
position: "absolute",
|
||||
top: "0",
|
||||
right: "2px",
|
||||
background: "inherit",
|
||||
border: "none",
|
||||
font: "inherit",
|
||||
padding: 0,
|
||||
margin: 0
|
||||
}
|
||||
}
|
||||
});
|
||||
function severityWeight(sev) {
|
||||
return sev == "error" ? 4 : sev == "warning" ? 3 : sev == "info" ? 2 : 1;
|
||||
}
|
||||
function maxSeverity(diagnostics) {
|
||||
let sev = "hint", weight = 1;
|
||||
for (let d of diagnostics) {
|
||||
let w = severityWeight(d.severity);
|
||||
if (w > weight) {
|
||||
weight = w;
|
||||
sev = d.severity;
|
||||
}
|
||||
}
|
||||
return sev;
|
||||
}
|
||||
class LintGutterMarker extends GutterMarker {
|
||||
constructor(diagnostics) {
|
||||
super();
|
||||
this.diagnostics = diagnostics;
|
||||
this.severity = maxSeverity(diagnostics);
|
||||
}
|
||||
toDOM(view) {
|
||||
let elt = document.createElement("div");
|
||||
elt.className = "cm-lint-marker cm-lint-marker-" + this.severity;
|
||||
let diagnostics = this.diagnostics;
|
||||
let diagnosticsFilter = view.state.facet(lintGutterConfig).tooltipFilter;
|
||||
if (diagnosticsFilter)
|
||||
diagnostics = diagnosticsFilter(diagnostics, view.state);
|
||||
if (diagnostics.length)
|
||||
elt.onmouseover = () => gutterMarkerMouseOver(view, elt, diagnostics);
|
||||
return elt;
|
||||
}
|
||||
}
|
||||
function trackHoverOn(view, marker) {
|
||||
let mousemove = (event) => {
|
||||
let rect = marker.getBoundingClientRect();
|
||||
if (event.clientX > rect.left - 10 /* Hover.Margin */ && event.clientX < rect.right + 10 /* Hover.Margin */ &&
|
||||
event.clientY > rect.top - 10 /* Hover.Margin */ && event.clientY < rect.bottom + 10 /* Hover.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.elementAtHeight(marker.getBoundingClientRect().top + 5 - view.documentTop);
|
||||
const linePos = view.coordsAtPos(line.from);
|
||||
if (linePos) {
|
||||
view.dispatch({ effects: setLintGutterTooltip.of({
|
||||
pos: line.from,
|
||||
above: false,
|
||||
clip: false,
|
||||
create() {
|
||||
return {
|
||||
dom: diagnosticsTooltip(view, diagnostics),
|
||||
getCoords: () => marker.getBoundingClientRect()
|
||||
};
|
||||
}
|
||||
}) });
|
||||
}
|
||||
marker.onmouseout = marker.onmousemove = null;
|
||||
trackHoverOn(view, marker);
|
||||
}
|
||||
let { hoverTime } = view.state.facet(lintGutterConfig);
|
||||
let hoverTimeout = setTimeout(hovered, hoverTime);
|
||||
marker.onmouseout = () => {
|
||||
clearTimeout(hoverTimeout);
|
||||
marker.onmouseout = marker.onmousemove = null;
|
||||
};
|
||||
marker.onmousemove = () => {
|
||||
clearTimeout(hoverTimeout);
|
||||
hoverTimeout = setTimeout(hovered, hoverTime);
|
||||
};
|
||||
}
|
||||
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),
|
||||
widgetMarker: (view, widget, block) => {
|
||||
let diagnostics = [];
|
||||
view.state.field(lintGutterMarkers).between(block.from, block.to, (from, to, value) => {
|
||||
if (from > block.from && from < block.to)
|
||||
diagnostics.push(...value.diagnostics);
|
||||
});
|
||||
return diagnostics.length ? new LintGutterMarker(diagnostics) : null;
|
||||
}
|
||||
});
|
||||
const lintGutterMarkers = /*@__PURE__*/StateField.define({
|
||||
create() {
|
||||
return RangeSet.empty;
|
||||
},
|
||||
update(markers, tr) {
|
||||
markers = markers.map(tr.changes);
|
||||
let diagnosticFilter = tr.state.facet(lintGutterConfig).markerFilter;
|
||||
for (let effect of tr.effects) {
|
||||
if (effect.is(setDiagnosticsEffect)) {
|
||||
let diagnostics = effect.value;
|
||||
if (diagnosticFilter)
|
||||
diagnostics = diagnosticFilter(diagnostics || [], tr.state);
|
||||
markers = markersForDiagnostics(tr.state.doc, diagnostics.slice(0));
|
||||
}
|
||||
}
|
||||
return markers;
|
||||
}
|
||||
});
|
||||
const setLintGutterTooltip = /*@__PURE__*/StateEffect.define();
|
||||
const lintGutterTooltip = /*@__PURE__*/StateField.define({
|
||||
create() { return null; },
|
||||
update(tooltip, tr) {
|
||||
if (tooltip && tr.docChanged)
|
||||
tooltip = hideTooltip(tr, tooltip) ? null : { ...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: ".2em"
|
||||
}
|
||||
},
|
||||
".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": {
|
||||
content: /*@__PURE__*/svg(`<circle cx="20" cy="20" r="15" fill="#f87" stroke="#f43" stroke-width="6"/>`)
|
||||
},
|
||||
});
|
||||
const lintExtensions = [
|
||||
lintState,
|
||||
/*@__PURE__*/EditorView.decorations.compute([lintState], state => {
|
||||
let { selected, panel } = state.field(lintState);
|
||||
return !selected || !panel || selected.from == selected.to ? Decoration.none : Decoration.set([
|
||||
activeMark.range(selected.from, selected.to)
|
||||
]);
|
||||
}),
|
||||
/*@__PURE__*/hoverTooltip(lintTooltip, { hideOn: hideTooltip }),
|
||||
baseTheme
|
||||
];
|
||||
const lintGutterConfig = /*@__PURE__*/Facet.define({
|
||||
combine(configs) {
|
||||
return combineConfig(configs, {
|
||||
hoverTime: 300 /* Hover.Time */,
|
||||
markerFilter: null,
|
||||
tooltipFilter: null
|
||||
});
|
||||
}
|
||||
});
|
||||
/**
|
||||
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(config = {}) {
|
||||
return [lintGutterConfig.of(config), lintGutterMarkers, lintGutterExtension, lintGutterTheme, lintGutterTooltip];
|
||||
}
|
||||
/**
|
||||
Iterate over the marked diagnostics for the given editor state,
|
||||
calling `f` for each of them. Note that, if the document changed
|
||||
since the diagnostics were created, the `Diagnostic` object will
|
||||
hold the original outdated position, whereas the `to` and `from`
|
||||
arguments hold the diagnostic's current position.
|
||||
*/
|
||||
function forEachDiagnostic(state, f) {
|
||||
let lState = state.field(lintState, false);
|
||||
if (lState && lState.diagnostics.size) {
|
||||
let pending = [], pendingStart = [], lastEnd = -1;
|
||||
for (let iter = RangeSet.iter([lState.diagnostics]);; iter.next()) {
|
||||
for (let i = 0; i < pending.length; i++)
|
||||
if (!iter.value || iter.value.spec.diagnostics.indexOf(pending[i]) < 0) {
|
||||
f(pending[i], pendingStart[i], lastEnd);
|
||||
pending.splice(i, 1);
|
||||
pendingStart.splice(i--, 1);
|
||||
}
|
||||
if (!iter.value)
|
||||
break;
|
||||
for (let d of iter.value.spec.diagnostics)
|
||||
if (pending.indexOf(d) < 0) {
|
||||
pending.push(d);
|
||||
pendingStart.push(iter.from);
|
||||
}
|
||||
lastEnd = iter.to;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { closeLintPanel, diagnosticCount, forEachDiagnostic, forceLinting, lintGutter, lintKeymap, linter, nextDiagnostic, openLintPanel, previousDiagnostic, setDiagnostics, setDiagnosticsEffect };
|
||||
40
frontend/node_modules/@codemirror/lint/package.json
generated
vendored
Normal file
40
frontend/node_modules/@codemirror/lint/package.json
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "@codemirror/lint",
|
||||
"version": "6.9.2",
|
||||
"description": "Linting support for the CodeMirror code editor",
|
||||
"scripts": {
|
||||
"test": "cm-runtests",
|
||||
"prepare": "cm-buildhelper src/lint.ts"
|
||||
},
|
||||
"keywords": [
|
||||
"editor",
|
||||
"code"
|
||||
],
|
||||
"author": {
|
||||
"name": "Marijn Haverbeke",
|
||||
"email": "marijn@haverbeke.berlin",
|
||||
"url": "http://marijnhaverbeke.nl"
|
||||
},
|
||||
"type": "module",
|
||||
"main": "dist/index.cjs",
|
||||
"exports": {
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.cjs"
|
||||
},
|
||||
"types": "dist/index.d.ts",
|
||||
"module": "dist/index.js",
|
||||
"sideEffects": false,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.35.0",
|
||||
"crelt": "^1.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@codemirror/buildhelper": "^1.0.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/codemirror/lint.git"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user