123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- /*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
- return new (P || (P = Promise))(function (resolve, reject) {
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
- step((generator = generator.apply(thisArg, _arguments || [])).next());
- });
- };
- import { timeout } from '../../../base/common/async.js';
- import { CancellationTokenSource } from '../../../base/common/cancellation.js';
- import { Disposable, DisposableStore, MutableDisposable } from '../../../base/common/lifecycle.js';
- export var TriggerAction;
- (function (TriggerAction) {
- /**
- * Do nothing after the button was clicked.
- */
- TriggerAction[TriggerAction["NO_ACTION"] = 0] = "NO_ACTION";
- /**
- * Close the picker.
- */
- TriggerAction[TriggerAction["CLOSE_PICKER"] = 1] = "CLOSE_PICKER";
- /**
- * Update the results of the picker.
- */
- TriggerAction[TriggerAction["REFRESH_PICKER"] = 2] = "REFRESH_PICKER";
- /**
- * Remove the item from the picker.
- */
- TriggerAction[TriggerAction["REMOVE_ITEM"] = 3] = "REMOVE_ITEM";
- })(TriggerAction || (TriggerAction = {}));
- function isPicksWithActive(obj) {
- const candidate = obj;
- return Array.isArray(candidate.items);
- }
- function isFastAndSlowPicks(obj) {
- const candidate = obj;
- return !!candidate.picks && candidate.additionalPicks instanceof Promise;
- }
- export class PickerQuickAccessProvider extends Disposable {
- constructor(prefix, options) {
- super();
- this.prefix = prefix;
- this.options = options;
- }
- provide(picker, token) {
- var _a;
- const disposables = new DisposableStore();
- // Apply options if any
- picker.canAcceptInBackground = !!((_a = this.options) === null || _a === void 0 ? void 0 : _a.canAcceptInBackground);
- // Disable filtering & sorting, we control the results
- picker.matchOnLabel = picker.matchOnDescription = picker.matchOnDetail = picker.sortByLabel = false;
- // Set initial picks and update on type
- let picksCts = undefined;
- const picksDisposable = disposables.add(new MutableDisposable());
- const updatePickerItems = () => __awaiter(this, void 0, void 0, function* () {
- const picksDisposables = picksDisposable.value = new DisposableStore();
- // Cancel any previous ask for picks and busy
- picksCts === null || picksCts === void 0 ? void 0 : picksCts.dispose(true);
- picker.busy = false;
- // Create new cancellation source for this run
- picksCts = new CancellationTokenSource(token);
- // Collect picks and support both long running and short or combined
- const picksToken = picksCts.token;
- const picksFilter = picker.value.substr(this.prefix.length).trim();
- const providedPicks = this._getPicks(picksFilter, picksDisposables, picksToken);
- const applyPicks = (picks, skipEmpty) => {
- var _a;
- let items;
- let activeItem = undefined;
- if (isPicksWithActive(picks)) {
- items = picks.items;
- activeItem = picks.active;
- }
- else {
- items = picks;
- }
- if (items.length === 0) {
- if (skipEmpty) {
- return false;
- }
- if (picksFilter.length > 0 && ((_a = this.options) === null || _a === void 0 ? void 0 : _a.noResultsPick)) {
- items = [this.options.noResultsPick];
- }
- }
- picker.items = items;
- if (activeItem) {
- picker.activeItems = [activeItem];
- }
- return true;
- };
- // No Picks
- if (providedPicks === null) {
- // Ignore
- }
- // Fast and Slow Picks
- else if (isFastAndSlowPicks(providedPicks)) {
- let fastPicksApplied = false;
- let slowPicksApplied = false;
- yield Promise.all([
- // Fast Picks: to reduce amount of flicker, we race against
- // the slow picks over 500ms and then set the fast picks.
- // If the slow picks are faster, we reduce the flicker by
- // only setting the items once.
- (() => __awaiter(this, void 0, void 0, function* () {
- yield timeout(PickerQuickAccessProvider.FAST_PICKS_RACE_DELAY);
- if (picksToken.isCancellationRequested) {
- return;
- }
- if (!slowPicksApplied) {
- fastPicksApplied = applyPicks(providedPicks.picks, true /* skip over empty to reduce flicker */);
- }
- }))(),
- // Slow Picks: we await the slow picks and then set them at
- // once together with the fast picks, but only if we actually
- // have additional results.
- (() => __awaiter(this, void 0, void 0, function* () {
- picker.busy = true;
- try {
- const awaitedAdditionalPicks = yield providedPicks.additionalPicks;
- if (picksToken.isCancellationRequested) {
- return;
- }
- let picks;
- let activePick = undefined;
- if (isPicksWithActive(providedPicks.picks)) {
- picks = providedPicks.picks.items;
- activePick = providedPicks.picks.active;
- }
- else {
- picks = providedPicks.picks;
- }
- let additionalPicks;
- let additionalActivePick = undefined;
- if (isPicksWithActive(awaitedAdditionalPicks)) {
- additionalPicks = awaitedAdditionalPicks.items;
- additionalActivePick = awaitedAdditionalPicks.active;
- }
- else {
- additionalPicks = awaitedAdditionalPicks;
- }
- if (additionalPicks.length > 0 || !fastPicksApplied) {
- // If we do not have any activePick or additionalActivePick
- // we try to preserve the currently active pick from the
- // fast results. This fixes an issue where the user might
- // have made a pick active before the additional results
- // kick in.
- // See https://github.com/microsoft/vscode/issues/102480
- let fallbackActivePick = undefined;
- if (!activePick && !additionalActivePick) {
- const fallbackActivePickCandidate = picker.activeItems[0];
- if (fallbackActivePickCandidate && picks.indexOf(fallbackActivePickCandidate) !== -1) {
- fallbackActivePick = fallbackActivePickCandidate;
- }
- }
- applyPicks({
- items: [...picks, ...additionalPicks],
- active: activePick || additionalActivePick || fallbackActivePick
- });
- }
- }
- finally {
- if (!picksToken.isCancellationRequested) {
- picker.busy = false;
- }
- slowPicksApplied = true;
- }
- }))()
- ]);
- }
- // Fast Picks
- else if (!(providedPicks instanceof Promise)) {
- applyPicks(providedPicks);
- }
- // Slow Picks
- else {
- picker.busy = true;
- try {
- const awaitedPicks = yield providedPicks;
- if (picksToken.isCancellationRequested) {
- return;
- }
- applyPicks(awaitedPicks);
- }
- finally {
- if (!picksToken.isCancellationRequested) {
- picker.busy = false;
- }
- }
- }
- });
- disposables.add(picker.onDidChangeValue(() => updatePickerItems()));
- updatePickerItems();
- // Accept the pick on accept and hide picker
- disposables.add(picker.onDidAccept(event => {
- const [item] = picker.selectedItems;
- if (typeof (item === null || item === void 0 ? void 0 : item.accept) === 'function') {
- if (!event.inBackground) {
- picker.hide(); // hide picker unless we accept in background
- }
- item.accept(picker.keyMods, event);
- }
- }));
- // Trigger the pick with button index if button triggered
- disposables.add(picker.onDidTriggerItemButton(({ button, item }) => __awaiter(this, void 0, void 0, function* () {
- var _b, _c;
- if (typeof item.trigger === 'function') {
- const buttonIndex = (_c = (_b = item.buttons) === null || _b === void 0 ? void 0 : _b.indexOf(button)) !== null && _c !== void 0 ? _c : -1;
- if (buttonIndex >= 0) {
- const result = item.trigger(buttonIndex, picker.keyMods);
- const action = (typeof result === 'number') ? result : yield result;
- if (token.isCancellationRequested) {
- return;
- }
- switch (action) {
- case TriggerAction.NO_ACTION:
- break;
- case TriggerAction.CLOSE_PICKER:
- picker.hide();
- break;
- case TriggerAction.REFRESH_PICKER:
- updatePickerItems();
- break;
- case TriggerAction.REMOVE_ITEM:
- const index = picker.items.indexOf(item);
- if (index !== -1) {
- const items = picker.items.slice();
- const removed = items.splice(index, 1);
- const activeItems = picker.activeItems.filter(activeItem => activeItem !== removed[0]);
- const keepScrollPositionBefore = picker.keepScrollPosition;
- picker.keepScrollPosition = true;
- picker.items = items;
- if (activeItems) {
- picker.activeItems = activeItems;
- }
- picker.keepScrollPosition = keepScrollPositionBefore;
- }
- break;
- }
- }
- }
- })));
- return disposables;
- }
- }
- PickerQuickAccessProvider.FAST_PICKS_RACE_DELAY = 200; // timeout before we accept fast results before slow results are present
|