abstractTaskService.ts 94.5 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6 7 8
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import * as nls from 'vs/nls';
import Severity from 'vs/base/common/severity';
import * as Objects from 'vs/base/common/objects';
9
import * as resources from 'vs/base/common/resources';
10
import { URI } from 'vs/base/common/uri';
E
Erich Gamma 已提交
11 12
import { IStringDictionary } from 'vs/base/common/collections';
import { Action } from 'vs/base/common/actions';
13
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
B
Benjamin Pasero 已提交
14
import { Event, Emitter } from 'vs/base/common/event';
E
Erich Gamma 已提交
15
import * as Types from 'vs/base/common/types';
16
import { TerminateResponseCode } from 'vs/base/common/processes';
17
import * as strings from 'vs/base/common/strings';
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';
D
Dirk Baeumer 已提交
21
import { LinkedMap, Touch } from 'vs/base/common/map';
E
Erich Gamma 已提交
22

A
Alex Ross 已提交
23
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
24
import { IMarkerService } from 'vs/platform/markers/common/markers';
E
Erich Gamma 已提交
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 } from 'vs/platform/commands/common/commands';
30
import { ProblemMatcherRegistry, NamedProblemMatcher } from 'vs/workbench/contrib/tasks/common/problemMatcher';
B
Benjamin Pasero 已提交
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, IPromptChoice } from 'vs/platform/notification/common/notification';
37
import { IDialogService, IConfirmationResult } from 'vs/platform/dialogs/common/dialogs';
38

E
Erich Gamma 已提交
39 40
import { IModelService } from 'vs/editor/common/services/modelService';

41
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
S
Sandeep Somavarapu 已提交
42
import Constants from 'vs/workbench/contrib/markers/browser/constants';
43
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
44
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
45
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
46
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace';
E
Erich Gamma 已提交
47

48
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
A
Alex Ross 已提交
49
import { IOutputService, IOutputChannel } from 'vs/workbench/contrib/output/common/output';
E
Erich Gamma 已提交
50

