blob: ec1dd23900ab2c1b10bb963384f3ef2ab3be656d [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/* eslint-disable rulesdir/no_underscored_properties */
import * as Diff from '../diff/diff.js';
import * as UI from '../ui/legacy/legacy.js'; // eslint-disable-line no-unused-vars
import {FilteredListWidget, Provider} from './FilteredListWidget.js';
export interface QuickPickItem {
label: string;
description?: string;
detail?: string;
}
export interface QuickPickOptions {
placeHolder: string;
matchOnDescription?: boolean;
matchOnDetail?: boolean;
}
export class QuickPick {
private constructor() {
throw new ReferenceError('Instance type not implemented.');
}
static show(items: QuickPickItem[], options: QuickPickOptions): Promise<QuickPickItem|undefined> {
let canceledPromise: Promise<undefined> =
new Promise<undefined>(_r => {}); // Intentionally creates an unresolved promise
const fulfilledPromise = new Promise<QuickPickItem>(resolve => {
const provider =
new QuickPickProvider(items, resolve, options.matchOnDescription ? 0.5 : 0, options.matchOnDetail ? 0.25 : 0);
const widget = new FilteredListWidget(provider);
widget.setPlaceholder(options.placeHolder);
widget.setPromptTitle(options.placeHolder);
widget.showAsDialog(options.placeHolder);
canceledPromise = (widget.once('hidden') as Promise<undefined>);
widget.setQuery('');
});
return Promise.race([fulfilledPromise, canceledPromise]).then(values => {
// If it was fulfilled, then `result` will have a value.
// If it was canceled, then `result` will be undefined.
// Either way, it has the value that we want.
return values;
});
}
}
class QuickPickProvider extends Provider {
_resolve: Function;
_items: QuickPickItem[];
_matchOnDescription: number;
_matchOnDetail: number;
constructor(
items: QuickPickItem[], resolve: Function, matchOnDescription: number|undefined = 0.5,
matchOnDetail: number|undefined = 0.25) {
super();
this._resolve = resolve;
this._items = items;
this._matchOnDescription = matchOnDescription;
this._matchOnDetail = matchOnDetail;
}
itemCount(): number {
return this._items.length;
}
itemKeyAt(itemIndex: number): string {
const item = this._items[itemIndex];
let key = item.label;
if (this._matchOnDescription) {
key += ' ' + item.description;
}
if (this._matchOnDetail) {
key += ' ' + item.detail;
}
return key;
}
itemScoreAt(itemIndex: number, query: string): number {
const item = this._items[itemIndex];
const test = query.toLowerCase();
let score = Diff.Diff.DiffWrapper.characterScore(test, item.label.toLowerCase());
if (this._matchOnDescription && item.description) {
const descriptionScore = Diff.Diff.DiffWrapper.characterScore(test, item.description.toLowerCase());
score += descriptionScore * this._matchOnDescription;
}
if (this._matchOnDetail && item.detail) {
const detailScore = Diff.Diff.DiffWrapper.characterScore(test, item.detail.toLowerCase());
score += detailScore * this._matchOnDetail;
}
return score;
}
renderItem(itemIndex: number, query: string, titleElement: Element, subtitleElement: Element): void {
const item = this._items[itemIndex];
titleElement.removeChildren();
const labelElement = titleElement.createChild('span');
UI.UIUtils.createTextChild(labelElement, item.label);
FilteredListWidget.highlightRanges(titleElement, query, true);
if (item.description) {
const descriptionElement = titleElement.createChild('span', 'quickpick-description');
UI.UIUtils.createTextChild(descriptionElement, item.description);
if (this._matchOnDescription) {
FilteredListWidget.highlightRanges(descriptionElement, query, true);
}
}
if (item.detail) {
UI.UIUtils.createTextChild(subtitleElement, item.detail);
if (this._matchOnDetail) {
FilteredListWidget.highlightRanges(subtitleElement, query, true);
}
}
}
renderAsTwoRows(): boolean {
return this._items.some(i => Boolean(i.detail));
}
selectItem(itemIndex: number|null, _promptValue: string): void {
if (typeof itemIndex === 'number') {
this._resolve(this._items[itemIndex]);
return;
}
this._resolve(undefined);
}
}