From 0a5d652bdfdd4465f5dccee950f5dff06d5a20ec Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 3 Jun 2021 22:44:32 +0200 Subject: [PATCH] Fixes #125332. --- .../inlineCompletionsModel.ts | 16 ++++-- .../editor/contrib/inlineCompletions/utils.ts | 51 +++++++++++++++++++ 2 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 src/vs/editor/contrib/inlineCompletions/utils.ts diff --git a/src/vs/editor/contrib/inlineCompletions/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/inlineCompletionsModel.ts index a85b13e6669..89e0bfd7577 100644 --- a/src/vs/editor/contrib/inlineCompletions/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/inlineCompletionsModel.ts @@ -7,7 +7,7 @@ import { CancelablePromise, createCancelablePromise, RunOnceScheduler } from 'vs import { CancellationToken } from 'vs/base/common/cancellation'; import { onUnexpectedError, onUnexpectedExternalError } from 'vs/base/common/errors'; import { Emitter } from 'vs/base/common/event'; -import { Disposable, IDisposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import * as strings from 'vs/base/common/strings'; import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; import { Position } from 'vs/editor/common/core/position'; @@ -18,6 +18,7 @@ import { BaseGhostTextWidgetModel, GhostText, GhostTextWidgetModel } from 'vs/ed import { EditOperation } from 'vs/editor/common/core/editOperation'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { MutableDisposable } from 'vs/editor/contrib/inlineCompletions/utils'; export class InlineCompletionsModel extends Disposable implements GhostTextWidgetModel { protected readonly onDidChangeEmitter = new Emitter(); @@ -120,6 +121,7 @@ export class InlineCompletionsModel extends Disposable implements GhostTextWidge } public commitCurrentSuggestion(): void { + // Don't dispose the session, so that after committing, more suggestions are shown. this.session?.commitCurrentCompletion(); } @@ -339,7 +341,10 @@ class InlineCompletionsSession extends BaseGhostTextWidgetModel { } public commit(completion: LiveInlineCompletion): void { - this.cache.clear(); + // Mark the cache as stale, but don't dispose it yet, + // otherwise command args might get disposed. + const cache = this.cache.replace(undefined); + this.editor.executeEdits( 'inlineCompletions.accept', [ @@ -347,7 +352,12 @@ class InlineCompletionsSession extends BaseGhostTextWidgetModel { ] ); if (completion.command) { - this.commandService.executeCommand(completion.command.id, ...(completion.command.arguments || [])).then(undefined, onUnexpectedExternalError); + this.commandService + .executeCommand(completion.command.id, ...(completion.command.arguments || [])) + .finally(() => { + cache?.dispose(); + }) + .then(undefined, onUnexpectedExternalError); } this.onDidChangeEmitter.fire(); diff --git a/src/vs/editor/contrib/inlineCompletions/utils.ts b/src/vs/editor/contrib/inlineCompletions/utils.ts new file mode 100644 index 00000000000..9a3b1a07469 --- /dev/null +++ b/src/vs/editor/contrib/inlineCompletions/utils.ts @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IDisposable, trackDisposable } from 'vs/base/common/lifecycle'; + +// TODO: merge this class into Matt's MutableDisposable. +/** + * Manages the lifecycle of a disposable value that may be changed. + * + * This ensures that when the disposable value is changed, the previously held disposable is disposed of. You can + * also register a `MutableDisposable` on a `Disposable` to ensure it is automatically cleaned up. + */ +export class MutableDisposable implements IDisposable { + private _value?: T; + private _isDisposed = false; + + constructor() { + trackDisposable(this); + } + + get value(): T | undefined { + return this._isDisposed ? undefined : this._value; + } + + set value(value: T | undefined) { + if (this._isDisposed || value === this._value) { + return; + } + + this._value?.dispose(); + this._value = value; + } + + clear() { + this.value = undefined; + } + + dispose(): void { + this._isDisposed = true; + this._value?.dispose(); + this._value = undefined; + } + + replace(newValue: T | undefined): T | undefined { + const oldValue = this._value; + this._value = newValue; + return oldValue; + } +}