51
import { ITerminalService, ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal';
52

53
import { ITaskSystem, ITaskResolver, ITaskSummary, TaskExecuteKind, TaskError, TaskErrors, TaskTerminateResponse, TaskSystemInfo, ITaskExecuteResult } from 'vs/workbench/contrib/tasks/common/taskSystem';
54 55
import {
	Task, CustomTask, ConfiguringTask, ContributedTask, InMemoryTask, TaskEvent,
A
Alex Ross 已提交
56
	TaskSet, TaskGroup, GroupType, ExecutionEngine, JsonSchemaVersion, TaskSourceKind,
57 58
	TaskSorter, TaskIdentifier, KeyedTaskIdentifier, TASK_RUNNING_STATE, TaskRunSource,
	KeyedTaskIdentifier as NKeyedTaskIdentifier, TaskDefinition
59 60 61
} from 'vs/workbench/contrib/tasks/common/tasks';
import { ITaskService, ITaskProvider, ProblemMatcherRunOptions, CustomizationProperties, TaskFilter, WorkspaceFolderTaskResult } from 'vs/workbench/contrib/tasks/common/taskService';
import { getTemplates as getTaskTemplates } from 'vs/workbench/contrib/tasks/common/taskTemplates';
E
Erich Gamma 已提交
62

63
import * as TaskConfig from '../common/taskConfiguration';
64
import { TerminalTaskSystem } from './terminalTaskSystem';
65

C
Christof Marti 已提交
66
import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput';
67

68
import { TaskDefinitionRegistry } from 'vs/workbench/contrib/tasks/common/taskDefinitionRegistry';
69
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
A
Alex Ross 已提交
70
import { RunAutomaticTasks } from 'vs/workbench/contrib/tasks/browser/runAutomaticTasks';
71

72
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
A
Alex Ross 已提交
73
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
74 75 76 77 78
import { format } from 'vs/base/common/jsonFormatter';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { applyEdits } from 'vs/base/common/jsonEdit';
import { ITextEditor } from 'vs/workbench/common/editor';
import { ITextEditorSelection } from 'vs/platform/editor/common/editor';
79
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
80
import { find } from 'vs/base/common/arrays';
81
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
82

A
Alex Ross 已提交
83
export namespace ConfigureTaskAction {
84 85
	export const ID = 'workbench.action.tasks.configureTaskRunner';
	export const TEXT = nls.localize('ConfigureTaskRunnerAction.label', "Configure Task");
86 87
}

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
class ProblemReporter implements TaskConfig.IProblemReporter {

	private _validationStatus: ValidationStatus;

	constructor(private _outputChannel: IOutputChannel) {
		this._validationStatus = new ValidationStatus();
	}

	public info(message: string): void {
		this._validationStatus.state = ValidationState.Info;
		this._outputChannel.append(message + '\n');
	}

	public warn(message: string): void {
		this._validationStatus.state = ValidationState.Warning;
		this._outputChannel.append(message + '\n');
	}

	public error(message: string): void {
		this._validationStatus.state = ValidationState.Error;
		this._outputChannel.append(message + '\n');
	}

	public fatal(message: string): void {
		this._validationStatus.state = ValidationState.Fatal;
		this._outputChannel.append(message + '\n');
	}

	public get status(): ValidationStatus {
		return this._validationStatus;
	}
}

A
Alex Ross 已提交
121
export interface WorkspaceFolderConfigurationResult {
S
Sandeep Somavarapu 已提交
122
	workspaceFolder: IWorkspaceFolder;
123
	config: TaskConfig.ExternalTaskRunnerConfiguration | undefined;
124 125 126
	hasErrors: boolean;
}

127
interface TaskCustomizationTelemetryEvent {
128 129 130
	properties: string[];
}

A
Alex Ross 已提交
131 132 133 134
function isWorkspaceFolder(folder: IWorkspace | IWorkspaceFolder): folder is IWorkspaceFolder {
	return 'uri' in folder;
}

135 136 137 138 139 140 141
class TaskMap {
	private _store: Map<string, Task[]> = new Map();

	public forEach(callback: (value: Task[], folder: string) => void): void {
		this._store.forEach(callback);
	}

A
Alex Ross 已提交
142 143 144 145 146 147 148 149 150 151 152 153 154 155
	private getKey(workspaceFolder: IWorkspace | IWorkspaceFolder | string): string {
		let key: string | undefined;
		if (Types.isString(workspaceFolder)) {
			key = workspaceFolder;
		} else {
			const uri: URI | null | undefined = isWorkspaceFolder(workspaceFolder) ? workspaceFolder.uri : workspaceFolder.configuration;
			key = uri ? uri.toString() : '';
		}
		return key;
	}

	public get(workspaceFolder: IWorkspace | IWorkspaceFolder | string): Task[] {
		const key = this.getKey(workspaceFolder);
		let result: Task[] | undefined = this._store.get(key);
156 157
		if (!result) {
			result = [];
A
Alex Ross 已提交
158
			this._store.set(key, result);
159 160 161 162
		}
		return result;
	}

A
Alex Ross 已提交
163 164 165
	public add(workspaceFolder: IWorkspace | IWorkspaceFolder | string, ...task: Task[]): void {
		const key = this.getKey(workspaceFolder);
		let values = this._store.get(key);
166 167
		if (!values) {
			values = [];
A
Alex Ross 已提交
168
			this._store.set(key, values);
169 170 171 172 173 174 175 176 177 178 179
		}
		values.push(...task);
	}

	public all(): Task[] {
		let result: Task[] = [];
		this._store.forEach((values) => result.push(...values));
		return result;
	}
}

C
Christof Marti 已提交
180
interface TaskQuickPickEntry extends IQuickPickItem {
181
	task: Task | undefined | null;
182 183
}

A
Alex Ross 已提交
184
export abstract class AbstractTaskService extends Disposable implements ITaskService {
185

186
	// private static autoDetectTelemetryName: string = 'taskServer.autoDetect';
187 188
	private static readonly RecentlyUsedTasks_Key = 'workbench.tasks.recentlyUsedTasks';
	private static readonly IgnoreTask010DonotShowAgain_key = 'workbench.tasks.ignoreTask010Shown';
189

190
	private static CustomizationTelemetryEventName: string = 'taskService.customize';
191
	public _serviceBrand: undefined;
J
Johannes Rieken 已提交
192 193
	public static OutputChannelId: string = 'tasks';
	public static OutputChannelLabel: string = nls.localize('tasks', "Tasks");
E
Erich Gamma 已提交
194

195 196
	private static nextHandle: number = 0;

197 198 199
	private _schemaVersion: JsonSchemaVersion | undefined;
	private _executionEngine: ExecutionEngine | undefined;
	private _workspaceFolders: IWorkspaceFolder[] | undefined;
200
	private _workspace: IWorkspace | undefined;
201
	private _ignoredWorkspaceFolders: IWorkspaceFolder[] | undefined;
202
	private _showIgnoreMessage?: boolean;
203
	private _providers: Map<number, ITaskProvider>;
204
	private _providerTypes: Map<number, string>;
A
Alex Ross 已提交
205
	protected _taskSystemInfos: Map<string, TaskSystemInfo>;
206

A
Alex Ross 已提交
207
	protected _workspaceTasksPromise?: Promise<Map<string, WorkspaceFolderTaskResult>>;
A
Alex Ross 已提交
208
	protected _areJsonTasksSupportedPromise: Promise<boolean> = Promise.resolve(false);
209

A
Alex Ross 已提交
210 211
	protected _taskSystem?: ITaskSystem;
	protected _taskSystemListener?: IDisposable;
212
	private _recentlyUsedTasks: LinkedMap<string, string> | undefined;
213

A
Alex Ross 已提交
214
	protected _taskRunningState: IContextKey<boolean>;
D
Dirk Baeumer 已提交
215

A
Alex Ross 已提交
216 217
	protected _outputChannel: IOutputChannel;
	protected readonly _onDidStateChange: Emitter<TaskEvent>;
E
Erich Gamma 已提交
218

D
Dirk Baeumer 已提交
219
	constructor(
220
		@IConfigurationService private readonly configurationService: IConfigurationService,
A
Alex Ross 已提交
221 222
		@IMarkerService protected readonly markerService: IMarkerService,
		@IOutputService protected readonly outputService: IOutputService,
223
		@IPanelService private readonly panelService: IPanelService,
224
		@IEditorService private readonly editorService: IEditorService,
A
Alex Ross 已提交
225 226 227
		@IFileService protected readonly fileService: IFileService,
		@IWorkspaceContextService protected readonly contextService: IWorkspaceContextService,
		@ITelemetryService protected readonly telemetryService: ITelemetryService,
228
		@ITextFileService private readonly textFileService: ITextFileService,
229
		@ILifecycleService lifecycleService: ILifecycleService,
A
Alex Ross 已提交
230
		@IModelService protected readonly modelService: IModelService,
231 232
		@IExtensionService private readonly extensionService: IExtensionService,
		@IQuickInputService private readonly quickInputService: IQuickInputService,
A
Alex Ross 已提交
233
		@IConfigurationResolverService protected readonly configurationResolverService: IConfigurationResolverService,
234 235
		@ITerminalService private readonly terminalService: ITerminalService,
		@IStorageService private readonly storageService: IStorageService,
236
		@IProgressService private readonly progressService: IProgressService,
237
		@IOpenerService private readonly openerService: IOpenerService,
238
		@IHostService private readonly _hostService: IHostService,
239 240
		@IDialogService private readonly dialogService: IDialogService,
		@INotificationService private readonly notificationService: INotificationService,
D
Dirk Baeumer 已提交
241
		@IContextKeyService contextKeyService: IContextKeyService,
242
		@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
243 244
		@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
		@ITerminalInstanceService private readonly terminalInstanceService: ITerminalInstanceService,
245
		@IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService,
246 247
		@ITextModelService private readonly textModelResolverService: ITextModelService,
		@IPreferencesService private readonly preferencesService: IPreferencesService
248
	) {
249 250
		super();

251
		this._workspaceTasksPromise = undefined;
252
		this._taskSystem = undefined;
253
		this._taskSystemListener = undefined;
A
Alex Ross 已提交
254
		this._outputChannel = this.outputService.getChannel(AbstractTaskService.OutputChannelId)!;
255
		this._providers = new Map<number, ITaskProvider>();
256
		this._providerTypes = new Map<number, string>();
257
		this._taskSystemInfos = new Map<string, TaskSystemInfo>();
258
		this._register(this.contextService.onDidChangeWorkspaceFolders(() => {
259
			if (!this._taskSystem && !this._workspaceTasksPromise) {
260 261
				return;
			}
D
Dirk Baeumer 已提交
262
			let folderSetup = this.computeWorkspaceFolderSetup();
D
Dirk Baeumer 已提交
263
			if (this.executionEngine !== folderSetup[2]) {
264
				if (this._taskSystem && this._taskSystem.getActiveTasks().length > 0) {
265 266 267 268 269 270 271 272
					this.notificationService.prompt(
						Severity.Info,
						nls.localize(
							'TaskSystem.noHotSwap',
							'Changing the task execution engine with an active task running requires to reload the Window'
						),
						[{
							label: nls.localize('reloadWindow', "Reload Window"),
273
							run: () => this._hostService.reload()
B
Benjamin Pasero 已提交
274 275
						}],
						{ sticky: true }
276
					);
277 278 279 280 281
					return;
				} else {
					this.disposeTaskSystemListeners();
					this._taskSystem = undefined;
				}
D
Dirk Baeumer 已提交
282
			}
D
Dirk Baeumer 已提交
283
			this.updateSetup(folderSetup);
D
Dirk Baeumer 已提交
284
			this.updateWorkspaceTasks();
285
		}));
286 287 288
		this._register(Event.debounce(this.configurationService.onDidChangeConfiguration, () => {
			return;
		}, 1000)(() => {
289 290 291 292 293 294
			if (!this._taskSystem && !this._workspaceTasksPromise) {
				return;
			}
			if (!this._taskSystem || this._taskSystem instanceof TerminalTaskSystem) {
				this._outputChannel.clear();
			}
295
			this.updateWorkspaceTasks(TaskRunSource.ConfigurationChange);
296
		}));
D
Dirk Baeumer 已提交
297
		this._taskRunningState = TASK_RUNNING_STATE.bindTo(contextKeyService);
298
		this._register(lifecycleService.onBeforeShutdown(event => event.veto(this.beforeShutdown())));
299
		this._onDidStateChange = this._register(new Emitter());
300
		this.registerCommands();
301 302 303 304 305 306 307 308 309 310 311 312 313
		this.configurationResolverService.contributeVariable('defaultBuildTask', async (): Promise<string | undefined> => {
			let tasks = await this.getTasksForGroup(TaskGroup.Build);
			if (tasks.length > 0) {
				let { defaults, users } = this.splitPerGroupType(tasks);
				if (defaults.length === 1) {
					return defaults[0]._label;
				} else if (defaults.length + users.length > 0) {
					tasks = defaults.concat(users);
				}
			}

			let entry: TaskQuickPickEntry | null | undefined;
			if (tasks && tasks.length > 0) {
314
				entry = await this.showQuickPick(tasks, nls.localize('TaskService.pickBuildTaskForLabel', 'Select the build task (there is no default build task defined)'));
315 316 317 318 319 320 321 322
			}

			let task: Task | undefined | null = entry ? entry.task : undefined;
			if (!task) {
				return undefined;
			}
			return task._label;
		});
323 324
	}

325 326 327 328
	public get onDidStateChange(): Event<TaskEvent> {
		return this._onDidStateChange.event;
	}

329 330 331 332
	public get supportsMultipleTaskExecutions(): boolean {
		return this.inTerminal();
	}

333
	private registerCommands(): void {
334 335 336 337 338 339 340 341 342 343 344 345 346 347
		CommandsRegistry.registerCommand({
			id: 'workbench.action.tasks.runTask',
			handler: (accessor, arg) => {
				this.runTaskCommand(arg);
			},
			description: {
				description: 'Run Task',
				args: [{
					name: 'args',
					schema: {
						'type': 'string',
					}
				}]
			}
348 349
		});

A
Alex Ross 已提交
350
		CommandsRegistry.registerCommand('workbench.action.tasks.reRunTask', (accessor, arg) => {
351
			this.reRunTaskCommand();
A
Alex Ross 已提交
352 353
		});

354
		CommandsRegistry.registerCommand('workbench.action.tasks.restartTask', (accessor, arg) => {
355
			this.runRestartTaskCommand(arg);
356 357
		});

358
		CommandsRegistry.registerCommand('workbench.action.tasks.terminate', (accessor, arg) => {
359
			this.runTerminateCommand(arg);
360 361 362 363 364 365 366 367 368 369 370 371 372
		});

		CommandsRegistry.registerCommand('workbench.action.tasks.showLog', () => {
			if (!this.canRunCommand()) {
				return;
			}
			this.showOutput();
		});

		CommandsRegistry.registerCommand('workbench.action.tasks.build', () => {
			if (!this.canRunCommand()) {
				return;
			}
373
			this.runBuildCommand();
374 375 376 377 378
		});

		CommandsRegistry.registerCommand('workbench.action.tasks.test', () => {
			if (!this.canRunCommand()) {
				return;
379
			}
380
			this.runTestCommand();
381
		});
382

383 384 385 386
		CommandsRegistry.registerCommand('workbench.action.tasks.configureTaskRunner', () => {
			this.runConfigureTasks();
		});

387 388 389 390 391 392 393
		CommandsRegistry.registerCommand('workbench.action.tasks.configureDefaultBuildTask', () => {
			this.runConfigureDefaultBuildTask();
		});

		CommandsRegistry.registerCommand('workbench.action.tasks.configureDefaultTestTask', () => {
			this.runConfigureDefaultTestTask();
		});
394

395 396
		CommandsRegistry.registerCommand('workbench.action.tasks.showTasks', async () => {
			return this.runShowTasks();
397
		});
398 399 400 401 402 403 404 405 406

		CommandsRegistry.registerCommand('workbench.action.tasks.toggleProblems', () => {
			const panel = this.panelService.getActivePanel();
			if (panel && panel.getId() === Constants.MARKERS_PANEL_ID) {
				this.layoutService.setPanelHidden(true);
			} else {
				this.panelService.openPanel(Constants.MARKERS_PANEL_ID, true);
			}
		});
407 408 409 410 411 412 413

		CommandsRegistry.registerCommand('workbench.action.tasks.configureUserTask', async () => {
			const resource = this.getResourceForKind(TaskSourceKind.User);
			if (resource) {
				this.openTaskFile(resource);
			}
		});
E
Erich Gamma 已提交
414 415
	}

D
Dirk Baeumer 已提交
416
	private get workspaceFolders(): IWorkspaceFolder[] {
417
		if (!this._workspaceFolders) {
D
Dirk Baeumer 已提交
418 419
			this.updateSetup();
		}
420
		return this._workspaceFolders!;
D
Dirk Baeumer 已提交
421 422
	}

D
Dirk Baeumer 已提交
423
	private get ignoredWorkspaceFolders(): IWorkspaceFolder[] {
424
		if (!this._ignoredWorkspaceFolders) {
D
Dirk Baeumer 已提交
425 426
			this.updateSetup();
		}
427
		return this._ignoredWorkspaceFolders!;
D
Dirk Baeumer 已提交
428 429
	}

A
Alex Ross 已提交
430
	protected get executionEngine(): ExecutionEngine {
R
Rob Lourens 已提交
431
		if (this._executionEngine === undefined) {
D
Dirk Baeumer 已提交
432 433
			this.updateSetup();
		}
434
		return this._executionEngine!;
D
Dirk Baeumer 已提交
435 436 437
	}

	private get schemaVersion(): JsonSchemaVersion {
R
Rob Lourens 已提交
438
		if (this._schemaVersion === undefined) {
D
Dirk Baeumer 已提交
439 440
			this.updateSetup();
		}
441
		return this._schemaVersion!;
D
Dirk Baeumer 已提交
442 443
	}

D
Dirk Baeumer 已提交
444
	private get showIgnoreMessage(): boolean {
R
Rob Lourens 已提交
445
		if (this._showIgnoreMessage === undefined) {
A
Alex Ross 已提交
446
			this._showIgnoreMessage = !this.storageService.getBoolean(AbstractTaskService.IgnoreTask010DonotShowAgain_key, StorageScope.WORKSPACE, false);
D
Dirk Baeumer 已提交
447
		}
448
		return this._showIgnoreMessage;
D
Dirk Baeumer 已提交
449 450
	}

451
	private updateSetup(setup?: [IWorkspaceFolder[], IWorkspaceFolder[], ExecutionEngine, JsonSchemaVersion, IWorkspace | undefined]): void {
D
Dirk Baeumer 已提交
452 453 454
		if (!setup) {
			setup = this.computeWorkspaceFolderSetup();
		}
455 456 457 458
		this._workspaceFolders = setup[0];
		if (this._ignoredWorkspaceFolders) {
			if (this._ignoredWorkspaceFolders.length !== setup[1].length) {
				this._showIgnoreMessage = undefined;
D
Dirk Baeumer 已提交
459 460
			} else {
				let set: Set<string> = new Set();
461
				this._ignoredWorkspaceFolders.forEach(folder => set.add(folder.uri.toString()));
D
Dirk Baeumer 已提交
462 463
				for (let folder of setup[1]) {
					if (!set.has(folder.uri.toString())) {
464
						this._showIgnoreMessage = undefined;
D
Dirk Baeumer 已提交
465 466 467 468 469
						break;
					}
				}
			}
		}
470 471 472
		this._ignoredWorkspaceFolders = setup[1];
		this._executionEngine = setup[2];
		this._schemaVersion = setup[3];
473
		this._workspace = setup[4];
D
Dirk Baeumer 已提交
474 475
	}

A
Alex Ross 已提交
476
	protected showOutput(runSource: TaskRunSource = TaskRunSource.User): void {
A
Alex Ross 已提交
477
		if ((runSource === TaskRunSource.User) || (runSource === TaskRunSource.ConfigurationChange)) {
478 479 480 481 482 483 484
			this.notificationService.prompt(Severity.Warning, nls.localize('taskServiceOutputPrompt', 'There are task errors. See the output for details.'),
				[{
					label: nls.localize('showOutput', "Show output"),
					run: () => {
						this.outputService.showChannel(this._outputChannel.id, true);
					}
				}]);
485
		}
486 487
	}

E
Erich Gamma 已提交
488
	private disposeTaskSystemListeners(): void {
489 490 491
		if (this._taskSystemListener) {
			this._taskSystemListener.dispose();
		}
E
Erich Gamma 已提交
492 493
	}

494
	public registerTaskProvider(provider: ITaskProvider, type: string): IDisposable {
495
		if (!provider) {
496 497 498
			return {
				dispose: () => { }
			};
499
		}
A
Alex Ross 已提交
500
		let handle = AbstractTaskService.nextHandle++;
501
		this._providers.set(handle, provider);
502
		this._providerTypes.set(handle, type);
503 504 505
		return {
			dispose: () => {
				this._providers.delete(handle);
506
				this._providerTypes.delete(handle);
507 508
			}
		};
509 510
	}

511 512 513 514
	public registerTaskSystem(key: string, info: TaskSystemInfo): void {
		this._taskSystemInfos.set(key, info);
	}

G
Gabriel DeBacker 已提交
515
	public extensionCallbackTaskComplete(task: Task, result: number): Promise<void> {
516 517 518
		if (!this._taskSystem) {
			return Promise.resolve();
		}
G
Gabriel DeBacker 已提交
519
		return this._taskSystem.customExecutionComplete(task, result);
520 521
	}

A
Alex Ross 已提交
522 523
	public getTask(folder: IWorkspace | IWorkspaceFolder | string, identifier: string | TaskIdentifier, compareId: boolean = false): Promise<Task | undefined> {
		const name = Types.isString(folder) ? folder : isWorkspaceFolder(folder) ? folder.name : folder.configuration ? resources.basename(folder.configuration) : undefined;
D
Dirk Baeumer 已提交
524
		if (this.ignoredWorkspaceFolders.some(ignored => ignored.name === name)) {
525
			return Promise.reject(new Error(nls.localize('TaskServer.folderIgnored', 'The folder {0} is ignored since it uses task version 0.1.0', name)));
D
Dirk Baeumer 已提交
526
		}
527 528 529 530
		const key: string | KeyedTaskIdentifier | undefined = !Types.isString(identifier)
			? TaskDefinition.createTaskIdentifier(identifier, console)
			: identifier;

R
Rob Lourens 已提交
531
		if (key === undefined) {
532
			return Promise.resolve(undefined);
533
		}
534
		return this.getGroupedTasks().then((map) => {
535
			const values = map.get(folder);
536 537 538
			if (!values) {
				return undefined;
			}
539
			return find(values, task => task.matches(key, compareId));
540 541 542
		});
	}

A
Alex Ross 已提交
543
	protected abstract versionAndEngineCompatible(filter?: TaskFilter): boolean;
544

A
Alex Ross 已提交
545 546
	public tasks(filter?: TaskFilter): Promise<Task[]> {
		if (!this.versionAndEngineCompatible(filter)) {
547
			return Promise.resolve<Task[]>([]);
548
		}
549
		return this.getGroupedTasks(filter ? filter.type : undefined).then((map) => {
550 551 552 553 554 555
			if (!filter || !filter.type) {
				return map.all();
			}
			let result: Task[] = [];
			map.forEach((tasks) => {
				for (let task of tasks) {
556
					if (ContributedTask.is(task) && task.defines.type === filter.type) {
557
						result.push(task);
558 559 560 561
					} else if (CustomTask.is(task)) {
						if (task.type === filter.type) {
							result.push(task);
						} else {
A
Alex Ross 已提交
562
							let customizes = task.customizes();
563 564 565 566
							if (customizes && customizes.type === filter.type) {
								result.push(task);
							}
						}
567 568 569 570 571
					}
				}
			});
			return result;
		});
572
	}
573

574 575 576 577
	public createSorter(): TaskSorter {
		return new TaskSorter(this.contextService.getWorkspace() ? this.contextService.getWorkspace().folders : []);
	}

J
Johannes Rieken 已提交
578
	public isActive(): Promise<boolean> {
579
		if (!this._taskSystem) {
580
			return Promise.resolve(false);
581 582 583 584
		}
		return this._taskSystem.isActive();
	}

J
Johannes Rieken 已提交
585
	public getActiveTasks(): Promise<Task[]> {
586
		if (!this._taskSystem) {
587
			return Promise.resolve([]);
588
		}
589
		return Promise.resolve(this._taskSystem.getActiveTasks());
590 591
	}

592 593 594 595 596 597 598
	public getBusyTasks(): Promise<Task[]> {
		if (!this._taskSystem) {
			return Promise.resolve([]);
		}
		return Promise.resolve(this._taskSystem.getBusyTasks());
	}

599 600 601 602 603
	public getRecentlyUsedTasks(): LinkedMap<string, string> {
		if (this._recentlyUsedTasks) {
			return this._recentlyUsedTasks;
		}
		this._recentlyUsedTasks = new LinkedMap<string, string>();
A
Alex Ross 已提交
604
		let storageValue = this.storageService.get(AbstractTaskService.RecentlyUsedTasks_Key, StorageScope.WORKSPACE);
605 606 607 608 609 610 611 612 613 614 615 616 617 618 619
		if (storageValue) {
			try {
				let values: string[] = JSON.parse(storageValue);
				if (Array.isArray(values)) {
					for (let value of values) {
						this._recentlyUsedTasks.set(value, value);
					}
				}
			} catch (error) {
				// Ignore. We use the empty result
			}
		}
		return this._recentlyUsedTasks;
	}

620 621 622 623 624 625
	private setRecentlyUsedTask(key: string): void {
		this.getRecentlyUsedTasks().set(key, key, Touch.AsOld);
		this.saveRecentlyUsedTasks();
	}

	private saveRecentlyUsedTasks(): void {
626
		if (!this._taskSystem || !this._recentlyUsedTasks) {
627 628 629 630 631 632
			return;
		}
		let values = this._recentlyUsedTasks.values();
		if (values.length > 30) {
			values = values.slice(0, 30);
		}
A
Alex Ross 已提交
633
		this.storageService.store(AbstractTaskService.RecentlyUsedTasks_Key, JSON.stringify(values), StorageScope.WORKSPACE);
634
	}
635

636
	private openDocumentation(): void {
637 638 639
		this.openerService.open(URI.parse('https://go.microsoft.com/fwlink/?LinkId=733558'));
	}

J
Johannes Rieken 已提交
640
	public build(): Promise<ITaskSummary> {
641
		return this.getGroupedTasks().then((tasks) => {
D
Dirk Baeumer 已提交
642
			let runnable = this.createRunnableTask(tasks, TaskGroup.Build);
643
			if (!runnable || !runnable.task) {
D
Dirk Baeumer 已提交
644
				if (this.schemaVersion === JsonSchemaVersion.V0_1_0) {
645 646 647 648
					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);
				} else {
					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);
				}
649 650 651 652
			}
			return this.executeTask(runnable.task, runnable.resolver);
		}).then(value => value, (error) => {
			this.handleError(error);
653
			return Promise.reject(error);
654 655 656
		});
	}

J
Johannes Rieken 已提交
657
	public runTest(): Promise<ITaskSummary> {
658
		return this.getGroupedTasks().then((tasks) => {
D
Dirk Baeumer 已提交
659
			let runnable = this.createRunnableTask(tasks, TaskGroup.Test);
660
			if (!runnable || !runnable.task) {
D
Dirk Baeumer 已提交
661
				if (this.schemaVersion === JsonSchemaVersion.V0_1_0) {
662 663 664 665
					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);
				} else {
					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);
				}
666 667 668 669
			}
			return this.executeTask(runnable.task, runnable.resolver);
		}).then(value => value, (error) => {
			this.handleError(error);
670
			return Promise.reject(error);
671 672 673
		});
	}

