"Fossies" - the Fresh Open Source Software Archive

Member "vscode-1.49.1/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts" (16 Sep 2020, 119825 Bytes) of package /linux/misc/vscode-1.49.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) TypeScript source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "abstractTaskService.ts": 1.48.2_vs_1.49.0.

    1 /*---------------------------------------------------------------------------------------------
    2  *  Copyright (c) Microsoft Corporation. All rights reserved.
    3  *  Licensed under the MIT License. See License.txt in the project root for license information.
    4  *--------------------------------------------------------------------------------------------*/
    5 
    6 import * as nls from 'vs/nls';
    7 import Severity from 'vs/base/common/severity';
    8 import * as Objects from 'vs/base/common/objects';
    9 import * as resources from 'vs/base/common/resources';
   10 import * as json from 'vs/base/common/json';
   11 import { URI } from 'vs/base/common/uri';
   12 import { IStringDictionary } from 'vs/base/common/collections';
   13 import { Action } from 'vs/base/common/actions';
   14 import { IDisposable, Disposable, IReference } from 'vs/base/common/lifecycle';
   15 import { Event, Emitter } from 'vs/base/common/event';
   16 import * as Types from 'vs/base/common/types';
   17 import { TerminateResponseCode } from 'vs/base/common/processes';
   18 import { ValidationStatus, ValidationState } from 'vs/base/common/parsers';
   19 import * as UUID from 'vs/base/common/uuid';
   20 import * as Platform from 'vs/base/common/platform';
   21 import { LRUCache, Touch } from 'vs/base/common/map';
   22 
   23 import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
   24 import { IMarkerService } from 'vs/platform/markers/common/markers';
   25 import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
   26 import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
   27 import { IFileService, IFileStat } from 'vs/platform/files/common/files';
   28 import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
   29 import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
   30 import { ProblemMatcherRegistry, NamedProblemMatcher } from 'vs/workbench/contrib/tasks/common/problemMatcher';
   31 import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
   32 import { IProgressService, IProgressOptions, ProgressLocation } from 'vs/platform/progress/common/progress';
   33 
   34 import { IOpenerService } from 'vs/platform/opener/common/opener';
   35 import { IHostService } from 'vs/workbench/services/host/browser/host';
   36 import { INotificationService } from 'vs/platform/notification/common/notification';
   37 import { IDialogService, IConfirmationResult } from 'vs/platform/dialogs/common/dialogs';
   38 
   39 import { IModelService } from 'vs/editor/common/services/modelService';
   40 
   41 import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
   42 import Constants from 'vs/workbench/contrib/markers/browser/constants';
   43 import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
   44 import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
   45 import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace';
   46 
   47 import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
   48 import { IOutputService, IOutputChannel } from 'vs/workbench/contrib/output/common/output';
   49 
   50 import { ITerminalService, ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal';
   51 
   52 import { ITaskSystem, ITaskResolver, ITaskSummary, TaskExecuteKind, TaskError, TaskErrors, TaskTerminateResponse, TaskSystemInfo, ITaskExecuteResult } from 'vs/workbench/contrib/tasks/common/taskSystem';
   53 import {
   54     Task, CustomTask, ConfiguringTask, ContributedTask, InMemoryTask, TaskEvent,
   55     TaskSet, TaskGroup, GroupType, ExecutionEngine, JsonSchemaVersion, TaskSourceKind,
   56     TaskSorter, TaskIdentifier, KeyedTaskIdentifier, TASK_RUNNING_STATE, TaskRunSource,
   57     KeyedTaskIdentifier as NKeyedTaskIdentifier, TaskDefinition
   58 } from 'vs/workbench/contrib/tasks/common/tasks';
   59 import { ITaskService, ITaskProvider, ProblemMatcherRunOptions, CustomizationProperties, TaskFilter, WorkspaceFolderTaskResult, USER_TASKS_GROUP_KEY, CustomExecutionSupportedContext, ShellExecutionSupportedContext, ProcessExecutionSupportedContext } from 'vs/workbench/contrib/tasks/common/taskService';
   60 import { getTemplates as getTaskTemplates } from 'vs/workbench/contrib/tasks/common/taskTemplates';
   61 
   62 import * as TaskConfig from '../common/taskConfiguration';
   63 import { TerminalTaskSystem } from './terminalTaskSystem';
   64 
   65 import { IQuickInputService, IQuickPickItem, QuickPickInput, IQuickPick } from 'vs/platform/quickinput/common/quickInput';
   66 
   67 import { TaskDefinitionRegistry } from 'vs/workbench/contrib/tasks/common/taskDefinitionRegistry';
   68 import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
   69 import { RunAutomaticTasks } from 'vs/workbench/contrib/tasks/browser/runAutomaticTasks';
   70 
   71 import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
   72 import { IPathService } from 'vs/workbench/services/path/common/pathService';
   73 import { format } from 'vs/base/common/jsonFormatter';
   74 import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService';
   75 import { applyEdits } from 'vs/base/common/jsonEdit';
   76 import { SaveReason } from 'vs/workbench/common/editor';
   77 import { ITextEditorSelection, TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor';
   78 import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
   79 import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
   80 import { IViewsService, IViewDescriptorService } from 'vs/workbench/common/views';
   81 import { isWorkspaceFolder, TaskQuickPickEntry, QUICKOPEN_DETAIL_CONFIG, TaskQuickPick, QUICKOPEN_SKIP_CONFIG } from 'vs/workbench/contrib/tasks/browser/taskQuickPick';
   82 import { ILogService } from 'vs/platform/log/common/log';
   83 import { once } from 'vs/base/common/functional';
   84 
   85 const QUICKOPEN_HISTORY_LIMIT_CONFIG = 'task.quickOpen.history';
   86 const PROBLEM_MATCHER_NEVER_CONFIG = 'task.problemMatchers.neverPrompt';
   87 const USE_SLOW_PICKER = 'task.quickOpen.showAll';
   88 
   89 export namespace ConfigureTaskAction {
   90     export const ID = 'workbench.action.tasks.configureTaskRunner';
   91     export const TEXT = nls.localize('ConfigureTaskRunnerAction.label', "Configure Task");
   92 }
   93 
   94 type TaskQuickPickEntryType = (IQuickPickItem & { task: Task; }) | (IQuickPickItem & { folder: IWorkspaceFolder; });
   95 
   96 class ProblemReporter implements TaskConfig.IProblemReporter {
   97 
   98     private _validationStatus: ValidationStatus;
   99 
  100     constructor(private _outputChannel: IOutputChannel) {
  101         this._validationStatus = new ValidationStatus();
  102     }
  103 
  104     public info(message: string): void {
  105         this._validationStatus.state = ValidationState.Info;
  106         this._outputChannel.append(message + '\n');
  107     }
  108 
  109     public warn(message: string): void {
  110         this._validationStatus.state = ValidationState.Warning;
  111         this._outputChannel.append(message + '\n');
  112     }
  113 
  114     public error(message: string): void {
  115         this._validationStatus.state = ValidationState.Error;
  116         this._outputChannel.append(message + '\n');
  117     }
  118 
  119     public fatal(message: string): void {
  120         this._validationStatus.state = ValidationState.Fatal;
  121         this._outputChannel.append(message + '\n');
  122     }
  123 
  124     public get status(): ValidationStatus {
  125         return this._validationStatus;
  126     }
  127 }
  128 
  129 export interface WorkspaceFolderConfigurationResult {
  130     workspaceFolder: IWorkspaceFolder;
  131     config: TaskConfig.ExternalTaskRunnerConfiguration | undefined;
  132     hasErrors: boolean;
  133 }
  134 
  135 interface TaskCustomizationTelemetryEvent {
  136     properties: string[];
  137 }
  138 
  139 class TaskMap {
  140     private _store: Map<string, Task[]> = new Map();
  141 
  142     public forEach(callback: (value: Task[], folder: string) => void): void {
  143         this._store.forEach(callback);
  144     }
  145 
  146     private getKey(workspaceFolder: IWorkspace | IWorkspaceFolder | string): string {
  147         let key: string | undefined;
  148         if (Types.isString(workspaceFolder)) {
  149             key = workspaceFolder;
  150         } else {
  151             const uri: URI | null | undefined = isWorkspaceFolder(workspaceFolder) ? workspaceFolder.uri : workspaceFolder.configuration;
  152             key = uri ? uri.toString() : '';
  153         }
  154         return key;
  155     }
  156 
  157     public get(workspaceFolder: IWorkspace | IWorkspaceFolder | string): Task[] {
  158         const key = this.getKey(workspaceFolder);
  159         let result: Task[] | undefined = this._store.get(key);
  160         if (!result) {
  161             result = [];
  162             this._store.set(key, result);
  163         }
  164         return result;
  165     }
  166 
  167     public add(workspaceFolder: IWorkspace | IWorkspaceFolder | string, ...task: Task[]): void {
  168         const key = this.getKey(workspaceFolder);
  169         let values = this._store.get(key);
  170         if (!values) {
  171             values = [];
  172             this._store.set(key, values);
  173         }
  174         values.push(...task);
  175     }
  176 
  177     public all(): Task[] {
  178         let result: Task[] = [];
  179         this._store.forEach((values) => result.push(...values));
  180         return result;
  181     }
  182 }
  183 
  184 interface ProblemMatcherDisableMetrics {
  185     type: string;
  186 }
  187 type ProblemMatcherDisableMetricsClassification = {
  188     type: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
  189 };
  190 
  191 export abstract class AbstractTaskService extends Disposable implements ITaskService {
  192 
  193     // private static autoDetectTelemetryName: string = 'taskServer.autoDetect';
  194     private static readonly RecentlyUsedTasks_Key = 'workbench.tasks.recentlyUsedTasks';
  195     private static readonly RecentlyUsedTasks_KeyV2 = 'workbench.tasks.recentlyUsedTasks2';
  196     private static readonly IgnoreTask010DonotShowAgain_key = 'workbench.tasks.ignoreTask010Shown';
  197 
  198     private static CustomizationTelemetryEventName: string = 'taskService.customize';
  199     public _serviceBrand: undefined;
  200     public static OutputChannelId: string = 'tasks';
  201     public static OutputChannelLabel: string = nls.localize('tasks', "Tasks");
  202 
  203     private static nextHandle: number = 0;
  204 
  205     private _schemaVersion: JsonSchemaVersion | undefined;
  206     private _executionEngine: ExecutionEngine | undefined;
  207     private _workspaceFolders: IWorkspaceFolder[] | undefined;
  208     private _workspace: IWorkspace | undefined;
  209     private _ignoredWorkspaceFolders: IWorkspaceFolder[] | undefined;
  210     private _showIgnoreMessage?: boolean;
  211     private _providers: Map<number, ITaskProvider>;
  212     private _providerTypes: Map<number, string>;
  213     protected _taskSystemInfos: Map<string, TaskSystemInfo>;
  214 
  215     protected _workspaceTasksPromise?: Promise<Map<string, WorkspaceFolderTaskResult>>;
  216     protected _areJsonTasksSupportedPromise: Promise<boolean> = Promise.resolve(false);
  217 
  218     protected _taskSystem?: ITaskSystem;
  219     protected _taskSystemListener?: IDisposable;
  220     private _recentlyUsedTasksV1: LRUCache<string, string> | undefined;
  221     private _recentlyUsedTasks: LRUCache<string, string> | undefined;
  222 
  223     protected _taskRunningState: IContextKey<boolean>;
  224 
  225     protected _outputChannel: IOutputChannel;
  226     protected readonly _onDidStateChange: Emitter<TaskEvent>;
  227     private _waitForSupportedExecutions: Promise<void>;
  228     private _onDidRegisterSupportedExecutions: Emitter<void> = new Emitter();
  229 
  230     constructor(
  231         @IConfigurationService private readonly configurationService: IConfigurationService,
  232         @IMarkerService protected readonly markerService: IMarkerService,
  233         @IOutputService protected readonly outputService: IOutputService,
  234         @IPanelService private readonly panelService: IPanelService,
  235         @IViewsService private readonly viewsService: IViewsService,
  236         @ICommandService private readonly commandService: ICommandService,
  237         @IEditorService private readonly editorService: IEditorService,
  238         @IFileService protected readonly fileService: IFileService,
  239         @IWorkspaceContextService protected readonly contextService: IWorkspaceContextService,
  240         @ITelemetryService protected readonly telemetryService: ITelemetryService,
  241         @ITextFileService private readonly textFileService: ITextFileService,
  242         @ILifecycleService lifecycleService: ILifecycleService,
  243         @IModelService protected readonly modelService: IModelService,
  244         @IExtensionService private readonly extensionService: IExtensionService,
  245         @IQuickInputService private readonly quickInputService: IQuickInputService,
  246         @IConfigurationResolverService protected readonly configurationResolverService: IConfigurationResolverService,
  247         @ITerminalService private readonly terminalService: ITerminalService,
  248         @IStorageService private readonly storageService: IStorageService,
  249         @IProgressService private readonly progressService: IProgressService,
  250         @IOpenerService private readonly openerService: IOpenerService,
  251         @IHostService private readonly _hostService: IHostService,
  252         @IDialogService private readonly dialogService: IDialogService,
  253         @INotificationService private readonly notificationService: INotificationService,
  254         @IContextKeyService protected readonly contextKeyService: IContextKeyService,
  255         @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
  256         @ITerminalInstanceService private readonly terminalInstanceService: ITerminalInstanceService,
  257         @IPathService private readonly pathService: IPathService,
  258         @ITextModelService private readonly textModelResolverService: ITextModelService,
  259         @IPreferencesService private readonly preferencesService: IPreferencesService,
  260         @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService,
  261         @ILogService private readonly logService: ILogService
  262     ) {
  263         super();
  264 
  265         this._workspaceTasksPromise = undefined;
  266         this._taskSystem = undefined;
  267         this._taskSystemListener = undefined;
  268         this._outputChannel = this.outputService.getChannel(AbstractTaskService.OutputChannelId)!;
  269         this._providers = new Map<number, ITaskProvider>();
  270         this._providerTypes = new Map<number, string>();
  271         this._taskSystemInfos = new Map<string, TaskSystemInfo>();
  272         this._register(this.contextService.onDidChangeWorkspaceFolders(() => {
  273             if (!this._taskSystem && !this._workspaceTasksPromise) {
  274                 return;
  275             }
  276             let folderSetup = this.computeWorkspaceFolderSetup();
  277             if (this.executionEngine !== folderSetup[2]) {
  278                 if (this._taskSystem && this._taskSystem.getActiveTasks().length > 0) {
  279                     this.notificationService.prompt(
  280                         Severity.Info,
  281                         nls.localize(
  282                             'TaskSystem.noHotSwap',
  283                             'Changing the task execution engine with an active task running requires to reload the Window'
  284                         ),
  285                         [{
  286                             label: nls.localize('reloadWindow', "Reload Window"),
  287                             run: () => this._hostService.reload()
  288                         }],
  289                         { sticky: true }
  290                     );
  291                     return;
  292                 } else {
  293                     this.disposeTaskSystemListeners();
  294                     this._taskSystem = undefined;
  295                 }
  296             }
  297             this.updateSetup(folderSetup);
  298             this.updateWorkspaceTasks();
  299         }));
  300         this._register(this.configurationService.onDidChangeConfiguration(() => {
  301             if (!this._taskSystem && !this._workspaceTasksPromise) {
  302                 return;
  303             }
  304             if (!this._taskSystem || this._taskSystem instanceof TerminalTaskSystem) {
  305                 this._outputChannel.clear();
  306             }
  307 
  308             this.setTaskLRUCacheLimit();
  309             this.updateWorkspaceTasks(TaskRunSource.ConfigurationChange);
  310         }));
  311         this._taskRunningState = TASK_RUNNING_STATE.bindTo(contextKeyService);
  312         this._register(lifecycleService.onBeforeShutdown(event => event.veto(this.beforeShutdown())));
  313         this._onDidStateChange = this._register(new Emitter());
  314         this.registerCommands();
  315         this.configurationResolverService.contributeVariable('defaultBuildTask', async (): Promise<string | undefined> => {
  316             let tasks = await this.getTasksForGroup(TaskGroup.Build);
  317             if (tasks.length > 0) {
  318                 let { defaults, users } = this.splitPerGroupType(tasks);
  319                 if (defaults.length === 1) {
  320                     return defaults[0]._label;
  321                 } else if (defaults.length + users.length > 0) {
  322                     tasks = defaults.concat(users);
  323                 }
  324             }
  325 
  326             let entry: TaskQuickPickEntry | null | undefined;
  327             if (tasks && tasks.length > 0) {
  328                 entry = await this.showQuickPick(tasks, nls.localize('TaskService.pickBuildTaskForLabel', 'Select the build task (there is no default build task defined)'));
  329             }
  330 
  331             let task: Task | undefined | null = entry ? entry.task : undefined;
  332             if (!task) {
  333                 return undefined;
  334             }
  335             return task._label;
  336         });
  337 
  338         this._waitForSupportedExecutions = new Promise(resolve => {
  339             once(this._onDidRegisterSupportedExecutions.event)(() => resolve());
  340         });
  341     }
  342 
  343     public registerSupportedExecutions(custom?: boolean, shell?: boolean, process?: boolean) {
  344         if (custom !== undefined) {
  345             const customContext = CustomExecutionSupportedContext.bindTo(this.contextKeyService);
  346             customContext.set(custom);
  347         }
  348         if (shell !== undefined) {
  349             const shellContext = ShellExecutionSupportedContext.bindTo(this.contextKeyService);
  350             shellContext.set(shell);
  351         }
  352         if (process !== undefined) {
  353             const processContext = ProcessExecutionSupportedContext.bindTo(this.contextKeyService);
  354             processContext.set(process);
  355         }
  356         this._onDidRegisterSupportedExecutions.fire();
  357     }
  358 
  359     public get onDidStateChange(): Event<TaskEvent> {
  360         return this._onDidStateChange.event;
  361     }
  362 
  363     public get supportsMultipleTaskExecutions(): boolean {
  364         return this.inTerminal();
  365     }
  366 
  367     private registerCommands(): void {
  368         CommandsRegistry.registerCommand({
  369             id: 'workbench.action.tasks.runTask',
  370             handler: (accessor, arg) => {
  371                 this.runTaskCommand(arg);
  372             },
  373             description: {
  374                 description: 'Run Task',
  375                 args: [{
  376                     name: 'args',
  377                     schema: {
  378                         'type': 'string',
  379                     }
  380                 }]
  381             }
  382         });
  383 
  384         CommandsRegistry.registerCommand('workbench.action.tasks.reRunTask', (accessor, arg) => {
  385             this.reRunTaskCommand();
  386         });
  387 
  388         CommandsRegistry.registerCommand('workbench.action.tasks.restartTask', (accessor, arg) => {
  389             this.runRestartTaskCommand(arg);
  390         });
  391 
  392         CommandsRegistry.registerCommand('workbench.action.tasks.terminate', (accessor, arg) => {
  393             this.runTerminateCommand(arg);
  394         });
  395 
  396         CommandsRegistry.registerCommand('workbench.action.tasks.showLog', () => {
  397             if (!this.canRunCommand()) {
  398                 return;
  399             }
  400             this.showOutput();
  401         });
  402 
  403         CommandsRegistry.registerCommand('workbench.action.tasks.build', () => {
  404             if (!this.canRunCommand()) {
  405                 return;
  406             }
  407             this.runBuildCommand();
  408         });
  409 
  410         CommandsRegistry.registerCommand('workbench.action.tasks.test', () => {
  411             if (!this.canRunCommand()) {
  412                 return;
  413             }
  414             this.runTestCommand();
  415         });
  416 
  417         CommandsRegistry.registerCommand('workbench.action.tasks.configureTaskRunner', () => {
  418             this.runConfigureTasks();
  419         });
  420 
  421         CommandsRegistry.registerCommand('workbench.action.tasks.configureDefaultBuildTask', () => {
  422             this.runConfigureDefaultBuildTask();
  423         });
  424 
  425         CommandsRegistry.registerCommand('workbench.action.tasks.configureDefaultTestTask', () => {
  426             this.runConfigureDefaultTestTask();
  427         });
  428 
  429         CommandsRegistry.registerCommand('workbench.action.tasks.showTasks', async () => {
  430             return this.runShowTasks();
  431         });
  432 
  433         CommandsRegistry.registerCommand('workbench.action.tasks.toggleProblems', () => this.commandService.executeCommand(Constants.TOGGLE_MARKERS_VIEW_ACTION_ID));
  434 
  435         CommandsRegistry.registerCommand('workbench.action.tasks.openUserTasks', async () => {
  436             const resource = this.getResourceForKind(TaskSourceKind.User);
  437             if (resource) {
  438                 this.openTaskFile(resource, TaskSourceKind.User);
  439             }
  440         });
  441 
  442         CommandsRegistry.registerCommand('workbench.action.tasks.openWorkspaceFileTasks', async () => {
  443             const resource = this.getResourceForKind(TaskSourceKind.WorkspaceFile);
  444             if (resource) {
  445                 this.openTaskFile(resource, TaskSourceKind.WorkspaceFile);
  446             }
  447         });
  448     }
  449 
  450     private get workspaceFolders(): IWorkspaceFolder[] {
  451         if (!this._workspaceFolders) {
  452             this.updateSetup();
  453         }
  454         return this._workspaceFolders!;
  455     }
  456 
  457     private get ignoredWorkspaceFolders(): IWorkspaceFolder[] {
  458         if (!this._ignoredWorkspaceFolders) {
  459             this.updateSetup();
  460         }
  461         return this._ignoredWorkspaceFolders!;
  462     }
  463 
  464     protected get executionEngine(): ExecutionEngine {
  465         if (this._executionEngine === undefined) {
  466             this.updateSetup();
  467         }
  468         return this._executionEngine!;
  469     }
  470 
  471     private get schemaVersion(): JsonSchemaVersion {
  472         if (this._schemaVersion === undefined) {
  473             this.updateSetup();
  474         }
  475         return this._schemaVersion!;
  476     }
  477 
  478     private get showIgnoreMessage(): boolean {
  479         if (this._showIgnoreMessage === undefined) {
  480             this._showIgnoreMessage = !this.storageService.getBoolean(AbstractTaskService.IgnoreTask010DonotShowAgain_key, StorageScope.WORKSPACE, false);
  481         }
  482         return this._showIgnoreMessage;
  483     }
  484 
  485     private updateSetup(setup?: [IWorkspaceFolder[], IWorkspaceFolder[], ExecutionEngine, JsonSchemaVersion, IWorkspace | undefined]): void {
  486         if (!setup) {
  487             setup = this.computeWorkspaceFolderSetup();
  488         }
  489         this._workspaceFolders = setup[0];
  490         if (this._ignoredWorkspaceFolders) {
  491             if (this._ignoredWorkspaceFolders.length !== setup[1].length) {
  492                 this._showIgnoreMessage = undefined;
  493             } else {
  494                 let set: Set<string> = new Set();
  495                 this._ignoredWorkspaceFolders.forEach(folder => set.add(folder.uri.toString()));
  496                 for (let folder of setup[1]) {
  497                     if (!set.has(folder.uri.toString())) {
  498                         this._showIgnoreMessage = undefined;
  499                         break;
  500                     }
  501                 }
  502             }
  503         }
  504         this._ignoredWorkspaceFolders = setup[1];
  505         this._executionEngine = setup[2];
  506         this._schemaVersion = setup[3];
  507         this._workspace = setup[4];
  508     }
  509 
  510     protected showOutput(runSource: TaskRunSource = TaskRunSource.User): void {
  511         if ((runSource === TaskRunSource.User) || (runSource === TaskRunSource.ConfigurationChange)) {
  512             this.notificationService.prompt(Severity.Warning, nls.localize('taskServiceOutputPrompt', 'There are task errors. See the output for details.'),
  513                 [{
  514                     label: nls.localize('showOutput', "Show output"),
  515                     run: () => {
  516                         this.outputService.showChannel(this._outputChannel.id, true);
  517                     }
  518                 }]);
  519         }
  520     }
  521 
  522     private disposeTaskSystemListeners(): void {
  523         if (this._taskSystemListener) {
  524             this._taskSystemListener.dispose();
  525         }
  526     }
  527 
  528     public registerTaskProvider(provider: ITaskProvider, type: string): IDisposable {
  529         if (!provider) {
  530             return {
  531                 dispose: () => { }
  532             };
  533         }
  534         let handle = AbstractTaskService.nextHandle++;
  535         this._providers.set(handle, provider);
  536         this._providerTypes.set(handle, type);
  537         return {
  538             dispose: () => {
  539                 this._providers.delete(handle);
  540                 this._providerTypes.delete(handle);
  541             }
  542         };
  543     }
  544 
  545     public registerTaskSystem(key: string, info: TaskSystemInfo): void {
  546         this._taskSystemInfos.set(key, info);
  547     }
  548 
  549     public extensionCallbackTaskComplete(task: Task, result: number): Promise<void> {
  550         if (!this._taskSystem) {
  551             return Promise.resolve();
  552         }
  553         return this._taskSystem.customExecutionComplete(task, result);
  554     }
  555 
  556     public getTask(folder: IWorkspace | IWorkspaceFolder | string, identifier: string | TaskIdentifier, compareId: boolean = false): Promise<Task | undefined> {
  557         const name = Types.isString(folder) ? folder : isWorkspaceFolder(folder) ? folder.name : folder.configuration ? resources.basename(folder.configuration) : undefined;
  558         if (this.ignoredWorkspaceFolders.some(ignored => ignored.name === name)) {
  559             return Promise.reject(new Error(nls.localize('TaskServer.folderIgnored', 'The folder {0} is ignored since it uses task version 0.1.0', name)));
  560         }
  561         const key: string | KeyedTaskIdentifier | undefined = !Types.isString(identifier)
  562             ? TaskDefinition.createTaskIdentifier(identifier, console)
  563             : identifier;
  564 
  565         if (key === undefined) {
  566             return Promise.resolve(undefined);
  567         }
  568         return this.getGroupedTasks().then((map) => {
  569             let values = map.get(folder);
  570             values = values.concat(map.get(USER_TASKS_GROUP_KEY));
  571 
  572             if (!values) {
  573                 return undefined;
  574             }
  575             return values.find(task => task.matches(key, compareId));
  576         });
  577     }
  578 
  579     public async tryResolveTask(configuringTask: ConfiguringTask): Promise<Task | undefined> {
  580         await Promise.all([this.extensionService.activateByEvent('onCommand:workbench.action.tasks.runTask'), this.extensionService.whenInstalledExtensionsRegistered()]);
  581         let matchingProvider: ITaskProvider | undefined;
  582         let matchingProviderUnavailable: boolean = false;
  583         for (const [handle, provider] of this._providers) {
  584             const providerType = this._providerTypes.get(handle);
  585             if (configuringTask.type === providerType) {
  586                 if (providerType && !this.isTaskProviderEnabled(providerType)) {
  587                     matchingProviderUnavailable = true;
  588                     continue;
  589                 }
  590                 matchingProvider = provider;
  591                 break;
  592             }
  593         }
  594 
  595         if (!matchingProvider) {
  596             if (matchingProviderUnavailable) {
  597                 this._outputChannel.append(nls.localize(
  598                     'TaskService.providerUnavailable',
  599                     'Warning: {0} tasks are unavailable in the current environment.\n',
  600                     configuringTask.configures.type
  601                 ));
  602             }
  603             return;
  604         }
  605 
  606         // Try to resolve the task first
  607         try {
  608             const resolvedTask = await matchingProvider.resolveTask(configuringTask);
  609             if (resolvedTask && (resolvedTask._id === configuringTask._id)) {
  610                 return TaskConfig.createCustomTask(resolvedTask, configuringTask);
  611             }
  612         } catch (error) {
  613             // Ignore errors. The task could not be provided by any of the providers.
  614         }
  615 
  616         // The task couldn't be resolved. Instead, use the less efficient provideTask.
  617         const tasks = await this.tasks({ type: configuringTask.type });
  618         for (const task of tasks) {
  619             if (task._id === configuringTask._id) {
  620                 return TaskConfig.createCustomTask(<ContributedTask>task, configuringTask);
  621             }
  622         }
  623 
  624         return;
  625     }
  626 
  627     protected abstract versionAndEngineCompatible(filter?: TaskFilter): boolean;
  628 
  629     public tasks(filter?: TaskFilter): Promise<Task[]> {
  630         if (!this.versionAndEngineCompatible(filter)) {
  631             return Promise.resolve<Task[]>([]);
  632         }
  633         return this.getGroupedTasks(filter ? filter.type : undefined).then((map) => {
  634             if (!filter || !filter.type) {
  635                 return map.all();
  636             }
  637             let result: Task[] = [];
  638             map.forEach((tasks) => {
  639                 for (let task of tasks) {
  640                     if (ContributedTask.is(task) && ((task.defines.type === filter.type) || (task._source.label === filter.type))) {
  641                         result.push(task);
  642                     } else if (CustomTask.is(task)) {
  643                         if (task.type === filter.type) {
  644                             result.push(task);
  645                         } else {
  646                             let customizes = task.customizes();
  647                             if (customizes && customizes.type === filter.type) {
  648                                 result.push(task);
  649                             }
  650                         }
  651                     }
  652                 }
  653             });
  654             return result;
  655         });
  656     }
  657 
  658     public taskTypes(): string[] {
  659         const types: string[] = [];
  660         if (this.isProvideTasksEnabled()) {
  661             for (const [handle] of this._providers) {
  662                 const type = this._providerTypes.get(handle);
  663                 if (type && this.isTaskProviderEnabled(type)) {
  664                     types.push(type);
  665                 }
  666             }
  667         }
  668         return types;
  669     }
  670 
  671     public createSorter(): TaskSorter {
  672         return new TaskSorter(this.contextService.getWorkspace() ? this.contextService.getWorkspace().folders : []);
  673     }
  674 
  675     public isActive(): Promise<boolean> {
  676         if (!this._taskSystem) {
  677             return Promise.resolve(false);
  678         }
  679         return this._taskSystem.isActive();
  680     }
  681 
  682     public getActiveTasks(): Promise<Task[]> {
  683         if (!this._taskSystem) {
  684             return Promise.resolve([]);
  685         }
  686         return Promise.resolve(this._taskSystem.getActiveTasks());
  687     }
  688 
  689     public getBusyTasks(): Promise<Task[]> {
  690         if (!this._taskSystem) {
  691             return Promise.resolve([]);
  692         }
  693         return Promise.resolve(this._taskSystem.getBusyTasks());
  694     }
  695 
  696     public getRecentlyUsedTasksV1(): LRUCache<string, string> {
  697         if (this._recentlyUsedTasksV1) {
  698             return this._recentlyUsedTasksV1;
  699         }
  700         const quickOpenHistoryLimit = this.configurationService.getValue<number>(QUICKOPEN_HISTORY_LIMIT_CONFIG);
  701         this._recentlyUsedTasksV1 = new LRUCache<string, string>(quickOpenHistoryLimit);
  702 
  703         let storageValue = this.storageService.get(AbstractTaskService.RecentlyUsedTasks_Key, StorageScope.WORKSPACE);
  704         if (storageValue) {
  705             try {
  706                 let values: string[] = JSON.parse(storageValue);
  707                 if (Array.isArray(values)) {
  708                     for (let value of values) {
  709                         this._recentlyUsedTasksV1.set(value, value);
  710                     }
  711                 }
  712             } catch (error) {
  713                 // Ignore. We use the empty result
  714             }
  715         }
  716         return this._recentlyUsedTasksV1;
  717     }
  718 
  719     public getRecentlyUsedTasks(): LRUCache<string, string> {
  720         if (this._recentlyUsedTasks) {
  721             return this._recentlyUsedTasks;
  722         }
  723         const quickOpenHistoryLimit = this.configurationService.getValue<number>(QUICKOPEN_HISTORY_LIMIT_CONFIG);
  724         this._recentlyUsedTasks = new LRUCache<string, string>(quickOpenHistoryLimit);
  725 
  726         let storageValue = this.storageService.get(AbstractTaskService.RecentlyUsedTasks_KeyV2, StorageScope.WORKSPACE);
  727         if (storageValue) {
  728             try {
  729                 let values: [string, string][] = JSON.parse(storageValue);
  730                 if (Array.isArray(values)) {
  731                     for (let value of values) {
  732                         this._recentlyUsedTasks.set(value[0], value[1]);
  733                     }
  734                 }
  735             } catch (error) {
  736                 // Ignore. We use the empty result
  737             }
  738         }
  739         return this._recentlyUsedTasks;
  740     }
  741 
  742     private getFolderFromTaskKey(key: string): string | undefined {
  743         const keyValue: { folder: string | undefined } = JSON.parse(key);
  744         return keyValue.folder;
  745     }
  746 
  747     public async readRecentTasks(): Promise<(Task | ConfiguringTask)[]> {
  748         const folderMap: IStringDictionary<IWorkspaceFolder> = Object.create(null);
  749         this.workspaceFolders.forEach(folder => {
  750             folderMap[folder.uri.toString()] = folder;
  751         });
  752         const folderToTasksMap: Map<string, any> = new Map();
  753         const recentlyUsedTasks = this.getRecentlyUsedTasks();
  754         const tasks: (Task | ConfiguringTask)[] = [];
  755         for (const entry of recentlyUsedTasks.entries()) {
  756             const key = entry[0];
  757             const task = JSON.parse(entry[1]);
  758             const folder = this.getFolderFromTaskKey(key);
  759             if (folder && !folderToTasksMap.has(folder)) {
  760                 folderToTasksMap.set(folder, []);
  761             }
  762             if (folder && (folderMap[folder] || (folder === USER_TASKS_GROUP_KEY)) && task) {
  763                 folderToTasksMap.get(folder).push(task);
  764             }
  765         }
  766         const readTasksMap: Map<string, (Task | ConfiguringTask)> = new Map();
  767         for (const key of folderToTasksMap.keys()) {
  768             let custom: CustomTask[] = [];
  769             let customized: IStringDictionary<ConfiguringTask> = Object.create(null);
  770             await this.computeTasksForSingleConfig(folderMap[key] ?? this.workspaceFolders[0], {
  771                 version: '2.0.0',
  772                 tasks: folderToTasksMap.get(key)
  773             }, TaskRunSource.System, custom, customized, folderMap[key] ? TaskConfig.TaskConfigSource.TasksJson : TaskConfig.TaskConfigSource.User, true);
  774             custom.forEach(task => {
  775                 const taskKey = task.getRecentlyUsedKey();
  776                 if (taskKey) {
  777                     readTasksMap.set(taskKey, task);
  778                 }
  779             });
  780             for (const configuration in customized) {
  781                 const taskKey = customized[configuration].getRecentlyUsedKey();
  782                 if (taskKey) {
  783                     readTasksMap.set(taskKey, customized[configuration]);
  784                 }
  785             }
  786         }
  787 
  788         for (const key of recentlyUsedTasks.keys()) {
  789             if (readTasksMap.has(key)) {
  790                 tasks.push(readTasksMap.get(key)!);
  791             }
  792         }
  793         return tasks;
  794     }
  795 
  796     private setTaskLRUCacheLimit() {
  797         const quickOpenHistoryLimit = this.configurationService.getValue<number>(QUICKOPEN_HISTORY_LIMIT_CONFIG);
  798         if (this._recentlyUsedTasks) {
  799             this._recentlyUsedTasks.limit = quickOpenHistoryLimit;
  800         }
  801     }
  802 
  803     private async setRecentlyUsedTask(task: Task): Promise<void> {
  804         let key = task.getRecentlyUsedKey();
  805         if (!InMemoryTask.is(task) && key) {
  806             const customizations = this.createCustomizableTask(task);
  807             if (ContributedTask.is(task) && customizations) {
  808                 let custom: CustomTask[] = [];
  809                 let customized: IStringDictionary<ConfiguringTask> = Object.create(null);
  810                 await this.computeTasksForSingleConfig(task._source.workspaceFolder ?? this.workspaceFolders[0], {
  811                     version: '2.0.0',
  812                     tasks: [customizations]
  813                 }, TaskRunSource.System, custom, customized, TaskConfig.TaskConfigSource.TasksJson, true);
  814                 for (const configuration in customized) {
  815                     key = customized[configuration].getRecentlyUsedKey()!;
  816                 }
  817             }
  818             this.getRecentlyUsedTasks().set(key, JSON.stringify(customizations));
  819             this.saveRecentlyUsedTasks();
  820         }
  821     }
  822 
  823     private saveRecentlyUsedTasks(): void {
  824         if (!this._recentlyUsedTasks) {
  825             return;
  826         }
  827         const quickOpenHistoryLimit = this.configurationService.getValue<number>(QUICKOPEN_HISTORY_LIMIT_CONFIG);
  828         // setting history limit to 0 means no LRU sorting
  829         if (quickOpenHistoryLimit === 0) {
  830             return;
  831         }
  832         let keys = [...this._recentlyUsedTasks.keys()];
  833         if (keys.length > quickOpenHistoryLimit) {
  834             keys = keys.slice(0, quickOpenHistoryLimit);
  835         }
  836         const keyValues: [string, string][] = [];
  837         for (const key of keys) {
  838             keyValues.push([key, this._recentlyUsedTasks.get(key, Touch.None)!]);
  839         }
  840         this.storageService.store(AbstractTaskService.RecentlyUsedTasks_KeyV2, JSON.stringify(keyValues), StorageScope.WORKSPACE);
  841     }
  842 
  843     private openDocumentation(): void {
  844         this.openerService.open(URI.parse('https://go.microsoft.com/fwlink/?LinkId=733558'));
  845     }
  846 
  847     public async build(): Promise<ITaskSummary> {
  848         return this.getGroupedTasks().then((tasks) => {
  849             let runnable = this.createRunnableTask(tasks, TaskGroup.Build);
  850             if (!runnable || !runnable.task) {
  851                 if (this.schemaVersion === JsonSchemaVersion.V0_1_0) {
  852                     throw new TaskError(Severity.Info, nls.localize('TaskService.noBuildTask1', 'No build task defined. Mark a task with \'isBuildCommand\' in the tasks.json file.'), TaskErrors.NoBuildTask);
  853                 } else {
  854                     throw new TaskError(Severity.Info, nls.localize('TaskService.noBuildTask2', 'No build task defined. Mark a task with as a \'build\' group in the tasks.json file.'), TaskErrors.NoBuildTask);
  855                 }
  856             }
  857             return this.executeTask(runnable.task, runnable.resolver, TaskRunSource.User);
  858         }).then(value => value, (error) => {
  859             this.handleError(error);
  860             return Promise.reject(error);
  861         });
  862     }
  863 
  864     public runTest(): Promise<ITaskSummary> {
  865         return this.getGroupedTasks().then((tasks) => {
  866             let runnable = this.createRunnableTask(tasks, TaskGroup.Test);
  867             if (!runnable || !runnable.task) {
  868                 if (this.schemaVersion === JsonSchemaVersion.V0_1_0) {
  869                     throw new TaskError(Severity.Info, nls.localize('TaskService.noTestTask1', 'No test task defined. Mark a task with \'isTestCommand\' in the tasks.json file.'), TaskErrors.NoTestTask);
  870                 } else {
  871                     throw new TaskError(Severity.Info, nls.localize('TaskService.noTestTask2', 'No test task defined. Mark a task with as a \'test\' group in the tasks.json file.'), TaskErrors.NoTestTask);
  872                 }
  873             }
  874             return this.executeTask(runnable.task, runnable.resolver, TaskRunSource.User);
  875         }).then(value => value, (error) => {
  876             this.handleError(error);
  877             return Promise.reject(error);
  878         });
  879     }
  880 
  881     public run(task: Task | undefined, options?: ProblemMatcherRunOptions, runSource: TaskRunSource = TaskRunSource.System): Promise<ITaskSummary | undefined> {
  882         if (!task) {
  883             throw new TaskError(Severity.Info, nls.localize('TaskServer.noTask', 'Task to execute is undefined'), TaskErrors.TaskNotFound);
  884         }
  885 
  886         return new Promise<ITaskSummary | undefined>(async (resolve) => {
  887             let resolver = this.createResolver();
  888             if (options && options.attachProblemMatcher && this.shouldAttachProblemMatcher(task) && !InMemoryTask.is(task)) {
  889                 const toExecute = await this.attachProblemMatcher(task);
  890                 if (toExecute) {
  891                     resolve(this.executeTask(toExecute, resolver, runSource));
  892                 } else {
  893                     resolve(undefined);
  894                 }
  895             } else {
  896                 resolve(this.executeTask(task, resolver, runSource));
  897             }
  898         }).then((value) => {
  899             if (runSource === TaskRunSource.User) {
  900                 this.getWorkspaceTasks().then(workspaceTasks => {
  901                     RunAutomaticTasks.promptForPermission(this, this.storageService, this.notificationService, workspaceTasks);
  902                 });
  903             }
  904             return value;
  905         }, (error) => {
  906             this.handleError(error);
  907             return Promise.reject(error);
  908         });
  909     }
  910 
  911     private isProvideTasksEnabled(): boolean {
  912         const settingValue = this.configurationService.getValue('task.autoDetect');
  913         return settingValue === 'on';
  914     }
  915 
  916     private isProblemMatcherPromptEnabled(type?: string): boolean {
  917         const settingValue = this.configurationService.getValue(PROBLEM_MATCHER_NEVER_CONFIG);
  918         if (Types.isBoolean(settingValue)) {
  919             return !settingValue;
  920         }
  921         if (type === undefined) {
  922             return true;
  923         }
  924         const settingValueMap: IStringDictionary<boolean> = <any>settingValue;
  925         return !settingValueMap[type];
  926     }
  927 
  928     private getTypeForTask(task: Task): string {
  929         let type: string;
  930         if (CustomTask.is(task)) {
  931             let configProperties: TaskConfig.ConfigurationProperties = task._source.config.element;
  932             type = (<any>configProperties).type;
  933         } else {
  934             type = task.getDefinition()!.type;
  935         }
  936         return type;
  937     }
  938 
  939     private shouldAttachProblemMatcher(task: Task): boolean {
  940         const enabled = this.isProblemMatcherPromptEnabled(this.getTypeForTask(task));
  941         if (enabled === false) {
  942             return false;
  943         }
  944         if (!this.canCustomize(task)) {
  945             return false;
  946         }
  947         if (task.configurationProperties.group !== undefined && task.configurationProperties.group !== TaskGroup.Build) {
  948             return false;
  949         }
  950         if (task.configurationProperties.problemMatchers !== undefined && task.configurationProperties.problemMatchers.length > 0) {
  951             return false;
  952         }
  953         if (ContributedTask.is(task)) {
  954             return !task.hasDefinedMatchers && !!task.configurationProperties.problemMatchers && (task.configurationProperties.problemMatchers.length === 0);
  955         }
  956         if (CustomTask.is(task)) {
  957             let configProperties: TaskConfig.ConfigurationProperties = task._source.config.element;
  958             return configProperties.problemMatcher === undefined && !task.hasDefinedMatchers;
  959         }
  960         return false;
  961     }
  962 
  963     private async updateNeverProblemMatcherSetting(type: string): Promise<void> {
  964         this.telemetryService.publicLog2<ProblemMatcherDisableMetrics, ProblemMatcherDisableMetricsClassification>('problemMatcherDisabled', { type });
  965         const current = this.configurationService.getValue(PROBLEM_MATCHER_NEVER_CONFIG);
  966         if (current === true) {
  967             return;
  968         }
  969         let newValue: IStringDictionary<boolean>;
  970         if (current !== false) {
  971             newValue = <any>current;
  972         } else {
  973             newValue = Object.create(null);
  974         }
  975         newValue[type] = true;
  976         return this.configurationService.updateValue(PROBLEM_MATCHER_NEVER_CONFIG, newValue, ConfigurationTarget.USER);
  977     }
  978 
  979     private attachProblemMatcher(task: ContributedTask | CustomTask): Promise<Task | undefined> {
  980         interface ProblemMatcherPickEntry extends IQuickPickItem {
  981             matcher: NamedProblemMatcher | undefined;
  982             never?: boolean;
  983             learnMore?: boolean;
  984             setting?: string;
  985         }
  986         let entries: QuickPickInput<ProblemMatcherPickEntry>[] = [];
  987         for (let key of ProblemMatcherRegistry.keys()) {
  988             let matcher = ProblemMatcherRegistry.get(key);
  989             if (matcher.deprecated) {
  990                 continue;
  991             }
  992             if (matcher.name === matcher.label) {
  993                 entries.push({ label: matcher.name, matcher: matcher });
  994             } else {
  995                 entries.push({
  996                     label: matcher.label,
  997                     description: `$${matcher.name}`,
  998                     matcher: matcher
  999                 });
 1000             }
 1001         }
 1002         if (entries.length > 0) {
 1003             entries = entries.sort((a, b) => {
 1004                 if (a.label && b.label) {
 1005                     return a.label.localeCompare(b.label);
 1006                 } else {
 1007                     return 0;
 1008                 }
 1009             });
 1010             entries.unshift({ type: 'separator', label: nls.localize('TaskService.associate', 'associate') });
 1011             let taskType: string;
 1012             if (CustomTask.is(task)) {
 1013                 let configProperties: TaskConfig.ConfigurationProperties = task._source.config.element;
 1014                 taskType = (<any>configProperties).type;
 1015             } else {
 1016                 taskType = task.getDefinition().type;
 1017             }
 1018             entries.unshift(
 1019                 { label: nls.localize('TaskService.attachProblemMatcher.continueWithout', 'Continue without scanning the task output'), matcher: undefined },
 1020                 { label: nls.localize('TaskService.attachProblemMatcher.never', 'Never scan the task output for this task'), matcher: undefined, never: true },
 1021                 { label: nls.localize('TaskService.attachProblemMatcher.neverType', 'Never scan the task output for {0} tasks', taskType), matcher: undefined, setting: taskType },
 1022                 { label: nls.localize('TaskService.attachProblemMatcher.learnMoreAbout', 'Learn more about scanning the task output'), matcher: undefined, learnMore: true }
 1023             );
 1024             return this.quickInputService.pick(entries, {
 1025                 placeHolder: nls.localize('selectProblemMatcher', 'Select for which kind of errors and warnings to scan the task output'),
 1026             }).then(async (selected) => {
 1027                 if (selected) {
 1028                     if (selected.learnMore) {
 1029                         this.openDocumentation();
 1030                         return undefined;
 1031                     } else if (selected.never) {
 1032                         this.customize(task, { problemMatcher: [] }, true);
 1033                         return task;
 1034                     } else if (selected.matcher) {
 1035                         let newTask = task.clone();
 1036                         let matcherReference = `$${selected.matcher.name}`;
 1037                         let properties: CustomizationProperties = { problemMatcher: [matcherReference] };
 1038                         newTask.configurationProperties.problemMatchers = [matcherReference];
 1039                         let matcher = ProblemMatcherRegistry.get(selected.matcher.name);
 1040                         if (matcher && matcher.watching !== undefined) {
 1041                             properties.isBackground = true;
 1042                             newTask.configurationProperties.isBackground = true;
 1043                         }
 1044                         this.customize(task, properties, true);
 1045                         return newTask;
 1046                     } else if (selected.setting) {
 1047                         await this.updateNeverProblemMatcherSetting(selected.setting);
 1048                         return task;
 1049                     } else {
 1050                         return task;
 1051                     }
 1052                 } else {
 1053                     return undefined;
 1054                 }
 1055             });
 1056         }
 1057         return Promise.resolve(task);
 1058     }
 1059 
 1060     public getTasksForGroup(group: string): Promise<Task[]> {
 1061         return this.getGroupedTasks().then((groups) => {
 1062             let result: Task[] = [];
 1063             groups.forEach((tasks) => {
 1064                 for (let task of tasks) {
 1065                     if (task.configurationProperties.group === group) {
 1066                         result.push(task);
 1067                     }
 1068                 }
 1069             });
 1070             return result;
 1071         });
 1072     }
 1073 
 1074     public needsFolderQualification(): boolean {
 1075         return this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE;
 1076     }
 1077 
 1078     public canCustomize(task: Task): boolean {
 1079         if (this.schemaVersion !== JsonSchemaVersion.V2_0_0) {
 1080             return false;
 1081         }
 1082         if (CustomTask.is(task)) {
 1083             return true;
 1084         }
 1085         if (ContributedTask.is(task)) {
 1086             return !!task.getWorkspaceFolder();
 1087         }
 1088         return false;
 1089     }
 1090 
 1091     private async formatTaskForJson(resource: URI, task: TaskConfig.CustomTask | TaskConfig.ConfiguringTask): Promise<string> {
 1092         let reference: IReference<IResolvedTextEditorModel> | undefined;
 1093         let stringValue: string = '';
 1094         try {
 1095             reference = await this.textModelResolverService.createModelReference(resource);
 1096             const model = reference.object.textEditorModel;
 1097             const { tabSize, insertSpaces } = model.getOptions();
 1098             const eol = model.getEOL();
 1099             const edits = format(JSON.stringify(task), undefined, { eol, tabSize, insertSpaces });
 1100             let stringified = applyEdits(JSON.stringify(task), edits);
 1101             const regex = new RegExp(eol + (insertSpaces ? ' '.repeat(tabSize) : '\\t'), 'g');
 1102             stringified = stringified.replace(regex, eol + (insertSpaces ? ' '.repeat(tabSize * 3) : '\t\t\t'));
 1103             const twoTabs = insertSpaces ? ' '.repeat(tabSize * 2) : '\t\t';
 1104             stringValue = twoTabs + stringified.slice(0, stringified.length - 1) + twoTabs + stringified.slice(stringified.length - 1);
 1105         } finally {
 1106             if (reference) {
 1107                 reference.dispose();
 1108             }
 1109         }
 1110         return stringValue;
 1111     }
 1112 
 1113     private openEditorAtTask(resource: URI | undefined, task: TaskConfig.CustomTask | TaskConfig.ConfiguringTask | string | undefined, configIndex: number = -1): Promise<boolean> {
 1114         if (resource === undefined) {
 1115             return Promise.resolve(false);
 1116         }
 1117         let selection: ITextEditorSelection | undefined;
 1118         return this.fileService.readFile(resource).then(content => content.value).then(async content => {
 1119             if (!content) {
 1120                 return false;
 1121             }
 1122             if (task) {
 1123                 const contentValue = content.toString();
 1124                 let stringValue: string | undefined;
 1125                 if (configIndex !== -1) {
 1126                     const json: TaskConfig.ExternalTaskRunnerConfiguration = this.configurationService.getValue<TaskConfig.ExternalTaskRunnerConfiguration>('tasks', { resource });
 1127                     if (json.tasks && (json.tasks.length > configIndex)) {
 1128                         stringValue = await this.formatTaskForJson(resource, json.tasks[configIndex]);
 1129                     }
 1130                 }
 1131                 if (!stringValue) {
 1132                     if (typeof task === 'string') {
 1133                         stringValue = task;
 1134                     } else {
 1135                         stringValue = await this.formatTaskForJson(resource, task);
 1136                     }
 1137                 }
 1138 
 1139                 const index = contentValue.indexOf(stringValue);
 1140                 let startLineNumber = 1;
 1141                 for (let i = 0; i < index; i++) {
 1142                     if (contentValue.charAt(i) === '\n') {
 1143                         startLineNumber++;
 1144                     }
 1145                 }
 1146                 let endLineNumber = startLineNumber;
 1147                 for (let i = 0; i < stringValue.length; i++) {
 1148                     if (stringValue.charAt(i) === '\n') {
 1149                         endLineNumber++;
 1150                     }
 1151                 }
 1152                 selection = startLineNumber > 1 ? { startLineNumber, startColumn: startLineNumber === endLineNumber ? 4 : 3, endLineNumber, endColumn: startLineNumber === endLineNumber ? undefined : 4 } : undefined;
 1153             }
 1154 
 1155             return this.editorService.openEditor({
 1156                 resource,
 1157                 options: {
 1158                     pinned: false,
 1159                     forceReload: true, // because content might have changed
 1160                     selection,
 1161                     selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport
 1162                 }
 1163             }).then(() => !!selection);
 1164         });
 1165     }
 1166 
 1167     private createCustomizableTask(task: ContributedTask | CustomTask | ConfiguringTask): TaskConfig.CustomTask | TaskConfig.ConfiguringTask | undefined {
 1168         let toCustomize: TaskConfig.CustomTask | TaskConfig.ConfiguringTask | undefined;
 1169         let taskConfig = CustomTask.is(task) || ConfiguringTask.is(task) ? task._source.config : undefined;
 1170         if (taskConfig && taskConfig.element) {
 1171             toCustomize = { ...(taskConfig.element) };
 1172         } else if (ContributedTask.is(task)) {
 1173             toCustomize = {
 1174             };
 1175             let identifier: TaskConfig.TaskIdentifier = Object.assign(Object.create(null), task.defines);
 1176             delete identifier['_key'];
 1177             Object.keys(identifier).forEach(key => (<any>toCustomize)![key] = identifier[key]);
 1178             if (task.configurationProperties.problemMatchers && task.configurationProperties.problemMatchers.length > 0 && Types.isStringArray(task.configurationProperties.problemMatchers)) {
 1179                 toCustomize.problemMatcher = task.configurationProperties.problemMatchers;
 1180             }
 1181             if (task.configurationProperties.group) {
 1182                 toCustomize.group = task.configurationProperties.group;
 1183             }
 1184         }
 1185         if (!toCustomize) {
 1186             return undefined;
 1187         }
 1188         if (toCustomize.problemMatcher === undefined && task.configurationProperties.problemMatchers === undefined || (task.configurationProperties.problemMatchers && task.configurationProperties.problemMatchers.length === 0)) {
 1189             toCustomize.problemMatcher = [];
 1190         }
 1191         if (task._source.label !== 'Workspace') {
 1192             toCustomize.label = task.configurationProperties.identifier;
 1193         } else {
 1194             toCustomize.label = task._label;
 1195         }
 1196         toCustomize.detail = task.configurationProperties.detail;
 1197         return toCustomize;
 1198     }
 1199 
 1200     public customize(task: ContributedTask | CustomTask | ConfiguringTask, properties?: CustomizationProperties, openConfig?: boolean): Promise<void> {
 1201         const workspaceFolder = task.getWorkspaceFolder();
 1202         if (!workspaceFolder) {
 1203             return Promise.resolve(undefined);
 1204         }
 1205         let configuration = this.getConfiguration(workspaceFolder, task._source.kind);
 1206         if (configuration.hasParseErrors) {
 1207             this.notificationService.warn(nls.localize('customizeParseErrors', 'The current task configuration has errors. Please fix the errors first before customizing a task.'));
 1208             return Promise.resolve<void>(undefined);
 1209         }
 1210 
 1211         let fileConfig = configuration.config;
 1212         const toCustomize = this.createCustomizableTask(task);
 1213         if (!toCustomize) {
 1214             return Promise.resolve(undefined);
 1215         }
 1216         const index: number | undefined = CustomTask.is(task) ? task._source.config.index : undefined;
 1217         if (properties) {
 1218             for (let property of Object.getOwnPropertyNames(properties)) {
 1219                 let value = (<any>properties)[property];
 1220                 if (value !== undefined && value !== null) {
 1221                     (<any>toCustomize)[property] = value;
 1222                 }
 1223             }
 1224         }
 1225 
 1226         let promise: Promise<void> | undefined;
 1227         if (!fileConfig) {
 1228             let value = {
 1229                 version: '2.0.0',
 1230                 tasks: [toCustomize]
 1231             };
 1232             let content = [
 1233                 '{',
 1234                 nls.localize('tasksJsonComment', '\t// See https://go.microsoft.com/fwlink/?LinkId=733558 \n\t// for the documentation about the tasks.json format'),
 1235             ].join('\n') + JSON.stringify(value, null, '\t').substr(1);
 1236             let editorConfig = this.configurationService.getValue<any>();
 1237             if (editorConfig.editor.insertSpaces) {
 1238                 content = content.replace(/(\n)(\t+)/g, (_, s1, s2) => s1 + ' '.repeat(s2.length * editorConfig.editor.tabSize));
 1239             }
 1240             promise = this.textFileService.create(workspaceFolder.toResource('.vscode/tasks.json'), content).then(() => { });
 1241         } else {
 1242             // We have a global task configuration
 1243             if ((index === -1) && properties) {
 1244                 if (properties.problemMatcher !== undefined) {
 1245                     fileConfig.problemMatcher = properties.problemMatcher;
 1246                     promise = this.writeConfiguration(workspaceFolder, 'tasks.problemMatchers', fileConfig.problemMatcher, task._source.kind);
 1247                 } else if (properties.group !== undefined) {
 1248                     fileConfig.group = properties.group;
 1249                     promise = this.writeConfiguration(workspaceFolder, 'tasks.group', fileConfig.group, task._source.kind);
 1250                 }
 1251             } else {
 1252                 if (!Array.isArray(fileConfig.tasks)) {
 1253                     fileConfig.tasks = [];
 1254                 }
 1255                 if (index === undefined) {
 1256                     fileConfig.tasks.push(toCustomize);
 1257                 } else {
 1258                     fileConfig.tasks[index] = toCustomize;
 1259                 }
 1260                 promise = this.writeConfiguration(workspaceFolder, 'tasks.tasks', fileConfig.tasks, task._source.kind);
 1261             }
 1262         }
 1263         if (!promise) {
 1264             return Promise.resolve(undefined);
 1265         }
 1266         return promise.then(() => {
 1267             let event: TaskCustomizationTelemetryEvent = {
 1268                 properties: properties ? Object.getOwnPropertyNames(properties) : []
 1269             };
 1270             /* __GDPR__
 1271                 "taskService.customize" : {
 1272                     "properties" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
 1273                 }
 1274             */
 1275             this.telemetryService.publicLog(AbstractTaskService.CustomizationTelemetryEventName, event);
 1276             if (openConfig) {
 1277                 this.openEditorAtTask(this.getResourceForTask(task), toCustomize);
 1278             }
 1279         });
 1280     }
 1281 
 1282     private writeConfiguration(workspaceFolder: IWorkspaceFolder, key: string, value: any, source?: string): Promise<void> | undefined {
 1283         let target: ConfigurationTarget | undefined = undefined;
 1284         switch (source) {
 1285             case TaskSourceKind.User: target = ConfigurationTarget.USER; break;
 1286             case TaskSourceKind.WorkspaceFile: target = ConfigurationTarget.WORKSPACE; break;
 1287             default: if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) {
 1288                 target = ConfigurationTarget.WORKSPACE;
 1289             } else if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
 1290                 target = ConfigurationTarget.WORKSPACE_FOLDER;
 1291             }
 1292         }
 1293         if (target) {
 1294             return this.configurationService.updateValue(key, value, { resource: workspaceFolder.uri }, target);
 1295         } else {
 1296             return undefined;
 1297         }
 1298     }
 1299 
 1300     private getResourceForKind(kind: string): URI | undefined {
 1301         this.updateSetup();
 1302         switch (kind) {
 1303             case TaskSourceKind.User: {
 1304                 return resources.joinPath(resources.dirname(this.preferencesService.userSettingsResource), 'tasks.json');
 1305             }
 1306             case TaskSourceKind.WorkspaceFile: {
 1307                 if (this._workspace && this._workspace.configuration) {
 1308                     return this._workspace.configuration;
 1309                 }
 1310             }
 1311             default: {
 1312                 return undefined;
 1313             }
 1314         }
 1315     }
 1316 
 1317     private getResourceForTask(task: CustomTask | ConfiguringTask | ContributedTask): URI {
 1318         if (CustomTask.is(task)) {
 1319             let uri = this.getResourceForKind(task._source.kind);
 1320             if (!uri) {
 1321                 const taskFolder = task.getWorkspaceFolder();
 1322                 if (taskFolder) {
 1323                     uri = taskFolder.toResource(task._source.config.file);
 1324                 } else {
 1325                     uri = this.workspaceFolders[0].uri;
 1326                 }
 1327             }
 1328             return uri;
 1329         } else {
 1330             return task.getWorkspaceFolder()!.toResource('.vscode/tasks.json');
 1331         }
 1332     }
 1333 
 1334     public async openConfig(task: CustomTask | ConfiguringTask | undefined): Promise<boolean> {
 1335         let resource: URI | undefined;
 1336         if (task) {
 1337             resource = this.getResourceForTask(task);
 1338         } else {
 1339             resource = (this._workspaceFolders && (this._workspaceFolders.length > 0)) ? this._workspaceFolders[0].toResource('.vscode/tasks.json') : undefined;
 1340         }
 1341         return this.openEditorAtTask(resource, task ? task._label : undefined, task ? task._source.config.index : -1);
 1342     }
 1343 
 1344     private createRunnableTask(tasks: TaskMap, group: TaskGroup): { task: Task; resolver: ITaskResolver } | undefined {
 1345         interface ResolverData {
 1346             id: Map<string, Task>;
 1347             label: Map<string, Task>;
 1348             identifier: Map<string, Task>;
 1349         }
 1350 
 1351         let resolverData: Map<string, ResolverData> = new Map();
 1352         let workspaceTasks: Task[] = [];
 1353         let extensionTasks: Task[] = [];
 1354         tasks.forEach((tasks, folder) => {
 1355             let data = resolverData.get(folder);
 1356             if (!data) {
 1357                 data = {
 1358                     id: new Map<string, Task>(),
 1359                     label: new Map<string, Task>(),
 1360                     identifier: new Map<string, Task>()
 1361                 };
 1362                 resolverData.set(folder, data);
 1363             }
 1364             for (let task of tasks) {
 1365                 data.id.set(task._id, task);
 1366                 data.label.set(task._label, task);
 1367                 if (task.configurationProperties.identifier) {
 1368                     data.identifier.set(task.configurationProperties.identifier, task);
 1369                 }
 1370                 if (group && task.configurationProperties.group === group) {
 1371                     if (task._source.kind === TaskSourceKind.Workspace) {
 1372                         workspaceTasks.push(task);
 1373                     } else {
 1374                         extensionTasks.push(task);
 1375                     }
 1376                 }
 1377             }
 1378         });
 1379         let resolver: ITaskResolver = {
 1380             resolve: async (uri: URI | string, alias: string) => {
 1381                 let data = resolverData.get(typeof uri === 'string' ? uri : uri.toString());
 1382                 if (!data) {
 1383                     return undefined;
 1384                 }
 1385                 return data.id.get(alias) || data.label.get(alias) || data.identifier.get(alias);
 1386             }
 1387         };
 1388         if (workspaceTasks.length > 0) {
 1389             if (workspaceTasks.length > 1) {
 1390                 this._outputChannel.append(nls.localize('moreThanOneBuildTask', 'There are many build tasks defined in the tasks.json. Executing the first one.\n'));
 1391             }
 1392             return { task: workspaceTasks[0], resolver };
 1393         }
 1394         if (extensionTasks.length === 0) {
 1395             return undefined;
 1396         }
 1397 
 1398         // We can only have extension tasks if we are in version 2.0.0. Then we can even run
 1399         // multiple build tasks.
 1400         if (extensionTasks.length === 1) {
 1401             return { task: extensionTasks[0], resolver };
 1402         } else {
 1403             let id: string = UUID.generateUuid();
 1404             let task: InMemoryTask = new InMemoryTask(
 1405                 id,
 1406                 { kind: TaskSourceKind.InMemory, label: 'inMemory' },
 1407                 id,
 1408                 'inMemory',
 1409                 { reevaluateOnRerun: true },
 1410                 {
 1411                     identifier: id,
 1412                     dependsOn: extensionTasks.map((extensionTask) => { return { uri: extensionTask.getWorkspaceFolder()!.uri, task: extensionTask._id }; }),
 1413                     name: id,
 1414                 }
 1415             );
 1416             return { task, resolver };
 1417         }
 1418     }
 1419 
 1420     private createResolver(grouped?: TaskMap): ITaskResolver {
 1421         interface ResolverData {
 1422             label: Map<string, Task>;
 1423             identifier: Map<string, Task>;
 1424             taskIdentifier: Map<string, Task>;
 1425         }
 1426 
 1427         let resolverData: Map<string, ResolverData> | undefined;
 1428 
 1429         return {
 1430             resolve: async (uri: URI | string, identifier: string | TaskIdentifier | undefined) => {
 1431                 if (resolverData === undefined) {
 1432                     resolverData = new Map();
 1433                     (grouped || await this.getGroupedTasks()).forEach((tasks, folder) => {
 1434                         let data = resolverData!.get(folder);
 1435                         if (!data) {
 1436                             data = { label: new Map<string, Task>(), identifier: new Map<string, Task>(), taskIdentifier: new Map<string, Task>() };
 1437                             resolverData!.set(folder, data);
 1438                         }
 1439                         for (let task of tasks) {
 1440                             data.label.set(task._label, task);
 1441                             if (task.configurationProperties.identifier) {
 1442                                 data.identifier.set(task.configurationProperties.identifier, task);
 1443                             }
 1444                             let keyedIdentifier = task.getDefinition(true);
 1445                             if (keyedIdentifier !== undefined) {
 1446                                 data.taskIdentifier.set(keyedIdentifier._key, task);
 1447                             }
 1448                         }
 1449                     });
 1450                 }
 1451                 let data = resolverData.get(typeof uri === 'string' ? uri : uri.toString());
 1452                 if (!data || !identifier) {
 1453                     return undefined;
 1454                 }
 1455                 if (Types.isString(identifier)) {
 1456                     return data.label.get(identifier) || data.identifier.get(identifier);
 1457                 } else {
 1458                     let key = TaskDefinition.createTaskIdentifier(identifier, console);
 1459                     return key !== undefined ? data.taskIdentifier.get(key._key) : undefined;
 1460                 }
 1461             }
 1462         };
 1463     }
 1464 
 1465     private executeTask(task: Task, resolver: ITaskResolver, runSource: TaskRunSource): Promise<ITaskSummary> {
 1466         enum SaveBeforeRunConfigOptions {
 1467             Always = 'always',
 1468             Never = 'never',
 1469             Prompt = 'prompt'
 1470         }
 1471 
 1472         const saveBeforeRunTaskConfig: SaveBeforeRunConfigOptions = this.configurationService.getValue('task.saveBeforeRun');
 1473 
 1474         const execTask = async (task: Task, resolver: ITaskResolver): Promise<ITaskSummary> => {
 1475             return ProblemMatcherRegistry.onReady().then(() => {
 1476                 let executeResult = this.getTaskSystem().run(task, resolver);
 1477                 return this.handleExecuteResult(executeResult, runSource);
 1478             });
 1479         };
 1480 
 1481         const saveAllEditorsAndExecTask = async (task: Task, resolver: ITaskResolver): Promise<ITaskSummary> => {
 1482             return this.editorService.saveAll({ reason: SaveReason.AUTO }).then(() => {
 1483                 return execTask(task, resolver);
 1484             });
 1485         };
 1486 
 1487         const promptAsk = async (task: Task, resolver: ITaskResolver): Promise<ITaskSummary> => {
 1488             const dialogOptions = await this.dialogService.show(
 1489                 Severity.Info,
 1490                 nls.localize('TaskSystem.saveBeforeRun.prompt.title', 'Save all editors?'),
 1491                 [nls.localize('saveBeforeRun.save', 'Save'), nls.localize('saveBeforeRun.dontSave', 'Don\'t save')],
 1492                 {
 1493                     detail: nls.localize('detail', "Do you want to save all editors before running the task?"),
 1494                     cancelId: 1
 1495                 }
 1496             );
 1497 
 1498             if (dialogOptions.choice === 0) {
 1499                 return saveAllEditorsAndExecTask(task, resolver);
 1500             } else {
 1501                 return execTask(task, resolver);
 1502             }
 1503         };
 1504 
 1505         if (saveBeforeRunTaskConfig === SaveBeforeRunConfigOptions.Never) {
 1506             return execTask(task, resolver);
 1507         } else if (saveBeforeRunTaskConfig === SaveBeforeRunConfigOptions.Prompt) {
 1508             return promptAsk(task, resolver);
 1509         } else {
 1510             return saveAllEditorsAndExecTask(task, resolver);
 1511         }
 1512     }
 1513 
 1514     private async handleExecuteResult(executeResult: ITaskExecuteResult, runSource?: TaskRunSource): Promise<ITaskSummary> {
 1515         if (executeResult.task.taskLoadMessages && executeResult.task.taskLoadMessages.length > 0) {
 1516             executeResult.task.taskLoadMessages.forEach(loadMessage => {
 1517                 this._outputChannel.append(loadMessage + '\n');
 1518             });
 1519             this.showOutput();
 1520         }
 1521 
 1522         if (runSource === TaskRunSource.User) {
 1523             await this.setRecentlyUsedTask(executeResult.task);
 1524         }
 1525         if (executeResult.kind === TaskExecuteKind.Active) {
 1526             let active = executeResult.active;
 1527             if (active && active.same) {
 1528                 if (this._taskSystem?.isTaskVisible(executeResult.task)) {
 1529                     const message = nls.localize('TaskSystem.activeSame.noBackground', 'The task \'{0}\' is already active.', executeResult.task.getQualifiedLabel());
 1530                     let lastInstance = this.getTaskSystem().getLastInstance(executeResult.task) ?? executeResult.task;
 1531                     this.notificationService.prompt(Severity.Warning, message,
 1532                         [{
 1533                             label: nls.localize('terminateTask', "Terminate Task"),
 1534                             run: () => this.terminate(lastInstance)
 1535                         },
 1536                         {
 1537                             label: nls.localize('restartTask', "Restart Task"),
 1538                             run: () => this.restart(lastInstance)
 1539                         }],
 1540                         { sticky: true }
 1541                     );
 1542                 } else {
 1543                     this._taskSystem?.revealTask(executeResult.task);
 1544                 }
 1545             } else {
 1546                 throw new TaskError(Severity.Warning, nls.localize('TaskSystem.active', 'There is already a task running. Terminate it first before executing another task.'), TaskErrors.RunningTask);
 1547             }
 1548         }
 1549         return executeResult.promise;
 1550     }
 1551 
 1552     public restart(task: Task): void {
 1553         if (!this._taskSystem) {
 1554             return;
 1555         }
 1556         this._taskSystem.terminate(task).then((response) => {
 1557             if (response.success) {
 1558                 this.run(task).then(undefined, reason => {
 1559                     // eat the error, it has already been surfaced to the user and we don't care about it here
 1560                 });
 1561             } else {
 1562                 this.notificationService.warn(nls.localize('TaskSystem.restartFailed', 'Failed to terminate and restart task {0}', Types.isString(task) ? task : task.configurationProperties.name));
 1563             }
 1564             return response;
 1565         });
 1566     }
 1567 
 1568     public terminate(task: Task): Promise<TaskTerminateResponse> {
 1569         if (!this._taskSystem) {
 1570             return Promise.resolve({ success: true, task: undefined });
 1571         }
 1572         return this._taskSystem.terminate(task);
 1573     }
 1574 
 1575     public terminateAll(): Promise<TaskTerminateResponse[]> {
 1576         if (!this._taskSystem) {
 1577             return Promise.resolve<TaskTerminateResponse[]>([]);
 1578         }
 1579         return this._taskSystem.terminateAll();
 1580     }
 1581 
 1582     protected createTerminalTaskSystem(): ITaskSystem {
 1583         return new TerminalTaskSystem(
 1584             this.terminalService, this.outputService, this.panelService, this.viewsService, this.markerService,
 1585             this.modelService, this.configurationResolverService, this.telemetryService,
 1586             this.contextService, this.environmentService,
 1587             AbstractTaskService.OutputChannelId, this.fileService, this.terminalInstanceService,
 1588             this.pathService, this.viewDescriptorService, this.logService,
 1589             (workspaceFolder: IWorkspaceFolder) => {
 1590                 if (!workspaceFolder) {
 1591                     return undefined;
 1592                 }
 1593                 return this._taskSystemInfos.get(workspaceFolder.uri.scheme);
 1594             }
 1595         );
 1596     }
 1597 
 1598     protected abstract getTaskSystem(): ITaskSystem;
 1599 
 1600     private isTaskProviderEnabled(type: string) {
 1601         const definition = TaskDefinitionRegistry.get(type);
 1602         return !definition || !definition.when || this.contextKeyService.contextMatchesRules(definition.when);
 1603     }
 1604 
 1605     private getGroupedTasks(type?: string): Promise<TaskMap> {
 1606         const needsRecentTasksMigration = this.needsRecentTasksMigration();
 1607         return Promise.all([this.extensionService.activateByEvent('onCommand:workbench.action.tasks.runTask'), this.extensionService.whenInstalledExtensionsRegistered()]).then(() => {
 1608             let validTypes: IStringDictionary<boolean> = Object.create(null);
 1609             TaskDefinitionRegistry.all().forEach(definition => validTypes[definition.taskType] = true);
 1610             validTypes['shell'] = true;
 1611             validTypes['process'] = true;
 1612             return new Promise<TaskSet[]>(resolve => {
 1613                 let result: TaskSet[] = [];
 1614                 let counter: number = 0;
 1615                 let done = (value: TaskSet | undefined) => {
 1616                     if (value) {
 1617                         result.push(value);
 1618                     }
 1619                     if (--counter === 0) {
 1620                         resolve(result);
 1621                     }
 1622                 };
 1623                 let error = (error: any) => {
 1624                     try {
 1625                         if (error && Types.isString(error.message)) {
 1626                             this._outputChannel.append('Error: ');
 1627                             this._outputChannel.append(error.message);
 1628                             this._outputChannel.append('\n');
 1629                             this.showOutput();
 1630                         } else {
 1631                             this._outputChannel.append('Unknown error received while collecting tasks from providers.\n');
 1632                             this.showOutput();
 1633                         }
 1634                     } finally {
 1635                         if (--counter === 0) {
 1636                             resolve(result);
 1637                         }
 1638                     }
 1639                 };
 1640                 if (this.isProvideTasksEnabled() && (this.schemaVersion === JsonSchemaVersion.V2_0_0) && (this._providers.size > 0)) {
 1641                     for (const [handle, provider] of this._providers) {
 1642                         const providerType = this._providerTypes.get(handle);
 1643                         if ((type === undefined) || (type === providerType)) {
 1644                             if (providerType && !this.isTaskProviderEnabled(providerType)) {
 1645                                 continue;
 1646                             }
 1647                             counter++;
 1648                             provider.provideTasks(validTypes).then((taskSet: TaskSet) => {
 1649                                 // Check that the tasks provided are of the correct type
 1650                                 for (const task of taskSet.tasks) {
 1651                                     if (task.type !== this._providerTypes.get(handle)) {
 1652                                         this._outputChannel.append(nls.localize('unexpectedTaskType', "The task provider for \"{0}\" tasks unexpectedly provided a task of type \"{1}\".\n", this._providerTypes.get(handle), task.type));
 1653                                         if ((task.type !== 'shell') && (task.type !== 'process')) {
 1654                                             this.showOutput();
 1655                                         }
 1656                                         break;
 1657                                     }
 1658                                 }
 1659                                 return done(taskSet);
 1660                             }, error);
 1661                         }
 1662                     }
 1663                 } else {
 1664                     resolve(result);
 1665                 }
 1666             });
 1667         }).then((contributedTaskSets) => {
 1668             let result: TaskMap = new TaskMap();
 1669             let contributedTasks: TaskMap = new TaskMap();
 1670 
 1671             for (let set of contributedTaskSets) {
 1672                 for (let task of set.tasks) {
 1673                     let workspaceFolder = task.getWorkspaceFolder();
 1674                     if (workspaceFolder) {
 1675                         contributedTasks.add(workspaceFolder, task);
 1676                     }
 1677                 }
 1678             }
 1679 
 1680             return this.getWorkspaceTasks().then(async (customTasks) => {
 1681                 const customTasksKeyValuePairs = Array.from(customTasks);
 1682                 const customTasksPromises = customTasksKeyValuePairs.map(async ([key, folderTasks]) => {
 1683                     let contributed = contributedTasks.get(key);
 1684                     if (!folderTasks.set) {
 1685                         if (contributed) {
 1686                             result.add(key, ...contributed);
 1687                         }
 1688                         return;
 1689                     }
 1690 
 1691                     if (!contributed) {
 1692                         result.add(key, ...folderTasks.set.tasks);
 1693                     } else {
 1694                         let configurations = folderTasks.configurations;
 1695                         let legacyTaskConfigurations = folderTasks.set ? this.getLegacyTaskConfigurations(folderTasks.set) : undefined;
 1696                         let customTasksToDelete: Task[] = [];
 1697                         if (configurations || legacyTaskConfigurations) {
 1698                             let unUsedConfigurations: Set<string> = new Set<string>();
 1699                             if (configurations) {
 1700                                 Object.keys(configurations.byIdentifier).forEach(key => unUsedConfigurations.add(key));
 1701                             }
 1702                             for (let task of contributed) {
 1703                                 if (!ContributedTask.is(task)) {
 1704                                     continue;
 1705                                 }
 1706                                 if (configurations) {
 1707                                     let configuringTask = configurations.byIdentifier[task.defines._key];
 1708                                     if (configuringTask) {
 1709                                         unUsedConfigurations.delete(task.defines._key);
 1710                                         result.add(key, TaskConfig.createCustomTask(task, configuringTask));
 1711                                     } else {
 1712                                         result.add(key, task);
 1713                                     }
 1714                                 } else if (legacyTaskConfigurations) {
 1715                                     let configuringTask = legacyTaskConfigurations[task.defines._key];
 1716                                     if (configuringTask) {
 1717                                         result.add(key, TaskConfig.createCustomTask(task, configuringTask));
 1718                                         customTasksToDelete.push(configuringTask);
 1719                                     } else {
 1720                                         result.add(key, task);
 1721                                     }
 1722                                 } else {
 1723                                     result.add(key, task);
 1724                                 }
 1725                             }
 1726                             if (customTasksToDelete.length > 0) {
 1727                                 let toDelete = customTasksToDelete.reduce<IStringDictionary<boolean>>((map, task) => {
 1728                                     map[task._id] = true;
 1729                                     return map;
 1730                                 }, Object.create(null));
 1731                                 for (let task of folderTasks.set.tasks) {
 1732                                     if (toDelete[task._id]) {
 1733                                         continue;
 1734                                     }
 1735                                     result.add(key, task);
 1736                                 }
 1737                             } else {
 1738                                 result.add(key, ...folderTasks.set.tasks);
 1739                             }
 1740 
 1741                             const unUsedConfigurationsAsArray = Array.from(unUsedConfigurations);
 1742 
 1743                             const unUsedConfigurationPromises = unUsedConfigurationsAsArray.map(async (value) => {
 1744                                 let configuringTask = configurations!.byIdentifier[value];
 1745                                 if (type && (type !== configuringTask.configures.type)) {
 1746                                     return;
 1747                                 }
 1748 
 1749                                 let requiredTaskProviderUnavailable: boolean = false;
 1750 
 1751                                 for (const [handle, provider] of this._providers) {
 1752                                     const providerType = this._providerTypes.get(handle);
 1753                                     if (configuringTask.type === providerType) {
 1754                                         if (providerType && !this.isTaskProviderEnabled(providerType)) {
 1755                                             requiredTaskProviderUnavailable = true;
 1756                                             continue;
 1757                                         }
 1758 
 1759                                         try {
 1760                                             const resolvedTask = await provider.resolveTask(configuringTask);
 1761                                             if (resolvedTask && (resolvedTask._id === configuringTask._id)) {
 1762                                                 result.add(key, TaskConfig.createCustomTask(resolvedTask, configuringTask));
 1763                                                 return;
 1764                                             }
 1765                                         } catch (error) {
 1766                                             // Ignore errors. The task could not be provided by any of the providers.
 1767                                         }
 1768                                     }
 1769                                 }
 1770 
 1771                                 if (requiredTaskProviderUnavailable) {
 1772                                     this._outputChannel.append(nls.localize(
 1773                                         'TaskService.providerUnavailable',
 1774                                         'Warning: {0} tasks are unavailable in the current environment.\n',
 1775                                         configuringTask.configures.type
 1776                                     ));
 1777                                 } else {
 1778                                     this._outputChannel.append(nls.localize(
 1779                                         'TaskService.noConfiguration',
 1780                                         'Error: The {0} task detection didn\'t contribute a task for the following configuration:\n{1}\nThe task will be ignored.\n',
 1781                                         configuringTask.configures.type,
 1782                                         JSON.stringify(configuringTask._source.config.element, undefined, 4)
 1783                                     ));
 1784                                     this.showOutput();
 1785                                 }
 1786                             });
 1787 
 1788                             await Promise.all(unUsedConfigurationPromises);
 1789                         } else {
 1790                             result.add(key, ...folderTasks.set.tasks);
 1791                             result.add(key, ...contributed);
 1792                         }
 1793                     }
 1794                 });
 1795 
 1796                 await Promise.all(customTasksPromises);
 1797                 if (needsRecentTasksMigration) {
 1798                     // At this point we have all the tasks and can migrate the recently used tasks.
 1799                     await this.migrateRecentTasks(result.all());
 1800                 }
 1801                 return result;
 1802             }, () => {
 1803                 // If we can't read the tasks.json file provide at least the contributed tasks
 1804                 let result: TaskMap = new TaskMap();
 1805                 for (let set of contributedTaskSets) {
 1806                     for (let task of set.tasks) {
 1807                         const folder = task.getWorkspaceFolder();
 1808                         if (folder) {
 1809                             result.add(folder, task);
 1810                         }
 1811                     }
 1812                 }
 1813                 return result;
 1814             });
 1815         });
 1816     }
 1817 
 1818     private getLegacyTaskConfigurations(workspaceTasks: TaskSet): IStringDictionary<CustomTask> | undefined {
 1819         let result: IStringDictionary<CustomTask> | undefined;
 1820         function getResult(): IStringDictionary<CustomTask> {
 1821             if (result) {
 1822                 return result;
 1823             }
 1824             result = Object.create(null);
 1825             return result!;
 1826         }
 1827         for (let task of workspaceTasks.tasks) {
 1828             if (CustomTask.is(task)) {
 1829                 let commandName = task.command && task.command.name;
 1830                 // This is for backwards compatibility with the 0.1.0 task annotation code
 1831                 // if we had a gulp, jake or grunt command a task specification was a annotation
 1832                 if (commandName === 'gulp' || commandName === 'grunt' || commandName === 'jake') {
 1833                     let identifier = NKeyedTaskIdentifier.create({
 1834                         type: commandName,
 1835                         task: task.configurationProperties.name
 1836                     });
 1837                     getResult()[identifier._key] = task;
 1838                 }
 1839             }
 1840         }
 1841         return result;
 1842     }
 1843 
 1844     public async getWorkspaceTasks(runSource: TaskRunSource = TaskRunSource.User): Promise<Map<string, WorkspaceFolderTaskResult>> {
 1845         await this._waitForSupportedExecutions;
 1846         if (this._workspaceTasksPromise) {
 1847             return this._workspaceTasksPromise;
 1848         }
 1849         this.updateWorkspaceTasks(runSource);
 1850         return this._workspaceTasksPromise!;
 1851     }
 1852 
 1853     protected abstract updateWorkspaceTasks(runSource: TaskRunSource | void): void;
 1854 
 1855     protected computeWorkspaceTasks(runSource: TaskRunSource = TaskRunSource.User): Promise<Map<string, WorkspaceFolderTaskResult>> {
 1856         if (this.workspaceFolders.length === 0) {
 1857             return Promise.resolve(new Map<string, WorkspaceFolderTaskResult>());
 1858         } else {
 1859             let promises: Promise<WorkspaceFolderTaskResult | undefined>[] = [];
 1860             for (let folder of this.workspaceFolders) {
 1861                 promises.push(this.computeWorkspaceFolderTasks(folder, runSource).then((value) => value, () => undefined));
 1862             }
 1863             return Promise.all(promises).then(async (values) => {
 1864                 let result = new Map<string, WorkspaceFolderTaskResult>();
 1865                 for (let value of values) {
 1866                     if (value) {
 1867                         result.set(value.workspaceFolder.uri.toString(), value);
 1868                     }
 1869                 }
 1870                 const userTasks = await this.computeUserTasks(this.workspaceFolders[0], runSource).then((value) => value, () => undefined);
 1871                 if (userTasks) {
 1872                     result.set(USER_TASKS_GROUP_KEY, userTasks);
 1873                 }
 1874                 const workspaceFileTasks = await this.computeWorkspaceFileTasks(this.workspaceFolders[0], runSource).then((value) => value, () => undefined);
 1875                 if (workspaceFileTasks && this._workspace && this._workspace.configuration) {
 1876                     result.set(this._workspace.configuration.toString(), workspaceFileTasks);
 1877                 }
 1878                 return result;
 1879             });
 1880         }
 1881     }
 1882 
 1883     public setJsonTasksSupported(areSupported: Promise<boolean>) {
 1884         this._areJsonTasksSupportedPromise = areSupported;
 1885     }
 1886 
 1887     private computeWorkspaceFolderTasks(workspaceFolder: IWorkspaceFolder, runSource: TaskRunSource = TaskRunSource.User): Promise<WorkspaceFolderTaskResult> {
 1888         return (this.executionEngine === ExecutionEngine.Process
 1889             ? this.computeLegacyConfiguration(workspaceFolder)
 1890             : this.computeConfiguration(workspaceFolder)).
 1891             then((workspaceFolderConfiguration) => {
 1892                 if (!workspaceFolderConfiguration || !workspaceFolderConfiguration.config || workspaceFolderConfiguration.hasErrors) {
 1893                     return Promise.resolve({ workspaceFolder, set: undefined, configurations: undefined, hasErrors: workspaceFolderConfiguration ? workspaceFolderConfiguration.hasErrors : false });
 1894                 }
 1895                 return ProblemMatcherRegistry.onReady().then(async (): Promise<WorkspaceFolderTaskResult> => {
 1896                     let taskSystemInfo: TaskSystemInfo | undefined = this._taskSystemInfos.get(workspaceFolder.uri.scheme);
 1897                     let problemReporter = new ProblemReporter(this._outputChannel);
 1898                     let parseResult = TaskConfig.parse(workspaceFolder, undefined, taskSystemInfo ? taskSystemInfo.platform : Platform.platform, workspaceFolderConfiguration.config!, problemReporter, TaskConfig.TaskConfigSource.TasksJson, this.contextKeyService);
 1899                     let hasErrors = false;
 1900                     if (!parseResult.validationStatus.isOK() && (parseResult.validationStatus.state !== ValidationState.Info)) {
 1901                         hasErrors = true;
 1902                         this.showOutput(runSource);
 1903                     }
 1904                     if (problemReporter.status.isFatal()) {
 1905                         problemReporter.fatal(nls.localize('TaskSystem.configurationErrors', 'Error: the provided task configuration has validation errors and can\'t not be used. Please correct the errors first.'));
 1906                         return { workspaceFolder, set: undefined, configurations: undefined, hasErrors };
 1907                     }
 1908                     let customizedTasks: { byIdentifier: IStringDictionary<ConfiguringTask>; } | undefined;
 1909                     if (parseResult.configured && parseResult.configured.length > 0) {
 1910                         customizedTasks = {
 1911                             byIdentifier: Object.create(null)
 1912                         };
 1913                         for (let task of parseResult.configured) {
 1914                             customizedTasks.byIdentifier[task.configures._key] = task;
 1915                         }
 1916                     }
 1917                     if (!(await this._areJsonTasksSupportedPromise) && (parseResult.custom.length > 0)) {
 1918                         console.warn('Custom workspace tasks are not supported.');
 1919                     }
 1920                     return { workspaceFolder, set: { tasks: await this._areJsonTasksSupportedPromise ? parseResult.custom : [] }, configurations: customizedTasks, hasErrors };
 1921                 });
 1922             });
 1923     }
 1924 
 1925     private testParseExternalConfig(config: TaskConfig.ExternalTaskRunnerConfiguration | undefined, location: string): { config: TaskConfig.ExternalTaskRunnerConfiguration | undefined, hasParseErrors: boolean } {
 1926         if (!config) {
 1927             return { config: undefined, hasParseErrors: false };
 1928         }
 1929         let parseErrors: string[] = (config as any).$parseErrors;
 1930         if (parseErrors) {
 1931             let isAffected = false;
 1932             for (const parseError of parseErrors) {
 1933                 if (/tasks\.json$/.test(parseError)) {
 1934                     isAffected = true;
 1935                     break;
 1936                 }
 1937             }
 1938             if (isAffected) {
 1939                 this._outputChannel.append(nls.localize({ key: 'TaskSystem.invalidTaskJsonOther', comment: ['Message notifies of an error in one of several places there is tasks related json, not necessarily in a file named tasks.json'] }, 'Error: The content of the tasks json in {0} has syntax errors. Please correct them before executing a task.\n', location));
 1940                 this.showOutput();
 1941                 return { config, hasParseErrors: true };
 1942             }
 1943         }
 1944         return { config, hasParseErrors: false };
 1945     }
 1946 
 1947     private async computeWorkspaceFileTasks(workspaceFolder: IWorkspaceFolder, runSource: TaskRunSource = TaskRunSource.User): Promise<WorkspaceFolderTaskResult> {
 1948         if (this.executionEngine === ExecutionEngine.Process) {
 1949             return this.emptyWorkspaceTaskResults(workspaceFolder);
 1950         }
 1951         const configuration = this.testParseExternalConfig(this.configurationService.inspect<TaskConfig.ExternalTaskRunnerConfiguration>('tasks').workspaceValue, nls.localize('TasksSystem.locationWorkspaceConfig', 'workspace file'));
 1952         let customizedTasks: { byIdentifier: IStringDictionary<ConfiguringTask>; } = {
 1953             byIdentifier: Object.create(null)
 1954         };
 1955 
 1956         const custom: CustomTask[] = [];
 1957         await this.computeTasksForSingleConfig(workspaceFolder, configuration.config, runSource, custom, customizedTasks.byIdentifier, TaskConfig.TaskConfigSource.WorkspaceFile);
 1958         const engine = configuration.config ? TaskConfig.ExecutionEngine.from(configuration.config) : ExecutionEngine.Terminal;
 1959         if (engine === ExecutionEngine.Process) {
 1960             this.notificationService.warn(nls.localize('TaskSystem.versionWorkspaceFile', 'Only tasks version 2.0.0 permitted in .codeworkspace.'));
 1961             return this.emptyWorkspaceTaskResults(workspaceFolder);
 1962         }
 1963         return { workspaceFolder, set: { tasks: custom }, configurations: customizedTasks, hasErrors: configuration.hasParseErrors };
 1964     }
 1965 
 1966     private async computeUserTasks(workspaceFolder: IWorkspaceFolder, runSource: TaskRunSource = TaskRunSource.User): Promise<WorkspaceFolderTaskResult> {
 1967         if (this.executionEngine === ExecutionEngine.Process) {
 1968             return this.emptyWorkspaceTaskResults(workspaceFolder);
 1969         }
 1970         const configuration = this.testParseExternalConfig(this.configurationService.inspect<TaskConfig.ExternalTaskRunnerConfiguration>('tasks').userValue, nls.localize('TasksSystem.locationUserConfig', 'user settings'));
 1971         let customizedTasks: { byIdentifier: IStringDictionary<ConfiguringTask>; } = {
 1972             byIdentifier: Object.create(null)
 1973         };
 1974 
 1975         const custom: CustomTask[] = [];
 1976         await this.computeTasksForSingleConfig(workspaceFolder, configuration.config, runSource, custom, customizedTasks.byIdentifier, TaskConfig.TaskConfigSource.User);
 1977         const engine = configuration.config ? TaskConfig.ExecutionEngine.from(configuration.config) : ExecutionEngine.Terminal;
 1978         if (engine === ExecutionEngine.Process) {
 1979             this.notificationService.warn(nls.localize('TaskSystem.versionSettings', 'Only tasks version 2.0.0 permitted in user settings.'));
 1980             return this.emptyWorkspaceTaskResults(workspaceFolder);
 1981         }
 1982         return { workspaceFolder, set: { tasks: custom }, configurations: customizedTasks, hasErrors: configuration.hasParseErrors };
 1983     }
 1984 
 1985     private emptyWorkspaceTaskResults(workspaceFolder: IWorkspaceFolder): WorkspaceFolderTaskResult {
 1986         return { workspaceFolder, set: undefined, configurations: undefined, hasErrors: false };
 1987     }
 1988 
 1989     private async computeTasksForSingleConfig(workspaceFolder: IWorkspaceFolder, config: TaskConfig.ExternalTaskRunnerConfiguration | undefined, runSource: TaskRunSource, custom: CustomTask[], customized: IStringDictionary<ConfiguringTask>, source: TaskConfig.TaskConfigSource, isRecentTask: boolean = false): Promise<boolean> {
 1990         if (!config) {
 1991             return false;
 1992         }
 1993         let taskSystemInfo: TaskSystemInfo | undefined = workspaceFolder ? this._taskSystemInfos.get(workspaceFolder.uri.scheme) : undefined;
 1994         let problemReporter = new ProblemReporter(this._outputChannel);
 1995         let parseResult = TaskConfig.parse(workspaceFolder, this._workspace, taskSystemInfo ? taskSystemInfo.platform : Platform.platform, config, problemReporter, source, this.contextKeyService, isRecentTask);
 1996         let hasErrors = false;
 1997         if (!parseResult.validationStatus.isOK() && (parseResult.validationStatus.state !== ValidationState.Info)) {
 1998             this.showOutput(runSource);
 1999             hasErrors = true;
 2000         }
 2001         if (problemReporter.status.isFatal()) {
 2002             problemReporter.fatal(nls.localize('TaskSystem.configurationErrors', 'Error: the provided task configuration has validation errors and can\'t not be used. Please correct the errors first.'));
 2003             return hasErrors;
 2004         }
 2005         if (parseResult.configured && parseResult.configured.length > 0) {
 2006             for (let task of parseResult.configured) {
 2007                 customized[task.configures._key] = task;
 2008             }
 2009         }
 2010         if (!(await this._areJsonTasksSupportedPromise) && (parseResult.custom.length > 0)) {
 2011             console.warn('Custom workspace tasks are not supported.');
 2012         } else {
 2013             for (let task of parseResult.custom) {
 2014                 custom.push(task);
 2015             }
 2016         }
 2017         return hasErrors;
 2018     }
 2019 
 2020     private computeConfiguration(workspaceFolder: IWorkspaceFolder): Promise<WorkspaceFolderConfigurationResult> {
 2021         let { config, hasParseErrors } = this.getConfiguration(workspaceFolder);
 2022         return Promise.resolve<WorkspaceFolderConfigurationResult>({ workspaceFolder, config, hasErrors: hasParseErrors });
 2023     }
 2024 
 2025     protected abstract computeLegacyConfiguration(workspaceFolder: IWorkspaceFolder): Promise<WorkspaceFolderConfigurationResult>;
 2026 
 2027     private computeWorkspaceFolderSetup(): [IWorkspaceFolder[], IWorkspaceFolder[], ExecutionEngine, JsonSchemaVersion, IWorkspace | undefined] {
 2028         let workspaceFolders: IWorkspaceFolder[] = [];
 2029         let ignoredWorkspaceFolders: IWorkspaceFolder[] = [];
 2030         let executionEngine = ExecutionEngine.Terminal;
 2031         let schemaVersion = JsonSchemaVersion.V2_0_0;
 2032         let workspace: IWorkspace | undefined;
 2033         if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) {
 2034             let workspaceFolder: IWorkspaceFolder = this.contextService.getWorkspace().folders[0];
 2035             workspaceFolders.push(workspaceFolder);
 2036             executionEngine = this.computeExecutionEngine(workspaceFolder);
 2037             schemaVersion = this.computeJsonSchemaVersion(workspaceFolder);
 2038         } else if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
 2039             workspace = this.contextService.getWorkspace();
 2040             for (let workspaceFolder of this.contextService.getWorkspace().folders) {
 2041                 if (schemaVersion === this.computeJsonSchemaVersion(workspaceFolder)) {
 2042                     workspaceFolders.push(workspaceFolder);
 2043                 } else {
 2044                     ignoredWorkspaceFolders.push(workspaceFolder);
 2045                     this._outputChannel.append(nls.localize(
 2046                         'taskService.ignoreingFolder',
 2047                         'Ignoring task configurations for workspace folder {0}. Multi folder workspace task support requires that all folders use task version 2.0.0\n',
 2048                         workspaceFolder.uri.fsPath));
 2049                 }
 2050             }
 2051         }
 2052         return [workspaceFolders, ignoredWorkspaceFolders, executionEngine, schemaVersion, workspace];
 2053     }
 2054 
 2055     private computeExecutionEngine(workspaceFolder: IWorkspaceFolder): ExecutionEngine {
 2056         let { config } = this.getConfiguration(workspaceFolder);
 2057         if (!config) {
 2058             return ExecutionEngine._default;
 2059         }
 2060         return TaskConfig.ExecutionEngine.from(config);
 2061     }
 2062 
 2063     private computeJsonSchemaVersion(workspaceFolder: IWorkspaceFolder): JsonSchemaVersion {
 2064         let { config } = this.getConfiguration(workspaceFolder);
 2065         if (!config) {
 2066             return JsonSchemaVersion.V2_0_0;
 2067         }
 2068         return TaskConfig.JsonSchemaVersion.from(config);
 2069     }
 2070 
 2071     protected getConfiguration(workspaceFolder: IWorkspaceFolder, source?: string): { config: TaskConfig.ExternalTaskRunnerConfiguration | undefined; hasParseErrors: boolean } {
 2072         let result;
 2073         if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
 2074             result = undefined;
 2075         } else {
 2076             const wholeConfig = this.configurationService.inspect<TaskConfig.ExternalTaskRunnerConfiguration>('tasks', { resource: workspaceFolder.uri });
 2077             switch (source) {
 2078                 case TaskSourceKind.User: result = Objects.deepClone(wholeConfig.userValue); break;
 2079                 case TaskSourceKind.Workspace: result = Objects.deepClone(wholeConfig.workspaceFolderValue); break;
 2080                 case TaskSourceKind.WorkspaceFile: result = Objects.deepClone(wholeConfig.workspaceValue); break;
 2081                 default: result = Objects.deepClone(wholeConfig.workspaceFolderValue);
 2082             }
 2083         }
 2084         if (!result) {
 2085             return { config: undefined, hasParseErrors: false };
 2086         }
 2087         let parseErrors: string[] = (result as any).$parseErrors;
 2088         if (parseErrors) {
 2089             let isAffected = false;
 2090             for (const parseError of parseErrors) {
 2091                 if (/tasks\.json$/.test(parseError)) {
 2092                     isAffected = true;
 2093                     break;
 2094                 }
 2095             }
 2096             if (isAffected) {
 2097                 this._outputChannel.append(nls.localize('TaskSystem.invalidTaskJson', 'Error: The content of the tasks.json file has syntax errors. Please correct them before executing a task.\n'));
 2098                 this.showOutput();
 2099                 return { config: undefined, hasParseErrors: true };
 2100             }
 2101         }
 2102         return { config: result, hasParseErrors: false };
 2103     }
 2104 
 2105     public inTerminal(): boolean {
 2106         if (this._taskSystem) {
 2107             return this._taskSystem instanceof TerminalTaskSystem;
 2108         }
 2109         return this.executionEngine === ExecutionEngine.Terminal;
 2110     }
 2111 
 2112     public configureAction(): Action {
 2113         const thisCapture: AbstractTaskService = this;
 2114         return new class extends Action {
 2115             constructor() {
 2116                 super(ConfigureTaskAction.ID, ConfigureTaskAction.TEXT, undefined, true, () => { thisCapture.runConfigureTasks(); return Promise.resolve(undefined); });
 2117             }
 2118         };
 2119     }
 2120 
 2121     public beforeShutdown(): boolean | Promise<boolean> {
 2122         if (!this._taskSystem) {
 2123             return false;
 2124         }
 2125         if (!this._taskSystem.isActiveSync()) {
 2126             return false;
 2127         }
 2128         // The terminal service kills all terminal on shutdown. So there
 2129         // is nothing we can do to prevent this here.
 2130         if (this._taskSystem instanceof TerminalTaskSystem) {
 2131             return false;
 2132         }
 2133 
 2134         let terminatePromise: Promise<IConfirmationResult>;
 2135         if (this._taskSystem.canAutoTerminate()) {
 2136             terminatePromise = Promise.resolve({ confirmed: true });
 2137         } else {
 2138             terminatePromise = this.dialogService.confirm({
 2139                 message: nls.localize('TaskSystem.runningTask', 'There is a task running. Do you want to terminate it?'),
 2140                 primaryButton: nls.localize({ key: 'TaskSystem.terminateTask', comment: ['&& denotes a mnemonic'] }, "&&Terminate Task"),
 2141                 type: 'question'
 2142             });
 2143         }
 2144 
 2145         return terminatePromise.then(res => {
 2146             if (res.confirmed) {
 2147                 return this._taskSystem!.terminateAll().then((responses) => {
 2148                     let success = true;
 2149                     let code: number | undefined = undefined;
 2150                     for (let response of responses) {
 2151                         success = success && response.success;
 2152                         // We only have a code in the old output runner which only has one task
 2153                         // So we can use the first code.
 2154                         if (code === undefined && response.code !== undefined) {
 2155                             code = response.code;
 2156                         }
 2157                     }
 2158                     if (success) {
 2159                         this._taskSystem = undefined;
 2160                         this.disposeTaskSystemListeners();
 2161                         return false; // no veto
 2162                     } else if (code && code === TerminateResponseCode.ProcessNotFound) {
 2163                         return this.dialogService.confirm({
 2164                             message: nls.localize('TaskSystem.noProcess', 'The launched task doesn\'t exist anymore. If the task spawned background processes exiting VS Code might result in orphaned processes. To avoid this start the last background process with a wait flag.'),
 2165                             primaryButton: nls.localize({ key: 'TaskSystem.exitAnyways', comment: ['&& denotes a mnemonic'] }, "&&Exit Anyways"),
 2166                             type: 'info'
 2167                         }).then(res => !res.confirmed);
 2168                     }
 2169                     return true; // veto
 2170                 }, (err) => {
 2171                     return true; // veto
 2172                 });
 2173             }
 2174 
 2175             return true; // veto
 2176         });
 2177     }
 2178 
 2179     private handleError(err: any): void {
 2180         let showOutput = true;
 2181         if (err instanceof TaskError) {
 2182             let buildError = <TaskError>err;
 2183             let needsConfig = buildError.code === TaskErrors.NotConfigured || buildError.code === TaskErrors.NoBuildTask || buildError.code === TaskErrors.NoTestTask;
 2184             let needsTerminate = buildError.code === TaskErrors.RunningTask;
 2185             if (needsConfig || needsTerminate) {
 2186                 this.notificationService.prompt(buildError.severity, buildError.message, [{
 2187                     label: needsConfig ? ConfigureTaskAction.TEXT : nls.localize('TerminateAction.label', "Terminate Task"),
 2188                     run: () => {
 2189                         if (needsConfig) {
 2190                             this.runConfigureTasks();
 2191                         } else {
 2192                             this.runTerminateCommand();
 2193                         }
 2194                     }
 2195                 }]);
 2196             } else {
 2197                 this.notificationService.notify({ severity: buildError.severity, message: buildError.message });
 2198             }
 2199         } else if (err instanceof Error) {
 2200             let error = <Error>err;
 2201             this.notificationService.error(error.message);
 2202             showOutput = false;
 2203         } else if (Types.isString(err)) {
 2204             this.notificationService.error(<string>err);
 2205         } else {
 2206             this.notificationService.error(nls.localize('TaskSystem.unknownError', 'An error has occurred while running a task. See task log for details.'));
 2207         }
 2208         if (showOutput) {
 2209             this.showOutput();
 2210         }
 2211     }
 2212 
 2213     private canRunCommand(): boolean {
 2214         if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
 2215             this.notificationService.prompt(
 2216                 Severity.Info,
 2217                 nls.localize('TaskService.noWorkspace', "Tasks are only available on a workspace folder."),
 2218                 [{
 2219                     label: nls.localize('TaskService.learnMore', "Learn More"),
 2220                     run: () => this.openerService.open(URI.parse('https://code.visualstudio.com/docs/editor/tasks'))
 2221                 }]
 2222             );
 2223             return false;
 2224         }
 2225         return true;
 2226     }
 2227 
 2228     private showDetail(): boolean {
 2229         return this.configurationService.getValue<boolean>(QUICKOPEN_DETAIL_CONFIG);
 2230     }
 2231 
 2232     private async createTaskQuickPickEntries(tasks: Task[], group: boolean = false, sort: boolean = false, selectedEntry?: TaskQuickPickEntry, includeRecents: boolean = true): Promise<TaskQuickPickEntry[]> {
 2233         let count: { [key: string]: number; } = {};
 2234         if (tasks === undefined || tasks === null || tasks.length === 0) {
 2235             return [];
 2236         }
 2237         const TaskQuickPickEntry = (task: Task): TaskQuickPickEntry => {
 2238             let entryLabel = task._label;
 2239             if (count[task._id]) {
 2240                 entryLabel = entryLabel + ' (' + count[task._id].toString() + ')';
 2241                 count[task._id]++;
 2242             } else {
 2243                 count[task._id] = 1;
 2244             }
 2245             return { label: entryLabel, description: this.getTaskDescription(task), task, detail: this.showDetail() ? task.configurationProperties.detail : undefined };
 2246 
 2247         };
 2248         function fillEntries(entries: QuickPickInput<TaskQuickPickEntry>[], tasks: Task[], groupLabel: string): void {
 2249             if (tasks.length) {
 2250                 entries.push({ type: 'separator', label: groupLabel });
 2251             }
 2252             for (let task of tasks) {
 2253                 let entry: TaskQuickPickEntry = TaskQuickPickEntry(task);
 2254                 entry.buttons = [{ iconClass: 'codicon-gear', tooltip: nls.localize('configureTask', "Configure Task") }];
 2255                 if (selectedEntry && (task === selectedEntry.task)) {
 2256                     entries.unshift(selectedEntry);
 2257                 } else {
 2258                     entries.push(entry);
 2259                 }
 2260             }
 2261         }
 2262         let entries: TaskQuickPickEntry[];
 2263         if (group) {
 2264             entries = [];
 2265             if (tasks.length === 1) {
 2266                 entries.push(TaskQuickPickEntry(tasks[0]));
 2267             } else {
 2268                 let recentlyUsedTasks = await this.readRecentTasks();
 2269                 let recent: Task[] = [];
 2270                 let recentSet: Set<string> = new Set();
 2271                 let configured: Task[] = [];
 2272                 let detected: Task[] = [];
 2273                 let taskMap: IStringDictionary<Task> = Object.create(null);
 2274                 tasks.forEach(task => {
 2275                     let key = task.getCommonTaskId();
 2276                     if (key) {
 2277                         taskMap[key] = task;
 2278                     }
 2279                 });
 2280                 recentlyUsedTasks.reverse().forEach(recentTask => {
 2281                     const key = recentTask.getCommonTaskId();
 2282                     if (key) {
 2283                         recentSet.add(key);
 2284                         let task = taskMap[key];
 2285                         if (task) {
 2286                             recent.push(task);
 2287                         }
 2288                     }
 2289                 });
 2290                 for (let task of tasks) {
 2291                     let key = task.getCommonTaskId();
 2292                     if (!key || !recentSet.has(key)) {
 2293                         if ((task._source.kind === TaskSourceKind.Workspace) || (task._source.kind === TaskSourceKind.User)) {
 2294                             configured.push(task);
 2295                         } else {
 2296                             detected.push(task);
 2297                         }
 2298                     }
 2299                 }
 2300                 const sorter = this.createSorter();
 2301                 if (includeRecents) {
 2302                     fillEntries(entries, recent, nls.localize('recentlyUsed', 'recently used tasks'));
 2303                 }
 2304                 configured = configured.sort((a, b) => sorter.compare(a, b));
 2305                 fillEntries(entries, configured, nls.localize('configured', 'configured tasks'));
 2306                 detected = detected.sort((a, b) => sorter.compare(a, b));
 2307                 fillEntries(entries, detected, nls.localize('detected', 'detected tasks'));
 2308             }
 2309         } else {
 2310             if (sort) {
 2311                 const sorter = this.createSorter();
 2312                 tasks = tasks.sort((a, b) => sorter.compare(a, b));
 2313             }
 2314             entries = tasks.map<TaskQuickPickEntry>(task => TaskQuickPickEntry(task));
 2315         }
 2316         count = {};
 2317         return entries;
 2318     }
 2319 
 2320     private async showTwoLevelQuickPick(placeHolder: string, defaultEntry?: TaskQuickPickEntry) {
 2321         return TaskQuickPick.show(this, this.configurationService, this.quickInputService, this.notificationService, placeHolder, defaultEntry);
 2322     }
 2323 
 2324     private async showQuickPick(tasks: Promise<Task[]> | Task[], placeHolder: string, defaultEntry?: TaskQuickPickEntry, group: boolean = false, sort: boolean = false, selectedEntry?: TaskQuickPickEntry, additionalEntries?: TaskQuickPickEntry[]): Promise<TaskQuickPickEntry | undefined | null> {
 2325         const tokenSource = new CancellationTokenSource();
 2326         const cancellationToken: CancellationToken = tokenSource.token;
 2327         let _createEntries = new Promise<QuickPickInput<TaskQuickPickEntry>[]>((resolve) => {
 2328             if (Array.isArray(tasks)) {
 2329                 resolve(this.createTaskQuickPickEntries(tasks, group, sort, selectedEntry));
 2330             } else {
 2331                 resolve(tasks.then((tasks) => this.createTaskQuickPickEntries(tasks, group, sort, selectedEntry)));
 2332             }
 2333         });
 2334 
 2335         const timeout: boolean = await Promise.race([new Promise<boolean>(async (resolve) => {
 2336             await _createEntries;
 2337             resolve(false);
 2338         }), new Promise<boolean>((resolve) => {
 2339             const timer = setTimeout(() => {
 2340                 clearTimeout(timer);
 2341                 resolve(true);
 2342             }, 200);
 2343         })]);
 2344 
 2345         if (!timeout && ((await _createEntries).length === 1) && this.configurationService.getValue<boolean>(QUICKOPEN_SKIP_CONFIG)) {
 2346             return (<TaskQuickPickEntry>(await _createEntries)[0]);
 2347         }
 2348 
 2349         const pickEntries = _createEntries.then((entries) => {
 2350             if ((entries.length === 1) && this.configurationService.getValue<boolean>(QUICKOPEN_SKIP_CONFIG)) {
 2351                 tokenSource.cancel();
 2352             } else if ((entries.length === 0) && defaultEntry) {
 2353                 entries.push(defaultEntry);
 2354             } else if (entries.length > 1 && additionalEntries && additionalEntries.length > 0) {
 2355                 entries.push({ type: 'separator', label: '' });
 2356                 entries.push(additionalEntries[0]);
 2357             }
 2358             return entries;
 2359         });
 2360 
 2361         const picker: IQuickPick<TaskQuickPickEntry> = this.quickInputService.createQuickPick();
 2362         picker.placeholder = placeHolder;
 2363         picker.matchOnDescription = true;
 2364 
 2365         picker.onDidTriggerItemButton(context => {
 2366             let task = context.item.task;
 2367             this.quickInputService.cancel();
 2368             if (ContributedTask.is(task)) {
 2369                 this.customize(task, undefined, true);
 2370             } else if (CustomTask.is(task)) {
 2371                 this.openConfig(task);
 2372             }
 2373         });
 2374         picker.busy = true;
 2375         pickEntries.then(entries => {
 2376             picker.busy = false;
 2377             picker.items = entries;
 2378         });
 2379         picker.show();
 2380 
 2381         return new Promise<TaskQuickPickEntry | undefined | null>(resolve => {
 2382             this._register(picker.onDidAccept(async () => {
 2383                 let selection = picker.selectedItems ? picker.selectedItems[0] : undefined;
 2384                 if (cancellationToken.isCancellationRequested) {
 2385                     // canceled when there's only one task
 2386                     const task = (await pickEntries)[0];
 2387                     if ((<any>task).task) {
 2388                         selection = <TaskQuickPickEntry>task;
 2389                     }
 2390                 }
 2391                 picker.dispose();
 2392                 if (!selection) {
 2393                     resolve();
 2394                 }
 2395                 resolve(selection);
 2396             }));
 2397         });
 2398     }
 2399 
 2400     private needsRecentTasksMigration(): boolean {
 2401         return (this.getRecentlyUsedTasksV1().size > 0) && (this.getRecentlyUsedTasks().size === 0);
 2402     }
 2403 
 2404     public async migrateRecentTasks(tasks: Task[]) {
 2405         if (!this.needsRecentTasksMigration()) {
 2406             return;
 2407         }
 2408         let recentlyUsedTasks = this.getRecentlyUsedTasksV1();
 2409         let taskMap: IStringDictionary<Task> = Object.create(null);
 2410         tasks.forEach(task => {
 2411             let key = task.getRecentlyUsedKey();
 2412             if (key) {
 2413                 taskMap[key] = task;
 2414             }
 2415         });
 2416         const reversed = [...recentlyUsedTasks.keys()].reverse();
 2417         for (const key in reversed) {
 2418             let task = taskMap[key];
 2419             if (task) {
 2420                 await this.setRecentlyUsedTask(task);
 2421             }
 2422         }
 2423         this.storageService.remove(AbstractTaskService.RecentlyUsedTasks_Key, StorageScope.WORKSPACE);
 2424     }
 2425 
 2426     private showIgnoredFoldersMessage(): Promise<void> {
 2427         if (this.ignoredWorkspaceFolders.length === 0 || !this.showIgnoreMessage) {
 2428             return Promise.resolve(undefined);
 2429         }
 2430 
 2431         this.notificationService.prompt(
 2432             Severity.Info,
 2433             nls.localize('TaskService.ignoredFolder', 'The following workspace folders are ignored since they use task version 0.1.0: {0}', this.ignoredWorkspaceFolders.map(f => f.name).join(', ')),
 2434             [{
 2435                 label: nls.localize('TaskService.notAgain', "Don't Show Again"),
 2436                 isSecondary: true,
 2437                 run: () => {
 2438                     this.storageService.store(AbstractTaskService.IgnoreTask010DonotShowAgain_key, true, StorageScope.WORKSPACE);
 2439                     this._showIgnoreMessage = false;
 2440                 }
 2441             }]
 2442         );
 2443 
 2444         return Promise.resolve(undefined);
 2445     }
 2446 
 2447     private runTaskCommand(arg?: any): void {
 2448         if (!this.canRunCommand()) {
 2449             return;
 2450         }
 2451         let identifier = this.getTaskIdentifier(arg);
 2452         if (identifier !== undefined) {
 2453             this.getGroupedTasks().then(async (grouped) => {
 2454                 let resolver = this.createResolver(grouped);
 2455                 let folderURIs: (URI | string)[] = this.contextService.getWorkspace().folders.map(folder => folder.uri);
 2456                 if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
 2457                     folderURIs.push(this.contextService.getWorkspace().configuration!);
 2458                 }
 2459                 folderURIs.push(USER_TASKS_GROUP_KEY);
 2460                 for (let uri of folderURIs) {
 2461                     let task = await resolver.resolve(uri, identifier);
 2462                     if (task) {
 2463                         this.run(task).then(undefined, reason => {
 2464                             // eat the error, it has already been surfaced to the user and we don't care about it here
 2465                         });
 2466                         return;
 2467                     }
 2468                 }
 2469                 this.doRunTaskCommand(grouped.all());
 2470             }, () => {
 2471                 this.doRunTaskCommand();
 2472             });
 2473         } else {
 2474             this.doRunTaskCommand();
 2475         }
 2476     }
 2477 
 2478     private tasksAndGroupedTasks(filter?: TaskFilter): { tasks: Promise<Task[]>, grouped: Promise<TaskMap> } {
 2479         if (!this.versionAndEngineCompatible(filter)) {
 2480             return { tasks: Promise.resolve<Task[]>([]), grouped: Promise.resolve(new TaskMap()) };
 2481         }
 2482         const grouped = this.getGroupedTasks(filter ? filter.type : undefined);
 2483         const tasks = grouped.then((map) => {
 2484             if (!filter || !filter.type) {
 2485                 return map.all();
 2486             }
 2487             let result: Task[] = [];
 2488             map.forEach((tasks) => {
 2489                 for (let task of tasks) {
 2490                     if (ContributedTask.is(task) && task.defines.type === filter.type) {
 2491                         result.push(task);
 2492                     } else if (CustomTask.is(task)) {
 2493                         if (task.type === filter.type) {
 2494                             result.push(task);
 2495                         } else {
 2496                             let customizes = task.customizes();
 2497                             if (customizes && customizes.type === filter.type) {
 2498                                 result.push(task);
 2499                             }
 2500                         }
 2501                     }
 2502                 }
 2503             });
 2504             return result;
 2505         });
 2506         return { tasks, grouped };
 2507     }
 2508 
 2509     private doRunTaskCommand(tasks?: Task[]): void {
 2510         const pickThen = (task: Task | undefined | null) => {
 2511             if (task === undefined) {
 2512                 return;
 2513             }
 2514             if (task === null) {
 2515                 this.runConfigureTasks();
 2516             } else {
 2517                 this.run(task, { attachProblemMatcher: true }, TaskRunSource.User).then(undefined, reason => {
 2518                     // eat the error, it has already been surfaced to the user and we don't care about it here
 2519                 });
 2520             }
 2521         };
 2522 
 2523         const placeholder = nls.localize('TaskService.pickRunTask', 'Select the task to run');
 2524 
 2525         this.showIgnoredFoldersMessage().then(() => {
 2526             if (this.configurationService.getValue(USE_SLOW_PICKER)) {
 2527                 let taskResult: { tasks: Promise<Task[]>, grouped: Promise<TaskMap> } | undefined = undefined;
 2528                 if (!tasks) {
 2529                     taskResult = this.tasksAndGroupedTasks();
 2530                 }
 2531                 this.showQuickPick(tasks ? tasks : taskResult!.tasks, placeholder,
 2532                     {
 2533                         label: nls.localize('TaskService.noEntryToRunSlow', 'No task to run found. Configure Tasks...'),
 2534                         task: null
 2535                     },
 2536                     true).
 2537                     then((entry) => {
 2538                         return pickThen(entry ? entry.task : undefined);
 2539                     });
 2540             } else {
 2541                 this.showTwoLevelQuickPick(placeholder,
 2542                     {
 2543                         label: nls.localize('TaskService.noEntryToRun', 'No configured tasks. Configure Tasks...'),
 2544                         task: null
 2545                     }).
 2546                     then(pickThen);
 2547             }
 2548         });
 2549     }
 2550 
 2551     private reRunTaskCommand(): void {
 2552         if (!this.canRunCommand()) {
 2553             return;
 2554         }
 2555 
 2556         ProblemMatcherRegistry.onReady().then(() => {
 2557             return this.editorService.saveAll({ reason: SaveReason.AUTO }).then(() => { // make sure all dirty editors are saved
 2558                 let executeResult = this.getTaskSystem().rerun();
 2559                 if (executeResult) {
 2560                     return this.handleExecuteResult(executeResult);
 2561                 } else {
 2562                     this.doRunTaskCommand();
 2563                     return Promise.resolve(undefined);
 2564                 }
 2565             });
 2566         });
 2567     }
 2568 
 2569     private splitPerGroupType(tasks: Task[]): { none: Task[], defaults: Task[], users: Task[] } {
 2570         let none: Task[] = [];
 2571         let defaults: Task[] = [];
 2572         let users: Task[] = [];
 2573         for (let task of tasks) {
 2574             if (task.configurationProperties.groupType === GroupType.default) {
 2575                 defaults.push(task);
 2576             } else if (task.configurationProperties.groupType === GroupType.user) {
 2577                 users.push(task);
 2578             } else {
 2579                 none.push(task);
 2580             }
 2581         }
 2582         return { none, defaults, users };
 2583     }
 2584 
 2585     private runBuildCommand(): void {
 2586         if (!this.canRunCommand()) {
 2587             return;
 2588         }
 2589         if (this.schemaVersion === JsonSchemaVersion.V0_1_0) {
 2590             this.build();
 2591             return;
 2592         }
 2593         let options: IProgressOptions = {
 2594             location: ProgressLocation.Window,
 2595             title: nls.localize('TaskService.fetchingBuildTasks', 'Fetching build tasks...')
 2596         };
 2597         let promise = this.getWorkspaceTasks().then(tasks => {
 2598             const buildTasks: ConfiguringTask[] = [];
 2599             for (const taskSource of tasks) {
 2600                 for (const task in taskSource[1].configurations?.byIdentifier) {
 2601                     if ((taskSource[1].configurations?.byIdentifier[task].configurationProperties.group === TaskGroup.Build) &&
 2602                         (taskSource[1].configurations?.byIdentifier[task].configurationProperties.groupType === GroupType.default)) {
 2603                         buildTasks.push(taskSource[1].configurations.byIdentifier[task]);
 2604                     }
 2605                 }
 2606             }
 2607             if (buildTasks.length === 1) {
 2608                 this.tryResolveTask(buildTasks[0]).then(resolvedTask => {
 2609                     this.run(resolvedTask, undefined, TaskRunSource.User).then(undefined, reason => {
 2610                         // eat the error, it has already been surfaced to the user and we don't care about it here
 2611                     });
 2612                 });
 2613                 return;
 2614             }
 2615 
 2616             return this.getTasksForGroup(TaskGroup.Build).then((tasks) => {
 2617                 if (tasks.length > 0) {
 2618                     let { defaults, users } = this.splitPerGroupType(tasks);
 2619                     if (defaults.length === 1) {
 2620                         this.run(defaults[0], undefined, TaskRunSource.User).then(undefined, reason => {
 2621                             // eat the error, it has already been surfaced to the user and we don't care about it here
 2622                         });
 2623                         return;
 2624                     } else if (defaults.length + users.length > 0) {
 2625                         tasks = defaults.concat(users);
 2626                     }
 2627                 }
 2628                 this.showIgnoredFoldersMessage().then(() => {
 2629                     this.showQuickPick(tasks,
 2630                         nls.localize('TaskService.pickBuildTask', 'Select the build task to run'),
 2631                         {
 2632                             label: nls.localize('TaskService.noBuildTask', 'No build task to run found. Configure Build Task...'),
 2633                             task: null
 2634                         },
 2635                         true).then((entry) => {
 2636                             let task: Task | undefined | null = entry ? entry.task : undefined;
 2637                             if (task === undefined) {
 2638                                 return;
 2639                             }
 2640                             if (task === null) {
 2641                                 this.runConfigureDefaultBuildTask();
 2642                                 return;
 2643                             }
 2644                             this.run(task, { attachProblemMatcher: true }, TaskRunSource.User).then(undefined, reason => {
 2645                                 // eat the error, it has already been surfaced to the user and we don't care about it here
 2646                             });
 2647                         });
 2648                 });
 2649             });
 2650         });
 2651         this.progressService.withProgress(options, () => promise);
 2652     }
 2653 
 2654     private runTestCommand(): void {
 2655         if (!this.canRunCommand()) {
 2656             return;
 2657         }
 2658         if (this.schemaVersion === JsonSchemaVersion.V0_1_0) {
 2659             this.runTest();
 2660             return;
 2661         }
 2662         let options: IProgressOptions = {
 2663             location: ProgressLocation.Window,
 2664             title: nls.localize('TaskService.fetchingTestTasks', 'Fetching test tasks...')
 2665         };
 2666         let promise = this.getTasksForGroup(TaskGroup.Test).then((tasks) => {
 2667             if (tasks.length > 0) {
 2668                 let { defaults, users } = this.splitPerGroupType(tasks);
 2669                 if (defaults.length === 1) {
 2670                     this.run(defaults[0], undefined, TaskRunSource.User).then(undefined, reason => {
 2671                         // eat the error, it has already been surfaced to the user and we don't care about it here
 2672                     });
 2673                     return;
 2674                 } else if (defaults.length + users.length > 0) {
 2675                     tasks = defaults.concat(users);
 2676                 }
 2677             }
 2678             this.showIgnoredFoldersMessage().then(() => {
 2679                 this.showQuickPick(tasks,
 2680                     nls.localize('TaskService.pickTestTask', 'Select the test task to run'),
 2681                     {
 2682                         label: nls.localize('TaskService.noTestTaskTerminal', 'No test task to run found. Configure Tasks...'),
 2683                         task: null
 2684                     }, true
 2685                 ).then((entry) => {
 2686                     let task: Task | undefined | null = entry ? entry.task : undefined;
 2687                     if (task === undefined) {
 2688                         return;
 2689                     }
 2690                     if (task === null) {
 2691                         this.runConfigureTasks();
 2692                         return;
 2693                     }
 2694                     this.run(task, undefined, TaskRunSource.User).then(undefined, reason => {
 2695                         // eat the error, it has already been surfaced to the user and we don't care about it here
 2696                     });
 2697                 });
 2698             });
 2699         });
 2700         this.progressService.withProgress(options, () => promise);
 2701     }
 2702 
 2703     private runTerminateCommand(arg?: any): void {
 2704         if (!this.canRunCommand()) {
 2705             return;
 2706         }
 2707         if (arg === 'terminateAll') {
 2708             this.terminateAll();
 2709             return;
 2710         }
 2711         let runQuickPick = (promise?: Promise<Task[]>) => {
 2712             this.showQuickPick(promise || this.getActiveTasks(),
 2713                 nls.localize('TaskService.taskToTerminate', 'Select a task to terminate'),
 2714                 {
 2715                     label: nls.localize('TaskService.noTaskRunning', 'No task is currently running'),
 2716                     task: undefined
 2717                 },
 2718                 false, true,
 2719                 undefined,
 2720                 [{
 2721                     label: nls.localize('TaskService.terminateAllRunningTasks', 'All Running Tasks'),
 2722                     id: 'terminateAll',
 2723                     task: undefined
 2724                 }]
 2725             ).then(entry => {
 2726                 if (entry && entry.id === 'terminateAll') {
 2727                     this.terminateAll();
 2728                 }
 2729                 let task: Task | undefined | null = entry ? entry.task : undefined;
 2730                 if (task === undefined || task === null) {
 2731                     return;
 2732                 }
 2733                 this.terminate(task);
 2734             });
 2735         };
 2736         if (this.inTerminal()) {
 2737             let identifier = this.getTaskIdentifier(arg);
 2738             let promise: Promise<Task[]>;
 2739             if (identifier !== undefined) {
 2740                 promise = this.getActiveTasks();
 2741                 promise.then((tasks) => {
 2742                     for (let task of tasks) {
 2743                         if (task.matches(identifier)) {
 2744                             this.terminate(task);
 2745                             return;
 2746                         }
 2747                     }
 2748                     runQuickPick(promise);
 2749                 });
 2750             } else {
 2751                 runQuickPick();
 2752             }
 2753         } else {
 2754             this.isActive().then((active) => {
 2755                 if (active) {
 2756                     this.terminateAll().then((responses) => {
 2757                         // the output runner has only one task
 2758                         let response = responses[0];
 2759                         if (response.success) {
 2760                             return;
 2761                         }
 2762                         if (response.code && response.code === TerminateResponseCode.ProcessNotFound) {
 2763                             this.notificationService.error(nls.localize('TerminateAction.noProcess', 'The launched process doesn\'t exist anymore. If the task spawned background tasks exiting VS Code might result in orphaned processes.'));
 2764                         } else {
 2765                             this.notificationService.error(nls.localize('TerminateAction.failed', 'Failed to terminate running task'));
 2766                         }
 2767                     });
 2768                 }
 2769             });
 2770         }
 2771     }
 2772 
 2773     private runRestartTaskCommand(arg?: any): void {
 2774         if (!this.canRunCommand()) {
 2775             return;
 2776         }
 2777         let runQuickPick = (promise?: Promise<Task[]>) => {
 2778             this.showQuickPick(promise || this.getActiveTasks(),
 2779                 nls.localize('TaskService.taskToRestart', 'Select the task to restart'),
 2780                 {
 2781                     label: nls.localize('TaskService.noTaskToRestart', 'No task to restart'),
 2782                     task: null
 2783                 },
 2784                 false, true
 2785             ).then(entry => {
 2786                 let task: Task | undefined | null = entry ? entry.task : undefined;
 2787                 if (task === undefined || task === null) {
 2788                     return;
 2789                 }
 2790                 this.restart(task);
 2791             });
 2792         };
 2793         if (this.inTerminal()) {
 2794             let identifier = this.getTaskIdentifier(arg);
 2795             let promise: Promise<Task[]>;
 2796             if (identifier !== undefined) {
 2797                 promise = this.getActiveTasks();
 2798                 promise.then((tasks) => {
 2799                     for (let task of tasks) {
 2800                         if (task.matches(identifier)) {
 2801                             this.restart(task);
 2802                             return;
 2803                         }
 2804                     }
 2805                     runQuickPick(promise);
 2806                 });
 2807             } else {
 2808                 runQuickPick();
 2809             }
 2810         } else {
 2811             this.getActiveTasks().then((activeTasks) => {
 2812                 if (activeTasks.length === 0) {
 2813                     return;
 2814                 }
 2815                 let task = activeTasks[0];
 2816                 this.restart(task);
 2817             });
 2818         }
 2819     }
 2820 
 2821     private getTaskIdentifier(arg?: any): string | KeyedTaskIdentifier | undefined {
 2822         let result: string | KeyedTaskIdentifier | undefined = undefined;
 2823         if (Types.isString(arg)) {
 2824             result = arg;
 2825         } else if (arg && Types.isString((arg as TaskIdentifier).type)) {
 2826             result = TaskDefinition.createTaskIdentifier(arg as TaskIdentifier, console);
 2827         }
 2828         return result;
 2829     }
 2830 
 2831     private configHasTasks(taskConfig?: TaskConfig.ExternalTaskRunnerConfiguration): boolean {
 2832         return !!taskConfig && !!taskConfig.tasks && taskConfig.tasks.length > 0;
 2833     }
 2834 
 2835     private openTaskFile(resource: URI, taskSource: string) {
 2836         let configFileCreated = false;
 2837         this.fileService.resolve(resource).then((stat) => stat, () => undefined).then(async (stat) => {
 2838             const fileExists: boolean = !!stat;
 2839             const configValue = this.configurationService.inspect<TaskConfig.ExternalTaskRunnerConfiguration>('tasks');
 2840             let tasksExistInFile: boolean;
 2841             let target: ConfigurationTarget;
 2842             switch (taskSource) {
 2843                 case TaskSourceKind.User: tasksExistInFile = this.configHasTasks(configValue.userValue); target = ConfigurationTarget.USER; break;
 2844                 case TaskSourceKind.WorkspaceFile: tasksExistInFile = this.configHasTasks(configValue.workspaceValue); target = ConfigurationTarget.WORKSPACE; break;
 2845                 default: tasksExistInFile = this.configHasTasks(configValue.value); target = ConfigurationTarget.WORKSPACE_FOLDER;
 2846             }
 2847             let content;
 2848             if (!tasksExistInFile) {
 2849                 const pickTemplateResult = await this.quickInputService.pick(getTaskTemplates(), { placeHolder: nls.localize('TaskService.template', 'Select a Task Template') });
 2850                 if (!pickTemplateResult) {
 2851                     return Promise.resolve(undefined);
 2852                 }
 2853                 content = pickTemplateResult.content;
 2854                 let editorConfig = this.configurationService.getValue<any>();
 2855                 if (editorConfig.editor.insertSpaces) {
 2856                     content = content.replace(/(\n)(\t+)/g, (_, s1, s2) => s1 + ' '.repeat(s2.length * editorConfig.editor.tabSize));
 2857                 }
 2858                 configFileCreated = true;
 2859                 type TaskServiceTemplateClassification = {
 2860                     templateId?: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
 2861                     autoDetect: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
 2862                 };
 2863                 type TaskServiceEvent = {
 2864                     templateId?: string;
 2865                     autoDetect: boolean;
 2866                 };
 2867                 this.telemetryService.publicLog2<TaskServiceEvent, TaskServiceTemplateClassification>('taskService.template', {
 2868                     templateId: pickTemplateResult.id,
 2869                     autoDetect: pickTemplateResult.autoDetect
 2870                 });
 2871             }
 2872 
 2873             if (!fileExists && content) {
 2874                 return this.textFileService.create(resource, content).then((result): URI => {
 2875                     return result.resource;
 2876                 });
 2877             } else if (fileExists && (tasksExistInFile || content)) {
 2878                 if (content) {
 2879                     this.configurationService.updateValue('tasks', json.parse(content), target);
 2880                 }
 2881                 return stat?.resource;
 2882             }
 2883             return undefined;
 2884         }).then((resource) => {
 2885             if (!resource) {
 2886                 return;
 2887             }
 2888             this.editorService.openEditor({
 2889                 resource,
 2890                 options: {
 2891                     pinned: configFileCreated // pin only if config file is created #8727
 2892                 }
 2893             });
 2894         });
 2895     }
 2896 
 2897     private isTaskEntry(value: IQuickPickItem): value is IQuickPickItem & { task: Task } {
 2898         let candidate: IQuickPickItem & { task: Task } = value as any;
 2899         return candidate && !!candidate.task;
 2900     }
 2901 
 2902     private configureTask(task: Task) {
 2903         if (ContributedTask.is(task)) {
 2904             this.customize(task, undefined, true);
 2905         } else if (CustomTask.is(task)) {
 2906             this.openConfig(task);
 2907         } else if (ConfiguringTask.is(task)) {
 2908             // Do nothing.
 2909         }
 2910     }
 2911 
 2912     private handleSelection(selection: TaskQuickPickEntryType | undefined) {
 2913         if (!selection) {
 2914             return;
 2915         }
 2916         if (this.isTaskEntry(selection)) {
 2917             this.configureTask(selection.task);
 2918         } else {
 2919             this.openTaskFile(selection.folder.toResource('.vscode/tasks.json'), TaskSourceKind.Workspace);
 2920         }
 2921     }
 2922 
 2923     public getTaskDescription(task: Task | ConfiguringTask): string | undefined {
 2924         let description: string | undefined;
 2925         if (task._source.kind === TaskSourceKind.User) {
 2926             description = nls.localize('taskQuickPick.userSettings', 'User Settings');
 2927         } else if (task._source.kind === TaskSourceKind.WorkspaceFile) {
 2928             description = task.getWorkspaceFileName();
 2929         } else if (this.needsFolderQualification()) {
 2930             let workspaceFolder = task.getWorkspaceFolder();
 2931             if (workspaceFolder) {
 2932                 description = workspaceFolder.name;
 2933             }
 2934         }
 2935         return description;
 2936     }
 2937 
 2938     private async runConfigureTasks(): Promise<void> {
 2939         if (!this.canRunCommand()) {
 2940             return undefined;
 2941         }
 2942         let taskPromise: Promise<TaskMap>;
 2943         if (this.schemaVersion === JsonSchemaVersion.V2_0_0) {
 2944             taskPromise = this.getGroupedTasks();
 2945         } else {
 2946             taskPromise = Promise.resolve(new TaskMap());
 2947         }
 2948 
 2949         let stats = this.contextService.getWorkspace().folders.map<Promise<IFileStat | undefined>>((folder) => {
 2950             return this.fileService.resolve(folder.toResource('.vscode/tasks.json')).then(stat => stat, () => undefined);
 2951         });
 2952 
 2953         let createLabel = nls.localize('TaskService.createJsonFile', 'Create tasks.json file from template');
 2954         let openLabel = nls.localize('TaskService.openJsonFile', 'Open tasks.json file');
 2955         const tokenSource = new CancellationTokenSource();
 2956         const cancellationToken: CancellationToken = tokenSource.token;
 2957         let entries = Promise.all(stats).then((stats) => {
 2958             return taskPromise.then((taskMap) => {
 2959                 let entries: QuickPickInput<TaskQuickPickEntryType>[] = [];
 2960                 let needsCreateOrOpen: boolean = true;
 2961                 if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY) {
 2962                     let tasks = taskMap.all();
 2963                     if (tasks.length > 0) {
 2964                         tasks = tasks.sort((a, b) => a._label.localeCompare(b._label));
 2965                         for (let task of tasks) {
 2966                             entries.push({ label: task._label, task, description: this.getTaskDescription(task), detail: this.showDetail() ? task.configurationProperties.detail : undefined });
 2967                             if (!ContributedTask.is(task)) {
 2968                                 needsCreateOrOpen = false;
 2969                             }
 2970                         }
 2971                     }
 2972                     if (needsCreateOrOpen) {
 2973                         let label = stats[0] !== undefined ? openLabel : createLabel;
 2974                         if (entries.length) {
 2975                             entries.push({ type: 'separator' });
 2976                         }
 2977                         entries.push({ label, folder: this.contextService.getWorkspace().folders[0] });
 2978                     }
 2979                 } else {
 2980                     let folders = this.contextService.getWorkspace().folders;
 2981                     let index = 0;
 2982                     for (let folder of folders) {
 2983                         let tasks = taskMap.get(folder);
 2984                         if (tasks.length > 0) {
 2985                             tasks = tasks.slice().sort((a, b) => a._label.localeCompare(b._label));
 2986                             for (let i = 0; i < tasks.length; i++) {
 2987                                 let entry: TaskQuickPickEntryType = { label: tasks[i]._label, task: tasks[i], description: this.getTaskDescription(tasks[i]) };
 2988                                 if (i === 0) {
 2989                                     entries.push({ type: 'separator', label: folder.name });
 2990                                 }
 2991                                 entries.push(entry);
 2992                             }
 2993                         } else {
 2994                             let label = stats[index] !== undefined ? openLabel : createLabel;
 2995                             let entry: TaskQuickPickEntryType = { label, folder: folder };
 2996                             entries.push({ type: 'separator', label: folder.name });
 2997                             entries.push(entry);
 2998                         }
 2999                         index++;
 3000                     }
 3001                 }
 3002                 if ((entries.length === 1) && !needsCreateOrOpen) {
 3003                     tokenSource.cancel();
 3004                 }
 3005                 return entries;
 3006             });
 3007         });
 3008 
 3009         const timeout: boolean = await Promise.race([new Promise<boolean>(async (resolve) => {
 3010             await entries;
 3011             resolve(false);
 3012         }), new Promise<boolean>((resolve) => {
 3013             const timer = setTimeout(() => {
 3014                 clearTimeout(timer);
 3015                 resolve(true);
 3016             }, 200);
 3017         })]);
 3018 
 3019         if (!timeout && ((await entries).length === 1) && this.configurationService.getValue<boolean>(QUICKOPEN_SKIP_CONFIG)) {
 3020             const entry: any = <any>((await entries)[0]);
 3021             if (entry.task) {
 3022                 this.handleSelection(entry);
 3023                 return;
 3024             }
 3025         }
 3026 
 3027         this.quickInputService.pick(entries,
 3028             { placeHolder: nls.localize('TaskService.pickTask', 'Select a task to configure') }, cancellationToken).
 3029             then(async (selection) => {
 3030                 if (cancellationToken.isCancellationRequested) {
 3031                     // canceled when there's only one task
 3032                     const task = (await entries)[0];
 3033                     if ((<any>task).task) {
 3034                         selection = <TaskQuickPickEntryType>task;
 3035                     }
 3036                 }
 3037                 this.handleSelection(selection);
 3038             });
 3039     }
 3040 
 3041     private runConfigureDefaultBuildTask(): void {
 3042         if (!this.canRunCommand()) {
 3043             return;
 3044         }
 3045         if (this.schemaVersion === JsonSchemaVersion.V2_0_0) {
 3046             this.tasks().then((tasks => {
 3047                 if (tasks.length === 0) {
 3048                     this.runConfigureTasks();
 3049                     return;
 3050                 }
 3051                 let selectedTask: Task | undefined;
 3052                 let selectedEntry: TaskQuickPickEntry;
 3053                 for (let task of tasks) {
 3054                     if (task.configurationProperties.group === TaskGroup.Build && task.configurationProperties.groupType === GroupType.default) {
 3055                         selectedTask = task;
 3056                         break;
 3057                     }
 3058                 }
 3059                 if (selectedTask) {
 3060                     selectedEntry = {
 3061                         label: nls.localize('TaskService.defaultBuildTaskExists', '{0} is already marked as the default build task', selectedTask.getQualifiedLabel()),
 3062                         task: selectedTask,
 3063                         detail: this.showDetail() ? selectedTask.configurationProperties.detail : undefined
 3064                     };
 3065                 }
 3066                 this.showIgnoredFoldersMessage().then(() => {
 3067                     this.showQuickPick(tasks,
 3068                         nls.localize('TaskService.pickDefaultBuildTask', 'Select the task to be used as the default build task'), undefined, true, false, selectedEntry).
 3069                         then((entry) => {
 3070                             let task: Task | undefined | null = entry ? entry.task : undefined;
 3071                             if ((task === undefined) || (task === null)) {
 3072                                 return;
 3073                             }
 3074                             if (task === selectedTask && CustomTask.is(task)) {
 3075                                 this.openConfig(task);
 3076                             }
 3077                             if (!InMemoryTask.is(task)) {
 3078                                 this.customize(task, { group: { kind: 'build', isDefault: true } }, true).then(() => {
 3079                                     if (selectedTask && (task !== selectedTask) && !InMemoryTask.is(selectedTask)) {
 3080                                         this.customize(selectedTask, { group: 'build' }, false);
 3081                                     }
 3082                                 });
 3083                             }
 3084                         });
 3085                 });
 3086             }));
 3087         } else {
 3088             this.runConfigureTasks();
 3089         }
 3090     }
 3091 
 3092     private runConfigureDefaultTestTask(): void {
 3093         if (!this.canRunCommand()) {
 3094             return;
 3095         }
 3096         if (this.schemaVersion === JsonSchemaVersion.V2_0_0) {
 3097             this.tasks().then((tasks => {
 3098                 if (tasks.length === 0) {
 3099                     this.runConfigureTasks();
 3100                     return;
 3101                 }
 3102                 let selectedTask: Task | undefined;
 3103                 let selectedEntry: TaskQuickPickEntry;
 3104 
 3105                 for (let task of tasks) {
 3106                     if (task.configurationProperties.group === TaskGroup.Test && task.configurationProperties.groupType === GroupType.default) {
 3107                         selectedTask = task;
 3108                         break;
 3109                     }
 3110                 }
 3111                 if (selectedTask) {
 3112                     selectedEntry = {
 3113                         label: nls.localize('TaskService.defaultTestTaskExists', '{0} is already marked as the default test task.', selectedTask.getQualifiedLabel()),
 3114                         task: selectedTask,
 3115                         detail: this.showDetail() ? selectedTask.configurationProperties.detail : undefined
 3116                     };
 3117                 }
 3118 
 3119                 this.showIgnoredFoldersMessage().then(() => {
 3120                     this.showQuickPick(tasks,
 3121                         nls.localize('TaskService.pickDefaultTestTask', 'Select the task to be used as the default test task'), undefined, true, false, selectedEntry).then((entry) => {
 3122                             let task: Task | undefined | null = entry ? entry.task : undefined;
 3123                             if (!task) {
 3124                                 return;
 3125                             }
 3126                             if (task === selectedTask && CustomTask.is(task)) {
 3127                                 this.openConfig(task);
 3128                             }
 3129                             if (!InMemoryTask.is(task)) {
 3130                                 this.customize(task, { group: { kind: 'test', isDefault: true } }, true).then(() => {
 3131                                     if (selectedTask && (task !== selectedTask) && !InMemoryTask.is(selectedTask)) {
 3132                                         this.customize(selectedTask, { group: 'test' }, false);
 3133                                     }
 3134                                 });
 3135                             }
 3136                         });
 3137                 });
 3138             }));
 3139         } else {
 3140             this.runConfigureTasks();
 3141         }
 3142     }
 3143 
 3144     public async runShowTasks(): Promise<void> {
 3145         if (!this.canRunCommand()) {
 3146             return;
 3147         }
 3148         const activeTasks: Task[] = await this.getActiveTasks();
 3149         if (activeTasks.length === 1) {
 3150             this._taskSystem!.revealTask(activeTasks[0]);
 3151         } else {
 3152             this.showQuickPick(this.getActiveTasks(),
 3153                 nls.localize('TaskService.pickShowTask', 'Select the task to show its output'),
 3154                 {
 3155                     label: nls.localize('TaskService.noTaskIsRunning', 'No task is running'),
 3156                     task: null
 3157                 },
 3158                 false, true
 3159             ).then((entry) => {
 3160                 let task: Task | undefined | null = entry ? entry.task : undefined;
 3161                 if (task === undefined || task === null) {
 3162                     return;
 3163                 }
 3164                 this._taskSystem!.revealTask(task);
 3165             });
 3166         }
 3167     }
 3168 }