From 105a5e57dbca3f28e2d6950205dfe48ef50dbdf7 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 14 Jan 2026 14:20:14 +0100 Subject: [PATCH] Implements inline completion provider change hint (#287748) * Implements inline completion provider change hint * Fixes tests * Allows command to set data --- src/vs/editor/common/languages.ts | 25 +++++- .../browser/controller/commands.ts | 4 +- .../browser/model/inlineCompletionsModel.ts | 17 +++-- .../test/browser/inlineCompletions.test.ts | 76 +++++++++++++++++++ .../inlineCompletions/test/browser/utils.ts | 17 ++++- .../editor/test/browser/editorTestServices.ts | 3 +- src/vs/monaco.d.ts | 24 +++++- .../api/browser/mainThreadLanguageFeatures.ts | 14 ++-- .../workbench/api/common/extHost.protocol.ts | 6 +- .../api/common/extHostLanguageFeatures.ts | 2 +- ...e.proposed.inlineCompletionsAdditions.d.ts | 25 +++++- 11 files changed, 189 insertions(+), 24 deletions(-) diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index 95af648f724c..25724438958d 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -753,6 +753,18 @@ export enum InlineCompletionTriggerKind { Explicit = 1, } +/** + * Arbitrary data that the provider can pass when firing {@link InlineCompletionsProvider.onDidChangeInlineCompletions}. + * This data is passed back to the provider in {@link InlineCompletionContext.changeHint}. + */ +export interface IInlineCompletionChangeHint { + /** + * Arbitrary data that the provider can use to identify what triggered the change. + * This data must be JSON serializable. + */ + readonly data?: unknown; +} + export interface InlineCompletionContext { /** @@ -775,6 +787,12 @@ export interface InlineCompletionContext { readonly includeInlineCompletions: boolean; readonly requestIssuedDateTime: number; readonly earliestShownDateTime: number; + + /** + * The change hint that was passed to {@link InlineCompletionsProvider.onDidChangeInlineCompletions}. + * Only set if this request was triggered by such an event. + */ + readonly changeHint?: IInlineCompletionChangeHint; } export interface IInlineCompletionModelInfo { @@ -946,7 +964,12 @@ export interface InlineCompletionsProvider; + /** + * Fired when the provider wants to trigger a new completion request. + * The event can pass a {@link IInlineCompletionChangeHint} which will be + * included in the {@link InlineCompletionContext} of the subsequent request. + */ + onDidChangeInlineCompletions?: Event; /** * Only used for {@link yieldsToGroupIds}. diff --git a/src/vs/editor/contrib/inlineCompletions/browser/controller/commands.ts b/src/vs/editor/contrib/inlineCompletions/browser/controller/commands.ts index 8c3e791e0897..97392ce8d7e7 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/controller/commands.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/controller/commands.ts @@ -6,7 +6,7 @@ import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js'; import { asyncTransaction, transaction } from '../../../../../base/common/observable.js'; import { splitLines } from '../../../../../base/common/strings.js'; -import { vBoolean, vObj, vOptionalProp, vString, vUndefined, vUnion, vWithJsonSchemaRef } from '../../../../../base/common/validation.js'; +import { vBoolean, vObj, vOptionalProp, vString, vUnchecked, vUndefined, vUnion, vWithJsonSchemaRef } from '../../../../../base/common/validation.js'; import * as nls from '../../../../../nls.js'; import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from '../../../../../platform/accessibility/common/accessibility.js'; import { Action2, MenuId } from '../../../../../platform/actions/common/actions.js'; @@ -80,6 +80,7 @@ const argsValidator = vUnion(vObj({ showNoResultNotification: vOptionalProp(vBoolean()), providerId: vOptionalProp(vWithJsonSchemaRef(providerIdSchemaUri, vString())), explicit: vOptionalProp(vBoolean()), + changeHintData: vOptionalProp(vUnchecked()), }), vUndefined()); export class TriggerInlineSuggestionAction extends EditorAction { @@ -118,6 +119,7 @@ export class TriggerInlineSuggestionAction extends EditorAction { await controller?.model.get()?.trigger(tx, { provider: provider, explicit: validatedArgs?.explicit ?? true, + changeHint: validatedArgs?.changeHintData ? { data: validatedArgs.changeHintData } : undefined, }); controller?.playAccessibilitySignal(tx); }); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts index 87928882cf50..222e76c5e513 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts @@ -25,7 +25,7 @@ import { Selection } from '../../../../common/core/selection.js'; import { TextReplacement, TextEdit } from '../../../../common/core/edits/textEdit.js'; import { TextLength } from '../../../../common/core/text/textLength.js'; import { ScrollType } from '../../../../common/editorCommon.js'; -import { InlineCompletionEndOfLifeReasonKind, InlineCompletion, InlineCompletionTriggerKind, PartialAcceptTriggerKind, InlineCompletionsProvider, InlineCompletionCommand } from '../../../../common/languages.js'; +import { IInlineCompletionChangeHint, InlineCompletionEndOfLifeReasonKind, InlineCompletion, InlineCompletionTriggerKind, PartialAcceptTriggerKind, InlineCompletionsProvider, InlineCompletionCommand } from '../../../../common/languages.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; import { EndOfLinePreference, IModelDeltaDecoration, ITextModel } from '../../../../common/model.js'; import { TextModelText } from '../../../../common/model/textModelText.js'; @@ -61,7 +61,7 @@ export class InlineCompletionsModel extends Disposable { private readonly _forceUpdateExplicitlySignal = observableSignal(this); private readonly _noDelaySignal = observableSignal(this); - private readonly _fetchSpecificProviderSignal = observableSignal(this); + private readonly _fetchSpecificProviderSignal = observableSignal<{ provider: InlineCompletionsProvider; changeHint?: IInlineCompletionChangeHint } | undefined>(this); // We use a semantic id to keep the same inline completion selected even if the provider reorders the completions. private readonly _selectedInlineCompletionId = observableValue(this, undefined); @@ -215,7 +215,7 @@ export class InlineCompletionsModel extends Disposable { return; } - store.add(provider.onDidChangeInlineCompletions(() => { + store.add(provider.onDidChangeInlineCompletions(changeHint => { if (!this._enabled.get()) { return; } @@ -240,7 +240,7 @@ export class InlineCompletionsModel extends Disposable { } transaction(tx => { - this._fetchSpecificProviderSignal.trigger(tx, provider); + this._fetchSpecificProviderSignal.trigger(tx, { provider, changeHint: changeHint ?? undefined }); this.trigger(tx); }); @@ -334,6 +334,7 @@ export class InlineCompletionsModel extends Disposable { onlyRequestInlineEdits: false, shouldDebounce: true, provider: undefined as InlineCompletionsProvider | undefined, + changeHint: undefined as IInlineCompletionChangeHint | undefined, textChange: false, changeReason: '', }), @@ -354,7 +355,8 @@ export class InlineCompletionsModel extends Disposable { } else if (ctx.didChange(this._onlyRequestInlineEditsSignal)) { changeSummary.onlyRequestInlineEdits = true; } else if (ctx.didChange(this._fetchSpecificProviderSignal)) { - changeSummary.provider = ctx.change; + changeSummary.provider = ctx.change?.provider; + changeSummary.changeHint = ctx.change?.changeHint; } return true; }, @@ -424,6 +426,7 @@ export class InlineCompletionsModel extends Disposable { includeInlineEdits: this._inlineEditsEnabled.read(reader), requestIssuedDateTime: requestInfo.startTime, earliestShownDateTime: requestInfo.startTime + (changeSummary.inlineCompletionTriggerKind === InlineCompletionTriggerKind.Explicit || this.inAcceptFlow.read(undefined) ? 0 : this._minShowDelay.read(undefined)), + changeHint: changeSummary.changeHint, }; if (context.triggerKind === InlineCompletionTriggerKind.Automatic && changeSummary.textChange) { @@ -474,7 +477,7 @@ export class InlineCompletionsModel extends Disposable { return availableProviders; } - public async trigger(tx?: ITransaction, options: { onlyFetchInlineEdits?: boolean; noDelay?: boolean; provider?: InlineCompletionsProvider; explicit?: boolean } = {}): Promise { + public async trigger(tx?: ITransaction, options: { onlyFetchInlineEdits?: boolean; noDelay?: boolean; provider?: InlineCompletionsProvider; explicit?: boolean; changeHint?: IInlineCompletionChangeHint } = {}): Promise { subtransaction(tx, tx => { if (options.onlyFetchInlineEdits) { this._onlyRequestInlineEditsSignal.trigger(tx); @@ -489,7 +492,7 @@ export class InlineCompletionsModel extends Disposable { this._forceUpdateExplicitlySignal.trigger(tx); } if (options.provider) { - this._fetchSpecificProviderSignal.trigger(tx, options.provider); + this._fetchSpecificProviderSignal.trigger(tx, { provider: options.provider, changeHint: options.changeHint }); } }); await this._fetchInlineCompletionsPromise.get(); diff --git a/src/vs/editor/contrib/inlineCompletions/test/browser/inlineCompletions.test.ts b/src/vs/editor/contrib/inlineCompletions/test/browser/inlineCompletions.test.ts index 0f26af1bbb8e..c9909d453460 100644 --- a/src/vs/editor/contrib/inlineCompletions/test/browser/inlineCompletions.test.ts +++ b/src/vs/editor/contrib/inlineCompletions/test/browser/inlineCompletions.test.ts @@ -761,4 +761,80 @@ suite('Multi Cursor Support', () => { } ); }); + + test('Change hint is passed from onDidChange to provideInlineCompletions', async function () { + const provider = new MockInlineCompletionsProvider(); + await withAsyncTestCodeEditorAndInlineCompletionsModel('', + { fakeClock: true, provider, inlineSuggest: { enabled: true } }, + async ({ editor, editorViewModel, model, context }) => { + context.keyboardType('foo'); + provider.setReturnValue({ insertText: 'foobar', range: new Range(1, 1, 1, 4) }); + model.triggerExplicitly(); + await timeout(1000); + + const firstCallHistory = provider.getAndClearCallHistory(); + assert.strictEqual(firstCallHistory.length, 1); + assert.strictEqual((firstCallHistory[0] as { changeHint?: unknown }).changeHint, undefined); + + // Change cursor position to avoid cache hit + editor.setPosition({ lineNumber: 1, column: 3 }); + + + const changeHintData = { reason: 'modelUpdated', version: 42 }; + provider.setReturnValue({ insertText: 'foobaz', range: new Range(1, 1, 1, 4) }); + provider.fireOnDidChange({ data: changeHintData }); + await timeout(1000); + + const secondCallHistory = provider.getAndClearCallHistory(); + + assert.deepStrictEqual( + secondCallHistory, + [{ + changeHint: { + data: { + reason: 'modelUpdated', + version: 42, + } + }, + position: '(1,3)', + text: 'foo', + triggerKind: 0 + }] + ); + } + ); + }); + + test('Change hint is undefined when onDidChange fires without hint', async function () { + const provider = new MockInlineCompletionsProvider(); + await withAsyncTestCodeEditorAndInlineCompletionsModel('', + { fakeClock: true, provider, inlineSuggest: { enabled: true } }, + async ({ editor, editorViewModel, model, context }) => { + context.keyboardType('foo'); + provider.setReturnValue({ insertText: 'foobar', range: new Range(1, 1, 1, 4) }); + model.triggerExplicitly(); + await timeout(1000); + + provider.getAndClearCallHistory(); + + // Change cursor position to avoid cache hit + editor.setPosition({ lineNumber: 1, column: 3 }); + + provider.setReturnValue({ insertText: 'foobaz', range: new Range(1, 1, 1, 4) }); + provider.fireOnDidChange(); + await timeout(1000); + + const callHistory = provider.getAndClearCallHistory(); + + assert.deepStrictEqual( + callHistory, + [{ + position: '(1,3)', + text: 'foo', + triggerKind: 0 + }] + ); + } + ); + }); }); diff --git a/src/vs/editor/contrib/inlineCompletions/test/browser/utils.ts b/src/vs/editor/contrib/inlineCompletions/test/browser/utils.ts index 2d9a1a5e2f58..bbd453dcaf5a 100644 --- a/src/vs/editor/contrib/inlineCompletions/test/browser/utils.ts +++ b/src/vs/editor/contrib/inlineCompletions/test/browser/utils.ts @@ -9,7 +9,7 @@ import { Disposable, DisposableStore } from '../../../../../base/common/lifecycl import { CoreEditingCommands, CoreNavigationCommands } from '../../../../browser/coreCommands.js'; import { Position } from '../../../../common/core/position.js'; import { ITextModel } from '../../../../common/model.js'; -import { InlineCompletion, InlineCompletionContext, InlineCompletions, InlineCompletionsProvider } from '../../../../common/languages.js'; +import { IInlineCompletionChangeHint, InlineCompletion, InlineCompletionContext, InlineCompletions, InlineCompletionsProvider } from '../../../../common/languages.js'; import { ITestCodeEditor, TestCodeEditorInstantiationOptions, withAsyncTestCodeEditor } from '../../../../test/browser/testCodeEditor.js'; import { InlineCompletionsModel } from '../../browser/model/inlineCompletionsModel.js'; import { autorun, derived } from '../../../../../base/common/observable.js'; @@ -27,7 +27,7 @@ import { PositionOffsetTransformer } from '../../../../common/core/text/position import { InlineSuggestionsView } from '../../browser/view/inlineSuggestionsView.js'; import { IBulkEditService } from '../../../../browser/services/bulkEditService.js'; import { IDefaultAccountService } from '../../../../../platform/defaultAccount/common/defaultAccount.js'; -import { Event } from '../../../../../base/common/event.js'; +import { Emitter, Event } from '../../../../../base/common/event.js'; export class MockInlineCompletionsProvider implements InlineCompletionsProvider { private returnValue: InlineCompletion[] = []; @@ -36,6 +36,9 @@ export class MockInlineCompletionsProvider implements InlineCompletionsProvider private callHistory = new Array(); private calledTwiceIn50Ms = false; + private readonly _onDidChangeEmitter = new Emitter(); + public readonly onDidChangeInlineCompletions: Event = this._onDidChangeEmitter.event; + constructor( public readonly enableForwardStability = false, ) { } @@ -62,6 +65,13 @@ export class MockInlineCompletionsProvider implements InlineCompletionsProvider } } + /** + * Fire an onDidChange event with an optional change hint. + */ + public fireOnDidChange(changeHint?: IInlineCompletionChangeHint): void { + this._onDidChangeEmitter.fire(changeHint); + } + private lastTimeMs: number | undefined = undefined; async provideInlineCompletions(model: ITextModel, position: Position, context: InlineCompletionContext, token: CancellationToken): Promise { @@ -74,7 +84,8 @@ export class MockInlineCompletionsProvider implements InlineCompletionsProvider this.callHistory.push({ position: position.toString(), triggerKind: context.triggerKind, - text: model.getValue() + text: model.getValue(), + ...(context.changeHint !== undefined ? { changeHint: context.changeHint } : {}), }); const result = new Array(); for (const v of this.returnValue) { diff --git a/src/vs/editor/test/browser/editorTestServices.ts b/src/vs/editor/test/browser/editorTestServices.ts index 4567ca518379..38594483bac3 100644 --- a/src/vs/editor/test/browser/editorTestServices.ts +++ b/src/vs/editor/test/browser/editorTestServices.ts @@ -19,7 +19,8 @@ export class TestCodeEditorService extends AbstractCodeEditorService { } getActiveCodeEditor(): ICodeEditor | null { - return null; + const editors = this.listCodeEditors(); + return editors.length > 0 ? editors[editors.length - 1] : null; } public lastInput?: IResourceEditorInput; override openCodeEditor(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index bdd5231f202b..a85f63bf973e 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -7494,6 +7494,18 @@ declare namespace monaco.languages { Explicit = 1 } + /** + * Arbitrary data that the provider can pass when firing {@link InlineCompletionsProvider.onDidChangeInlineCompletions}. + * This data is passed back to the provider in {@link InlineCompletionContext.changeHint}. + */ + export interface IInlineCompletionChangeHint { + /** + * Arbitrary data that the provider can use to identify what triggered the change. + * This data must be JSON serializable. + */ + readonly data?: unknown; + } + export interface InlineCompletionContext { /** * How the completion was triggered. @@ -7504,6 +7516,11 @@ declare namespace monaco.languages { readonly includeInlineCompletions: boolean; readonly requestIssuedDateTime: number; readonly earliestShownDateTime: number; + /** + * The change hint that was passed to {@link InlineCompletionsProvider.onDidChangeInlineCompletions}. + * Only set if this request was triggered by such an event. + */ + readonly changeHint?: IInlineCompletionChangeHint; } export interface IInlineCompletionModelInfo { @@ -7648,7 +7665,12 @@ declare namespace monaco.languages { * Will be called when a completions list is no longer in use and can be garbage-collected. */ disposeInlineCompletions(completions: T, reason: InlineCompletionsDisposeReason): void; - onDidChangeInlineCompletions?: IEvent; + /** + * Fired when the provider wants to trigger a new completion request. + * The event can pass a {@link IInlineCompletionChangeHint} which will be + * included in the {@link InlineCompletionContext} of the subsequent request. + */ + onDidChangeInlineCompletions?: IEvent; /** * Only used for {@link yieldsToGroupIds}. * Multiple providers can have the same group id. diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 62db354cc8e6..18430fbfffa8 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -33,7 +33,7 @@ import * as callh from '../../contrib/callHierarchy/common/callHierarchy.js'; import * as search from '../../contrib/search/common/search.js'; import * as typeh from '../../contrib/typeHierarchy/common/typeHierarchy.js'; import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js'; -import { ExtHostContext, ExtHostLanguageFeaturesShape, HoverWithId, ICallHierarchyItemDto, ICodeActionDto, ICodeActionProviderMetadataDto, IdentifiableInlineCompletion, IdentifiableInlineCompletions, IDocumentDropEditDto, IDocumentDropEditProviderMetadata, IDocumentFilterDto, IIndentationRuleDto, IInlayHintDto, IInlineCompletionModelInfoDto, ILanguageConfigurationDto, ILanguageWordDefinitionDto, ILinkDto, ILocationDto, ILocationLinkDto, IOnEnterRuleDto, IPasteEditDto, IPasteEditProviderMetadataDto, IRegExpDto, ISignatureHelpProviderMetadataDto, ISuggestDataDto, ISuggestDataDtoField, ISuggestResultDtoField, ITypeHierarchyItemDto, IWorkspaceSymbolDto, MainContext, MainThreadLanguageFeaturesShape } from '../common/extHost.protocol.js'; +import { ExtHostContext, ExtHostLanguageFeaturesShape, HoverWithId, ICallHierarchyItemDto, ICodeActionDto, ICodeActionProviderMetadataDto, IdentifiableInlineCompletion, IdentifiableInlineCompletions, IDocumentDropEditDto, IDocumentDropEditProviderMetadata, IDocumentFilterDto, IIndentationRuleDto, IInlayHintDto, IInlineCompletionChangeHintDto, IInlineCompletionModelInfoDto, ILanguageConfigurationDto, ILanguageWordDefinitionDto, ILinkDto, ILocationDto, ILocationLinkDto, IOnEnterRuleDto, IPasteEditDto, IPasteEditProviderMetadataDto, IRegExpDto, ISignatureHelpProviderMetadataDto, ISuggestDataDto, ISuggestDataDtoField, ISuggestResultDtoField, ITypeHierarchyItemDto, IWorkspaceSymbolDto, MainContext, MainThreadLanguageFeaturesShape } from '../common/extHost.protocol.js'; import { InlineCompletionEndOfLifeReasonKind } from '../common/extHostTypes.js'; import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js'; import { DataChannelForwardingTelemetryService, forwardToChannelIf, isCopilotLikeExtension } from '../../../platform/dataChannel/browser/forwardingTelemetryService.js'; @@ -683,10 +683,10 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread this._registrations.set(handle, provider); } - $emitInlineCompletionsChange(handle: number): void { + $emitInlineCompletionsChange(handle: number, changeHint: IInlineCompletionChangeHintDto | undefined): void { const obj = this._registrations.get(handle); if (obj instanceof ExtensionBackedInlineCompletionsProvider) { - obj._emitDidChange(); + obj._emitDidChange(changeHint); } } @@ -1290,8 +1290,8 @@ export class MainThreadDocumentRangeSemanticTokensProvider implements languages. class ExtensionBackedInlineCompletionsProvider extends Disposable implements languages.InlineCompletionsProvider { public readonly setModelId: ((modelId: string) => Promise) | undefined; - public readonly _onDidChangeEmitter = new Emitter(); - public readonly onDidChangeInlineCompletions: Event | undefined; + public readonly _onDidChangeEmitter = new Emitter(); + public readonly onDidChangeInlineCompletions: Event | undefined; public readonly _onDidChangeModelInfoEmitter = new Emitter(); public readonly onDidChangeModelInfo: Event | undefined; @@ -1334,9 +1334,9 @@ class ExtensionBackedInlineCompletionsProvider extends Disposable implements lan } } - public _emitDidChange() { + public _emitDidChange(changeHint: IInlineCompletionChangeHintDto | undefined) { if (this._supportsOnDidChange) { - this._onDidChangeEmitter.fire(); + this._onDidChangeEmitter.fire(changeHint); } } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 5c4dd634a4b2..000764668baa 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -491,6 +491,10 @@ export interface IInlineCompletionModelInfoDto { readonly currentModelId: string; } +export interface IInlineCompletionChangeHintDto { + readonly data?: unknown; +} + export interface MainThreadLanguageFeaturesShape extends IDisposable { $unregister(handle: number): void; $registerDocumentSymbolProvider(handle: number, selector: IDocumentFilterDto[], label: string): void; @@ -537,7 +541,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { initialModelInfo: IInlineCompletionModelInfoDto | undefined, supportsOnDidChangeModelInfo: boolean, ): void; - $emitInlineCompletionsChange(handle: number): void; + $emitInlineCompletionsChange(handle: number, changeHint: IInlineCompletionChangeHintDto | undefined): void; $emitInlineCompletionModelInfoChange(handle: number, data: IInlineCompletionModelInfoDto | undefined): void; $registerSignatureHelpProvider(handle: number, selector: IDocumentFilterDto[], metadata: ISignatureHelpProviderMetadataDto): void; $registerInlayHintsProvider(handle: number, selector: IDocumentFilterDto[], supportsResolve: boolean, eventHandle: number | undefined, displayName: string | undefined): void; diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 098b7a0e5d4a..4311937f5c28 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -2619,7 +2619,7 @@ export class ExtHostLanguageFeatures extends CoreDisposable implements extHostPr const supportsOnDidChange = isProposedApiEnabled(extension, 'inlineCompletionsAdditions') && typeof provider.onDidChange === 'function'; if (supportsOnDidChange) { - const subscription = provider.onDidChange!(_ => this._proxy.$emitInlineCompletionsChange(handle)); + const subscription = provider.onDidChange!(e => this._proxy.$emitInlineCompletionsChange(handle, e ? { data: e.data } : undefined)); result = Disposable.from(result, subscription); } diff --git a/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts b/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts index ed9f5022b2f4..deef5f2a74fa 100644 --- a/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts +++ b/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts @@ -135,7 +135,12 @@ declare module 'vscode' { // eslint-disable-next-line local/vscode-dts-provider-naming handleListEndOfLifetime?(list: InlineCompletionList, reason: InlineCompletionsDisposeReason): void; - readonly onDidChange?: Event; + /** + * Fired when the provider wants to trigger a new completion request. + * Can optionally pass a {@link InlineCompletionChangeHint} which will be + * included in the {@link InlineCompletionContext.changeHint} of the subsequent request. + */ + readonly onDidChange?: Event; readonly modelInfo?: InlineCompletionModelInfo; readonly onDidChangeModelInfo?: Event; @@ -199,6 +204,18 @@ declare module 'vscode' { export type InlineCompletionsDisposeReason = { kind: InlineCompletionsDisposeReasonKind }; + /** + * Arbitrary data that the provider can pass when firing {@link InlineCompletionItemProvider.onDidChange}. + * This data is passed back to the provider in {@link InlineCompletionContext.changeHint}. + */ + export interface InlineCompletionChangeHint { + /** + * Arbitrary data that the provider can use to identify what triggered the change. + * This data must be JSON serializable. + */ + readonly data?: unknown; + } + export interface InlineCompletionContext { readonly userPrompt?: string; @@ -207,6 +224,12 @@ declare module 'vscode' { readonly requestIssuedDateTime: number; readonly earliestShownDateTime: number; + + /** + * The change hint that was passed to {@link InlineCompletionItemProvider.onDidChange}. + * Only set if this request was triggered by such an event. + */ + readonly changeHint?: InlineCompletionChangeHint; } export interface PartialAcceptInfo {