674
	public run(task: Task | undefined, options?: ProblemMatcherRunOptions, runSource: TaskRunSource = TaskRunSource.System): Promise<ITaskSummary> {
675 676 677
		if (!task) {
			throw new TaskError(Severity.Info, nls.localize('TaskServer.noTask', 'Task to execute is undefined'), TaskErrors.TaskNotFound);
		}
678
		return this.getGroupedTasks().then((grouped) => {
679 680 681 682 683 684 685 686 687
			let resolver = this.createResolver(grouped);
			if (options && options.attachProblemMatcher && this.shouldAttachProblemMatcher(task) && !InMemoryTask.is(task)) {
				return this.attachProblemMatcher(task).then((toExecute) => {
					if (toExecute) {
						return this.executeTask(toExecute, resolver);
					} else {
						return Promise.resolve(undefined);
					}
				});
688
			}
689
			return this.executeTask(task, resolver);
690 691 692 693 694 695 696 697
		}).then((value) => {
			if (runSource === TaskRunSource.User) {
				this.getWorkspaceTasks().then(workspaceTasks => {
					RunAutomaticTasks.promptForPermission(this, this.storageService, this.notificationService, workspaceTasks);
				});
			}
			return value;
		}, (error) => {
698
			this.handleError(error);
699
			return Promise.reject(error);
700 701 702
		});
	}

703 704 705 706 707
	private isProvideTasksEnabled(): boolean {
		const settingValue = this.configurationService.getValue('task.autoDetect');
		return settingValue === true;
	}

708
	private shouldAttachProblemMatcher(task: Task): boolean {
709 710 711 712 713 714
		const settingValue = this.configurationService.getValue('task.problemMatchers.neverPrompt');
		if (settingValue === true) {
			return false;
		} else if (task.type && Types.isStringArray(settingValue) && (settingValue.indexOf(task.type) >= 0)) {
			return false;
		}
D
Dirk Baeumer 已提交
715
		if (!this.canCustomize(task)) {
716 717
			return false;
		}
R
Rob Lourens 已提交
718
		if (task.configurationProperties.group !== undefined && task.configurationProperties.group !== TaskGroup.Build) {
719 720
			return false;
		}
R
Rob Lourens 已提交
721
		if (task.configurationProperties.problemMatchers !== undefined && task.configurationProperties.problemMatchers.length > 0) {
722 723
			return false;
		}
724
		if (ContributedTask.is(task)) {
725
			return !task.hasDefinedMatchers && !!task.configurationProperties.problemMatchers && (task.configurationProperties.problemMatchers.length === 0);
726
		}
727 728
		if (CustomTask.is(task)) {
			let configProperties: TaskConfig.ConfigurationProperties = task._source.config.element;
R
Rob Lourens 已提交
729
			return configProperties.problemMatcher === undefined && !task.hasDefinedMatchers;
730 731
		}
		return false;
732 733
	}

734
	private attachProblemMatcher(task: ContributedTask | CustomTask): Promise<Task | undefined> {
C
Christof Marti 已提交
735
		interface ProblemMatcherPickEntry extends IQuickPickItem {
736
			matcher: NamedProblemMatcher | undefined;
737
			never?: boolean;
738 739
			learnMore?: boolean;
		}
C
Christof Marti 已提交
740
		let entries: QuickPickInput<ProblemMatcherPickEntry>[] = [];
741 742
		for (let key of ProblemMatcherRegistry.keys()) {
			let matcher = ProblemMatcherRegistry.get(key);
743 744 745
			if (matcher.deprecated) {
				continue;
			}
746 747 748 749 750 751 752 753 754 755 756
			if (matcher.name === matcher.label) {
				entries.push({ label: matcher.name, matcher: matcher });
			} else {
				entries.push({
					label: matcher.label,
					description: `$${matcher.name}`,
					matcher: matcher
				});
			}
		}
		if (entries.length > 0) {
757 758 759 760 761 762 763
			entries = entries.sort((a, b) => {
				if (a.label && b.label) {
					return a.label.localeCompare(b.label);
				} else {
					return 0;
				}
			});
C
Christof Marti 已提交
764
			entries.unshift({ type: 'separator', label: nls.localize('TaskService.associate', 'associate') });
765
			entries.unshift(
766 767 768
				{ label: nls.localize('TaskService.attachProblemMatcher.continueWithout', 'Continue without scanning the task output'), matcher: undefined },
				{ label: nls.localize('TaskService.attachProblemMatcher.never', 'Never scan the task output'), matcher: undefined, never: true },
				{ label: nls.localize('TaskService.attachProblemMatcher.learnMoreAbout', 'Learn more about scanning the task output'), matcher: undefined, learnMore: true }
769
			);
C
Christof Marti 已提交
770
			return this.quickInputService.pick(entries, {
771
				placeHolder: nls.localize('selectProblemMatcher', 'Select for which kind of errors and warnings to scan the task output'),
772 773 774 775 776
			}).then((selected) => {
				if (selected) {
					if (selected.learnMore) {
						this.openDocumentation();
						return undefined;
777 778 779
					} else if (selected.never) {
						this.customize(task, { problemMatcher: [] }, true);
						return task;
780
					} else if (selected.matcher) {
A
Alex Ross 已提交
781
						let newTask = task.clone();
782
						let matcherReference = `$${selected.matcher.name}`;
783
						let properties: CustomizationProperties = { problemMatcher: [matcherReference] };
A
Alex Ross 已提交
784
						newTask.configurationProperties.problemMatchers = [matcherReference];
785
						let matcher = ProblemMatcherRegistry.get(selected.matcher.name);
R
Rob Lourens 已提交
786
						if (matcher && matcher.watching !== undefined) {
787
							properties.isBackground = true;
A
Alex Ross 已提交
788
							newTask.configurationProperties.isBackground = true;
789 790
						}
						this.customize(task, properties, true);
791 792 793 794 795
						return newTask;
					} else {
						return task;
					}
				} else {
796
					return undefined;
797 798 799
				}
			});
		}
800
		return Promise.resolve(task);
801 802
	}

J
Johannes Rieken 已提交
803
	public getTasksForGroup(group: string): Promise<Task[]> {
804
		return this.getGroupedTasks().then((groups) => {
805
			let result: Task[] = [];
806 807
			groups.forEach((tasks) => {
				for (let task of tasks) {
A
Alex Ross 已提交
808
					if (task.configurationProperties.group === group) {
809 810
						result.push(task);
					}
811
				}
812
			});
813 814 815 816
			return result;
		});
	}

817 818
	public needsFolderQualification(): boolean {
		return this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE;
D
Dirk Baeumer 已提交
819 820 821
	}

	public canCustomize(task: Task): boolean {
D
Dirk Baeumer 已提交
822
		if (this.schemaVersion !== JsonSchemaVersion.V2_0_0) {
D
Dirk Baeumer 已提交
823 824 825 826 827 828
			return false;
		}
		if (CustomTask.is(task)) {
			return true;
		}
		if (ContributedTask.is(task)) {
A
Alex Ross 已提交
829
			return !!task.getWorkspaceFolder();
D
Dirk Baeumer 已提交
830 831
		}
		return false;
832 833
	}

834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887
	private openEditorAtTask(resource: URI | undefined, task: TaskConfig.CustomTask | TaskConfig.ConfiguringTask | string | undefined): Promise<ITextEditor | null | undefined> {
		if (resource === undefined) {
			return Promise.resolve(undefined);
		}
		let selection: ITextEditorSelection | undefined;
		return this.fileService.readFile(resource).then(content => content.value).then(async content => {
			if (!content) {
				return undefined;
			}
			if (task) {
				const contentValue = content.toString();
				let stringValue: string;
				if (typeof task === 'string') {
					stringValue = task;
				} else {
					const model = (await this.textModelResolverService.createModelReference(resource)).object.textEditorModel;
					const { tabSize, insertSpaces } = model.getOptions();
					const eol = model.getEOL();
					const edits = format(JSON.stringify(task), undefined, { eol, tabSize, insertSpaces });
					let stringified = applyEdits(JSON.stringify(task), edits);
					const regex = new RegExp(eol + '\\t', 'g');
					stringified = stringified.replace(regex, eol + '\t\t\t');
					const twoTabs = '\t\t';
					stringValue = twoTabs + stringified.slice(0, stringified.length - 1) + twoTabs + stringified.slice(stringified.length - 1);
				}

				const index = contentValue.indexOf(stringValue);
				let startLineNumber = 1;
				for (let i = 0; i < index; i++) {
					if (contentValue.charAt(i) === '\n') {
						startLineNumber++;
					}
				}
				let endLineNumber = startLineNumber;
				for (let i = 0; i < stringValue.length; i++) {
					if (stringValue.charAt(i) === '\n') {
						endLineNumber++;
					}
				}
				selection = startLineNumber > 1 ? { startLineNumber, startColumn: startLineNumber === endLineNumber ? 4 : 3, endLineNumber, endColumn: startLineNumber === endLineNumber ? undefined : 4 } : undefined;
			}

			return this.editorService.openEditor({
				resource,
				options: {
					pinned: false,
					forceReload: true, // because content might have changed
					selection,
					revealInCenterIfOutsideViewport: !!selection
				}
			});
		});
	}

J
Johannes Rieken 已提交
888
	public customize(task: ContributedTask | CustomTask, properties?: CustomizationProperties, openConfig?: boolean): Promise<void> {
889
		const workspaceFolder = task.getWorkspaceFolder();
D
Dirk Baeumer 已提交
890
		if (!workspaceFolder) {
891
			return Promise.resolve(undefined);
D
Dirk Baeumer 已提交
892 893
		}
		let configuration = this.getConfiguration(workspaceFolder);
D
Dirk Baeumer 已提交
894
		if (configuration.hasParseErrors) {
895
			this.notificationService.warn(nls.localize('customizeParseErrors', 'The current task configuration has errors. Please fix the errors first before customizing a task.'));
896
			return Promise.resolve<void>(undefined);
D
Dirk Baeumer 已提交
897
		}
898

D
Dirk Baeumer 已提交
899
		let fileConfig = configuration.config;
900 901
		let index: number | undefined;
		let toCustomize: TaskConfig.CustomTask | TaskConfig.ConfiguringTask | undefined;
902
		let taskConfig = CustomTask.is(task) ? task._source.config : undefined;
903 904 905
		if (taskConfig && taskConfig.element) {
			index = taskConfig.index;
			toCustomize = taskConfig.element;
906 907 908 909 910
		} else if (ContributedTask.is(task)) {
			toCustomize = {
			};
			let identifier: TaskConfig.TaskIdentifier = Objects.assign(Object.create(null), task.defines);
			delete identifier['_key'];
911
			Object.keys(identifier).forEach(key => (<any>toCustomize)![key] = identifier[key]);
A
Alex Ross 已提交
912 913
			if (task.configurationProperties.problemMatchers && task.configurationProperties.problemMatchers.length > 0 && Types.isStringArray(task.configurationProperties.problemMatchers)) {
				toCustomize.problemMatcher = task.configurationProperties.problemMatchers;
914
			}
A
Alex Ross 已提交
915 916 917
			if (task.configurationProperties.group) {
				toCustomize.group = task.configurationProperties.group;
			}
918 919
		}
		if (!toCustomize) {
920
			return Promise.resolve(undefined);
921
		}
922 923
		if (properties) {
			for (let property of Object.getOwnPropertyNames(properties)) {
924
				let value = (<any>properties)[property];
R
Rob Lourens 已提交
925
				if (value !== undefined && value !== null) {
926
					(<any>toCustomize)[property] = value;
927 928 929
				}
			}
		} else {
930
			if (toCustomize.problemMatcher === undefined && task.configurationProperties.problemMatchers === undefined || (task.configurationProperties.problemMatchers && task.configurationProperties.problemMatchers.length === 0)) {
931
				toCustomize.problemMatcher = [];
932
			}
933
		}
934

935
		let promise: Promise<void> | undefined;
D
Dirk Baeumer 已提交
936
		if (!fileConfig) {
937
			let value = {
D
Dirk Baeumer 已提交
938
				version: '2.0.0',
939
				tasks: [toCustomize]
D
Dirk Baeumer 已提交
940
			};
941 942
			let content = [
				'{',
943
				nls.localize('tasksJsonComment', '\t// See https://go.microsoft.com/fwlink/?LinkId=733558 \n\t// for the documentation about the tasks.json format'),
944
			].join('\n') + JSON.stringify(value, null, '\t').substr(1);
945
			let editorConfig = this.configurationService.getValue<any>();
946 947 948
			if (editorConfig.editor.insertSpaces) {
				content = content.replace(/(\n)(\t+)/g, (_, s1, s2) => s1 + strings.repeat(' ', s2.length * editorConfig.editor.tabSize));
			}
949
			promise = this.textFileService.create(workspaceFolder.toResource('.vscode/tasks.json'), content).then(() => { });
D
Dirk Baeumer 已提交
950
		} else {
951
			// We have a global task configuration
952
			if ((index === -1) && properties) {
R
Rob Lourens 已提交
953
				if (properties.problemMatcher !== undefined) {
954
					fileConfig.problemMatcher = properties.problemMatcher;
955
					promise = this.writeConfiguration(workspaceFolder, 'tasks.problemMatchers', fileConfig.problemMatcher);
R
Rob Lourens 已提交
956
				} else if (properties.group !== undefined) {
957
					fileConfig.group = properties.group;
958
					promise = this.writeConfiguration(workspaceFolder, 'tasks.group', fileConfig.group);
959
				}
960 961 962 963
			} else {
				if (!Array.isArray(fileConfig.tasks)) {
					fileConfig.tasks = [];
				}
R
Rob Lourens 已提交
964
				if (index === undefined) {
965 966 967 968
					fileConfig.tasks.push(toCustomize);
				} else {
					fileConfig.tasks[index] = toCustomize;
				}
969
				promise = this.writeConfiguration(workspaceFolder, 'tasks.tasks', fileConfig.tasks);
D
Dirk Baeumer 已提交
970
			}
971
		}
972
		if (!promise) {
973
			return Promise.resolve(undefined);
974
		}
975
		return promise.then(() => {
976
			let event: TaskCustomizationTelemetryEvent = {
977 978
				properties: properties ? Object.getOwnPropertyNames(properties) : []
			};
K
kieferrm 已提交
979
			/* __GDPR__
K
kieferrm 已提交
980 981 982 983
				"taskService.customize" : {
					"properties" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
				}
			*/
A
Alex Ross 已提交
984
			this.telemetryService.publicLog(AbstractTaskService.CustomizationTelemetryEventName, event);
D
Dirk Baeumer 已提交
985
			if (openConfig) {
986
				this.openEditorAtTask(workspaceFolder.toResource('.vscode/tasks.json'), toCustomize);
D
Dirk Baeumer 已提交
987 988 989 990
			}
		});
	}

