Adds inlineCompletionsNew and inlineCompletionsAdditions proposed APIs to avoid breaking changes when finalizing inline completions API.

This commit is contained in:
Henning Dieterichs
2022-03-10 11:23:01 +01:00
parent 06b2dd2130
commit 3626e6ab0c
6 changed files with 290 additions and 2 deletions

View File

@@ -7,7 +7,7 @@ import { URI, UriComponents } from 'vs/base/common/uri';
import { mixin } from 'vs/base/common/objects';
import type * as vscode from 'vscode';
import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters';
import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, SymbolInformation, DocumentSymbol, SemanticTokensEdits, SemanticTokens, SemanticTokensEdit, Location } from 'vs/workbench/api/common/extHostTypes';
import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, SymbolInformation, DocumentSymbol, SemanticTokensEdits, SemanticTokens, SemanticTokensEdit, Location, InlineCompletionTriggerKindNew } from 'vs/workbench/api/common/extHostTypes';
import { ISingleEditOperation } from 'vs/editor/common/core/editOperation';
import * as languages from 'vs/editor/common/languages';
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
@@ -34,6 +34,7 @@ import { StopWatch } from 'vs/base/common/stopwatch';
import { isCancellationError } from 'vs/base/common/errors';
import { Emitter } from 'vs/base/common/event';
import { raceCancellationError } from 'vs/base/common/async';
import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
// --- adapter
@@ -1112,6 +1113,101 @@ class InlineCompletionAdapter {
}
}
class InlineCompletionAdapterNew {
private readonly _cache = new Cache<vscode.InlineCompletionItemNew>('InlineCompletionItemNew');
private readonly _disposables = new Map<number, DisposableStore>();
private readonly isAdditionProposedApiEnabled = isProposedApiEnabled(this.extension, 'inlineCompletionsAdditions');
constructor(
private readonly extension: IExtensionDescription,
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.InlineCompletionItemProviderNew,
private readonly _commands: CommandsConverter,
) { }
private readonly languageTriggerKindToVSCodeTriggerKind: Record<languages.InlineCompletionTriggerKind, vscode.InlineCompletionTriggerKindNew> = {
[languages.InlineCompletionTriggerKind.Automatic]: InlineCompletionTriggerKindNew.Automatic,
[languages.InlineCompletionTriggerKind.Explicit]: InlineCompletionTriggerKindNew.Invoke,
};
public async provideInlineCompletions(resource: URI, position: IPosition, context: languages.InlineCompletionContext, token: CancellationToken): Promise<extHostProtocol.IdentifiableInlineCompletions | undefined> {
const doc = this._documents.getDocument(resource);
const pos = typeConvert.Position.to(position);
const result = await this._provider.provideInlineCompletionItems(doc, pos, {
selectedCompletionInfo:
context.selectedSuggestionInfo
? {
range: typeConvert.Range.to(context.selectedSuggestionInfo.range),
text: context.selectedSuggestionInfo.text
}
: undefined,
triggerKind: this.languageTriggerKindToVSCodeTriggerKind[context.triggerKind]
}, token);
if (!result) {
// undefined and null are valid results
return undefined;
}
if (token.isCancellationRequested) {
// cancelled -> return without further ado, esp no caching
// of results as they will leak
return undefined;
}
const normalizedResult = isArray(result) ? result : result.items;
const pid = this._cache.add(normalizedResult);
let disposableStore: DisposableStore | undefined = undefined;
return {
pid,
items: normalizedResult.map<extHostProtocol.IdentifiableInlineCompletion>((item, idx) => {
let command: languages.Command | undefined = undefined;
if (item.command) {
if (!disposableStore) {
disposableStore = new DisposableStore();
this._disposables.set(pid, disposableStore);
}
command = this._commands.toInternal(item.command, disposableStore);
}
const insertText = item.insertText;
if (insertText === undefined) {
throw new Error('text or insertText must be defined');
}
return ({
text: typeof insertText === 'string' ? insertText : { snippet: insertText.value },
range: item.range ? typeConvert.Range.from(item.range) : undefined,
command,
idx: idx,
completeBracketPairs: this.isAdditionProposedApiEnabled ? item.completeBracketPairs : false
});
}),
};
}
public disposeCompletions(pid: number) {
this._cache.delete(pid);
const d = this._disposables.get(pid);
if (d) {
d.clear();
}
this._disposables.delete(pid);
}
public handleDidShowCompletionItem(pid: number, idx: number): void {
const completionItem = this._cache.get(pid, idx);
if (completionItem) {
if (this._provider.handleDidShowCompletionItem && isProposedApiEnabled(this.extension, 'inlineCompletionsAdditions')) {
this._provider.handleDidShowCompletionItem(completionItem);
}
}
}
}
export class InlineCompletionController<T extends vscode.InlineCompletionItem> implements vscode.InlineCompletionController<T> {
private static readonly map = new WeakMap<vscode.InlineCompletionItemProvider<any>, InlineCompletionController<any>>();
@@ -1616,7 +1712,7 @@ type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | Hov
| SelectionRangeAdapter | CallHierarchyAdapter | TypeHierarchyAdapter
| DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter
| EvaluatableExpressionAdapter | InlineValuesAdapter
| LinkedEditingRangeAdapter | InlayHintsAdapter | InlineCompletionAdapter;
| LinkedEditingRangeAdapter | InlayHintsAdapter | InlineCompletionAdapter | InlineCompletionAdapterNew;
class AdapterData {
constructor(
@@ -2050,6 +2146,12 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
return this._createDisposable(handle);
}
registerInlineCompletionsProviderNew(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.InlineCompletionItemProviderNew): vscode.Disposable {
const handle = this._addNewAdapter(new InlineCompletionAdapterNew(extension, this._documents, provider, this._commands.converter), extension);
this._proxy.$registerInlineCompletionsSupport(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
$provideInlineCompletions(handle: number, resource: UriComponents, position: IPosition, context: languages.InlineCompletionContext, token: CancellationToken): Promise<extHostProtocol.IdentifiableInlineCompletions | undefined> {
return this._withAdapter(handle, InlineCompletionAdapter, adapter => adapter.provideInlineCompletions(URI.revive(resource), position, context, token), undefined, token);
}