diff --git a/src/vs/editor/contrib/inlineCompletions/browser/ghostTextModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/ghostTextModel.ts index 02dd033f8cb71..a804be067ec9c 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/ghostTextModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/ghostTextModel.ts @@ -9,13 +9,11 @@ import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { InlineCompletionTriggerKind } from 'vs/editor/common/languages'; -import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; -import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { GhostText, GhostTextWidgetModel } from 'vs/editor/contrib/inlineCompletions/browser/ghostText'; import { InlineCompletionsModel, SynchronizedInlineCompletionsCache, TrackedInlineCompletions } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel'; import { SuggestWidgetPreviewModel } from 'vs/editor/contrib/inlineCompletions/browser/suggestWidgetPreviewModel'; import { createDisposableRef } from 'vs/editor/contrib/inlineCompletions/browser/utils'; -import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; export abstract class DelegatingModel extends Disposable implements GhostTextWidgetModel { private readonly onDidChangeEmitter = new Emitter(); @@ -69,8 +67,8 @@ export abstract class DelegatingModel extends Disposable implements GhostTextWid */ export class GhostTextModel extends DelegatingModel implements GhostTextWidgetModel { public readonly sharedCache = this._register(new SharedInlineCompletionCache()); - public readonly suggestWidgetAdapterModel = this._register(new SuggestWidgetPreviewModel(this.editor, this.sharedCache, this.languageFeaturesService)); - public readonly inlineCompletionsModel = this._register(new InlineCompletionsModel(this.editor, this.sharedCache, this.commandService, this.languageConfigurationService, this.languageFeaturesService)); + public readonly suggestWidgetAdapterModel = this._register(this.instantiationService.createInstance(SuggestWidgetPreviewModel, this.editor, this.sharedCache)); + public readonly inlineCompletionsModel = this._register(this.instantiationService.createInstance(InlineCompletionsModel, this.editor, this.sharedCache)); public get activeInlineCompletionsModel(): InlineCompletionsModel | undefined { if (this.targetModel === this.inlineCompletionsModel) { @@ -81,9 +79,7 @@ export class GhostTextModel extends DelegatingModel implements GhostTextWidgetMo constructor( private readonly editor: IActiveCodeEditor, - @ICommandService private readonly commandService: ICommandService, - @ILanguageConfigurationService private readonly languageConfigurationService: ILanguageConfigurationService, - @ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService, + @IInstantiationService private readonly instantiationService: IInstantiationService, ) { super(); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts index be0c4f7d49258..9545d8a1a6e8a 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts @@ -26,56 +26,80 @@ import { ILanguageConfigurationService } from 'vs/editor/common/languages/langua import { fixBracketsInLine } from 'vs/editor/common/model/bracketPairsTextModelPart/fixBrackets'; import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; +import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce'; -export class InlineCompletionsModel extends Disposable implements GhostTextWidgetModel { +export class InlineCompletionsModel + extends Disposable + implements GhostTextWidgetModel { protected readonly onDidChangeEmitter = new Emitter(); public readonly onDidChange = this.onDidChangeEmitter.event; - public readonly completionSession = this._register(new MutableDisposable()); + public readonly completionSession = this._register( + new MutableDisposable() + ); private active: boolean = false; private disposed = false; + private readonly debounceValue = this.debounceService.for( + this.languageFeaturesService.inlineCompletionsProvider, + 'InlineCompletionsDebounce', + { min: 50, max: 200 } + ); constructor( private readonly editor: IActiveCodeEditor, private readonly cache: SharedInlineCompletionCache, @ICommandService private readonly commandService: ICommandService, - @ILanguageConfigurationService private readonly languageConfigurationService: ILanguageConfigurationService, - @ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService, + @ILanguageConfigurationService + private readonly languageConfigurationService: ILanguageConfigurationService, + @ILanguageFeaturesService + private readonly languageFeaturesService: ILanguageFeaturesService, + @ILanguageFeatureDebounceService + private readonly debounceService: ILanguageFeatureDebounceService ) { super(); - this._register(commandService.onDidExecuteCommand(e => { - // These commands don't trigger onDidType. - const commands = new Set([ - CoreEditingCommands.Tab.id, - CoreEditingCommands.DeleteLeft.id, - CoreEditingCommands.DeleteRight.id, - inlineSuggestCommitId, - 'acceptSelectedSuggestion' - ]); - if (commands.has(e.commandId) && editor.hasTextFocus()) { - this.handleUserInput(); - } - })); + this._register( + commandService.onDidExecuteCommand((e) => { + // These commands don't trigger onDidType. + const commands = new Set([ + CoreEditingCommands.Tab.id, + CoreEditingCommands.DeleteLeft.id, + CoreEditingCommands.DeleteRight.id, + inlineSuggestCommitId, + 'acceptSelectedSuggestion', + ]); + if (commands.has(e.commandId) && editor.hasTextFocus()) { + this.handleUserInput(); + } + }) + ); - this._register(this.editor.onDidType((e) => { - this.handleUserInput(); - })); + this._register( + this.editor.onDidType((e) => { + this.handleUserInput(); + }) + ); - this._register(this.editor.onDidChangeCursorPosition((e) => { - if (this.session && !this.session.isValid) { - this.hide(); - } - })); + this._register( + this.editor.onDidChangeCursorPosition((e) => { + if (this.session && !this.session.isValid) { + this.hide(); + } + }) + ); - this._register(toDisposable(() => { - this.disposed = true; - })); + this._register( + toDisposable(() => { + this.disposed = true; + }) + ); - this._register(this.editor.onDidBlurEditorWidget(() => { - this.hide(); - })); + this._register( + this.editor.onDidBlurEditorWidget(() => { + this.hide(); + }) + ); } private handleUserInput() { @@ -146,7 +170,8 @@ export class InlineCompletionsModel extends Disposable implements GhostTextWidge this.cache, triggerKind, this.languageConfigurationService, - this.languageFeaturesService.inlineCompletionsProvider + this.languageFeaturesService.inlineCompletionsProvider, + this.debounceValue ); this.completionSession.value.takeOwnership( this.completionSession.value.onDidChange(() => { @@ -199,7 +224,8 @@ export class InlineCompletionsSession extends BaseGhostTextWidgetModel { private readonly cache: SharedInlineCompletionCache, private initialTriggerKind: InlineCompletionTriggerKind, private readonly languageConfigurationService: ILanguageConfigurationService, - private readonly registry: LanguageFeatureRegistry + private readonly registry: LanguageFeatureRegistry, + private readonly debounce: IFeatureDebounceInformation, ) { super(editor); @@ -231,7 +257,7 @@ export class InlineCompletionsSession extends BaseGhostTextWidgetModel { })); this._register(this.registry.onDidChange(() => { - this.updateSoon.schedule(); + this.updateSoon.schedule(this.debounce.get(this.editor.getModel())); })); this.scheduleAutomaticUpdate(); @@ -336,7 +362,7 @@ export class InlineCompletionsSession extends BaseGhostTextWidgetModel { // Since updateSoon debounces, starvation can happen. // To prevent stale cache, we clear the current update operation. this.updateOperation.clear(); - this.updateSoon.schedule(); + this.updateSoon.schedule(this.debounce.get(this.editor.getModel())); } private async update(triggerKind: InlineCompletionTriggerKind): Promise { @@ -346,6 +372,8 @@ export class InlineCompletionsSession extends BaseGhostTextWidgetModel { const position = this.editor.getPosition(); + const startTime = new Date(); + const promise = createCancelablePromise(async token => { let result; try { @@ -355,6 +383,10 @@ export class InlineCompletionsSession extends BaseGhostTextWidgetModel { token, this.languageConfigurationService ); + + const endTime = new Date(); + this.debounce.update(this.editor.getModel(), endTime.getTime() - startTime.getTime()); + } catch (e) { onUnexpectedError(e); return;