991
	private writeConfiguration(workspaceFolder: IWorkspaceFolder, key: string, value: any): Promise<void> | undefined {
D
Dirk Baeumer 已提交
992
		if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) {
993
			return this.configurationService.updateValue(key, value, { resource: workspaceFolder.uri }, ConfigurationTarget.WORKSPACE);
D
Dirk Baeumer 已提交
994
		} else if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
995
			return this.configurationService.updateValue(key, value, { resource: workspaceFolder.uri }, ConfigurationTarget.WORKSPACE_FOLDER);
D
Dirk Baeumer 已提交
996 997 998 999 1000
		} else {
			return undefined;
		}
	}

1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
	private getResourceForKind(kind: string): URI | undefined {
		switch (kind) {
			case TaskSourceKind.User: {
				return resources.joinPath(resources.dirname(this.preferencesService.userSettingsResource), 'tasks.json');
			}
			case TaskSourceKind.WorkspaceFile: {
				if (this._workspace && this._workspace.configuration) {
					return this._workspace.configuration;
				}
			}
			default: {
				return undefined;
			}
		}
	}

	private getResourceForTask(task: CustomTask): URI {
		let uri = this.getResourceForKind(task._source.kind);
		if (!uri) {
A
Alex Ross 已提交
1020 1021 1022 1023 1024 1025
			const taskFolder = task.getWorkspaceFolder();
			if (taskFolder) {
				uri = taskFolder.toResource(task._source.config.file);
			} else {
				uri = this.workspaceFolders[0].uri;
			}
1026 1027 1028 1029
		}
		return uri;
	}

J
Johannes Rieken 已提交
1030
	public openConfig(task: CustomTask | undefined): Promise<void> {
1031
		let resource: URI | undefined;
1032
		if (task) {
1033
			resource = this.getResourceForTask(task);
1034 1035 1036
		} else {
			resource = (this._workspaceFolders && (this._workspaceFolders.length > 0)) ? this._workspaceFolders[0].toResource('.vscode/tasks.json') : undefined;
		}
1037
		return this.openEditorAtTask(resource, task ? task._label : undefined).then(() => undefined);
1038 1039
	}

1040
	private createRunnableTask(tasks: TaskMap, group: TaskGroup): { task: Task; resolver: ITaskResolver } | undefined {
1041 1042 1043 1044 1045
		interface ResolverData {
			id: Map<string, Task>;
			label: Map<string, Task>;
			identifier: Map<string, Task>;
		}
1046

1047
		let resolverData: Map<string, ResolverData> = new Map();
1048 1049
		let workspaceTasks: Task[] = [];
		let extensionTasks: Task[] = [];
1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062
		tasks.forEach((tasks, folder) => {
			let data = resolverData.get(folder);
			if (!data) {
				data = {
					id: new Map<string, Task>(),
					label: new Map<string, Task>(),
					identifier: new Map<string, Task>()
				};
				resolverData.set(folder, data);
			}
			for (let task of tasks) {
				data.id.set(task._id, task);
				data.label.set(task._label, task);
1063 1064 1065
				if (task.configurationProperties.identifier) {
					data.identifier.set(task.configurationProperties.identifier, task);
				}
A
Alex Ross 已提交
1066
				if (group && task.configurationProperties.group === group) {
1067 1068 1069 1070 1071
					if (task._source.kind === TaskSourceKind.Workspace) {
						workspaceTasks.push(task);
					} else {
						extensionTasks.push(task);
					}
1072
				}
D
Dirk Baeumer 已提交
1073
			}
1074 1075
		});
		let resolver: ITaskResolver = {
1076 1077
			resolve: (uri: URI, alias: string) => {
				let data = resolverData.get(uri.toString());
1078 1079 1080 1081
				if (!data) {
					return undefined;
				}
				return data.id.get(alias) || data.label.get(alias) || data.identifier.get(alias);
1082 1083
			}
		};
1084 1085 1086 1087 1088 1089 1090 1091 1092 1093
		if (workspaceTasks.length > 0) {
			if (workspaceTasks.length > 1) {
				this._outputChannel.append(nls.localize('moreThanOneBuildTask', 'There are many build tasks defined in the tasks.json. Executing the first one.\n'));
			}
			return { task: workspaceTasks[0], resolver };
		}
		if (extensionTasks.length === 0) {
			return undefined;
		}

1094 1095
		// We can only have extension tasks if we are in version 2.0.0. Then we can even run
		// multiple build tasks.
1096 1097
		if (extensionTasks.length === 1) {
			return { task: extensionTasks[0], resolver };
1098 1099
		} else {
			let id: string = UUID.generateUuid();
A
Alex Ross 已提交
1100 1101 1102 1103 1104 1105 1106 1107
			let task: InMemoryTask = new InMemoryTask(
				id,
				{ kind: TaskSourceKind.InMemory, label: 'inMemory' },
				id,
				'inMemory',
				{ reevaluateOnRerun: true },
				{
					identifier: id,
1108
					dependsOn: extensionTasks.map((extensionTask) => { return { uri: extensionTask.getWorkspaceFolder()!.uri, task: extensionTask._id }; }),
A
Alex Ross 已提交
1109 1110 1111
					name: id,
				}
			);
1112
			return { task, resolver };
E
Erich Gamma 已提交
1113 1114 1115
		}
	}

1116 1117 1118 1119
	private createResolver(grouped: TaskMap): ITaskResolver {
		interface ResolverData {
			label: Map<string, Task>;
			identifier: Map<string, Task>;
1120
			taskIdentifier: Map<string, Task>;
1121
		}
1122

1123 1124 1125 1126
		let resolverData: Map<string, ResolverData> = new Map();
		grouped.forEach((tasks, folder) => {
			let data = resolverData.get(folder);
			if (!data) {
1127
				data = { label: new Map<string, Task>(), identifier: new Map<string, Task>(), taskIdentifier: new Map<string, Task>() };
1128 1129 1130 1131
				resolverData.set(folder, data);
			}
			for (let task of tasks) {
				data.label.set(task._label, task);
1132 1133 1134
				if (task.configurationProperties.identifier) {
					data.identifier.set(task.configurationProperties.identifier, task);
				}
A
Alex Ross 已提交
1135
				let keyedIdentifier = task.getDefinition(true);
R
Rob Lourens 已提交
1136
				if (keyedIdentifier !== undefined) {
1137 1138
					data.taskIdentifier.set(keyedIdentifier._key, task);
				}
1139
			}
1140
		});
1141

1142
		return {
1143 1144
			resolve: (uri: URI, identifier: string | TaskIdentifier | undefined) => {
				let data = uri ? resolverData.get(uri.toString()) : undefined;
1145
				if (!data || !identifier) {
1146 1147
					return undefined;
				}
1148 1149 1150 1151
				if (Types.isString(identifier)) {
					return data.label.get(identifier) || data.identifier.get(identifier);
				} else {
					let key = TaskDefinition.createTaskIdentifier(identifier, console);
R
Rob Lourens 已提交
1152
					return key !== undefined ? data.taskIdentifier.get(key._key) : undefined;
1153
				}
1154
			}
1155 1156 1157
		};
	}

J
Johannes Rieken 已提交
1158
	private executeTask(task: Task, resolver: ITaskResolver): Promise<ITaskSummary> {
1159 1160 1161
		return ProblemMatcherRegistry.onReady().then(() => {
			return this.textFileService.saveAll().then((value) => { // make sure all dirty files are saved
				let executeResult = this.getTaskSystem().run(task, resolver);
A
Alex Ross 已提交
1162
				return this.handleExecuteResult(executeResult);
1163
			});
1164 1165 1166
		});
	}

J
Johannes Rieken 已提交
1167
	private handleExecuteResult(executeResult: ITaskExecuteResult): Promise<ITaskSummary> {
1168 1169 1170 1171 1172 1173 1174
		if (executeResult.task.taskLoadMessages && executeResult.task.taskLoadMessages.length > 0) {
			executeResult.task.taskLoadMessages.forEach(loadMessage => {
				this._outputChannel.append(loadMessage + '\n');
			});
			this.showOutput();
		}

A
Alex Ross 已提交
1175
		let key = executeResult.task.getRecentlyUsedKey();
A
Alex Ross 已提交
1176
		if (key) {
1177
			this.setRecentlyUsedTask(key);
A
Alex Ross 已提交
1178 1179 1180
		}
		if (executeResult.kind === TaskExecuteKind.Active) {
			let active = executeResult.active;
1181
			if (active && active.same) {
A
Alex Ross 已提交
1182 1183
				let message;
				if (active.background) {
A
Alex Ross 已提交
1184
					message = nls.localize('TaskSystem.activeSame.background', 'The task \'{0}\' is already active and in background mode.', executeResult.task.getQualifiedLabel());
A
Alex Ross 已提交
1185
				} else {
A
Alex Ross 已提交
1186
					message = nls.localize('TaskSystem.activeSame.noBackground', 'The task \'{0}\' is already active.', executeResult.task.getQualifiedLabel());
A
Alex Ross 已提交
1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205
				}
				this.notificationService.prompt(Severity.Info, message,
					[{
						label: nls.localize('terminateTask', "Terminate Task"),
						run: () => this.terminate(executeResult.task)
					},
					{
						label: nls.localize('restartTask', "Restart Task"),
						run: () => this.restart(executeResult.task)
					}],
					{ sticky: true }
				);
			} else {
				throw new TaskError(Severity.Warning, nls.localize('TaskSystem.active', 'There is already a task running. Terminate it first before executing another task.'), TaskErrors.RunningTask);
			}
		}
		return executeResult.promise;
	}

1206
	public restart(task: Task): void {
1207 1208 1209
		if (!this._taskSystem) {
			return;
		}
1210
		this._taskSystem.terminate(task).then((response) => {
1211
			if (response.success) {
A
Alex Ross 已提交
1212 1213 1214
				this.run(task).then(undefined, reason => {
					// eat the error, it has already been surfaced to the user and we don't care about it here
				});
1215
			} else {
A
Alex Ross 已提交
1216
				this.notificationService.warn(nls.localize('TaskSystem.restartFailed', 'Failed to terminate and restart task {0}', Types.isString(task) ? task : task.configurationProperties.name));
1217 1218 1219 1220 1221
			}
			return response;
		});
	}

J
Johannes Rieken 已提交
1222
	public terminate(task: Task): Promise<TaskTerminateResponse> {
1223
		if (!this._taskSystem) {
1224
			return Promise.resolve({ success: true, task: undefined });
1225
		}
1226
		return this._taskSystem.terminate(task);
1227 1228
	}

J
Johannes Rieken 已提交
1229
	public terminateAll(): Promise<TaskTerminateResponse[]> {
1230
		if (!this._taskSystem) {
1231
			return Promise.resolve<TaskTerminateResponse[]>([]);
1232
		}
1233
		return this._taskSystem.terminateAll();
1234 1235
	}

A
Alex Ross 已提交
1236 1237 1238 1239 1240 1241
	protected createTerminalTaskSystem(): ITaskSystem {
		return new TerminalTaskSystem(
			this.terminalService, this.outputService, this.panelService, this.markerService,
			this.modelService, this.configurationResolverService, this.telemetryService,
			this.contextService, this.environmentService,
			AbstractTaskService.OutputChannelId, this.fileService, this.terminalInstanceService,
A
Alex Ross 已提交
1242
			this.remoteAgentService,
A
Alex Ross 已提交
1243 1244 1245
			(workspaceFolder: IWorkspaceFolder) => {
				if (!workspaceFolder) {
					return undefined;
1246
				}
A
Alex Ross 已提交
1247
				return this._taskSystemInfos.get(workspaceFolder.uri.scheme);
D
Dirk Baeumer 已提交
1248
			}
A
Alex Ross 已提交
1249
		);
1250 1251
	}

A
Alex Ross 已提交
1252 1253
	protected abstract getTaskSystem(): ITaskSystem;

1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286
	private async provideTasksWithWarning(provider: ITaskProvider, type: string, validTypes: IStringDictionary<boolean>): Promise<TaskSet> {
		return new Promise<TaskSet>(async (resolve, reject) => {
			let isDone = false;
			provider.provideTasks(validTypes).then((value) => {
				isDone = true;
				resolve(value);
			}, (e) => {
				isDone = true;
				reject(e);
			});
			let settingValue: boolean | string[] = this.configurationService.getValue('task.slowProviderWarning');
			if ((settingValue === true) || (Types.isStringArray(settingValue) && (settingValue.indexOf(type) < 0))) {
				setTimeout(() => {
					if (!isDone) {
						const settings: IPromptChoice = { label: nls.localize('TaskSystem.slowProvider.settings', "Settings"), run: () => this.preferencesService.openSettings(false, undefined) };
						const disableAll: IPromptChoice = { label: nls.localize('TaskSystem.slowProvider.disableAll', "Disable All"), run: () => this.configurationService.updateValue('task.autoDetect', false) };
						const dontShow: IPromptChoice = {
							label: nls.localize('TaskSystem.slowProvider.dontShow', "Don't warn again for {0} tasks", type), run: () => {
								if (!Types.isStringArray(settingValue)) {
									settingValue = [];
								}
								settingValue.push(type);
								return this.configurationService.updateValue('task.slowProviderWarning', settingValue);
							}
						};
						this.notificationService.prompt(Severity.Warning, nls.localize('TaskSystem.slowProvider', "The {0} task provider is slow. The extension that provides {0} tasks may provide a setting to disable it, or you can disable all tasks providers", type),
							[settings, disableAll, dontShow]);
					}
				}, 1000);
			}
		});
	}

