Change:
We have a temporary fix for the release for but we need to do something better long term.
while working on quick access for tasks I noticed that we seem to be having 2 pickers for the same thing (?): Task quick open: ! "Run Task" picker: ! Is that intentional? Should the one be merged into the other?
Yes, they do not share much code :( as part of improving the quick pick I will converge them.
sounds good, please only change  and leave the other one because I plan to delete those old ones soon.

Discussion:
return this.visible ? this.ui.inputBox.hasFocus() : false; } <ins> public focusOnInput() { this.ui.inputBox.setFocus(); } </ins> onDidChangeSelection = this.onDidChangeSelectionEmitter.event; onDidTriggerItemButton = this.onDidTriggerItemButtonEmitter.event;

validationMessage: string | undefined; inputHasFocus(): boolean; <ins> focusOnInput(): void; </ins> } export interface IInputBox extends IQuickInput {

import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IHostService } from 'vs/workbench/services/host/browser/host'; <del> import { INotificationService, IPromptChoice } from 'vs/platform/notification/common/notification'; </del> <ins> import { INotificationService } from 'vs/platform/notification/common/notification'; </ins> import { IDialogService, IConfirmationResult } from 'vs/platform/dialogs/common/dialogs'; import { IModelService } from 'vs/editor/common/services/modelService';

import * as TaskConfig from '../common/taskConfiguration'; import { TerminalTaskSystem } from './terminalTaskSystem'; <del> import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; </del> <ins> import { IQuickInputService, IQuickPickItem, QuickPickInput, IQuickPick } from 'vs/platform/quickinput/common/quickInput'; </ins> import { TaskDefinitionRegistry } from 'vs/workbench/contrib/tasks/common/taskDefinitionRegistry'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';

import { find } from 'vs/base/common/arrays'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { IViewsService } from 'vs/workbench/common/views'; <ins> import { ProviderProgressMananger } from 'vs/workbench/contrib/tasks/browser/providerProgressManager'; </ins> const QUICKOPEN_HISTORY_LIMIT_CONFIG = 'task.quickOpen.history'; const QUICKOPEN_DETAIL_CONFIG = 'task.quickOpen.detail';

private _providers: Map<number, ITaskProvider>; private _providerTypes: Map<number, string>; protected _taskSystemInfos: Map<string, TaskSystemInfo>; <ins> private _providerProgressManager: ProviderProgressMananger | undefined; </ins> protected _workspaceTasksPromise?: Promise<Map<string, WorkspaceFolderTaskResult>>; protected _areJsonTasksSupportedPromise: Promise<boolean> = Promise.resolve(false);

