From e142962e1b85751be3bc7b599467b0d181cb2063 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 14 May 2018 12:51:12 +0200 Subject: [PATCH] Multi-step input API (#49340) --- .vscode/launch.json | 1 + .../src/singlefolder-tests/window.test.ts | 78 +++++++++++++++++++ .../vscode-api-tests/src/typings/ref.d.ts | 1 + .../platform/quickinput/common/quickInput.ts | 15 ++-- src/vs/vscode.proposed.d.ts | 24 ++++++ .../electron-browser/mainThreadQuickOpen.ts | 54 +++++++++++-- src/vs/workbench/api/node/extHost.api.impl.ts | 7 +- src/vs/workbench/api/node/extHost.protocol.ts | 5 +- src/vs/workbench/api/node/extHostQuickOpen.ts | 59 +++++++++++--- .../browser/parts/quickinput/quickInput.ts | 42 ++++++++-- 10 files changed, 254 insertions(+), 32 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 04cf1cb01eba2..da7e7c092a558 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -83,6 +83,7 @@ "name": "VS Code API Tests (single folder)", "runtimeExecutable": "${execPath}", "args": [ + // "${workspaceFolder}", // Uncomment for running out of sources. "${workspaceFolder}/extensions/vscode-api-tests/testWorkspace", "--extensionDevelopmentPath=${workspaceFolder}/extensions/vscode-api-tests", "--extensionTestsPath=${workspaceFolder}/extensions/vscode-api-tests/out/singlefolder-tests" diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts index 84307f80d99fb..f6bbd075404bb 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts @@ -405,6 +405,84 @@ suite('window namespace tests', () => { return Promise.all([a, b]); }); + test('multiStepInput, two steps', async function () { + const picks = window.multiStepInput(async (input, token) => { + const pick1 = input.showQuickPick(['eins', 'zwei', 'drei']); + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + assert.equal(await pick1, 'eins'); + + const pick2 = input.showQuickPick(['vier', 'fünf', 'sechs']); + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + assert.equal(await pick2, 'vier'); + + return [ await pick1, await pick2 ]; + }); + assert.deepEqual(await picks, ['eins', 'vier']); + }); + + test('multiStepInput, interrupted by showQuickPick', async function () { + const picks = window.multiStepInput(async (input, token) => { + const pick1 = input.showQuickPick(['eins', 'zwei', 'drei']); + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + assert.equal(await pick1, 'eins'); + + assert.ok(!token.isCancellationRequested); + const otherPick = window.showQuickPick(['sieben', 'acht', 'neun']); + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + assert.equal(await otherPick, 'sieben'); + assert.ok(token.isCancellationRequested); + + const pick2 = input.showQuickPick(['vier', 'fünf', 'sechs']); + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + assert.equal(await pick2, undefined); + + return [ await pick1, await pick2 ]; + }); + assert.deepEqual(await picks, ['eins', undefined]); + }); + + test('multiStepInput, interrupted by multiStepInput', async function () { + const picks = window.multiStepInput(async (input, token) => { + const pick1 = input.showQuickPick(['eins', 'zwei', 'drei']); + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + assert.equal(await pick1, 'eins'); + + assert.ok(!token.isCancellationRequested); + const otherPick = window.multiStepInput(async (input, token) => { + const otherPick = window.showQuickPick(['sieben', 'acht', 'neun']); + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + assert.equal(await otherPick, 'sieben'); + + return otherPick; + }); + assert.equal(await otherPick, 'sieben'); + assert.ok(token.isCancellationRequested); + + const pick2 = input.showQuickPick(['vier', 'fünf', 'sechs']); + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + assert.equal(await pick2, undefined); + + return [ await pick1, await pick2 ]; + }); + assert.deepEqual(await picks, ['eins', undefined]); + }); + + test('multiStepInput, interrupted by error', async function () { + try { + const picks = window.multiStepInput(async (input, token) => { + const pick1 = input.showQuickPick(['eins', 'zwei', 'drei']); + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + assert.equal(await pick1, 'eins'); + + throw new Error('because'); + }); + await picks; + assert.ok(false); + } catch (error) { + assert.equal(error.message, 'because'); + } + }); + test('showWorkspaceFolderPick', function () { const p = window.showWorkspaceFolderPick(undefined); diff --git a/extensions/vscode-api-tests/src/typings/ref.d.ts b/extensions/vscode-api-tests/src/typings/ref.d.ts index 7780702916566..e3e47385d6634 100644 --- a/extensions/vscode-api-tests/src/typings/ref.d.ts +++ b/extensions/vscode-api-tests/src/typings/ref.d.ts @@ -4,4 +4,5 @@ *--------------------------------------------------------------------------------------------*/ /// +/// /// diff --git a/src/vs/platform/quickinput/common/quickInput.ts b/src/vs/platform/quickinput/common/quickInput.ts index b8aa997bdba6b..14edbfdfa229d 100644 --- a/src/vs/platform/quickinput/common/quickInput.ts +++ b/src/vs/platform/quickinput/common/quickInput.ts @@ -84,11 +84,7 @@ export interface IInputOptions { validateInput?: (input: string) => TPromise; } -export const IQuickInputService = createDecorator('quickInputService'); - -export interface IQuickInputService { - - _serviceBrand: any; +export interface IQuickInput { /** * Opens the quick input box for selecting items and returns a promise with the user selected item(s) if any. @@ -99,6 +95,15 @@ export interface IQuickInputService { * Opens the quick input box for text input and returns a promise with the user typed value if any. */ input(options?: IInputOptions, token?: CancellationToken): TPromise; +} + +export const IQuickInputService = createDecorator('quickInputService'); + +export interface IQuickInputService extends IQuickInput { + + _serviceBrand: any; + + multiStepInput(handler: (input: IQuickInput, token: CancellationToken) => Thenable, token?: CancellationToken): Thenable; focus(): void; diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 32be39d96c44f..97a964e0335a2 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -510,4 +510,28 @@ declare module 'vscode' { } //#endregion + + //#region Multi-step input + + export namespace window { + + /** + * Collect multiple inputs from the user. The provided handler will be called with a + * [`QuickInput`](#QuickInput) that should be used to control the UI. + * + * @param handler The callback that will collect the inputs. + */ + export function multiStepInput(handler: (input: QuickInput, token: CancellationToken) => Thenable, token?: CancellationToken): Thenable; + } + + /** + * Controls the UI within a multi-step input session. The handler passed to [`window.multiStepInput`](#window.multiStepInput) + * should use the instance of this interface passed to it to collect all inputs. + */ + export interface QuickInput { + showQuickPick: typeof window.showQuickPick; + showInputBox: typeof window.showInputBox; + } + + //#endregion } diff --git a/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts b/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts index 0d6947e576eb4..e98293c8a8a8a 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts @@ -6,11 +6,17 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { asWinJsPromise } from 'vs/base/common/async'; -import { IPickOptions, IInputOptions, IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { InputBoxOptions } from 'vscode'; +import { IPickOptions, IInputOptions, IQuickInputService, IQuickInput } from 'vs/platform/quickinput/common/quickInput'; +import { InputBoxOptions, CancellationToken } from 'vscode'; import { ExtHostContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, MyQuickPickItems, MainContext, IExtHostContext } from '../node/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; +interface MultiStepSession { + handle: number; + input: IQuickInput; + token: CancellationToken; +} + @extHostNamedCustomer(MainContext.MainThreadQuickOpen) export class MainThreadQuickOpen implements MainThreadQuickOpenShape { @@ -20,6 +26,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { private _doSetError: (error: Error) => any; private _contents: TPromise; private _token: number = 0; + private _multiStep: MultiStepSession; constructor( extHostContext: IExtHostContext, @@ -32,7 +39,13 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { public dispose(): void { } - $show(options: IPickOptions): TPromise { + $show(multiStepHandle: number | undefined, options: IPickOptions): TPromise { + + const multiStep = typeof multiStepHandle === 'number'; + if (multiStep && !(this._multiStep && multiStepHandle === this._multiStep.handle && !this._multiStep.token.isCancellationRequested)) { + return TPromise.as(undefined); + } + const input: IQuickInput = multiStep ? this._multiStep.input : this._quickInputService; const myToken = ++this._token; @@ -51,7 +64,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { }); if (options.canPickMany) { - return asWinJsPromise(token => this._quickInputService.pick(this._contents, options as { canPickMany: true }, token)).then(items => { + return asWinJsPromise(token => input.pick(this._contents, options as { canPickMany: true }, token)).then(items => { if (items) { return items.map(item => item.handle); } @@ -62,7 +75,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { } }); } else { - return asWinJsPromise(token => this._quickInputService.pick(this._contents, options, token)).then(item => { + return asWinJsPromise(token => input.pick(this._contents, options, token)).then(item => { if (item) { return item.handle; } @@ -91,7 +104,13 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { // ---- input - $input(options: InputBoxOptions, validateInput: boolean): TPromise { + $input(multiStepHandle: number | undefined, options: InputBoxOptions, validateInput: boolean): TPromise { + + const multiStep = typeof multiStepHandle === 'number'; + if (multiStep && !(this._multiStep && multiStepHandle === this._multiStep.handle && !this._multiStep.token.isCancellationRequested)) { + return TPromise.as(undefined); + } + const input: IQuickInput = multiStep ? this._multiStep.input : this._quickInputService; const inputOptions: IInputOptions = Object.create(null); @@ -110,6 +129,27 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { }; } - return asWinJsPromise(token => this._quickInputService.input(inputOptions, token)); + return asWinJsPromise(token => input.input(inputOptions, token)); + } + + // ---- Multi-step input + + $multiStep(handle: number): TPromise { + let outerReject: (err: any) => void; + let innerResolve: (value: void) => void; + const promise = new TPromise((_, rej) => outerReject = rej, () => innerResolve(undefined)); + this._quickInputService.multiStepInput((input, token) => { + this._multiStep = { handle, input, token }; + const promise = new TPromise(res => innerResolve = res); + token.onCancellationRequested(() => innerResolve(undefined)); + return promise; + }) + .then(() => promise.cancel(), err => outerReject(err)) + .then(() => { + if (this._multiStep && this._multiStep.handle === handle) { + this._multiStep = null; + } + }); + return promise; } } diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index b7cbc9ed57239..e1587f9724390 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -386,13 +386,16 @@ export function createApiFactory( return extHostMessageService.showMessage(extension, Severity.Error, message, first, rest); }, showQuickPick(items: any, options: vscode.QuickPickOptions, token?: vscode.CancellationToken): any { - return extHostQuickOpen.showQuickPick(items, options, token); + return extHostQuickOpen.showQuickPick(undefined, items, options, token); }, showWorkspaceFolderPick(options: vscode.WorkspaceFolderPickOptions) { return extHostQuickOpen.showWorkspaceFolderPick(options); }, showInputBox(options?: vscode.InputBoxOptions, token?: vscode.CancellationToken) { - return extHostQuickOpen.showInput(options, token); + return extHostQuickOpen.showInput(undefined, options, token); + }, + multiStepInput(handler: (input: vscode.QuickInput, token: vscode.CancellationToken) => Thenable, token?: vscode.CancellationToken): Thenable { + return extHostQuickOpen.multiStepInput(handler, token); }, showOpenDialog(options) { return extHostDialogs.showOpenDialog(options); diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 870a18c23580e..e27c0df03d213 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -332,10 +332,11 @@ export interface MyQuickPickItems extends IPickOpenEntry { handle: number; } export interface MainThreadQuickOpenShape extends IDisposable { - $show(options: IPickOptions): TPromise; + $show(multiStepHandle: number | undefined, options: IPickOptions): TPromise; $setItems(items: MyQuickPickItems[]): TPromise; $setError(error: Error): TPromise; - $input(options: vscode.InputBoxOptions, validateInput: boolean): TPromise; + $input(multiStepHandle: number | undefined, options: vscode.InputBoxOptions, validateInput: boolean): TPromise; + $multiStep(handle: number): TPromise; } export interface MainThreadStatusBarShape extends IDisposable { diff --git a/src/vs/workbench/api/node/extHostQuickOpen.ts b/src/vs/workbench/api/node/extHostQuickOpen.ts index 275f124a1e3f7..6f4bb3137c0c4 100644 --- a/src/vs/workbench/api/node/extHostQuickOpen.ts +++ b/src/vs/workbench/api/node/extHostQuickOpen.ts @@ -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; + 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, options: QuickPickOptions & { canPickMany: true; }, token?: CancellationToken): Thenable; - showQuickPick(itemsOrItemsPromise: string[] | Thenable, options?: QuickPickOptions, token?: CancellationToken): Thenable; - showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Thenable, options?: QuickPickOptions, token?: CancellationToken): Thenable; - showQuickPick(itemsOrItemsPromise: Item[] | Thenable, options?: QuickPickOptions, token: CancellationToken = CancellationToken.None): Thenable { + showQuickPick(multiStepHandle: number | undefined, itemsOrItemsPromise: QuickPickItem[] | Thenable, options: QuickPickOptions & { canPickMany: true; }, token?: CancellationToken): Thenable; + showQuickPick(multiStepHandle: number | undefined, itemsOrItemsPromise: string[] | Thenable, options?: QuickPickOptions, token?: CancellationToken): Thenable; + showQuickPick(multiStepHandle: number | undefined, itemsOrItemsPromise: QuickPickItem[] | Thenable, options?: QuickPickOptions, token?: CancellationToken): Thenable; + showQuickPick(multiStepHandle: number | undefined, itemsOrItemsPromise: Item[] | Thenable, options?: QuickPickOptions, token: CancellationToken = CancellationToken.None): Thenable { // clear state from last invocation this._onDidSelectItem = undefined; const itemsPromise = >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 { + showInput(multiStepHandle: number | undefined, options?: InputBoxOptions, token: CancellationToken = CancellationToken.None): Thenable { // 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(handler: (input: QuickInput, token: CancellationToken) => Thenable, clientToken: CancellationToken = CancellationToken.None): Thenable { + 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([ + 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; + }); + } } diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.ts index 83314d3a8ed9d..75d7ab833d336 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.ts @@ -7,7 +7,7 @@ import 'vs/css!./quickInput'; import { Component } from 'vs/workbench/common/component'; -import { IQuickInputService, IPickOpenEntry, IPickOptions, IInputOptions, IQuickNavigateConfiguration } from 'vs/platform/quickinput/common/quickInput'; +import { IQuickInputService, IPickOpenEntry, IPickOptions, IInputOptions, IQuickNavigateConfiguration, IQuickInput } from 'vs/platform/quickinput/common/quickInput'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import * as dom from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -16,7 +16,7 @@ import { contrastBorder, widgetShadow } from 'vs/platform/theme/common/colorRegi import { SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND } from 'vs/workbench/common/theme'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { TPromise } from 'vs/base/common/winjs.base'; -import { CancellationToken } from 'vs/base/common/cancellation'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { QuickInputList } from './quickInputList'; import { QuickInputBox } from './quickInputBox'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -361,6 +361,7 @@ export class QuickInputService extends Component implements IQuickInputService { private inQuickOpenContext: IContextKey; private controller: InputController; + private multiStepHandle: CancellationTokenSource; constructor( @IEnvironmentService private environmentService: IEnvironmentService, @@ -556,7 +557,11 @@ export class QuickInputService extends Component implements IQuickInputService { } pick(picks: TPromise, options: O = {}, token?: CancellationToken): TPromise { - return this.show({ + return this._pick(undefined, picks, options, token); + } + + private _pick(handle: CancellationTokenSource | undefined, picks: TPromise, options: O = {}, token?: CancellationToken): TPromise { + return this._show(handle, { type: options.canPickMany ? 'pickMany' : 'pickOne', picks, placeHolder: options.placeHolder, @@ -567,7 +572,11 @@ export class QuickInputService extends Component implements IQuickInputService { } input(options: IInputOptions = {}, token?: CancellationToken): TPromise { - return this.show({ + return this._input(undefined, options, token); + } + + private _input(handle: CancellationTokenSource | undefined, options: IInputOptions = {}, token?: CancellationToken): TPromise { + return this._show(handle, { type: 'textInput', value: options.value, valueSelection: options.valueSelection, @@ -579,9 +588,17 @@ export class QuickInputService extends Component implements IQuickInputService { }, token); } - show | PickManyParameters>(parameters: P, token?: CancellationToken): TPromise

? T[] : T>; - show(parameters: TextInputParameters, token?: CancellationToken): TPromise; - show(parameters: InputParameters, token: CancellationToken = CancellationToken.None): TPromise { + private _show | PickManyParameters>(multiStepHandle: CancellationTokenSource | undefined, parameters: P, token?: CancellationToken): TPromise

? T[] : T>; + private _show(multiStepHandle: CancellationTokenSource | undefined, parameters: TextInputParameters, token?: CancellationToken): TPromise; + private _show(multiStepHandle: CancellationTokenSource | undefined, parameters: InputParameters, token: CancellationToken = CancellationToken.None): TPromise { + if (multiStepHandle && multiStepHandle !== this.multiStepHandle) { + multiStepHandle.cancel(); + return TPromise.as(undefined); + } + if (!multiStepHandle && this.multiStepHandle) { + this.multiStepHandle.cancel(); + } + this.create(); this.quickOpenService.close(); if (this.controller) { @@ -644,6 +661,17 @@ export class QuickInputService extends Component implements IQuickInputService { } } + multiStepInput(handler: (input: IQuickInput, token: CancellationToken) => Thenable, token = CancellationToken.None): Thenable { + if (this.multiStepHandle) { + this.multiStepHandle.cancel(); + } + this.multiStepHandle = new CancellationTokenSource(); + return TPromise.wrap(handler({ + pick: this._pick.bind(this, this.multiStepHandle), + input: this._input.bind(this, this.multiStepHandle) + }, this.multiStepHandle.token)); + } + focus() { if (this.isDisplayed()) { this.ui.inputBox.setFocus();