1287
	private getGroupedTasks(type?: string): Promise<TaskMap> {
1288
		return Promise.all([this.extensionService.activateByEvent('onCommand:workbench.action.tasks.runTask'), TaskDefinitionRegistry.onReady()]).then(() => {
1289 1290
			let validTypes: IStringDictionary<boolean> = Object.create(null);
			TaskDefinitionRegistry.all().forEach(definition => validTypes[definition.taskType] = true);
1291 1292
			validTypes['shell'] = true;
			validTypes['process'] = true;
B
Benjamin Pasero 已提交
1293
			return new Promise<TaskSet[]>(resolve => {
D
Dirk Baeumer 已提交
1294 1295 1296 1297 1298 1299 1300 1301 1302 1303
				let result: TaskSet[] = [];
				let counter: number = 0;
				let done = (value: TaskSet) => {
					if (value) {
						result.push(value);
					}
					if (--counter === 0) {
						resolve(result);
					}
				};
1304 1305
				let error = (error: any) => {
					try {
1306
						if (error && Types.isString(error.message)) {
1307
							this._outputChannel.append('Error: ');
1308
							this._outputChannel.append(error.message);
1309
							this._outputChannel.append('\n');
1310
							this.showOutput();
1311 1312
						} else {
							this._outputChannel.append('Unknown error received while collecting tasks from providers.\n');
1313
							this.showOutput();
1314 1315 1316 1317 1318
						}
					} finally {
						if (--counter === 0) {
							resolve(result);
						}
D
Dirk Baeumer 已提交
1319 1320
					}
				};
1321
				if (this.isProvideTasksEnabled() && (this.schemaVersion === JsonSchemaVersion.V2_0_0) && (this._providers.size > 0)) {
1322 1323 1324
					for (const [handle, provider] of this._providers) {
						if ((type === undefined) || (type === this._providerTypes.get(handle))) {
							counter++;
1325
							this.provideTasksWithWarning(provider, this._providerTypes.get(handle)!, validTypes).then(done, error);
1326 1327
						}
					}
D
Dirk Baeumer 已提交
1328
				} else {
1329 1330
					resolve(result);
				}
D
Dirk Baeumer 已提交
1331
			});
D
Dirk Baeumer 已提交
1332
		}).then((contributedTaskSets) => {
1333 1334
			let result: TaskMap = new TaskMap();
			let contributedTasks: TaskMap = new TaskMap();
1335

D
Dirk Baeumer 已提交
1336 1337
			for (let set of contributedTaskSets) {
				for (let task of set.tasks) {
A
Alex Ross 已提交
1338
					let workspaceFolder = task.getWorkspaceFolder();
D
Dirk Baeumer 已提交
1339
					if (workspaceFolder) {
1340
						contributedTasks.add(workspaceFolder, task);
D
Dirk Baeumer 已提交
1341 1342 1343
					}
				}
			}
1344 1345 1346 1347

			return this.getWorkspaceTasks().then(async (customTasks) => {
				const customTasksKeyValuePairs = Array.from(customTasks);
				const customTasksPromises = customTasksKeyValuePairs.map(async ([key, folderTasks]) => {
D
Dirk Baeumer 已提交
1348
					let contributed = contributedTasks.get(key);
1349 1350
					if (!folderTasks.set) {
						if (contributed) {
1351
							result.add(key, ...contributed);
1352 1353 1354 1355
						}
						return;
					}

D
Dirk Baeumer 已提交
1356
					if (!contributed) {
1357
						result.add(key, ...folderTasks.set.tasks);
D
Dirk Baeumer 已提交
1358 1359 1360 1361 1362
					} else {
						let configurations = folderTasks.configurations;
						let legacyTaskConfigurations = folderTasks.set ? this.getLegacyTaskConfigurations(folderTasks.set) : undefined;
						let customTasksToDelete: Task[] = [];
						if (configurations || legacyTaskConfigurations) {
D
Dirk Baeumer 已提交
1363
							let unUsedConfigurations: Set<string> = new Set<string>();
1364 1365 1366
							if (configurations) {
								Object.keys(configurations.byIdentifier).forEach(key => unUsedConfigurations.add(key));
							}
D
Dirk Baeumer 已提交
1367 1368
							for (let task of contributed) {
								if (!ContributedTask.is(task)) {
1369 1370
									continue;
								}
D
Dirk Baeumer 已提交
1371 1372 1373
								if (configurations) {
									let configuringTask = configurations.byIdentifier[task.defines._key];
									if (configuringTask) {
D
Dirk Baeumer 已提交
1374
										unUsedConfigurations.delete(task.defines._key);
1375
										result.add(key, TaskConfig.createCustomTask(task, configuringTask));
D
Dirk Baeumer 已提交
1376
									} else {
1377
										result.add(key, task);
D
Dirk Baeumer 已提交
1378 1379 1380 1381
									}
								} else if (legacyTaskConfigurations) {
									let configuringTask = legacyTaskConfigurations[task.defines._key];
									if (configuringTask) {
1382
										result.add(key, TaskConfig.createCustomTask(task, configuringTask));
D
Dirk Baeumer 已提交
1383
										customTasksToDelete.push(configuringTask);
D
Dirk Baeumer 已提交
1384
									} else {
1385
										result.add(key, task);
D
Dirk Baeumer 已提交
1386 1387
									}
								} else {
1388
									result.add(key, task);
D
Dirk Baeumer 已提交
1389
								}
1390
							}
D
Dirk Baeumer 已提交
1391 1392 1393 1394 1395 1396 1397 1398 1399
							if (customTasksToDelete.length > 0) {
								let toDelete = customTasksToDelete.reduce<IStringDictionary<boolean>>((map, task) => {
									map[task._id] = true;
									return map;
								}, Object.create(null));
								for (let task of folderTasks.set.tasks) {
									if (toDelete[task._id]) {
										continue;
									}
1400
									result.add(key, task);
1401
								}
D
Dirk Baeumer 已提交
1402
							} else {
1403
								result.add(key, ...folderTasks.set.tasks);
1404
							}
1405 1406 1407 1408

							const unUsedConfigurationsAsArray = Array.from(unUsedConfigurations);

							const unUsedConfigurationPromises = unUsedConfigurationsAsArray.map(async (value) => {
1409
								let configuringTask = configurations!.byIdentifier[value];
1410 1411 1412 1413 1414

								for (const [handle, provider] of this._providers) {
									if (configuringTask.type === this._providerTypes.get(handle)) {
										try {
											const resolvedTask = await provider.resolveTask(configuringTask);
A
Alex Ross 已提交
1415
											if (resolvedTask && (resolvedTask._id === configuringTask._id)) {
1416 1417 1418 1419 1420 1421 1422 1423 1424
												result.add(key, TaskConfig.createCustomTask(resolvedTask, configuringTask));
												return;
											}
										} catch (error) {
											// Ignore errors. The task could not be provided by any of the providers.
										}
									}
								}

D
Dirk Baeumer 已提交
1425 1426
								this._outputChannel.append(nls.localize(
									'TaskService.noConfiguration',
1427 1428
									'Error: The {0} task detection didn\'t contribute a task for the following configuration:\n{1}\nThe task will be ignored.\n',
									configuringTask.configures.type,
D
Dirk Baeumer 已提交
1429 1430 1431 1432
									JSON.stringify(configuringTask._source.config.element, undefined, 4)
								));
								this.showOutput();
							});
1433 1434

							await Promise.all(unUsedConfigurationPromises);
D
Dirk Baeumer 已提交
1435
						} else {
1436 1437
							result.add(key, ...folderTasks.set.tasks);
							result.add(key, ...contributed);
1438 1439
						}
					}
D
Dirk Baeumer 已提交
1440
				});
1441 1442 1443

				await Promise.all(customTasksPromises);

1444 1445 1446
				return result;
			}, () => {
				// If we can't read the tasks.json file provide at least the contributed tasks
1447
				let result: TaskMap = new TaskMap();
D
Dirk Baeumer 已提交
1448
				for (let set of contributedTaskSets) {
1449
					for (let task of set.tasks) {
1450 1451 1452 1453
						const folder = task.getWorkspaceFolder();
						if (folder) {
							result.add(folder, task);
						}
1454
					}
D
Dirk Baeumer 已提交
1455
				}
1456 1457
				return result;
			});
1458 1459 1460
		});
	}

1461
	private getLegacyTaskConfigurations(workspaceTasks: TaskSet): IStringDictionary<CustomTask> | undefined {
1462
		let result: IStringDictionary<CustomTask> | undefined;
1463
		function getResult(): IStringDictionary<CustomTask> {
1464 1465 1466 1467
			if (result) {
				return result;
			}
			result = Object.create(null);
1468
			return result!;
1469 1470
		}
		for (let task of workspaceTasks.tasks) {
1471 1472 1473 1474 1475
			if (CustomTask.is(task)) {
				let commandName = task.command && task.command.name;
				// This is for backwards compatibility with the 0.1.0 task annotation code
				// if we had a gulp, jake or grunt command a task specification was a annotation
				if (commandName === 'gulp' || commandName === 'grunt' || commandName === 'jake') {
1476
					let identifier = NKeyedTaskIdentifier.create({
1477
						type: commandName,
A
Alex Ross 已提交
1478
						task: task.configurationProperties.name
1479
					});
1480 1481
					getResult()[identifier._key] = task;
				}
1482 1483 1484 1485 1486
			}
		}
		return result;
	}

J
Johannes Rieken 已提交
1487
	public getWorkspaceTasks(runSource: TaskRunSource = TaskRunSource.User): Promise<Map<string, WorkspaceFolderTaskResult>> {
1488 1489 1490
		if (this._workspaceTasksPromise) {
			return this._workspaceTasksPromise;
		}
1491
		this.updateWorkspaceTasks(runSource);
1492
		return this._workspaceTasksPromise!;
1493 1494
	}

A
Alex Ross 已提交
1495
	protected abstract updateWorkspaceTasks(runSource: TaskRunSource | void): void;
1496

A
Alex Ross 已提交
1497
	protected computeWorkspaceTasks(runSource: TaskRunSource = TaskRunSource.User): Promise<Map<string, WorkspaceFolderTaskResult>> {
D
Dirk Baeumer 已提交
1498
		if (this.workspaceFolders.length === 0) {
1499
			return Promise.resolve(new Map<string, WorkspaceFolderTaskResult>());
1500
		} else {
1501
			let promises: Promise<WorkspaceFolderTaskResult | undefined>[] = [];
D
Dirk Baeumer 已提交
1502
			for (let folder of this.workspaceFolders) {
1503
				promises.push(this.computeWorkspaceFolderTasks(folder, runSource).then((value) => value, () => undefined));
1504
			}
1505
			return Promise.all(promises).then(async (values) => {
1506 1507 1508 1509 1510 1511
				let result = new Map<string, WorkspaceFolderTaskResult>();
				for (let value of values) {
					if (value) {
						result.set(value.workspaceFolder.uri.toString(), value);
					}
				}
1512 1513 1514 1515 1516 1517 1518 1519
				const userTasks = await this.computeUserTasks(this.workspaceFolders[0], runSource).then((value) => value, () => undefined);
				if (userTasks) {
					result.set('settings', userTasks);
				}
				const workspaceFileTasks = await this.computeWorkspaceFileTasks(this.workspaceFolders[0], runSource).then((value) => value, () => undefined);
				if (workspaceFileTasks && this._workspace && this._workspace.configuration) {
					result.set(this._workspace.configuration.toString(), workspaceFileTasks);
				}
1520 1521 1522 1523 1524
				return result;
			});
		}
	}

A
Alex Ross 已提交
1525 1526 1527 1528
	public setJsonTasksSupported(areSupported: Promise<boolean>) {
		this._areJsonTasksSupportedPromise = areSupported;
	}

1529
	private computeWorkspaceFolderTasks(workspaceFolder: IWorkspaceFolder, runSource: TaskRunSource = TaskRunSource.User): Promise<WorkspaceFolderTaskResult> {
D
Dirk Baeumer 已提交
1530
		return (this.executionEngine === ExecutionEngine.Process
1531 1532 1533 1534
			? this.computeLegacyConfiguration(workspaceFolder)
			: this.computeConfiguration(workspaceFolder)).
			then((workspaceFolderConfiguration) => {
				if (!workspaceFolderConfiguration || !workspaceFolderConfiguration.config || workspaceFolderConfiguration.hasErrors) {
1535
					return Promise.resolve({ workspaceFolder, set: undefined, configurations: undefined, hasErrors: workspaceFolderConfiguration ? workspaceFolderConfiguration.hasErrors : false });
1536
				}
A
Alex Ross 已提交
1537
				return ProblemMatcherRegistry.onReady().then(async (): Promise<WorkspaceFolderTaskResult> => {
1538
					let taskSystemInfo: TaskSystemInfo | undefined = this._taskSystemInfos.get(workspaceFolder.uri.scheme);
1539
					let problemReporter = new ProblemReporter(this._outputChannel);
1540
					let parseResult = TaskConfig.parse(workspaceFolder, this._workspace, taskSystemInfo ? taskSystemInfo.platform : Platform.platform, workspaceFolderConfiguration.config!, problemReporter, TaskConfig.TaskConfigSource.TasksJson);
1541 1542 1543
					let hasErrors = false;
					if (!parseResult.validationStatus.isOK()) {
						hasErrors = true;
1544
						this.showOutput(runSource);
1545 1546 1547 1548 1549
					}
					if (problemReporter.status.isFatal()) {
						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.'));
						return { workspaceFolder, set: undefined, configurations: undefined, hasErrors };
					}
1550
					let customizedTasks: { byIdentifier: IStringDictionary<ConfiguringTask>; } | undefined;
1551 1552 1553 1554 1555 1556 1557 1558
					if (parseResult.configured && parseResult.configured.length > 0) {
						customizedTasks = {
							byIdentifier: Object.create(null)
						};
						for (let task of parseResult.configured) {
							customizedTasks.byIdentifier[task.configures._key] = task;
						}
					}
A
Alex Ross 已提交
1559 1560 1561 1562
					if (!(await this._areJsonTasksSupportedPromise) && (parseResult.custom.length > 0)) {
						console.warn('Custom workspace tasks are not supported.');
					}
					return { workspaceFolder, set: { tasks: await this._areJsonTasksSupportedPromise ? parseResult.custom : [] }, configurations: customizedTasks, hasErrors };
1563 1564 1565 1566
				});
			});
	}