protected abstract getTaskSystem(): ITaskSystem; <del> private async provideTasksWithWarning(provider: ITaskProvider, type: string, validTypes: IStringDictionary<boolean>): Promise<TaskSet> { </del> <ins> private async provideTasksWithWarning(provider: ITaskProvider, type: string, validTypes: IStringDictionary<boolean>): Promise<TaskSet | undefined> { </ins> return new Promise<TaskSet>(async (resolve, reject) => { let isDone = false; <del> provider.provideTasks(validTypes).then((value) => { </del> <ins> let disposable: IDisposable | undefined; const providePromise = provider.provideTasks(validTypes); this._providerProgressManager?.addProvider(type, providePromise); disposable = this._providerProgressManager?.canceled.token.onCancellationRequested(() => { if (!isDone) { resolve(); } }); providePromise.then((value) => { </ins> isDone = true; <ins> disposable?.dispose(); </ins> resolve(value); }, (e) => { isDone = true; <ins> disposable?.dispose(); </ins> reject(e); }); <del> let settingValue: boolean | string[] = this.configurationService.getValue('task.slowProviderWarning'); if ((settingValue === true) || (Types.isStringArray(settingValue) && (settingValue.indexOf(type) < 0))) { setTimeout(() => { if (!isDone) { const settings: IPromptChoice = { label: nls.localize('TaskSystem.slowProvider.settings', "Settings"), run: () => this.preferencesService.openSettings(false, undefined) }; const disableAll: IPromptChoice = { label: nls.localize('TaskSystem.slowProvider.disableAll', "Disable All"), run: () => this.configurationService.updateValue('task.autoDetect', 'off') }; const dontShow: IPromptChoice = { label: nls.localize('TaskSystem.slowProvider.dontShow', "Don't warn again for {0} tasks", type), run: () => { if (!Types.isStringArray(settingValue)) { settingValue = []; } settingValue.push(type); return this.configurationService.updateValue('task.slowProviderWarning', settingValue); } }; this.notificationService.prompt(Severity.Warning, nls.localize('TaskSystem.slowProvider', "The {0} task provider is slow. The extension that provides {0} tasks may provide a setting to disable it, or you can disable all tasks providers", type), [settings, disableAll, dontShow]); } }, 4000); } </del> }); }

TaskDefinitionRegistry.all().forEach(definition => validTypes[definition.taskType] = true); validTypes['shell'] = true; validTypes['process'] = true; <ins> this._providerProgressManager = new ProviderProgressMananger(); </ins> return new Promise<TaskSet[]>(resolve => { let result: TaskSet[] = []; let counter: number = 0; <del> let done = (value: TaskSet) => { </del> <ins> let done = (value: TaskSet | undefined) => { </ins> if (value) { result.push(value); }

} return entries; }); <del> return this.quickInputService.pick(pickEntries, { placeHolder, matchOnDescription: true, onDidTriggerItemButton: context => { let task = context.item.task; this.quickInputService.cancel(); if (ContributedTask.is(task)) { this.customize(task, undefined, true); } else if (CustomTask.is(task)) { this.openConfig(task); } } }, cancellationToken).then(async (selection) => { if (cancellationToken.isCancellationRequested) { // canceled when there's only one task const task = (await pickEntries)[0]; if ((<any>task).task) { selection = <TaskQuickPickEntry>task; } } if (!selection) { return; </del> <ins> const picker: IQuickPick<TaskQuickPickEntry> = this.quickInputService.createQuickPick(); picker.placeholder = placeHolder; picker.matchOnDescription = true; picker.ignoreFocusOut = true; picker.onDidTriggerItemButton(context => { let task = context.item.task; this.quickInputService.cancel(); if (ContributedTask.is(task)) { this.customize(task, undefined, true); } else if (CustomTask.is(task)) { this.openConfig(task); } }); picker.busy = true; const progressManager = this._providerProgressManager; const progressTimeout = setTimeout(() => { if (progressManager) { progressManager.showProgress = (stillProviding) => { let message = undefined; if (stillProviding.length > 0) { message = nls.localize('pickProgressManager.description', 'Getting tasks from extensions. {0} extension(s) remaining: {1}', stillProviding.length, stillProviding.join(', ')); } picker.description = message; }; progressManager.addOnDoneListener(() => { picker.focusOnInput(); picker.customButton = false; }); if (!progressManager.isDone) { picker.customLabel = nls.localize('taskQuickPick.cancel', "Cancel Remaining Extensions"); picker.onDidCustom(() => { this._providerProgressManager?.cancel(); }); picker.customButton = true; } </ins> } <del> return selection; </del> <ins> }, 1000); pickEntries.then(entries => { clearTimeout(progressTimeout); progressManager?.dispose(); picker.busy = false; picker.items = entries; }); picker.show(); return new Promise<TaskQuickPickEntry | undefined | null>(resolve => { this._register(picker.onDidAccept(async () => { let selection = picker.selectedItems ? picker.selectedItems[0] : undefined; if (cancellationToken.isCancellationRequested) { // canceled when there's only one task const task = (await pickEntries)[0]; if ((<any>task).task) { selection = <TaskQuickPickEntry>task; } } picker.dispose(); if (!selection) { resolve(); } resolve(selection); })); </ins> }); }

<ins> /*--------------------------------------------------------------------------------------------- *  Copyright (c) Microsoft Corporation. All rights reserved. *  Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { TaskSet } from 'vs/workbench/contrib/tasks/common/tasks'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; export class ProviderProgressMananger extends Disposable { private _onProviderComplete: Emitter<string> = new Emitter(); private _stillProviding: Set<string> = new Set(); private _onDone: Emitter<void> = new Emitter(); private _isDone: boolean = false; private _showProgress: ((remaining: string[]) => void) | undefined; public canceled: CancellationTokenSource = new CancellationTokenSource(); constructor() { super(); this._register(this._onProviderComplete.event(taskType => { this._stillProviding.delete(taskType); if (this._stillProviding.size === 0) { this._isDone = true; this._onDone.fire(); } if (this._showProgress) { this._showProgress(Array.from(this._stillProviding)); } })); } public addProvider(taskType: string, provider: Promise<TaskSet>) { this._stillProviding.add(taskType); provider.then(() => this._onProviderComplete.fire(taskType)); } public addOnDoneListener(onDoneListener: () => void) { this._register(this._onDone.event(onDoneListener)); } set showProgress(progressDisplayFunction: (remaining: string[]) => void) { this._showProgress = progressDisplayFunction; this._showProgress(Array.from(this._stillProviding)); } get isDone(): boolean { return this._isDone; } public cancel() { this._isDone = true; if (this._showProgress) { this._showProgress([]); } this._onDone.fire(); this.canceled.cancel(); } } </ins>