diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.ts index dfd57a3fe5639f5d7f3597978488d425f552e62c..cbe059709d61e66277b2129714b3df9201eae243 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.ts @@ -28,10 +28,10 @@ import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; import { attachBadgeStyler, attachProgressBarStyler, attachButtonStyler } from 'vs/platform/theme/common/styler'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; -import { chain } from 'vs/base/common/event'; +import { chain, debounceEvent } from 'vs/base/common/event'; import { Button } from 'vs/base/browser/ui/button/button'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { onUnexpectedError } from 'vs/base/common/errors'; +import { onUnexpectedError, canceled } from 'vs/base/common/errors'; const $ = dom.$; @@ -69,7 +69,7 @@ interface InputController { readonly showUI: { [k in keyof QuickInputUI]?: boolean; } & { ok?: boolean; }; readonly result: TPromise; readonly ready: TPromise; - readonly resolve: (ok?: true | Thenable) => void; + readonly resolve: (ok?: true | Thenable) => void | TPromise; } class SelectManyController implements InputController { @@ -112,13 +112,14 @@ class TextInputController implements InputController { public showUI = { inputBox: true, message: true }; public result: TPromise; public ready = TPromise.as(null); - public resolve: (ok?: true | Thenable) => void; + public resolveResult: (string) => void; private validationValue: string; + private validation: TPromise; private disposables: IDisposable[] = []; - constructor(ui: QuickInputUI, parameters: TextInputParameters) { + constructor(private ui: QuickInputUI, private parameters: TextInputParameters) { this.result = new TPromise((resolve, reject, progress) => { - this.resolve = ok => resolve(ok === true ? ui.inputBox.value : ok); + this.resolveResult = resolve; }); this.result.then(() => this.dispose()); @@ -130,19 +131,51 @@ class TextInputController implements InputController { ui.message.textContent = defaultMessage; if (parameters.validateInput) { - this.disposables.push(ui.inputBox.onDidChange(value => { - this.validationValue = value; - parameters.validateInput(value) + const onDidChange = debounceEvent(ui.inputBox.onDidChange, (last, cur) => cur, 100); + this.disposables.push(onDidChange(() => { + this.updatedValidation() .then(validationError => { - if (this.validationValue === value) { - ui.message.textContent = validationError || defaultMessage; - } + ui.message.textContent = validationError || defaultMessage; }) .then(null, onUnexpectedError); })); } } + resolve(ok?: true | Thenable) { + if (ok === true) { + return this.updatedValidation() + .then(validationError => { + if (validationError) { + throw canceled(); + } + this.resolveResult(this.ui.inputBox.value); + }); + } else { + this.resolveResult(ok); + } + return null; + } + + private updatedValidation() { + if (this.parameters.validateInput) { + const value = this.ui.inputBox.value; + if (value !== this.validationValue) { + this.validationValue = value; + this.validation = this.parameters.validateInput(value) + .then(validationError => { + if (this.validationValue !== value) { + throw canceled(); + } + return validationError; + }); + } + } else if (!this.validation) { + this.validation = TPromise.as(null); + } + return this.validation; + } + private dispose() { this.disposables = dispose(this.disposables); } @@ -315,7 +348,13 @@ export class QuickInputService extends Component implements IQuickInputService { private close(ok?: true | Thenable) { if (this.controller) { - this.controller.resolve(ok); + const resolved = this.controller.resolve(ok); + if (resolved) { + resolved + .then(() => this.container.style.display = 'none') + .then(null, onUnexpectedError); + return; + } } this.container.style.display = 'none'; } diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputBox.ts b/src/vs/workbench/browser/parts/quickinput/quickInputBox.ts index 0acd72de5f9c5ed41c183980f53c01daa468b45d..92e632c5fad51f4b3c25ab3bc6e144eaf7f5f4a0 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInputBox.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInputBox.ts @@ -40,13 +40,13 @@ export class QuickInputBox { inputElement.setAttribute('aria-autocomplete', 'list'); } - onKeyDown(handler: (event: StandardKeyboardEvent) => void): IDisposable { + onKeyDown = (handler: (event: StandardKeyboardEvent) => void): IDisposable => { return dom.addDisposableListener(this.inputBox.inputElement, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { handler(new StandardKeyboardEvent(e)); }); } - onDidChange(handler: (event: string) => void): IDisposable { + onDidChange = (handler: (event: string) => void): IDisposable => { return this.inputBox.onDidChange(handler); }