1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661
	private testParseExternalConfig(config: TaskConfig.ExternalTaskRunnerConfiguration | undefined, location: string): { config: TaskConfig.ExternalTaskRunnerConfiguration | undefined, hasParseErrors: boolean } {
		if (!config) {
			return { config: undefined, hasParseErrors: false };
		}
		let parseErrors: string[] = (config as any).$parseErrors;
		if (parseErrors) {
			let isAffected = false;
			for (const parseError of parseErrors) {
				if (/tasks\.json$/.test(parseError)) {
					isAffected = true;
					break;
				}
			}
			if (isAffected) {
				this._outputChannel.append(nls.localize('TaskSystem.invalidTaskJsonOther', 'Error: The content of the tasks json in {0} has syntax errors. Please correct them before executing a task.\n', location));
				this.showOutput();
				return { config, hasParseErrors: true };
			}
		}
		return { config, hasParseErrors: false };
	}

	private async computeWorkspaceFileTasks(workspaceFolder: IWorkspaceFolder, runSource: TaskRunSource = TaskRunSource.User): Promise<WorkspaceFolderTaskResult> {
		if (this.executionEngine === ExecutionEngine.Process) {
			return this.emptyWorkspaceTaskResults(workspaceFolder);
		}
		const configuration = this.testParseExternalConfig(this.configurationService.inspect<TaskConfig.ExternalTaskRunnerConfiguration>('tasks').workspace, nls.localize('TasksSystem.locationWorkspaceConfig', 'workspace file'));
		let customizedTasks: { byIdentifier: IStringDictionary<ConfiguringTask>; } = {
			byIdentifier: Object.create(null)
		};

		const custom: CustomTask[] = [];
		await this.computeTasksForSingleConfig(workspaceFolder, configuration.config, runSource, custom, customizedTasks.byIdentifier, TaskConfig.TaskConfigSource.WorkspaceFile);
		const engine = configuration.config ? TaskConfig.ExecutionEngine.from(configuration.config) : ExecutionEngine.Terminal;
		if (engine === ExecutionEngine.Process) {
			this.notificationService.warn(nls.localize('TaskSystem.versionWorkspaceFile', 'Only tasks version 2.0.0 permitted in .codeworkspace.'));
			return this.emptyWorkspaceTaskResults(workspaceFolder);
		}
		return { workspaceFolder, set: { tasks: custom }, configurations: customizedTasks, hasErrors: configuration.hasParseErrors };
	}

	private async computeUserTasks(workspaceFolder: IWorkspaceFolder, runSource: TaskRunSource = TaskRunSource.User): Promise<WorkspaceFolderTaskResult> {
		if (this.executionEngine === ExecutionEngine.Process) {
			return this.emptyWorkspaceTaskResults(workspaceFolder);
		}
		const configuration = this.testParseExternalConfig(this.configurationService.inspect<TaskConfig.ExternalTaskRunnerConfiguration>('tasks').user, nls.localize('TasksSystem.locationUserConfig', 'user settings'));
		let customizedTasks: { byIdentifier: IStringDictionary<ConfiguringTask>; } = {
			byIdentifier: Object.create(null)
		};

		const custom: CustomTask[] = [];
		await this.computeTasksForSingleConfig(workspaceFolder, configuration.config, runSource, custom, customizedTasks.byIdentifier, TaskConfig.TaskConfigSource.User);
		const engine = configuration.config ? TaskConfig.ExecutionEngine.from(configuration.config) : ExecutionEngine.Terminal;
		if (engine === ExecutionEngine.Process) {
			this.notificationService.warn(nls.localize('TaskSystem.versionSettings', 'Only tasks version 2.0.0 permitted in user settings.'));
			return this.emptyWorkspaceTaskResults(workspaceFolder);
		}
		return { workspaceFolder, set: { tasks: custom }, configurations: customizedTasks, hasErrors: configuration.hasParseErrors };
	}

	private emptyWorkspaceTaskResults(workspaceFolder: IWorkspaceFolder): WorkspaceFolderTaskResult {
		return { workspaceFolder, set: undefined, configurations: undefined, hasErrors: false };
	}

	private async computeTasksForSingleConfig(workspaceFolder: IWorkspaceFolder, config: TaskConfig.ExternalTaskRunnerConfiguration | undefined, runSource: TaskRunSource, custom: CustomTask[], customized: IStringDictionary<ConfiguringTask>, source: TaskConfig.TaskConfigSource): Promise<boolean> {
		if (!config) {
			return false;
		}
		let taskSystemInfo: TaskSystemInfo | undefined = workspaceFolder ? this._taskSystemInfos.get(workspaceFolder.uri.scheme) : undefined;
		let problemReporter = new ProblemReporter(this._outputChannel);
		let parseResult = TaskConfig.parse(workspaceFolder, this._workspace, taskSystemInfo ? taskSystemInfo.platform : Platform.platform, config, problemReporter, source);
		let hasErrors = false;
		if (!parseResult.validationStatus.isOK()) {
			this.showOutput(runSource);
			hasErrors = true;
		}
		if (problemReporter.status.isFatal()) {
			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.'));
			return hasErrors;
		}
		if (parseResult.configured && parseResult.configured.length > 0) {
			for (let task of parseResult.configured) {
				customized[task.configures._key] = task;
			}
		}
		if (!(await this._areJsonTasksSupportedPromise) && (parseResult.custom.length > 0)) {
			console.warn('Custom workspace tasks are not supported.');
		} else {
			for (let task of parseResult.custom) {
				custom.push(task);
			}
		}
		return hasErrors;
	}

1662
	private computeConfiguration(workspaceFolder: IWorkspaceFolder): Promise<WorkspaceFolderConfigurationResult> {
1663
		let { config, hasParseErrors } = this.getConfiguration(workspaceFolder);
1664
		return Promise.resolve<WorkspaceFolderConfigurationResult>({ workspaceFolder, config, hasErrors: hasParseErrors });
1665 1666
	}

A
Alex Ross 已提交
1667
	protected abstract computeLegacyConfiguration(workspaceFolder: IWorkspaceFolder): Promise<WorkspaceFolderConfigurationResult>;
1668

1669
	private computeWorkspaceFolderSetup(): [IWorkspaceFolder[], IWorkspaceFolder[], ExecutionEngine, JsonSchemaVersion, IWorkspace | undefined] {
S
Sandeep Somavarapu 已提交
1670
		let workspaceFolders: IWorkspaceFolder[] = [];
D
Dirk Baeumer 已提交
1671
		let ignoredWorkspaceFolders: IWorkspaceFolder[] = [];
D
Dirk Baeumer 已提交
1672 1673
		let executionEngine = ExecutionEngine.Terminal;
		let schemaVersion = JsonSchemaVersion.V2_0_0;
1674
		let workspace: IWorkspace | undefined;
D
Dirk Baeumer 已提交
1675
		if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) {
S
Sandeep Somavarapu 已提交
1676
			let workspaceFolder: IWorkspaceFolder = this.contextService.getWorkspace().folders[0];
D
Dirk Baeumer 已提交
1677 1678 1679
			workspaceFolders.push(workspaceFolder);
			executionEngine = this.computeExecutionEngine(workspaceFolder);
			schemaVersion = this.computeJsonSchemaVersion(workspaceFolder);
D
Dirk Baeumer 已提交
1680
		} else if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
1681
			workspace = this.contextService.getWorkspace();
1682
			for (let workspaceFolder of this.contextService.getWorkspace().folders) {
D
Dirk Baeumer 已提交
1683
				if (schemaVersion === this.computeJsonSchemaVersion(workspaceFolder)) {
D
Dirk Baeumer 已提交
1684
					workspaceFolders.push(workspaceFolder);
1685
				} else {
D
Dirk Baeumer 已提交
1686
					ignoredWorkspaceFolders.push(workspaceFolder);
1687 1688
					this._outputChannel.append(nls.localize(
						'taskService.ignoreingFolder',
1689
						'Ignoring task configurations for workspace folder {0}. Multi folder workspace task support requires that all folders use task version 2.0.0\n',
1690
						workspaceFolder.uri.fsPath));
1691 1692 1693
				}
			}
		}
1694
		return [workspaceFolders, ignoredWorkspaceFolders, executionEngine, schemaVersion, workspace];
1695
	}
1696

S
Sandeep Somavarapu 已提交
1697
	private computeExecutionEngine(workspaceFolder: IWorkspaceFolder): ExecutionEngine {
1698
		let { config } = this.getConfiguration(workspaceFolder);
1699
		if (!config) {
1700
			return ExecutionEngine._default;
1701 1702 1703 1704
		}
		return TaskConfig.ExecutionEngine.from(config);
	}

S
Sandeep Somavarapu 已提交
1705
	private computeJsonSchemaVersion(workspaceFolder: IWorkspaceFolder): JsonSchemaVersion {
1706
		let { config } = this.getConfiguration(workspaceFolder);
1707 1708 1709 1710 1711 1712
		if (!config) {
			return JsonSchemaVersion.V2_0_0;
		}
		return TaskConfig.JsonSchemaVersion.from(config);
	}

A
Alex Ross 已提交
1713
	protected getConfiguration(workspaceFolder: IWorkspaceFolder): { config: TaskConfig.ExternalTaskRunnerConfiguration | undefined; hasParseErrors: boolean } {
D
Dirk Baeumer 已提交
1714
		let result = this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY
1715
			? Objects.deepClone(this.configurationService.inspect<TaskConfig.ExternalTaskRunnerConfiguration>('tasks', { resource: workspaceFolder.uri }).workspaceFolder)
1716
			: undefined;
1717
		if (!result) {
1718
			return { config: undefined, hasParseErrors: false };
1719 1720 1721 1722
		}
		let parseErrors: string[] = (result as any).$parseErrors;
		if (parseErrors) {
			let isAffected = false;
1723 1724
			for (const parseError of parseErrors) {
				if (/tasks\.json$/.test(parseError)) {
1725 1726 1727
					isAffected = true;
					break;
				}
1728
			}
1729
			if (isAffected) {
1730
				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'));
1731
				this.showOutput();
1732
				return { config: undefined, hasParseErrors: true };
1733
			}
1734 1735
		}
		return { config: result, hasParseErrors: false };
1736 1737
	}

1738
	public inTerminal(): boolean {
1739 1740 1741
		if (this._taskSystem) {
			return this._taskSystem instanceof TerminalTaskSystem;
		}
D
Dirk Baeumer 已提交
1742
		return this.executionEngine === ExecutionEngine.Terminal;
1743 1744
	}

1745
	public configureAction(): Action {
A
Alex Ross 已提交
1746
		const thisCapture: AbstractTaskService = this;
1747 1748
		return new class extends Action {
			constructor() {
1749
				super(ConfigureTaskAction.ID, ConfigureTaskAction.TEXT, undefined, true, () => { thisCapture.runConfigureTasks(); return Promise.resolve(undefined); });
1750 1751
			}
		};
1752 1753
	}

J
Johannes Rieken 已提交
1754
	public beforeShutdown(): boolean | Promise<boolean> {
1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765
		if (!this._taskSystem) {
			return false;
		}
		if (!this._taskSystem.isActiveSync()) {
			return false;
		}
		// The terminal service kills all terminal on shutdown. So there
		// is nothing we can do to prevent this here.
		if (this._taskSystem instanceof TerminalTaskSystem) {
			return false;
		}
1766

J
Johannes Rieken 已提交
1767
		let terminatePromise: Promise<IConfirmationResult>;
1768
		if (this._taskSystem.canAutoTerminate()) {
1769
			terminatePromise = Promise.resolve({ confirmed: true });
1770
		} else {
1771
			terminatePromise = this.dialogService.confirm({
1772 1773 1774 1775
				message: nls.localize('TaskSystem.runningTask', 'There is a task running. Do you want to terminate it?'),
				primaryButton: nls.localize({ key: 'TaskSystem.terminateTask', comment: ['&& denotes a mnemonic'] }, "&&Terminate Task"),
				type: 'question'
			});
E
Erich Gamma 已提交
1776
		}
1777

1778 1779
		return terminatePromise.then(res => {
			if (res.confirmed) {
1780
				return this._taskSystem!.terminateAll().then((responses) => {
1781
					let success = true;
1782
					let code: number | undefined = undefined;
1783 1784 1785 1786
					for (let response of responses) {
						success = success && response.success;
						// We only have a code in the old output runner which only has one task
						// So we can use the first code.
R
Rob Lourens 已提交
1787
						if (code === undefined && response.code !== undefined) {
1788 1789 1790 1791
							code = response.code;
						}
					}
					if (success) {
1792
						this._taskSystem = undefined;
1793 1794 1795
						this.disposeTaskSystemListeners();
						return false; // no veto
					} else if (code && code === TerminateResponseCode.ProcessNotFound) {
1796
						return this.dialogService.confirm({
1797 1798 1799
							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.'),
							primaryButton: nls.localize({ key: 'TaskSystem.exitAnyways', comment: ['&& denotes a mnemonic'] }, "&&Exit Anyways"),
							type: 'info'
1800
						}).then(res => !res.confirmed);
1801 1802 1803 1804 1805 1806 1807 1808 1809
					}
					return true; // veto
				}, (err) => {
					return true; // veto
				});
			}

			return true; // veto
		});
E
Erich Gamma 已提交
1810 1811
	}

J
Johannes Rieken 已提交
1812
	private handleError(err: any): void {
E
Erich Gamma 已提交
1813 1814 1815
		let showOutput = true;
		if (err instanceof TaskError) {
			let buildError = <TaskError>err;
1816 1817 1818
			let needsConfig = buildError.code === TaskErrors.NotConfigured || buildError.code === TaskErrors.NoBuildTask || buildError.code === TaskErrors.NoTestTask;
			let needsTerminate = buildError.code === TaskErrors.RunningTask;
			if (needsConfig || needsTerminate) {
B
Benjamin Pasero 已提交
1819 1820 1821 1822 1823 1824 1825 1826 1827 1828
				this.notificationService.prompt(buildError.severity, buildError.message, [{
					label: needsConfig ? ConfigureTaskAction.TEXT : nls.localize('TerminateAction.label', "Terminate Task"),
					run: () => {
						if (needsConfig) {
							this.runConfigureTasks();
						} else {
							this.runTerminateCommand();
						}
					}
				}]);
E
Erich Gamma 已提交
1829
			} else {
1830
				this.notificationService.notify({ severity: buildError.severity, message: buildError.message });
E
Erich Gamma 已提交
1831 1832 1833
			}
		} else if (err instanceof Error) {
			let error = <Error>err;
1834
			this.notificationService.error(error.message);
1835
			showOutput = false;
E
Erich Gamma 已提交
1836
		} else if (Types.isString(err)) {
1837
			this.notificationService.error(<string>err);
E
Erich Gamma 已提交
1838
		} else {
1839
			this.notificationService.error(nls.localize('TaskSystem.unknownError', 'An error has occurred while running a task. See task log for details.'));
E
Erich Gamma 已提交
1840 1841
		}
		if (showOutput) {
1842
			this.showOutput();
E
Erich Gamma 已提交
1843 1844
		}
	}
1845 1846

	private canRunCommand(): boolean {
1847
		if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
1848 1849 1850 1851 1852
			this.notificationService.prompt(
				Severity.Info,
				nls.localize('TaskService.noWorkspace', "Tasks are only available on a workspace folder."),
				[{
					label: nls.localize('TaskService.learnMore', "Learn More"),
1853
					run: () => this.openerService.open(URI.parse('https://code.visualstudio.com/docs/editor/tasks'))
1854 1855
				}]
			);
1856 1857 1858 1859 1860
			return false;
		}
		return true;
	}

