diff --git a/editors/vscode/src/extension.bundle.cjs b/editors/vscode/src/extension.bundle.cjs index 3f238afd..63a01f03 100644 --- a/editors/vscode/src/extension.bundle.cjs +++ b/editors/vscode/src/extension.bundle.cjs @@ -1,4 +1,4 @@ -// WRN editor extension source hash: 7721e499707f7fb9b0b52fe428de3033cd624fa508322ec795960af7b6d96da5 +// WRN editor extension source hash: 875fa63e96381e6a0112442a3fce92717fb52e85f4e331e2b3e3487837e00a4d // WRN editor extension generator hash: 456d1d614e44e5fb1f19b784176c09cf2ade9b64ef73a17934c2698150b62728 "use strict"; var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports); @@ -22727,16 +22727,29 @@ function registerAutoCloseTags(context, client2) { const change = event.contentChanges[0]; if (!change || change.text !== ">" && change.text !== "/") return; + 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 client2.sendRequest("wrn/tagComplete", { textDocument: { uri: event.document.uri.toString() }, position: { line: position.line, character: position.character } }); if (typeof snippet !== "string" || !snippet) return; + 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); diff --git a/editors/vscode/src/extension.js b/editors/vscode/src/extension.js index eb804e7e..cb86233d 100644 --- a/editors/vscode/src/extension.js +++ b/editors/vscode/src/extension.js @@ -37,17 +37,31 @@ function registerAutoCloseTags(context, client) { 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); });