Multi-step input API (#49340)

This commit is contained in:
Christof Marti
2018-05-14 12:51:12 +02:00
parent 1055627274
commit e142962e1b
10 changed files with 254 additions and 32 deletions

View File

@@ -6,11 +6,12 @@
import { TPromise } from 'vs/base/common/winjs.base';
import { wireCancellationToken, asWinJsPromise } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { QuickPickOptions, QuickPickItem, InputBoxOptions, WorkspaceFolderPickOptions, WorkspaceFolder } from 'vscode';
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
import { QuickPickOptions, QuickPickItem, InputBoxOptions, WorkspaceFolderPickOptions, WorkspaceFolder, QuickInput } from 'vscode';
import { MainContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, MyQuickPickItems, IMainContext } from './extHost.protocol';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
import { isPromiseCanceledError } from 'vs/base/common/errors';
export type Item = string | QuickPickItem;
@@ -23,23 +24,25 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
private _onDidSelectItem: (handle: number) => void;
private _validateInput: (input: string) => string | Thenable<string>;
private _nextMultiStepHandle = 1;
constructor(mainContext: IMainContext, workspace: ExtHostWorkspace, commands: ExtHostCommands) {
this._proxy = mainContext.getProxy(MainContext.MainThreadQuickOpen);
this._workspace = workspace;
this._commands = commands;
}
showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Thenable<QuickPickItem[]>, options: QuickPickOptions & { canPickMany: true; }, token?: CancellationToken): Thenable<QuickPickItem[] | undefined>;
showQuickPick(itemsOrItemsPromise: string[] | Thenable<string[]>, options?: QuickPickOptions, token?: CancellationToken): Thenable<string | undefined>;
showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Thenable<QuickPickItem[]>, options?: QuickPickOptions, token?: CancellationToken): Thenable<QuickPickItem | undefined>;
showQuickPick(itemsOrItemsPromise: Item[] | Thenable<Item[]>, options?: QuickPickOptions, token: CancellationToken = CancellationToken.None): Thenable<Item | Item[] | undefined> {
showQuickPick(multiStepHandle: number | undefined, itemsOrItemsPromise: QuickPickItem[] | Thenable<QuickPickItem[]>, options: QuickPickOptions & { canPickMany: true; }, token?: CancellationToken): Thenable<QuickPickItem[] | undefined>;
showQuickPick(multiStepHandle: number | undefined, itemsOrItemsPromise: string[] | Thenable<string[]>, options?: QuickPickOptions, token?: CancellationToken): Thenable<string | undefined>;
showQuickPick(multiStepHandle: number | undefined, itemsOrItemsPromise: QuickPickItem[] | Thenable<QuickPickItem[]>, options?: QuickPickOptions, token?: CancellationToken): Thenable<QuickPickItem | undefined>;
showQuickPick(multiStepHandle: number | undefined, itemsOrItemsPromise: Item[] | Thenable<Item[]>, options?: QuickPickOptions, token: CancellationToken = CancellationToken.None): Thenable<Item | Item[] | undefined> {
// clear state from last invocation
this._onDidSelectItem = undefined;
const itemsPromise = <TPromise<Item[]>>TPromise.wrap(itemsOrItemsPromise);
const quickPickWidget = this._proxy.$show({
const quickPickWidget = this._proxy.$show(multiStepHandle, {
placeHolder: options && options.placeHolder,
matchOnDescription: options && options.matchOnDescription,
matchOnDetail: options && options.matchOnDetail,
@@ -115,12 +118,12 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
// ---- input
showInput(options?: InputBoxOptions, token: CancellationToken = CancellationToken.None): Thenable<string> {
showInput(multiStepHandle: number | undefined, options?: InputBoxOptions, token: CancellationToken = CancellationToken.None): Thenable<string> {
// global validate fn used in callback below
this._validateInput = options && options.validateInput;
const promise = this._proxy.$input(options, typeof this._validateInput === 'function');
const promise = this._proxy.$input(multiStepHandle, options, typeof this._validateInput === 'function');
return wireCancellationToken(token, promise, true);
}
@@ -142,4 +145,42 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
return this._workspace.getWorkspaceFolders().filter(folder => folder.uri.toString() === selectedFolder.uri.toString())[0];
});
}
// ---- Multi-step input
multiStepInput<T>(handler: (input: QuickInput, token: CancellationToken) => Thenable<T>, clientToken: CancellationToken = CancellationToken.None): Thenable<T> {
const handle = this._nextMultiStepHandle++;
const remotePromise = this._proxy.$multiStep(handle);
const cancellationSource = new CancellationTokenSource();
const handlerPromise = TPromise.wrap(handler({
showQuickPick: this.showQuickPick.bind(this, handle),
showInputBox: this.showInput.bind(this, handle)
}, cancellationSource.token));
clientToken.onCancellationRequested(() => {
remotePromise.cancel();
cancellationSource.cancel();
});
return TPromise.join<void, T>([
remotePromise.then(() => {
throw new Error('Unexpectedly fulfilled promise.');
}, err => {
if (!isPromiseCanceledError(err)) {
throw err;
}
cancellationSource.cancel();
}),
handlerPromise.then(result => {
remotePromise.cancel();
return result;
}, err => {
remotePromise.cancel();
throw err;
})
]).then(([_, result]) => result, ([remoteErr, handlerErr]) => {
throw handlerErr || remoteErr;
});
}
}