release: prepare WRNexusJS 0.8.8
This commit is contained in:
@@ -6,10 +6,15 @@ import {
|
||||
documentSymbols,
|
||||
formatDocument,
|
||||
hover,
|
||||
symbolLocations,
|
||||
offsetAt,
|
||||
positionAt,
|
||||
semanticTokens,
|
||||
semanticTokensLegend,
|
||||
wordAt,
|
||||
virtualTypeScriptDocument,
|
||||
workspaceCompletionItems,
|
||||
workspaceSymbolLocations,
|
||||
clearWorkspaceIndexCache,
|
||||
extractComponentRefactor,
|
||||
htmlToWrn,
|
||||
type TextDocument,
|
||||
@@ -47,9 +52,9 @@ function internalDiagnostic(document: TextDocument, error: unknown) {
|
||||
};
|
||||
}
|
||||
|
||||
function safeDocumentDiagnostics(document: TextDocument) {
|
||||
function safeDocumentDiagnostics(document: TextDocument, includeTypes = true) {
|
||||
try {
|
||||
return documentDiagnostics(document);
|
||||
return documentDiagnostics(document, { includeTypes });
|
||||
} catch (error) {
|
||||
process.stderr.write(
|
||||
`[wrnexus-lsp] diagnostics failed for ${document.uri}: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}\n`,
|
||||
@@ -58,11 +63,11 @@ function safeDocumentDiagnostics(document: TextDocument) {
|
||||
}
|
||||
}
|
||||
|
||||
function publish(document: TextDocument): void {
|
||||
function publish(document: TextDocument, includeTypes = true): void {
|
||||
send({
|
||||
jsonrpc: "2.0",
|
||||
method: "textDocument/publishDiagnostics",
|
||||
params: { uri: document.uri, diagnostics: safeDocumentDiagnostics(document) },
|
||||
params: { uri: document.uri, diagnostics: safeDocumentDiagnostics(document, includeTypes) },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -80,7 +85,7 @@ function schedulePublish(document: TextDocument): void {
|
||||
setTimeout(() => {
|
||||
diagnosticTimers.delete(document.uri);
|
||||
const current = documents.get(document.uri);
|
||||
if (current && current.version === expectedVersion) publish(current);
|
||||
if (current && current.version === expectedVersion) publish(current, false);
|
||||
}, DIAGNOSTIC_DEBOUNCE_MS),
|
||||
);
|
||||
}
|
||||
@@ -104,7 +109,7 @@ async function handle(message: JsonRpc): Promise<void> {
|
||||
result(message.id, {
|
||||
serverInfo: { name: "WRNexus Language Server", version: "0.8.3" },
|
||||
capabilities: {
|
||||
textDocumentSync: 1,
|
||||
textDocumentSync: { openClose: true, change: 1, save: true },
|
||||
documentFormattingProvider: true,
|
||||
completionProvider: { triggerCharacters: ["<", "@", ":", "."] },
|
||||
hoverProvider: true,
|
||||
@@ -112,8 +117,14 @@ async function handle(message: JsonRpc): Promise<void> {
|
||||
referencesProvider: true,
|
||||
renameProvider: { prepareProvider: true },
|
||||
documentSymbolProvider: true,
|
||||
semanticTokensProvider: { legend: semanticTokensLegend, full: true },
|
||||
codeActionProvider: {
|
||||
codeActionKinds: ["quickfix", "refactor.extract", "refactor.rewrite"],
|
||||
codeActionKinds: [
|
||||
"quickfix",
|
||||
"refactor.extract",
|
||||
"refactor.rewrite",
|
||||
"source.organizeImports",
|
||||
],
|
||||
},
|
||||
experimental: { wrnexusVirtualTypeScript: true },
|
||||
},
|
||||
@@ -127,6 +138,9 @@ async function handle(message: JsonRpc): Promise<void> {
|
||||
result(message.id, null);
|
||||
break;
|
||||
case "exit":
|
||||
for (const timer of diagnosticTimers.values()) clearTimeout(timer);
|
||||
diagnosticTimers.clear();
|
||||
documents.clear();
|
||||
process.exit(0);
|
||||
break;
|
||||
case "textDocument/didOpen": {
|
||||
@@ -146,6 +160,15 @@ async function handle(message: JsonRpc): Promise<void> {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "textDocument/didSave": {
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (document) {
|
||||
clearDiagnosticTimer(document.uri);
|
||||
clearWorkspaceIndexCache(workspaceRoot);
|
||||
publish(document, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "textDocument/didClose":
|
||||
clearDiagnosticTimer(params.textDocument.uri);
|
||||
documents.delete(params.textDocument.uri);
|
||||
@@ -180,6 +203,11 @@ async function handle(message: JsonRpc): Promise<void> {
|
||||
result(message.id, document ? documentSymbols(document) : []);
|
||||
break;
|
||||
}
|
||||
case "textDocument/semanticTokens/full": {
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
result(message.id, document ? semanticTokens(document) : { data: [] });
|
||||
break;
|
||||
}
|
||||
case "textDocument/hover": {
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
result(message.id, document ? hover(document, params.position) : null);
|
||||
@@ -192,7 +220,8 @@ async function handle(message: JsonRpc): Promise<void> {
|
||||
}
|
||||
case "textDocument/references": {
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
result(message.id, document ? symbolLocations(document, params.position) : []);
|
||||
const selected = document ? wordAt(document.text, params.position) : null;
|
||||
result(message.id, selected ? workspaceSymbolLocations(workspaceRoot, selected.word) : []);
|
||||
break;
|
||||
}
|
||||
case "textDocument/prepareRename": {
|
||||
@@ -202,13 +231,16 @@ async function handle(message: JsonRpc): Promise<void> {
|
||||
}
|
||||
case "textDocument/rename": {
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
const edits = document
|
||||
? symbolLocations(document, params.position).map(({ range }) => ({
|
||||
range,
|
||||
newText: params.newName,
|
||||
}))
|
||||
: [];
|
||||
result(message.id, document ? { changes: { [document.uri]: edits } } : null);
|
||||
const selected = document ? wordAt(document.text, params.position) : null;
|
||||
if (!selected || !/^[A-Za-z_$][\w$]*$/.test(params.newName)) {
|
||||
result(message.id, null);
|
||||
break;
|
||||
}
|
||||
const changes: Record<string, Array<{ range: any; newText: string }>> = {};
|
||||
for (const location of workspaceSymbolLocations(workspaceRoot, selected.word)) {
|
||||
(changes[location.uri] ??= []).push({ range: location.range, newText: params.newName });
|
||||
}
|
||||
result(message.id, { changes });
|
||||
break;
|
||||
}
|
||||
case "textDocument/codeAction": {
|
||||
@@ -231,6 +263,71 @@ async function handle(message: JsonRpc): Promise<void> {
|
||||
},
|
||||
},
|
||||
}));
|
||||
const imports = [
|
||||
...document.text.matchAll(
|
||||
/^import(?:\s+type)?[\s\S]*?(?:;\s*|\n(?=import|\s*(?:page|component|layout|global\s+store|page\s+store)\b))/gm,
|
||||
),
|
||||
].map((match) => ({
|
||||
start: match.index!,
|
||||
end: match.index! + match[0].length,
|
||||
text: match[0].trim(),
|
||||
}));
|
||||
if (imports.length > 1) {
|
||||
const ordered = [...imports].sort((a, b) => {
|
||||
const aType = /^import\s+type\b/.test(a.text) ? 0 : 1;
|
||||
const bType = /^import\s+type\b/.test(b.text) ? 0 : 1;
|
||||
return aType - bType || a.text.localeCompare(b.text);
|
||||
});
|
||||
const replacement = ordered.map((entry) => entry.text.replace(/;$/, "")).join("\n") + "\n";
|
||||
const start = imports[0]!.start;
|
||||
const end = imports.at(-1)!.end;
|
||||
if (document.text.slice(start, end) !== replacement) {
|
||||
actions.push({
|
||||
title: "Organize WRN imports",
|
||||
kind: "source.organizeImports",
|
||||
edit: {
|
||||
changes: {
|
||||
[document.uri]: [
|
||||
{
|
||||
range: {
|
||||
start: positionAt(document.text, start),
|
||||
end: positionAt(document.text, end),
|
||||
},
|
||||
newText: replacement,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const diagnostic of params.context?.diagnostics ?? []) {
|
||||
if (diagnostic.code !== "WRN-OUTPUT-LEGACY-EMIT") continue;
|
||||
const start = offsetAt(document.text, diagnostic.range.start);
|
||||
const end = offsetAt(document.text, diagnostic.range.end);
|
||||
const source = document.text.slice(start, end);
|
||||
const match = /\$emit\(\s*["']([A-Za-z_$][\w$]*)["']\s*,?/.exec(source);
|
||||
if (!match) continue;
|
||||
actions.push({
|
||||
title: `Convert $emit to output.${match[1]}`,
|
||||
kind: "quickfix",
|
||||
diagnostics: [diagnostic],
|
||||
isPreferred: true,
|
||||
edit: {
|
||||
changes: {
|
||||
[document.uri]: [
|
||||
{
|
||||
range: diagnostic.range,
|
||||
newText: source.replace(
|
||||
/\$emit\(\s*["'][A-Za-z_$][\w$]*["']\s*,?\s*/,
|
||||
`output.${match[1]}(`,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
const selected = document.text.slice(
|
||||
document.text
|
||||
.split(/\r?\n/)
|
||||
|
||||
Reference in New Issue
Block a user