diff --git a/packages/language-server/lib/register/registerLanguageFeatures.ts b/packages/language-server/lib/register/registerLanguageFeatures.ts index c41f66fd..e79ed7c8 100644 --- a/packages/language-server/lib/register/registerLanguageFeatures.ts +++ b/packages/language-server/lib/register/registerLanguageFeatures.ts @@ -279,7 +279,7 @@ export function registerLanguageFeatures( }); connection.onRequest(AutoInsertRequest.type, async (params, token) => { return worker(params.textDocument.uri, token, service => { - return service.doAutoInsert(params.textDocument.uri, params.position, params.lastChange, token); + return service.doAutoInsert(params.textDocument.uri, params.selection, params.change, token); }); }); diff --git a/packages/language-server/protocol.ts b/packages/language-server/protocol.ts index d581c76d..ed4f0894 100644 --- a/packages/language-server/protocol.ts +++ b/packages/language-server/protocol.ts @@ -39,13 +39,16 @@ export namespace GetMatchTsConfigRequest { } export namespace AutoInsertRequest { - export type ParamsType = vscode.TextDocumentPositionParams & { - lastChange: { - range: vscode.Range; + export type ParamsType = { + textDocument: vscode.TextDocumentIdentifier; + selection: vscode.Position; + change: { + rangeOffset: number; + rangeLength: number; text: string; }; }; - export type ResponseType = string | vscode.TextEdit | null | undefined; + export type ResponseType = string | null | undefined; export type ErrorType = never; export const type = new vscode.RequestType('volar/client/autoInsert'); } diff --git a/packages/language-service/lib/features/provideAutoInsertionEdit.ts b/packages/language-service/lib/features/provideAutoInsertionEdit.ts index 879b05f9..1ecd988e 100644 --- a/packages/language-service/lib/features/provideAutoInsertionEdit.ts +++ b/packages/language-service/lib/features/provideAutoInsertionEdit.ts @@ -6,21 +6,22 @@ import { isAutoInsertEnabled } from '@volar/language-core'; export function register(context: ServiceContext) { - return (uri: string, position: vscode.Position, lastChange: { range: vscode.Range; text: string; }, token = NoneCancellationToken) => { + return (uri: string, selection: vscode.Position, change: { rangeOffset: number; rangeLength: number; text: string; }, token = NoneCancellationToken) => { return languageFeatureWorker( context, uri, - () => ({ position, lastChange }), + () => ({ selection, change }), function* (map) { - for (const mappedPosition of map.getGeneratedPositions(position, isAutoInsertEnabled)) { - const range = map.getGeneratedRange(lastChange.range); - if (range) { + for (const mappedPosition of map.getGeneratedPositions(selection, isAutoInsertEnabled)) { + const mapped = map.map.getGeneratedOffset(change.rangeOffset); + if (mapped) { yield { - position: mappedPosition, - lastChange: { - text: lastChange.text, - range, + selection: mappedPosition, + change: { + text: change.text, + rangeOffset: mapped[0], + rangeLength: change.rangeLength, }, }; } @@ -30,7 +31,7 @@ export function register(context: ServiceContext) { if (token.isCancellationRequested) { return; } - return service[1].provideAutoInsertionEdit?.(document, args.position, args.lastChange, token); + return service[1].provideAutoInsertionEdit?.(document, args.selection, args.change, token); }, (item, map) => { if (!map || typeof item === 'string') { diff --git a/packages/language-service/lib/types.ts b/packages/language-service/lib/types.ts index 3dca8c9e..f0b51119 100644 --- a/packages/language-service/lib/types.ts +++ b/packages/language-service/lib/types.ts @@ -131,7 +131,7 @@ export interface LanguageServicePluginInstance

{ provideSemanticDiagnostics?(document: TextDocument, token: vscode.CancellationToken): NullableProviderResult; provideFileReferences?(document: TextDocument, token: vscode.CancellationToken): NullableProviderResult; // volar specific provideReferencesCodeLensRanges?(document: TextDocument, token: vscode.CancellationToken): NullableProviderResult; // volar specific - provideAutoInsertionEdit?(document: TextDocument, position: vscode.Position, lastChange: { range: vscode.Range; text: string; }, token: vscode.CancellationToken): NullableProviderResult; // volar specific + provideAutoInsertionEdit?(document: TextDocument, position: vscode.Position, lastChange: { rangeOffset: number; rangeLength: number; text: string; }, token: vscode.CancellationToken): NullableProviderResult; // volar specific provideFileRenameEdits?(oldUri: string, newUri: string, token: vscode.CancellationToken): NullableProviderResult; // volar specific provideDocumentDropEdits?(document: TextDocument, position: vscode.Position, dataTransfer: Map, token: vscode.CancellationToken): NullableProviderResult; // volar specific resolveCodeLens?(codeLens: vscode.CodeLens, token: vscode.CancellationToken): ProviderResult; diff --git a/packages/monaco/lib/editor.ts b/packages/monaco/lib/editor.ts index 50982604..ea61f38f 100644 --- a/packages/monaco/lib/editor.ts +++ b/packages/monaco/lib/editor.ts @@ -1,6 +1,6 @@ import type { LanguageService } from '@volar/language-service'; import type { editor, IDisposable, MonacoEditor, Uri } from 'monaco-types'; -import { fromPosition, fromRange, toMarkerData, toTextEdit } from 'monaco-languageserver-types'; +import { fromPosition, toMarkerData, toTextEdit } from 'monaco-languageserver-types'; import { markers } from './markers.js'; interface IInternalEditorModel extends editor.IModel { @@ -189,8 +189,9 @@ export function activateAutoInsertion( column: lastChange.range.startColumn + lastChange.text.length, }), { - range: fromRange(lastChange.range), text: lastChange.text, + rangeOffset: lastChange.rangeOffset, + rangeLength: lastChange.rangeLength, }, ); if (model.getVersionId() !== version) { diff --git a/packages/vscode/lib/features/autoInsertion.ts b/packages/vscode/lib/features/autoInsertion.ts index 04dcf521..94ad15e7 100644 --- a/packages/vscode/lib/features/autoInsertion.ts +++ b/packages/vscode/lib/features/autoInsertion.ts @@ -37,10 +37,6 @@ export function activate(selector: vscode.DocumentSelector, client: BaseLanguage if (document !== activeDocument) { return; } - if (timeout) { - clearTimeout(timeout); - } - const lastChange = contentChanges[contentChanges.length - 1]; doAutoInsert(document, lastChange); } @@ -51,47 +47,41 @@ export function activate(selector: vscode.DocumentSelector, client: BaseLanguage timeout = undefined; } const version = document.version; + const isCancel = () => document !== vscode.window.activeTextEditor?.document + || vscode.window.activeTextEditor?.document.version !== version; + timeout = setTimeout(async () => { timeout = undefined; - - const isCancel = () => document !== vscode.window.activeTextEditor?.document - || vscode.window.activeTextEditor?.document.version !== version; if (isCancel()) { return; } - - const rangeStart = lastChange.range.start; - const position = new vscode.Position(rangeStart.line, rangeStart.character + lastChange.text.length); - const params = { - ...client.code2ProtocolConverter.asTextDocumentPositionParams(document, position), - lastChange: { + const activeEditor = vscode.window.activeTextEditor; + if (!activeEditor) { + return; + } + const newTextRange = new vscode.Range( + lastChange.range.start, + document.positionAt( + document.offsetAt(lastChange.range.start) + + lastChange.text.length + ) + ); + const selection = activeEditor.selections.find(selection => newTextRange.contains(selection.active))?.active; + if (!selection) { + return; + } + const params: AutoInsertRequest.ParamsType = { + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), + selection: client.code2ProtocolConverter.asPosition(selection), + change: { + rangeLength: lastChange.rangeLength, + rangeOffset: lastChange.rangeOffset, text: lastChange.text, - range: client.code2ProtocolConverter.asRange(lastChange.range), }, }; const insertion = await client.sendRequest(AutoInsertRequest.type, params); - const activeEditor = vscode.window.activeTextEditor; - - if ( - insertion !== undefined - && insertion !== null - && isEnabled - && !isCancel() - && activeEditor - ) { - if (typeof insertion === 'string') { - const selections = activeEditor.selections; - if (selections.length && selections.some(s => s.active.isEqual(position))) { - activeEditor.insertSnippet(new vscode.SnippetString(insertion), selections.map(s => s.active)); - } - else { - activeEditor.insertSnippet(new vscode.SnippetString(insertion), position); - } - } - else { - const edit = client.protocol2CodeConverter.asTextEdit(insertion); - activeEditor.insertSnippet(new vscode.SnippetString(edit.newText), edit.range); - } + if (insertion && isEnabled && !isCancel()) { + activeEditor.insertSnippet(new vscode.SnippetString(insertion)); } }, 100); }