gracefully handle the case in which a delegate command has been disposed before execution, fixes #3265

This commit is contained in:
Johannes Rieken
2016-04-21 13:55:05 +02:00
parent 255c7d5a96
commit 56b71bd9c1
5 changed files with 51 additions and 27 deletions

View File

@@ -23,6 +23,7 @@ import {ExtHostMessageService} from 'vs/workbench/api/node/extHostMessageService
import {ExtHostEditors} from 'vs/workbench/api/node/extHostEditors';
import {ExtHostLanguages} from 'vs/workbench/api/node/extHostLanguages';
import {ExtHostLanguageFeatures} from 'vs/workbench/api/node/extHostLanguageFeatures';
import * as ExtHostTypeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import {registerApiCommands} from 'vs/workbench/api/node/extHostApiCommands';
import * as extHostTypes from 'vs/workbench/api/node/extHostTypes';
import Modes = require('vs/editor/common/modes');
@@ -148,6 +149,10 @@ export class ExtHostAPIImplementation {
const extHostStatusBar = new ExtHostStatusBar(this._threadService);
const extHostOutputService = new ExtHostOutputService(this._threadService);
// the converter might create delegate commands to avoid sending args
// around all the time
ExtHostTypeConverters.Command.initialize(extHostCommands);
// env namespace
let telemetryInfo: ITelemetryInfo;
this.env = Object.freeze({

View File

@@ -87,7 +87,7 @@ class CodeLensAdapter implements modes.ICodeLensSupport {
data.symbols.push(<modes.ICodeLensSymbol>{
id: String(i),
range: TypeConverters.fromRange(lens.range),
command: TypeConverters.Command.from(lens.command, { commands: this._commands, disposables: data.disposables })
command: TypeConverters.Command.from(lens.command, data.disposables)
});
});
@@ -144,7 +144,7 @@ class CodeLensAdapter implements modes.ICodeLensSupport {
};
}
symbol.command = TypeConverters.Command.from(command, { commands: this._commands, disposables: cachedData.disposables });
symbol.command = TypeConverters.Command.from(command, cachedData.disposables);
return symbol;
});
});
@@ -315,7 +315,6 @@ class QuickFixAdapter implements modes.IQuickFixSupport {
});
this._cachedCommands = dispose(this._cachedCommands);
const ctx = { commands: this._commands, disposables: this._cachedCommands };
return asWinJsPromise(token => this._provider.provideCodeActions(doc, ran, { diagnostics: allDiagnostics }, token)).then(commands => {
if (!Array.isArray(commands)) {
@@ -323,7 +322,7 @@ class QuickFixAdapter implements modes.IQuickFixSupport {
}
return commands.map((command, i) => {
return <modes.IQuickFix> {
command: TypeConverters.Command.from(command, ctx),
command: TypeConverters.Command.from(command, this._cachedCommands),
score: i
};
});

View File

@@ -494,10 +494,23 @@ export namespace SignatureHelp {
export namespace Command {
const _delegateId = '_internal_delegate_command';
const _cache: { [id: string]: vscode.Command } = Object.create(null);
let _idPool = 1;
export function from(command: vscode.Command, context: { commands: ExtHostCommands; disposables: IDisposable[]; }): modes.ICommand {
export function initialize(commands: ExtHostCommands) {
return commands.registerCommand(_delegateId, (args: [string]) => {
const [id] = args;
const command = _cache[id];
if (!command) {
// handle already disposed delegations graceful
return;
}
return commands.executeCommand(command.command, ...command.arguments);
});
}
export function from(command: vscode.Command, disposables: IDisposable[]): modes.ICommand {
if (!command) {
return;
@@ -510,22 +523,29 @@ export namespace Command {
if (!isFalsyOrEmpty(command.arguments)) {
// keep command around
const id = `${command.command}-no-args-wrapper-${_idPool++}`;
result.id = id;
// redirect to delegate command and store actual command
const id = `delegate/${_idPool++}/for/${command.command}`;
result.id = _delegateId;
result.arguments = [id];
_cache[id] = command;
const disposable1 = context.commands.registerCommand(id, () => context.commands.executeCommand(command.command, ..._cache[id].arguments));
const disposable2 = { dispose() { delete _cache[id]; } };
context.disposables.push(disposable1, disposable2);
disposables.push({
dispose() {
delete _cache[id];
}
});
}
return result;
}
export function to(command: modes.ICommand): vscode.Command {
let result = _cache[command.id];
let result: vscode.Command;
if (command.id === _delegateId) {
let [key] = command.arguments;
result = _cache[key];
}
if (!result) {
result = {
command: command.id,