feat(vscode): close HTML tags as they are typed in .wrn files
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// WRN editor extension source hash: c9938fa5f643ca435ead2c8dd5b545c6906c37c0024cf3ea788666236c28ddb6
|
||||
// WRN editor extension source hash: 7721e499707f7fb9b0b52fe428de3033cd624fa508322ec795960af7b6d96da5
|
||||
// WRN editor extension generator hash: 456d1d614e44e5fb1f19b784176c09cf2ade9b64ef73a17934c2698150b62728
|
||||
"use strict";
|
||||
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
||||
@@ -22718,6 +22718,29 @@ async function recoverWrnLanguage(document) {
|
||||
console.warn("[wrnexus] unable to recover .wrn language association:", error instanceof Error ? error.message : String(error));
|
||||
}
|
||||
}
|
||||
function registerAutoCloseTags(context, client2) {
|
||||
const listener = vscode.workspace.onDidChangeTextDocument(async (event) => {
|
||||
if (event.document.languageId !== "wrn")
|
||||
return;
|
||||
if (!vscode.workspace.getConfiguration("wrnexus.html").get("autoClosingTags", true))
|
||||
return;
|
||||
const change = event.contentChanges[0];
|
||||
if (!change || change.text !== ">" && change.text !== "/")
|
||||
return;
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (!editor || editor.document !== event.document)
|
||||
return;
|
||||
const position = change.range.start.translate(0, change.text.length);
|
||||
const snippet = await client2.sendRequest("wrn/tagComplete", {
|
||||
textDocument: { uri: event.document.uri.toString() },
|
||||
position: { line: position.line, character: position.character }
|
||||
});
|
||||
if (typeof snippet !== "string" || !snippet)
|
||||
return;
|
||||
await editor.insertSnippet(new vscode.SnippetString(snippet), position);
|
||||
});
|
||||
context.subscriptions.push(listener);
|
||||
}
|
||||
async function activate(context) {
|
||||
for (const document of vscode.workspace.textDocuments)
|
||||
recoverWrnLanguage(document);
|
||||
@@ -22730,6 +22753,7 @@ async function activate(context) {
|
||||
debug: { module: module2, transport: TransportKind.stdio, options: { execArgv: ["--nolazy"] } }
|
||||
}, { documentSelector: [{ scheme: "file", language: WRN_LANGUAGE_ID }] });
|
||||
await client.start();
|
||||
registerAutoCloseTags(context, client);
|
||||
}
|
||||
async function deactivate() {
|
||||
const running = client;
|
||||
@@ -22737,4 +22761,4 @@ async function deactivate() {
|
||||
if (running)
|
||||
await running.stop();
|
||||
}
|
||||
module.exports = { activate, deactivate, recoverWrnLanguage };
|
||||
module.exports = { activate, deactivate, recoverWrnLanguage, registerAutoCloseTags };
|
||||
|
||||
@@ -23,6 +23,37 @@ async function recoverWrnLanguage(document) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-close tags as they are typed.
|
||||
*
|
||||
* LSP has no request for this, so the client watches document changes and asks
|
||||
* the server whether the tag should close. The server owns the decision because
|
||||
* void elements and already-closed tags must not be closed.
|
||||
*/
|
||||
function registerAutoCloseTags(context, client) {
|
||||
const listener = vscode.workspace.onDidChangeTextDocument(async (event) => {
|
||||
if (event.document.languageId !== "wrn") return;
|
||||
if (!vscode.workspace.getConfiguration("wrnexus.html").get("autoClosingTags", true)) return;
|
||||
|
||||
const change = event.contentChanges[0];
|
||||
if (!change || (change.text !== ">" && change.text !== "/")) return;
|
||||
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (!editor || editor.document !== event.document) return;
|
||||
|
||||
const position = change.range.start.translate(0, change.text.length);
|
||||
const snippet = await client.sendRequest("wrn/tagComplete", {
|
||||
textDocument: { uri: event.document.uri.toString() },
|
||||
position: { line: position.line, character: position.character },
|
||||
});
|
||||
if (typeof snippet !== "string" || !snippet) return;
|
||||
|
||||
await editor.insertSnippet(new vscode.SnippetString(snippet), position);
|
||||
});
|
||||
|
||||
context.subscriptions.push(listener);
|
||||
}
|
||||
|
||||
/** @param {vscode.ExtensionContext} context */
|
||||
async function activate(context) {
|
||||
for (const document of vscode.workspace.textDocuments) void recoverWrnLanguage(document);
|
||||
@@ -43,6 +74,7 @@ async function activate(context) {
|
||||
{ documentSelector: [{ scheme: "file", language: WRN_LANGUAGE_ID }] },
|
||||
);
|
||||
await client.start();
|
||||
registerAutoCloseTags(context, client);
|
||||
}
|
||||
|
||||
async function deactivate() {
|
||||
@@ -51,4 +83,4 @@ async function deactivate() {
|
||||
if (running) await running.stop();
|
||||
}
|
||||
|
||||
module.exports = { activate, deactivate, recoverWrnLanguage };
|
||||
module.exports = { activate, deactivate, recoverWrnLanguage, registerAutoCloseTags };
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
// WRN editor language server source hash: 1317c4e318ab938fffab3dd5b65655b0e220a81c481c9fa92f23706ede60303c
|
||||
// WRN editor language server source hash: 1bda85712569242c96adb5fe2512cc07a59d469c50791a892c5146ab61043da9
|
||||
// WRN editor language server generator hash: f593a44aaf05495b789ce7a3086bee1eebb951b884d41c0e017bbcfe5f547e72
|
||||
// @bun @bun-cjs
|
||||
(function(exports, require, module, __filename, __dirname) {var __create = Object.create;
|
||||
var __create = Object.create;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
@@ -195906,20 +195905,12 @@ function regionsFor(document) {
|
||||
regionCache.set(document.uri, { version: document.version, regions });
|
||||
return regions;
|
||||
}
|
||||
// packages/language-server/src/index.ts
|
||||
function offsetAt2(text, position) {
|
||||
const lines = text.split(/\r?\n/);
|
||||
let offset = 0;
|
||||
for (let line = 0;line < Math.min(position.line, lines.length); line++)
|
||||
offset += (lines[line]?.length ?? 0) + 1;
|
||||
return Math.min(text.length, offset + Math.max(0, position.character));
|
||||
function clearHtmlRegionCache(uri) {
|
||||
if (uri)
|
||||
regionCache.delete(uri);
|
||||
else
|
||||
regionCache.clear();
|
||||
}
|
||||
var semanticTokenTypes2 = ["variable"];
|
||||
var semanticTokenModifiers2 = ["declaration", "modification"];
|
||||
var semanticTokensLegend2 = {
|
||||
tokenTypes: [...semanticTokenTypes2],
|
||||
tokenModifiers: [...semanticTokenModifiers2]
|
||||
};
|
||||
|
||||
// packages/language-server/src/html-service.ts
|
||||
var service = getLanguageService();
|
||||
@@ -195936,7 +195927,7 @@ function markdown(value) {
|
||||
return "";
|
||||
}
|
||||
function htmlCompletions(document, position) {
|
||||
if (!isInsideHtml(document, offsetAt2(document.text, position)))
|
||||
if (!isInsideHtml(document, offsetAt(document.text, position)))
|
||||
return [];
|
||||
const virtual = htmlDocument(document);
|
||||
const parsed = service.parseHTMLDocument(virtual);
|
||||
@@ -195951,7 +195942,7 @@ function htmlCompletions(document, position) {
|
||||
}));
|
||||
}
|
||||
function htmlHover(document, position) {
|
||||
if (!isInsideHtml(document, offsetAt2(document.text, position)))
|
||||
if (!isInsideHtml(document, offsetAt(document.text, position)))
|
||||
return null;
|
||||
const virtual = htmlDocument(document);
|
||||
const result = service.doHover(virtual, position, service.parseHTMLDocument(virtual));
|
||||
@@ -195964,12 +195955,51 @@ function htmlFoldingRanges(document) {
|
||||
return service.getFoldingRanges(htmlDocument(document)).map((range) => ({ startLine: range.startLine, endLine: range.endLine }));
|
||||
}
|
||||
function htmlLinkedEditingRanges(document, position) {
|
||||
if (!isInsideHtml(document, offsetAt2(document.text, position)))
|
||||
if (!isInsideHtml(document, offsetAt(document.text, position)))
|
||||
return null;
|
||||
const virtual = htmlDocument(document);
|
||||
const ranges = service.findLinkedEditingRanges(virtual, position, service.parseHTMLDocument(virtual));
|
||||
return ranges && ranges.length ? ranges : null;
|
||||
}
|
||||
function selfClosingTagCompletion(text, offset) {
|
||||
if (text.charAt(offset - 1) !== "/")
|
||||
return null;
|
||||
if (text.charAt(offset) === ">")
|
||||
return null;
|
||||
const tagStart = text.lastIndexOf("<", offset - 1);
|
||||
if (tagStart < 0)
|
||||
return null;
|
||||
if (!/^<[A-Za-z][\w-]*/.test(text.slice(tagStart)))
|
||||
return null;
|
||||
let quote = null;
|
||||
for (let i = tagStart + 1;i < offset - 1; i++) {
|
||||
const ch = text[i];
|
||||
if (quote) {
|
||||
if (ch === quote)
|
||||
quote = null;
|
||||
continue;
|
||||
}
|
||||
if (ch === '"' || ch === "'") {
|
||||
quote = ch;
|
||||
continue;
|
||||
}
|
||||
if (ch === "<" || ch === ">")
|
||||
return null;
|
||||
}
|
||||
if (quote)
|
||||
return null;
|
||||
return ">";
|
||||
}
|
||||
function htmlTagComplete(document, position) {
|
||||
const offset = offsetAt(document.text, position);
|
||||
if (!isInsideHtml(document, offset))
|
||||
return null;
|
||||
const virtual = htmlDocument(document);
|
||||
const result = service.doTagComplete(virtual, position, service.parseHTMLDocument(virtual));
|
||||
if (result)
|
||||
return result;
|
||||
return selfClosingTagCompletion(document.text, offset);
|
||||
}
|
||||
function mergeCompletions(wrnexus, html) {
|
||||
const taken = new Set(wrnexus.map((item) => item.label));
|
||||
return [
|
||||
@@ -195978,15 +196008,6 @@ function mergeCompletions(wrnexus, html) {
|
||||
];
|
||||
}
|
||||
|
||||
// packages/language-server/src/html-regions.ts
|
||||
var regionCache2 = new Map;
|
||||
function clearHtmlRegionCache(uri) {
|
||||
if (uri)
|
||||
regionCache2.delete(uri);
|
||||
else
|
||||
regionCache2.clear();
|
||||
}
|
||||
|
||||
// packages/language-server/src/server.ts
|
||||
var documents = new Map;
|
||||
var diagnosticTimers = new Map;
|
||||
@@ -196192,6 +196213,11 @@ async function handle(message) {
|
||||
result(message.id, ranges ? { ranges } : null);
|
||||
break;
|
||||
}
|
||||
case "wrn/tagComplete": {
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
result(message.id, document ? htmlTagComplete(document, params.position) : null);
|
||||
break;
|
||||
}
|
||||
case "textDocument/definition": {
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
result(message.id, document ? definitionLocation(document, params.position) : null);
|
||||
@@ -196388,4 +196414,3 @@ process.stdin.on("data", (chunk) => {
|
||||
}
|
||||
});
|
||||
process.stdin.resume();
|
||||
})(exports, require, module, __filename, __dirname);
|
||||
|
||||
Reference in New Issue
Block a user