[add] diags from server
This commit is contained in:
parent
5c759eb3f4
commit
2078055d05
8 changed files with 150 additions and 15 deletions
|
@ -1,6 +1,6 @@
|
||||||
import { EditorState } from '@codemirror/state';
|
import { EditorState } from '@codemirror/state';
|
||||||
import { openSearchPanel, highlightSelectionMatches,searchKeymap } from '@codemirror/search';
|
import { openSearchPanel, highlightSelectionMatches,searchKeymap } from '@codemirror/search';
|
||||||
import {openLintPanel, lintGutter, lintKeymap} from "@codemirror/lint"
|
import {openLintPanel, lintGutter, lintKeymap,linter,setDiagnostics} from "@codemirror/lint"
|
||||||
import { indentWithTab, history, defaultKeymap, historyKeymap } from '@codemirror/commands';
|
import { indentWithTab, history, defaultKeymap, historyKeymap } from '@codemirror/commands';
|
||||||
import { foldGutter, indentOnInput, indentUnit, bracketMatching, foldKeymap, syntaxHighlighting, defaultHighlightStyle } from '@codemirror/language';
|
import { foldGutter, indentOnInput, indentUnit, bracketMatching, foldKeymap, syntaxHighlighting, defaultHighlightStyle } from '@codemirror/language';
|
||||||
import { closeBrackets, autocompletion, closeBracketsKeymap, completionKeymap } from '@codemirror/autocomplete';
|
import { closeBrackets, autocompletion, closeBracketsKeymap, completionKeymap } from '@codemirror/autocomplete';
|
||||||
|
@ -8,7 +8,7 @@ import { lineNumbers, highlightActiveLineGutter, highlightSpecialChars, drawSele
|
||||||
import {markdown} from "@codemirror/lang-markdown";
|
import {markdown} from "@codemirror/lang-markdown";
|
||||||
// Theme
|
// Theme
|
||||||
import { oneDark } from "@codemirror/theme-one-dark";
|
import { oneDark } from "@codemirror/theme-one-dark";
|
||||||
import { LSPClient, languageServerSupport } from "@codemirror/lsp-client";
|
import { LSPClient, LSPPlugin,languageServerSupport } from "@codemirror/lsp-client";
|
||||||
// Language
|
// Language
|
||||||
import { xml } from "@codemirror/lang-xml";
|
import { xml } from "@codemirror/lang-xml";
|
||||||
|
|
||||||
|
@ -81,4 +81,5 @@ function createEditorView(state, parent) {
|
||||||
return new EditorView({ state, parent });
|
return new EditorView({ state, parent });
|
||||||
}
|
}
|
||||||
|
|
||||||
export { createEditorState, createEditorView, openSearchPanel,openLintPanel, languageServerSupport, baseExts ,client, simpleWebSocketTransport};
|
|
||||||
|
export { createEditorState, createEditorView, openSearchPanel,openLintPanel, languageServerSupport, baseExts ,client, simpleWebSocketTransport,linter, LSPPlugin,setDiagnostics};
|
|
@ -26,9 +26,8 @@ declare %unit:test function test:resolvePosition() {
|
||||||
(:~ convert all position. :)
|
(:~ convert all position. :)
|
||||||
declare %unit:test function test:toPosition() {
|
declare %unit:test function test:toPosition() {
|
||||||
let $text:=unparsed-text("sample.docs/pdfbox.xqm")
|
let $text:=unparsed-text("sample.docs/pdfbox.xqm")
|
||||||
let $nl:= index-of(string-to-codepoints($text),10)
|
|
||||||
return for $i in 0 to string-length($text)-1
|
return for $i in 0 to string-length($text)-1
|
||||||
let $pos:=pos:toPosition($text,$i,$nl)
|
let $pos:=pos:toPosition($text,$i)
|
||||||
return unit:assert-equals($i,pos:resolvePosition($text,$pos))
|
return unit:assert-equals($i,pos:resolvePosition($text,$pos))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ declare function lsp-diags:publish(
|
||||||
$xml as lsp-diags:ParseResult)
|
$xml as lsp-diags:ParseResult)
|
||||||
as map(*){
|
as map(*){
|
||||||
let $diagnostics:=if($xml/self::ERROR)
|
let $diagnostics:=if($xml/self::ERROR)
|
||||||
then [lsp-diags:parse-error($text, $xml)]
|
then array{lsp-diags:parse-error($text, $xml)}
|
||||||
else []
|
else []
|
||||||
|
|
||||||
return {"jsonrpc": "2.0",
|
return {"jsonrpc": "2.0",
|
||||||
|
@ -38,9 +38,13 @@ as map(*){
|
||||||
<ERROR b="10819" e="10820" o="234" s="43">syntax error, found '}' while expecting [S,'else'] at line 290, column 3: ...} }; ? return bookmark info for children of $outlineItem as s...</ERROR>
|
<ERROR b="10819" e="10820" o="234" s="43">syntax error, found '}' while expecting [S,'else'] at line 290, column 3: ...} }; ? return bookmark info for children of $outlineItem as s...</ERROR>
|
||||||
:)
|
:)
|
||||||
declare function lsp-diags:parse-error($text as xs:string, $xml as element(ERROR))
|
declare function lsp-diags:parse-error($text as xs:string, $xml as element(ERROR))
|
||||||
as map(*)?{
|
as map(*)*{
|
||||||
|
|
||||||
lsp-diags:nostic(pos:Range(pos:toPosition($text=>trace("EXML "), $xml/@b),
|
lsp-diags:nostic(pos:Range(pos:toPosition($text=>trace("EXML "), $xml/@b),
|
||||||
pos:toPosition($text, $xml/@e)),
|
pos:toPosition($text, $xml/@e)),
|
||||||
1,$xml/string())=>trace("EEEE ")
|
1,$xml/string())=>trace("EEEE "),
|
||||||
|
|
||||||
|
lsp-diags:nostic(pos:Range(pos:toPosition($text=>trace("EXML "), $xml/@e +1 ),
|
||||||
|
pos:toPosition($text, string-length($text)-1)),
|
||||||
|
1,"Previous parse error")
|
||||||
};
|
};
|
|
@ -3,13 +3,15 @@ text editing API
|
||||||
:)
|
:)
|
||||||
module namespace pos="lsp/position";
|
module namespace pos="lsp/position";
|
||||||
|
|
||||||
|
declare type pos:num as (xs:integer|xs:double);
|
||||||
|
|
||||||
(:~
|
(:~
|
||||||
@param line Line position in a document (zero-based).
|
@param line Line position in a document (zero-based).
|
||||||
@param character Character offset on a line in a document (zero-based).
|
@param character Character offset on a line in a document (zero-based).
|
||||||
:)
|
:)
|
||||||
declare record pos:Position(
|
declare record pos:Position(
|
||||||
line as xs:integer,
|
line as pos:num,
|
||||||
character as xs:integer
|
character as pos:num
|
||||||
);
|
);
|
||||||
|
|
||||||
(:~
|
(:~
|
||||||
|
@ -38,7 +40,7 @@ as xs:string{
|
||||||
|
|
||||||
(:~ find index from Position :)
|
(:~ find index from Position :)
|
||||||
declare function pos:resolvePosition($text as xs:string, $pos as pos:Position)
|
declare function pos:resolvePosition($text as xs:string, $pos as pos:Position)
|
||||||
as xs:integer
|
as pos:num
|
||||||
{
|
{
|
||||||
let $nl:= index-of(string-to-codepoints($text),10)
|
let $nl:= index-of(string-to-codepoints($text),10)
|
||||||
let $off:=if($pos?line eq 0)
|
let $off:=if($pos?line eq 0)
|
||||||
|
@ -49,7 +51,7 @@ as xs:integer
|
||||||
|
|
||||||
(:~ convert index into Position :)
|
(:~ convert index into Position :)
|
||||||
declare function pos:toPosition($text as xs:string,
|
declare function pos:toPosition($text as xs:string,
|
||||||
$index as xs:integer
|
$index as pos:num
|
||||||
)
|
)
|
||||||
as pos:Position {
|
as pos:Position {
|
||||||
let $nl:= index-of(string-to-codepoints($text),10)
|
let $nl:= index-of(string-to-codepoints($text),10)
|
||||||
|
@ -63,7 +65,7 @@ return pos:Position($line, $index - $off)
|
||||||
|
|
||||||
|
|
||||||
(:~ line number for $pos :)
|
(:~ line number for $pos :)
|
||||||
declare function pos:lineAt($nl as xs:integer*,$pos as xs:integer)
|
declare function pos:lineAt($nl as xs:integer*,$pos as pos:num)
|
||||||
as xs:integer
|
as xs:integer
|
||||||
{
|
{
|
||||||
if($pos le head($nl))
|
if($pos le head($nl))
|
||||||
|
|
|
@ -16730,6 +16730,16 @@ var lsp = (function (exports) {
|
||||||
return state.field(lintState, false) ? effects : effects.concat(StateEffect.appendConfig.of(lintExtensions));
|
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
|
The state effect that updates the set of active diagnostics. Can
|
||||||
be useful when writing an extension that needs to track these.
|
be useful when writing an extension that needs to track these.
|
||||||
*/
|
*/
|
||||||
|
@ -16846,6 +16856,65 @@ var lsp = (function (exports) {
|
||||||
{ key: "Mod-Shift-m", run: openLintPanel, preventDefault: true },
|
{ key: "Mod-Shift-m", run: openLintPanel, preventDefault: true },
|
||||||
{ key: "F8", run: nextDiagnostic }
|
{ 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({
|
const lintConfig = /*@__PURE__*/Facet.define({
|
||||||
combine(input) {
|
combine(input) {
|
||||||
return Object.assign({ sources: input.map(i => i.source).filter(x => x != null) }, combineConfig(input.map(i => i.config), {
|
return Object.assign({ sources: input.map(i => i.source).filter(x => x != null) }, combineConfig(input.map(i => i.config), {
|
||||||
|
@ -16859,6 +16928,19 @@ var lsp = (function (exports) {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
/**
|
||||||
|
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). If `null` is given as
|
||||||
|
source, this only configures the lint extension.
|
||||||
|
*/
|
||||||
|
function linter(source, config = {}) {
|
||||||
|
return [
|
||||||
|
lintConfig.of({ source, config }),
|
||||||
|
lintPlugin,
|
||||||
|
lintExtensions
|
||||||
|
];
|
||||||
|
}
|
||||||
function assignKeys(actions) {
|
function assignKeys(actions) {
|
||||||
let assigned = [];
|
let assigned = [];
|
||||||
if (actions)
|
if (actions)
|
||||||
|
@ -31357,13 +31439,16 @@ ${text}</tr>
|
||||||
return new EditorView({ state, parent });
|
return new EditorView({ state, parent });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.LSPPlugin = LSPPlugin;
|
||||||
exports.baseExts = baseExts;
|
exports.baseExts = baseExts;
|
||||||
exports.client = client;
|
exports.client = client;
|
||||||
exports.createEditorState = createEditorState;
|
exports.createEditorState = createEditorState;
|
||||||
exports.createEditorView = createEditorView;
|
exports.createEditorView = createEditorView;
|
||||||
exports.languageServerSupport = languageServerSupport;
|
exports.languageServerSupport = languageServerSupport;
|
||||||
|
exports.linter = linter;
|
||||||
exports.openLintPanel = openLintPanel;
|
exports.openLintPanel = openLintPanel;
|
||||||
exports.openSearchPanel = openSearchPanel;
|
exports.openSearchPanel = openSearchPanel;
|
||||||
|
exports.setDiagnostics = setDiagnostics;
|
||||||
exports.simpleWebSocketTransport = simpleWebSocketTransport;
|
exports.simpleWebSocketTransport = simpleWebSocketTransport;
|
||||||
|
|
||||||
return exports;
|
return exports;
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -65,6 +65,7 @@ function connect() {
|
||||||
const file = document.getElementById("iFile").value;
|
const file = document.getElementById("iFile").value;
|
||||||
lsp.simpleWebSocketTransport(server)
|
lsp.simpleWebSocketTransport(server)
|
||||||
.then(transport => {
|
.then(transport => {
|
||||||
|
transport.subscribe(incoming);
|
||||||
client = lsp.client(transport);
|
client = lsp.client(transport);
|
||||||
let plugin=lsp.languageServerSupport(client,file,"xquery")
|
let plugin=lsp.languageServerSupport(client,file,"xquery")
|
||||||
const doc = view.state.doc.toString();
|
const doc = view.state.doc.toString();
|
||||||
|
@ -73,3 +74,46 @@ function connect() {
|
||||||
})
|
})
|
||||||
.catch(r => alert("connection failed: "+ server));
|
.catch(r => alert("connection failed: "+ server));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function incoming(msg) {
|
||||||
|
const rpc=JSON.parse(msg)
|
||||||
|
switch (rpc.method) {
|
||||||
|
case "textDocument/publishDiagnostics":
|
||||||
|
diags(rpc.params);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
function diags(params){
|
||||||
|
console.log("--",params)
|
||||||
|
let plugin= lsp.LSPPlugin.get(view)
|
||||||
|
const diagnostics = params.diagnostics
|
||||||
|
.map(({ range, message, severity }) => ({
|
||||||
|
from: plugin.fromPosition( range.start,view.state.doc),
|
||||||
|
to: plugin.fromPosition( range.end,view.state.doc),
|
||||||
|
severity: 'error',
|
||||||
|
|
||||||
|
message,
|
||||||
|
}))
|
||||||
|
.filter(
|
||||||
|
({ from, to }) =>
|
||||||
|
from !== null &&
|
||||||
|
to !== null &&
|
||||||
|
from !== undefined &&
|
||||||
|
to !== undefined,
|
||||||
|
)
|
||||||
|
.sort((a, b) => {
|
||||||
|
switch (true) {
|
||||||
|
case a.from < b.from:
|
||||||
|
return -1;
|
||||||
|
case a.from > b.from:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
view.dispatch(lsp.setDiagnostics(view.state, diagnostics));
|
||||||
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue