Handle promise cancelled for codeActionModel
Fixes #114031 This handles the case where an external caller is waiting on the actions, but the model is disposed. In this case, we should return an empty code action set instead of throwing an exception
This commit is contained in:
parent
2b44e04c10
commit
b6377b8e71
1 changed files with 40 additions and 7 deletions
|
@ -4,10 +4,13 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CancelablePromise, createCancelablePromise, TimeoutTimer } from 'vs/base/common/async';
|
||||
import { isPromiseCanceledError } from 'vs/base/common/errors';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
|
@ -15,10 +18,8 @@ import { CodeActionProviderRegistry, CodeActionTriggerType } from 'vs/editor/com
|
|||
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IMarkerService } from 'vs/platform/markers/common/markers';
|
||||
import { IEditorProgressService, Progress } from 'vs/platform/progress/common/progress';
|
||||
import { getCodeActions, CodeActionSet } from './codeAction';
|
||||
import { CodeActionSet, getCodeActions } from './codeAction';
|
||||
import { CodeActionTrigger } from './types';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
|
||||
export const SUPPORTED_CODE_ACTIONS = new RawContextKey<string>('supportedCodeAction', '');
|
||||
|
||||
|
@ -147,17 +148,38 @@ export namespace CodeActionsState {
|
|||
export class Triggered {
|
||||
readonly type = Type.Triggered;
|
||||
|
||||
public readonly actions: Promise<CodeActionSet>;
|
||||
|
||||
constructor(
|
||||
public readonly trigger: CodeActionTrigger,
|
||||
public readonly rangeOrSelection: Range | Selection,
|
||||
public readonly position: Position,
|
||||
public readonly actions: CancelablePromise<CodeActionSet>,
|
||||
) { }
|
||||
private readonly _cancellablePromise: CancelablePromise<CodeActionSet>,
|
||||
) {
|
||||
this.actions = _cancellablePromise.catch((e): CodeActionSet => {
|
||||
if (isPromiseCanceledError(e)) {
|
||||
return emptyCodeActionSet;
|
||||
}
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
|
||||
public cancel() {
|
||||
this._cancellablePromise.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
export type State = typeof Empty | Triggered;
|
||||
}
|
||||
|
||||
const emptyCodeActionSet: CodeActionSet = {
|
||||
allActions: [],
|
||||
validActions: [],
|
||||
dispose: () => { },
|
||||
documentation: [],
|
||||
hasAutoFix: false
|
||||
};
|
||||
|
||||
export class CodeActionModel extends Disposable {
|
||||
|
||||
private readonly _codeActionOracle = this._register(new MutableDisposable<CodeActionOracle>());
|
||||
|
@ -167,6 +189,8 @@ export class CodeActionModel extends Disposable {
|
|||
private readonly _onDidChangeState = this._register(new Emitter<CodeActionsState.State>());
|
||||
public readonly onDidChangeState = this._onDidChangeState.event;
|
||||
|
||||
#isDisposed = false;
|
||||
|
||||
constructor(
|
||||
private readonly _editor: ICodeEditor,
|
||||
private readonly _markerService: IMarkerService,
|
||||
|
@ -184,11 +208,20 @@ export class CodeActionModel extends Disposable {
|
|||
}
|
||||
|
||||
dispose(): void {
|
||||
if (this.#isDisposed) {
|
||||
return;
|
||||
}
|
||||
this.#isDisposed = true;
|
||||
|
||||
super.dispose();
|
||||
this.setState(CodeActionsState.Empty, true);
|
||||
}
|
||||
|
||||
private _update(): void {
|
||||
if (this.#isDisposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._codeActionOracle.value = undefined;
|
||||
|
||||
this.setState(CodeActionsState.Empty);
|
||||
|
@ -240,12 +273,12 @@ export class CodeActionModel extends Disposable {
|
|||
|
||||
// Cancel old request
|
||||
if (this._state.type === CodeActionsState.Type.Triggered) {
|
||||
this._state.actions.cancel();
|
||||
this._state.cancel();
|
||||
}
|
||||
|
||||
this._state = newState;
|
||||
|
||||
if (!skipNotify) {
|
||||
if (!skipNotify && !this.#isDisposed) {
|
||||
this._onDidChangeState.fire(newState);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue