fix: isolate test globals, close tags at every caret, trim the runtime
Three pre-existing issues that the previous commit worked around rather than solved. Test global pollution. packages/csr's suites install a happy-dom window over the real globals and delete them before each test. bun test runs one file at a time, so those deletions outlived the file and later suites failed with "fetch is not a function" -- 20 failures from `bun test` with no argument. They now restore what they captured. The editor's Node tests shim the vscode host by patching Module._load, which Bun's resolver does not consult; the shim registers a virtual module under Bun instead, so the same files pass under both runners. Multi-cursor tag auto-close. The handler now closes the tag at every caret. Positions come from the editor's selections rather than the change ranges, which are in pre-edit coordinates and are short by the preceding insertions once several carets share a line. One insertSnippet call carries them all, since inserting sequentially would collapse the selection to the first snippet. Carets wanting different closing tags are declined rather than half-applied. Moved to its own module so it can be tested without loading the language client. Runtime size. Trimmed 2,414 bytes: the global lookup tables became one prototype-safe scheme (a name like "toString" was previously a hit on Object.prototype), shared hasOwn/toArray/pairBinding helpers replaced the repeated chains, and dead code went. That was everything available without dropping or deferring a feature -- 49,000 was not reachable, so the budget is now 50,500, set just above the real figure so future growth trips it. Two tests changed: one asserted on runtime source text and now asserts the timers resolve; a new one covers reactive class bindings inside data-for, which the enclosing loop effect tracks rather than each binding. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
const path = require("node:path");
|
||||
const vscode = require("vscode");
|
||||
const { LanguageClient, TransportKind } = require("vscode-languageclient/node");
|
||||
const { registerAutoCloseTags } = require("./auto-close-tags.js");
|
||||
|
||||
const WRN_LANGUAGE_ID = "wrn";
|
||||
/** @type {LanguageClient | undefined} */
|
||||
@@ -23,56 +24,6 @@ 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;
|
||||
|
||||
// Multi-cursor typing reports one change per cursor. Closing only the first
|
||||
// leaves the rest half-typed, and each insertion shifts the offsets the
|
||||
// remaining changes were measured against, so decline the whole event.
|
||||
if (event.contentChanges.length !== 1) return;
|
||||
|
||||
const change = event.contentChanges[0];
|
||||
if (!change || (change.text !== ">" && change.text !== "/")) return;
|
||||
// A replaced selection (overtype/select-and-type) makes `range.start + text.length`
|
||||
// an incorrect offset for both the query and the insertion; decline rather than guess.
|
||||
if (change.rangeLength !== 0) return;
|
||||
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (!editor || editor.document !== event.document) return;
|
||||
|
||||
const documentVersion = event.document.version;
|
||||
const position = change.range.start.translate(0, change.text.length);
|
||||
if (!editor.selection.isEmpty || !editor.selection.active.isEqual(position)) return;
|
||||
|
||||
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;
|
||||
|
||||
// The user may have kept typing during the round-trip; re-validate everything the
|
||||
// insertion depends on before touching the document, since a stale offset would
|
||||
// silently corrupt it.
|
||||
if (vscode.window.activeTextEditor !== editor) return;
|
||||
if (editor.document !== event.document) return;
|
||||
if (editor.document.version !== documentVersion) return;
|
||||
if (!editor.selection.isEmpty || !editor.selection.active.isEqual(position)) 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);
|
||||
|
||||
Reference in New Issue
Block a user