1861
	private createTaskQuickPickEntries(tasks: Task[], group: boolean = false, sort: boolean = false, selectedEntry?: TaskQuickPickEntry): TaskQuickPickEntry[] {
R
Rob Lourens 已提交
1862
		if (tasks === undefined || tasks === null || tasks.length === 0) {
1863
			return [];
1864
		}
1865
		const TaskQuickPickEntry = (task: Task): TaskQuickPickEntry => {
1866
			let description: string | undefined;
1867 1868 1869 1870 1871
			if (task._source.kind === TaskSourceKind.User) {
				description = nls.localize('taskQuickPick.userSettings', 'User Settings');
			} else if (task._source.kind === TaskSourceKind.WorkspaceFile) {
				description = task.getWorkspaceFileName();
			} else if (this.needsFolderQualification()) {
A
Alex Ross 已提交
1872
				let workspaceFolder = task.getWorkspaceFolder();
1873
				if (workspaceFolder) {
1874
					description = workspaceFolder.name;
1875 1876 1877 1878
				}
			}
			return { label: task._label, description, task };
		};
C
Christof Marti 已提交
1879
		function fillEntries(entries: QuickPickInput<TaskQuickPickEntry>[], tasks: Task[], groupLabel: string): void {
C
Christof Marti 已提交
1880
			if (tasks.length) {
C
Christof Marti 已提交
1881
				entries.push({ type: 'separator', label: groupLabel });
1882
			}
1883
			for (let task of tasks) {
1884
				let entry: TaskQuickPickEntry = TaskQuickPickEntry(task);
C
Christof Marti 已提交
1885
				entry.buttons = [{ iconClass: 'quick-open-task-configure', tooltip: nls.localize('configureTask', "Configure Task") }];
1886 1887 1888 1889 1890
				if (selectedEntry && (task === selectedEntry.task)) {
					entries.unshift(selectedEntry);
				} else {
					entries.push(entry);
				}
1891 1892
			}
		}
1893
		let entries: TaskQuickPickEntry[];
1894 1895 1896
		if (group) {
			entries = [];
			if (tasks.length === 1) {
1897
				entries.push(TaskQuickPickEntry(tasks[0]));
1898 1899 1900 1901 1902 1903
			} else {
				let recentlyUsedTasks = this.getRecentlyUsedTasks();
				let recent: Task[] = [];
				let configured: Task[] = [];
				let detected: Task[] = [];
				let taskMap: IStringDictionary<Task> = Object.create(null);
1904
				tasks.forEach(task => {
A
Alex Ross 已提交
1905
					let key = task.getRecentlyUsedKey();
1906 1907 1908 1909
					if (key) {
						taskMap[key] = task;
					}
				});
1910 1911 1912 1913 1914 1915 1916
				recentlyUsedTasks.keys().forEach(key => {
					let task = taskMap[key];
					if (task) {
						recent.push(task);
					}
				});
				for (let task of tasks) {
A
Alex Ross 已提交
1917
					let key = task.getRecentlyUsedKey();
1918
					if (!key || !recentlyUsedTasks.has(key)) {
1919 1920 1921 1922 1923 1924 1925
						if (task._source.kind === TaskSourceKind.Workspace) {
							configured.push(task);
						} else {
							detected.push(task);
						}
					}
				}
1926
				const sorter = this.createSorter();
1927
				fillEntries(entries, recent, nls.localize('recentlyUsed', 'recently used tasks'));
1928
				configured = configured.sort((a, b) => sorter.compare(a, b));
C
Christof Marti 已提交
1929
				fillEntries(entries, configured, nls.localize('configured', 'configured tasks'));
1930
				detected = detected.sort((a, b) => sorter.compare(a, b));
C
Christof Marti 已提交
1931
				fillEntries(entries, detected, nls.localize('detected', 'detected tasks'));
1932 1933 1934
			}
		} else {
			if (sort) {
1935 1936
				const sorter = this.createSorter();
				tasks = tasks.sort((a, b) => sorter.compare(a, b));
1937
			}
1938
			entries = tasks.map<TaskQuickPickEntry>(task => TaskQuickPickEntry(task));
1939
		}
1940 1941 1942
		return entries;
	}

1943
	private showQuickPick(tasks: Promise<Task[]> | Task[], placeHolder: string, defaultEntry?: TaskQuickPickEntry, group: boolean = false, sort: boolean = false, selectedEntry?: TaskQuickPickEntry, additionalEntries?: TaskQuickPickEntry[]): Promise<TaskQuickPickEntry | undefined | null> {
A
Alex Ross 已提交
1944
		let _createEntries = (): Promise<QuickPickInput<TaskQuickPickEntry>[]> => {
1945
			if (Array.isArray(tasks)) {
1946
				return Promise.resolve(this.createTaskQuickPickEntries(tasks, group, sort, selectedEntry));
1947
			} else {
1948
				return tasks.then((tasks) => this.createTaskQuickPickEntries(tasks, group, sort, selectedEntry));
1949 1950
			}
		};
C
Christof Marti 已提交
1951
		return this.quickInputService.pick(_createEntries().then((entries) => {
1952
			if ((entries.length === 0) && defaultEntry) {
1953
				entries.push(defaultEntry);
A
Alex Ross 已提交
1954 1955
			} else if (entries.length > 1 && additionalEntries && additionalEntries.length > 0) {
				entries.push({ type: 'separator', label: '' });
1956 1957
				entries.push(additionalEntries[0]);
			}
1958
			return entries;
C
Christof Marti 已提交
1959
		}), {
M
Matt Bierner 已提交
1960 1961 1962 1963 1964 1965 1966 1967 1968
			placeHolder,
			matchOnDescription: true,
			onDidTriggerItemButton: context => {
				let task = context.item.task;
				this.quickInputService.cancel();
				if (ContributedTask.is(task)) {
					this.customize(task, undefined, true);
				} else if (CustomTask.is(task)) {
					this.openConfig(task);
C
Christof Marti 已提交
1969
				}
M
Matt Bierner 已提交
1970 1971
			}
		});
1972 1973
	}

J
Johannes Rieken 已提交
1974
	private showIgnoredFoldersMessage(): Promise<void> {
D
Dirk Baeumer 已提交
1975
		if (this.ignoredWorkspaceFolders.length === 0 || !this.showIgnoreMessage) {
1976
			return Promise.resolve(undefined);
D
Dirk Baeumer 已提交
1977 1978
		}

1979 1980 1981 1982
		this.notificationService.prompt(
			Severity.Info,
			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(', ')),
			[{
H
Howard Hung 已提交
1983
				label: nls.localize('TaskService.notAgain', "Don't Show Again"),
1984 1985
				isSecondary: true,
				run: () => {
A
Alex Ross 已提交
1986
					this.storageService.store(AbstractTaskService.IgnoreTask010DonotShowAgain_key, true, StorageScope.WORKSPACE);
1987
					this._showIgnoreMessage = false;
1988 1989 1990
				}
			}]
		);
1991

1992
		return Promise.resolve(undefined);
D
Dirk Baeumer 已提交
1993 1994
	}

1995
	private runTaskCommand(arg?: any): void {
1996 1997 1998
		if (!this.canRunCommand()) {
			return;
		}
1999
		let identifier = this.getTaskIdentifier(arg);
R
Rob Lourens 已提交
2000
		if (identifier !== undefined) {
2001 2002 2003 2004
			this.getGroupedTasks().then((grouped) => {
				let resolver = this.createResolver(grouped);
				let folders = this.contextService.getWorkspace().folders;
				for (let folder of folders) {
2005
					let task = resolver.resolve(folder.uri, identifier);
2006
					if (task) {
A
Alex Ross 已提交
2007 2008 2009
						this.run(task).then(undefined, reason => {
							// eat the error, it has already been surfaced to the user and we don't care about it here
						});
2010 2011
						return;
					}
2012
				}
2013
				this.doRunTaskCommand(grouped.all());
2014
			}, () => {
2015
				this.doRunTaskCommand();
2016 2017
			});
		} else {
2018
			this.doRunTaskCommand();
2019 2020 2021
		}
	}

2022
	private doRunTaskCommand(tasks?: Task[]): void {
D
Dirk Baeumer 已提交
2023 2024 2025 2026
		this.showIgnoredFoldersMessage().then(() => {
			this.showQuickPick(tasks ? tasks : this.tasks(),
				nls.localize('TaskService.pickRunTask', 'Select the task to run'),
				{
2027
					label: nls.localize('TaskService.noEntryToRun', 'No task to run found. Configure Tasks...'),
D
Dirk Baeumer 已提交
2028 2029 2030
					task: null
				},
				true).
2031 2032
				then((entry) => {
					let task: Task | undefined | null = entry ? entry.task : undefined;
R
Rob Lourens 已提交
2033
					if (task === undefined) {
D
Dirk Baeumer 已提交
2034 2035 2036 2037 2038
						return;
					}
					if (task === null) {
						this.runConfigureTasks();
					} else {
2039
						this.run(task, { attachProblemMatcher: true }, TaskRunSource.User).then(undefined, reason => {
A
Alex Ross 已提交
2040 2041
							// eat the error, it has already been surfaced to the user and we don't care about it here
						});
D
Dirk Baeumer 已提交
2042 2043 2044
					}
				});
		});
2045 2046
	}

2047
	private reRunTaskCommand(): void {
A
Alex Ross 已提交
2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058
		if (!this.canRunCommand()) {
			return;
		}

		ProblemMatcherRegistry.onReady().then(() => {
			return this.textFileService.saveAll().then((value) => { // make sure all dirty files are saved
				let executeResult = this.getTaskSystem().rerun();
				if (executeResult) {
					return this.handleExecuteResult(executeResult);
				} else {
					this.doRunTaskCommand();
2059
					return Promise.resolve(undefined);
A
Alex Ross 已提交
2060 2061 2062 2063 2064
				}
			});
		});
	}

2065 2066 2067 2068 2069
	private splitPerGroupType(tasks: Task[]): { none: Task[], defaults: Task[], users: Task[] } {
		let none: Task[] = [];
		let defaults: Task[] = [];
		let users: Task[] = [];
		for (let task of tasks) {
A
Alex Ross 已提交
2070
			if (task.configurationProperties.groupType === GroupType.default) {
2071
				defaults.push(task);
A
Alex Ross 已提交
2072
			} else if (task.configurationProperties.groupType === GroupType.user) {
2073 2074 2075 2076 2077 2078 2079 2080
				users.push(task);
			} else {
				none.push(task);
			}
		}
		return { none, defaults, users };
	}

2081 2082 2083 2084
	private runBuildCommand(): void {
		if (!this.canRunCommand()) {
			return;
		}
D
Dirk Baeumer 已提交
2085
		if (this.schemaVersion === JsonSchemaVersion.V0_1_0) {
2086 2087 2088
			this.build();
			return;
		}
2089 2090 2091 2092 2093
		let options: IProgressOptions = {
			location: ProgressLocation.Window,
			title: nls.localize('TaskService.fetchingBuildTasks', 'Fetching build tasks...')
		};
		let promise = this.getTasksForGroup(TaskGroup.Build).then((tasks) => {
2094
			if (tasks.length > 0) {
D
Dirk Baeumer 已提交
2095
				let { defaults, users } = this.splitPerGroupType(tasks);
2096
				if (defaults.length === 1) {
A
Alex Ross 已提交
2097 2098 2099
					this.run(defaults[0]).then(undefined, reason => {
						// eat the error, it has already been surfaced to the user and we don't care about it here
					});
2100
					return;
2101 2102
				} else if (defaults.length + users.length > 0) {
					tasks = defaults.concat(users);
D
Dirk Baeumer 已提交
2103 2104
				}
			}
D
Dirk Baeumer 已提交
2105 2106 2107 2108
			this.showIgnoredFoldersMessage().then(() => {
				this.showQuickPick(tasks,
					nls.localize('TaskService.pickBuildTask', 'Select the build task to run'),
					{
2109
						label: nls.localize('TaskService.noBuildTask', 'No build task to run found. Configure Build Task...'),
D
Dirk Baeumer 已提交
2110 2111
						task: null
					},
2112 2113
					true).then((entry) => {
						let task: Task | undefined | null = entry ? entry.task : undefined;
R
Rob Lourens 已提交
2114
						if (task === undefined) {
D
Dirk Baeumer 已提交
2115 2116 2117
							return;
						}
						if (task === null) {
2118
							this.runConfigureDefaultBuildTask();
D
Dirk Baeumer 已提交
2119 2120
							return;
						}
A
Alex Ross 已提交
2121 2122 2123
						this.run(task, { attachProblemMatcher: true }).then(undefined, reason => {
							// eat the error, it has already been surfaced to the user and we don't care about it here
						});
D
Dirk Baeumer 已提交
2124 2125
					});
			});
2126
		});
2127
		this.progressService.withProgress(options, () => promise);
2128 2129 2130 2131 2132 2133
	}

	private runTestCommand(): void {
		if (!this.canRunCommand()) {
			return;
		}
D
Dirk Baeumer 已提交
2134
		if (this.schemaVersion === JsonSchemaVersion.V0_1_0) {
2135
			this.runTest();
2136 2137
			return;
		}
2138 2139 2140 2141 2142
		let options: IProgressOptions = {
			location: ProgressLocation.Window,
			title: nls.localize('TaskService.fetchingTestTasks', 'Fetching test tasks...')
		};
		let promise = this.getTasksForGroup(TaskGroup.Test).then((tasks) => {
2143
			if (tasks.length > 0) {
D
Dirk Baeumer 已提交
2144
				let { defaults, users } = this.splitPerGroupType(tasks);
2145
				if (defaults.length === 1) {
A
Alex Ross 已提交
2146 2147 2148
					this.run(defaults[0]).then(undefined, reason => {
						// eat the error, it has already been surfaced to the user and we don't care about it here
					});
2149
					return;
2150 2151
				} else if (defaults.length + users.length > 0) {
					tasks = defaults.concat(users);
D
Dirk Baeumer 已提交
2152 2153
				}
			}
D
Dirk Baeumer 已提交
2154 2155 2156 2157 2158 2159 2160
			this.showIgnoredFoldersMessage().then(() => {
				this.showQuickPick(tasks,
					nls.localize('TaskService.pickTestTask', 'Select the test task to run'),
					{
						label: nls.localize('TaskService.noTestTaskTerminal', 'No test task to run found. Configure Tasks...'),
						task: null
					}, true
2161 2162
				).then((entry) => {
					let task: Task | undefined | null = entry ? entry.task : undefined;
R
Rob Lourens 已提交
2163
					if (task === undefined) {
D
Dirk Baeumer 已提交
2164 2165 2166 2167 2168 2169
						return;
					}
					if (task === null) {
						this.runConfigureTasks();
						return;
					}
A
Alex Ross 已提交
2170 2171 2172
					this.run(task).then(undefined, reason => {
						// eat the error, it has already been surfaced to the user and we don't care about it here
					});
D
Dirk Baeumer 已提交
2173
				});
2174
			});
2175
		});
2176
		this.progressService.withProgress(options, () => promise);
2177 2178
	}

2179
	private runTerminateCommand(arg?: any): void {
2180 2181 2182
		if (!this.canRunCommand()) {
			return;
		}
2183 2184 2185 2186
		if (arg === 'terminateAll') {
			this.terminateAll();
			return;
		}
J
Johannes Rieken 已提交
2187
		let runQuickPick = (promise?: Promise<Task[]>) => {
2188
			this.showQuickPick(promise || this.getActiveTasks(),
A
Alex Ross 已提交
2189
				nls.localize('TaskService.taskToTerminate', 'Select a task to terminate'),
2190 2191
				{
					label: nls.localize('TaskService.noTaskRunning', 'No task is currently running'),
2192
					task: undefined
2193
				},
2194 2195 2196
				false, true,
				undefined,
				[{
A
Alex Ross 已提交
2197
					label: nls.localize('TaskService.terminateAllRunningTasks', 'All Running Tasks'),
2198 2199 2200 2201 2202 2203 2204 2205
					id: 'terminateAll',
					task: undefined
				}]
			).then(entry => {
				if (entry && entry.id === 'terminateAll') {
					this.terminateAll();
				}
				let task: Task | undefined | null = entry ? entry.task : undefined;
R
Rob Lourens 已提交
2206
				if (task === undefined || task === null) {
2207 2208
					return;
				}
2209
				this.terminate(task);
2210
			});
2211 2212 2213
		};
		if (this.inTerminal()) {
			let identifier = this.getTaskIdentifier(arg);
J
Johannes Rieken 已提交
2214
			let promise: Promise<Task[]>;
R
Rob Lourens 已提交
2215
			if (identifier !== undefined) {
2216 2217 2218
				promise = this.getActiveTasks();
				promise.then((tasks) => {
					for (let task of tasks) {
A
Alex Ross 已提交
2219
						if (task.matches(identifier)) {
2220 2221 2222 2223 2224 2225 2226 2227 2228
							this.terminate(task);
							return;
						}
					}
					runQuickPick(promise);
				});
			} else {
				runQuickPick();
			}
2229 2230 2231
		} else {
			this.isActive().then((active) => {
				if (active) {
2232 2233 2234
					this.terminateAll().then((responses) => {
						// the output runner has only one task
						let response = responses[0];
2235
						if (response.success) {
2236 2237 2238
							return;
						}
						if (response.code && response.code === TerminateResponseCode.ProcessNotFound) {
2239
							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.'));
2240
						} else {
2241
							this.notificationService.error(nls.localize('TerminateAction.failed', 'Failed to terminate running task'));
2242 2243 2244 2245 2246 2247
						}
					});
				}
			});
		}
	}
