Skip to content

Commit

Permalink
Validate input on Enter (#48116)
Browse files Browse the repository at this point in the history
  • Loading branch information
chrmarti committed Apr 23, 2018
1 parent 2b4dfcc commit d3cb5b2
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 15 deletions.
65 changes: 52 additions & 13 deletions src/vs/workbench/browser/parts/quickinput/quickInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.$;

Expand Down Expand Up @@ -69,7 +69,7 @@ interface InputController<R> {
readonly showUI: { [k in keyof QuickInputUI]?: boolean; } & { ok?: boolean; };
readonly result: TPromise<R>;
readonly ready: TPromise<void>;
readonly resolve: (ok?: true | Thenable<never>) => void;
readonly resolve: (ok?: true | Thenable<never>) => void | TPromise<void>;
}

class SelectManyController<T extends IPickOpenEntry> implements InputController<T[]> {
Expand Down Expand Up @@ -112,13 +112,14 @@ class TextInputController implements InputController<string> {
public showUI = { inputBox: true, message: true };
public result: TPromise<string>;
public ready = TPromise.as(null);
public resolve: (ok?: true | Thenable<never>) => void;
public resolveResult: (string) => void;
private validationValue: string;
private validation: TPromise<string>;
private disposables: IDisposable[] = [];

constructor(ui: QuickInputUI, parameters: TextInputParameters) {
constructor(private ui: QuickInputUI, private parameters: TextInputParameters) {
this.result = new TPromise<string>((resolve, reject, progress) => {
this.resolve = ok => resolve(ok === true ? ui.inputBox.value : ok);
this.resolveResult = resolve;
});
this.result.then(() => this.dispose());

Expand All @@ -130,19 +131,51 @@ class TextInputController implements InputController<string> {
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<never>) {
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);
}
Expand Down Expand Up @@ -315,7 +348,13 @@ export class QuickInputService extends Component implements IQuickInputService {

private close(ok?: true | Thenable<never>) {
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';
}
Expand Down
4 changes: 2 additions & 2 deletions src/vs/workbench/browser/parts/quickinput/quickInputBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down

0 comments on commit d3cb5b2

Please sign in to comment.