2248

2249
	private runRestartTaskCommand(arg?: any): void {
2250 2251 2252
		if (!this.canRunCommand()) {
			return;
		}
J
Johannes Rieken 已提交
2253
		let runQuickPick = (promise?: Promise<Task[]>) => {
2254
			this.showQuickPick(promise || this.getActiveTasks(),
2255
				nls.localize('TaskService.taskToRestart', 'Select the task to restart'),
2256 2257 2258 2259 2260
				{
					label: nls.localize('TaskService.noTaskToRestart', 'No task to restart'),
					task: null
				},
				false, true
2261 2262
			).then(entry => {
				let task: Task | undefined | null = entry ? entry.task : undefined;
R
Rob Lourens 已提交
2263
				if (task === undefined || task === null) {
2264 2265
					return;
				}
2266
				this.restart(task);
2267
			});
2268 2269 2270
		};
		if (this.inTerminal()) {
			let identifier = this.getTaskIdentifier(arg);
J
Johannes Rieken 已提交
2271
			let promise: Promise<Task[]>;
R
Rob Lourens 已提交
2272
			if (identifier !== undefined) {
2273 2274 2275
				promise = this.getActiveTasks();
				promise.then((tasks) => {
					for (let task of tasks) {
A
Alex Ross 已提交
2276
						if (task.matches(identifier)) {
2277 2278 2279 2280 2281 2282 2283 2284 2285
							this.restart(task);
							return;
						}
					}
					runQuickPick(promise);
				});
			} else {
				runQuickPick();
			}
2286 2287 2288 2289 2290 2291 2292 2293 2294 2295
		} else {
			this.getActiveTasks().then((activeTasks) => {
				if (activeTasks.length === 0) {
					return;
				}
				let task = activeTasks[0];
				this.restart(task);
			});
		}
	}
2296

2297
	private getTaskIdentifier(arg?: any): string | KeyedTaskIdentifier | undefined {
2298
		let result: string | KeyedTaskIdentifier | undefined = undefined;
2299 2300 2301 2302 2303 2304 2305 2306
		if (Types.isString(arg)) {
			result = arg;
		} else if (arg && Types.isString((arg as TaskIdentifier).type)) {
			result = TaskDefinition.createTaskIdentifier(arg as TaskIdentifier, console);
		}
		return result;
	}

2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352
	private openTaskFile(resource: URI) {
		let configFileCreated = false;
		this.fileService.resolve(resource).then((stat) => stat, () => undefined).then((stat) => {
			if (stat) {
				return stat.resource;
			}
			return this.quickInputService.pick(getTaskTemplates(), { placeHolder: nls.localize('TaskService.template', 'Select a Task Template') }).then((selection) => {
				if (!selection) {
					return Promise.resolve(undefined);
				}
				let content = selection.content;
				let editorConfig = this.configurationService.getValue<any>();
				if (editorConfig.editor.insertSpaces) {
					content = content.replace(/(\n)(\t+)/g, (_, s1, s2) => s1 + strings.repeat(' ', s2.length * editorConfig.editor.tabSize));
				}
				configFileCreated = true;
				type TaskServiceTemplateClassification = {
					templateId?: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
					autoDetect: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
				};
				type TaskServiceEvent = {
					templateId?: string;
					autoDetect: boolean;
				};
				return this.textFileService.create(resource, content).then((result): URI => {
					this.telemetryService.publicLog2<TaskServiceEvent, TaskServiceTemplateClassification>('taskService.template', {
						templateId: selection.id,
						autoDetect: selection.autoDetect
					});
					return result.resource;
				});
			});
		}).then((resource) => {
			if (!resource) {
				return;
			}
			this.editorService.openEditor({
				resource,
				options: {
					pinned: configFileCreated // pin only if config file is created #8727
				}
			});
		});
	}


2353 2354 2355 2356
	private runConfigureTasks(): void {
		if (!this.canRunCommand()) {
			return undefined;
		}
J
Johannes Rieken 已提交
2357
		let taskPromise: Promise<TaskMap>;
D
Dirk Baeumer 已提交
2358
		if (this.schemaVersion === JsonSchemaVersion.V2_0_0) {
2359 2360
			taskPromise = this.getGroupedTasks();
		} else {
2361
			taskPromise = Promise.resolve(new TaskMap());
2362 2363 2364 2365 2366 2367 2368
		}

		let configureTask = (task: Task): void => {
			if (ContributedTask.is(task)) {
				this.customize(task, undefined, true);
			} else if (CustomTask.is(task)) {
				this.openConfig(task);
D
Dirk Baeumer 已提交
2369 2370
			} else if (ConfiguringTask.is(task)) {
				// Do nothing.
2371 2372 2373
			}
		};

C
Christof Marti 已提交
2374 2375
		function isTaskEntry(value: IQuickPickItem): value is IQuickPickItem & { task: Task } {
			let candidate: IQuickPickItem & { task: Task } = value as any;
2376 2377 2378
			return candidate && !!candidate.task;
		}

2379
		let stats = this.contextService.getWorkspace().folders.map<Promise<IFileStat | undefined>>((folder) => {
B
Benjamin Pasero 已提交
2380
			return this.fileService.resolve(folder.toResource('.vscode/tasks.json')).then(stat => stat, () => undefined);
2381 2382 2383 2384
		});

		let createLabel = nls.localize('TaskService.createJsonFile', 'Create tasks.json file from template');
		let openLabel = nls.localize('TaskService.openJsonFile', 'Open tasks.json file');
2385 2386 2387
		const tokenSource = new CancellationTokenSource();
		const cancellationToken: CancellationToken = tokenSource.token;
		type EntryType = (IQuickPickItem & { task: Task; }) | (IQuickPickItem & { folder: IWorkspaceFolder; });
2388
		let entries = Promise.all(stats).then((stats) => {
2389
			return taskPromise.then((taskMap) => {
C
Christof Marti 已提交
2390
				let entries: QuickPickInput<EntryType>[] = [];
2391
				if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY) {
2392
					let tasks = taskMap.all();
2393
					let needsCreateOrOpen: boolean = true;
2394 2395
					if (tasks.length > 0) {
						tasks = tasks.sort((a, b) => a._label.localeCompare(b._label));
2396 2397 2398 2399 2400 2401 2402 2403
						for (let task of tasks) {
							entries.push({ label: task._label, task });
							if (!ContributedTask.is(task)) {
								needsCreateOrOpen = false;
							}
						}
					}
					if (needsCreateOrOpen) {
R
Rob Lourens 已提交
2404
						let label = stats[0] !== undefined ? openLabel : createLabel;
C
Christof Marti 已提交
2405
						if (entries.length) {
C
Christof Marti 已提交
2406
							entries.push({ type: 'separator' });
C
Christof Marti 已提交
2407 2408
						}
						entries.push({ label, folder: this.contextService.getWorkspace().folders[0] });
2409 2410 2411 2412 2413 2414 2415 2416 2417
					}
				} else {
					let folders = this.contextService.getWorkspace().folders;
					let index = 0;
					for (let folder of folders) {
						let tasks = taskMap.get(folder);
						if (tasks.length > 0) {
							tasks = tasks.slice().sort((a, b) => a._label.localeCompare(b._label));
							for (let i = 0; i < tasks.length; i++) {
2418
								let entry: EntryType = { label: tasks[i]._label, task: tasks[i], description: folder.name };
2419
								if (i === 0) {
C
Christof Marti 已提交
2420
									entries.push({ type: 'separator', label: folder.name });
2421 2422 2423 2424
								}
								entries.push(entry);
							}
						} else {
R
Rob Lourens 已提交
2425
							let label = stats[index] !== undefined ? openLabel : createLabel;
2426
							let entry: EntryType = { label, folder: folder };
C
Christof Marti 已提交
2427
							entries.push({ type: 'separator', label: folder.name });
2428 2429 2430 2431 2432
							entries.push(entry);
						}
						index++;
					}
				}
2433 2434 2435
				if (entries.length === 1) {
					tokenSource.cancel();
				}
2436 2437 2438 2439
				return entries;
			});
		});

C
Christof Marti 已提交
2440
		this.quickInputService.pick(entries,
2441 2442 2443 2444 2445 2446 2447 2448 2449
			{ placeHolder: nls.localize('TaskService.pickTask', 'Select a task to configure') }, cancellationToken).
			then(async (selection) => {
				if (cancellationToken.isCancellationRequested) {
					// canceled when there's only one task
					const task = (await entries)[0];
					if ((<any>task).task) {
						selection = <EntryType>task;
					}
				}
D
Dirk Baeumer 已提交
2450 2451 2452 2453 2454 2455
				if (!selection) {
					return;
				}
				if (isTaskEntry(selection)) {
					configureTask(selection.task);
				} else {
2456
					this.openTaskFile(selection.folder.toResource('.vscode/tasks.json'));
D
Dirk Baeumer 已提交
2457 2458
				}
			});
2459 2460
	}

2461 2462 2463 2464
	private runConfigureDefaultBuildTask(): void {
		if (!this.canRunCommand()) {
			return;
		}
D
Dirk Baeumer 已提交
2465
		if (this.schemaVersion === JsonSchemaVersion.V2_0_0) {
2466 2467
			this.tasks().then((tasks => {
				if (tasks.length === 0) {
2468
					this.runConfigureTasks();
2469 2470
					return;
				}
2471
				let selectedTask: Task | undefined;
2472
				let selectedEntry: TaskQuickPickEntry;
2473
				for (let task of tasks) {
A
Alex Ross 已提交
2474
					if (task.configurationProperties.group === TaskGroup.Build && task.configurationProperties.groupType === GroupType.default) {
2475
						selectedTask = task;
2476 2477 2478
						break;
					}
				}
2479 2480
				if (selectedTask) {
					selectedEntry = {
A
Alex Ross 已提交
2481
						label: nls.localize('TaskService.defaultBuildTaskExists', '{0} is already marked as the default build task', selectedTask.getQualifiedLabel()),
2482
						task: selectedTask
2483
					};
2484
				}
D
Dirk Baeumer 已提交
2485 2486
				this.showIgnoredFoldersMessage().then(() => {
					this.showQuickPick(tasks,
2487
						nls.localize('TaskService.pickDefaultBuildTask', 'Select the task to be used as the default build task'), undefined, true, false, selectedEntry).
2488 2489
						then((entry) => {
							let task: Task | undefined | null = entry ? entry.task : undefined;
2490
							if ((task === undefined) || (task === null)) {
D
Dirk Baeumer 已提交
2491 2492
								return;
							}
2493
							if (task === selectedTask && CustomTask.is(task)) {
D
Dirk Baeumer 已提交
2494 2495 2496
								this.openConfig(task);
							}
							if (!InMemoryTask.is(task)) {
2497 2498 2499 2500 2501
								this.customize(task, { group: { kind: 'build', isDefault: true } }, true).then(() => {
									if (selectedTask && (task !== selectedTask) && !InMemoryTask.is(selectedTask)) {
										this.customize(selectedTask, { group: 'build' }, true);
									}
								});
D
Dirk Baeumer 已提交
2502 2503 2504
							}
						});
				});
2505 2506
			}));
		} else {
2507
			this.runConfigureTasks();
2508 2509 2510 2511 2512 2513 2514
		}
	}

	private runConfigureDefaultTestTask(): void {
		if (!this.canRunCommand()) {
			return;
		}
D
Dirk Baeumer 已提交
2515
		if (this.schemaVersion === JsonSchemaVersion.V2_0_0) {
2516 2517
			this.tasks().then((tasks => {
				if (tasks.length === 0) {
2518
					this.runConfigureTasks();
2519
					return;
2520
				}
2521
				let selectedTask: Task | undefined;
2522 2523
				let selectedEntry: TaskQuickPickEntry;

2524
				for (let task of tasks) {
A
Alex Ross 已提交
2525
					if (task.configurationProperties.group === TaskGroup.Test && task.configurationProperties.groupType === GroupType.default) {
2526
						selectedTask = task;
2527 2528 2529
						break;
					}
				}
2530 2531
				if (selectedTask) {
					selectedEntry = {
A
Alex Ross 已提交
2532
						label: nls.localize('TaskService.defaultTestTaskExists', '{0} is already marked as the default test task.', selectedTask.getQualifiedLabel()),
2533 2534
						task: selectedTask
					};
2535
				}
2536

D
Dirk Baeumer 已提交
2537
				this.showIgnoredFoldersMessage().then(() => {
2538
					this.showQuickPick(tasks,
2539 2540
						nls.localize('TaskService.pickDefaultTestTask', 'Select the task to be used as the default test task'), undefined, true, false, selectedEntry).then((entry) => {
							let task: Task | undefined | null = entry ? entry.task : undefined;
2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554
							if (!task) {
								return;
							}
							if (task === selectedTask && CustomTask.is(task)) {
								this.openConfig(task);
							}
							if (!InMemoryTask.is(task)) {
								this.customize(task, { group: { kind: 'test', isDefault: true } }, true).then(() => {
									if (selectedTask && (task !== selectedTask) && !InMemoryTask.is(selectedTask)) {
										this.customize(selectedTask, { group: 'test' }, true);
									}
								});
							}
						});
2555 2556 2557
				});
			}));
		} else {
2558
			this.runConfigureTasks();
2559 2560
		}
	}
2561

2562
	public async runShowTasks(): Promise<void> {
2563 2564 2565
		if (!this.canRunCommand()) {
			return;
		}
2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584
		const activeTasks: Task[] = await this.getActiveTasks();
		if (activeTasks.length === 1) {
			this._taskSystem!.revealTask(activeTasks[0]);
		} else {
			this.showQuickPick(this.getActiveTasks(),
				nls.localize('TaskService.pickShowTask', 'Select the task to show its output'),
				{
					label: nls.localize('TaskService.noTaskIsRunning', 'No task is running'),
					task: null
				},
				false, true
			).then((entry) => {
				let task: Task | undefined | null = entry ? entry.task : undefined;
				if (task === undefined || task === null) {
					return;
				}
				this._taskSystem!.revealTask(task);
			});
		}
2585
	}
2586
}