Skip to content

Commit 328869d

Browse files
authored
Make changes to tasks to allow tasks from User Settings and workspace file (microsoft#81469)
Still need to make configuration changes. Part of microsoft#1435
1 parent 3f38682 commit 328869d

6 files changed

Lines changed: 231 additions & 46 deletions

File tree

src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts

Lines changed: 119 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ import Constants from 'vs/workbench/contrib/markers/browser/constants';
4444
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
4545
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
4646
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
47-
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
47+
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace';
4848

4949
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
5050
import { IOutputService, IOutputChannel } from 'vs/workbench/contrib/output/common/output';
@@ -178,6 +178,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
178178
private _schemaVersion: JsonSchemaVersion | undefined;
179179
private _executionEngine: ExecutionEngine | undefined;
180180
private _workspaceFolders: IWorkspaceFolder[] | undefined;
181+
private _workspace: IWorkspace | undefined;
181182
private _ignoredWorkspaceFolders: IWorkspaceFolder[] | undefined;
182183
private _showIgnoreMessage?: boolean;
183184
private _providers: Map<number, ITaskProvider>;
@@ -425,7 +426,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
425426
return this._showIgnoreMessage;
426427
}
427428

428-
private updateSetup(setup?: [IWorkspaceFolder[], IWorkspaceFolder[], ExecutionEngine, JsonSchemaVersion]): void {
429+
private updateSetup(setup?: [IWorkspaceFolder[], IWorkspaceFolder[], ExecutionEngine, JsonSchemaVersion, IWorkspace | undefined]): void {
429430
if (!setup) {
430431
setup = this.computeWorkspaceFolderSetup();
431432
}
@@ -447,6 +448,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
447448
this._ignoredWorkspaceFolders = setup[1];
448449
this._executionEngine = setup[2];
449450
this._schemaVersion = setup[3];
451+
this._workspace = setup[4];
450452
}
451453

452454
protected showOutput(runSource: TaskRunSource = TaskRunSource.User): void {
@@ -1402,13 +1404,21 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
14021404
for (let folder of this.workspaceFolders) {
14031405
promises.push(this.computeWorkspaceFolderTasks(folder, runSource).then((value) => value, () => undefined));
14041406
}
1405-
return Promise.all(promises).then((values) => {
1407+
return Promise.all(promises).then(async (values) => {
14061408
let result = new Map<string, WorkspaceFolderTaskResult>();
14071409
for (let value of values) {
14081410
if (value) {
14091411
result.set(value.workspaceFolder.uri.toString(), value);
14101412
}
14111413
}
1414+
const userTasks = await this.computeUserTasks(this.workspaceFolders[0], runSource).then((value) => value, () => undefined);
1415+
if (userTasks) {
1416+
result.set('settings', userTasks);
1417+
}
1418+
const workspaceFileTasks = await this.computeWorkspaceFileTasks(this.workspaceFolders[0], runSource).then((value) => value, () => undefined);
1419+
if (workspaceFileTasks && this._workspace && this._workspace.configuration) {
1420+
result.set(this._workspace.configuration.toString(), workspaceFileTasks);
1421+
}
14121422
return result;
14131423
});
14141424
}
@@ -1429,7 +1439,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
14291439
return ProblemMatcherRegistry.onReady().then(async (): Promise<WorkspaceFolderTaskResult> => {
14301440
let taskSystemInfo: TaskSystemInfo | undefined = this._taskSystemInfos.get(workspaceFolder.uri.scheme);
14311441
let problemReporter = new ProblemReporter(this._outputChannel);
1432-
let parseResult = TaskConfig.parse(workspaceFolder, taskSystemInfo ? taskSystemInfo.platform : Platform.platform, workspaceFolderConfiguration.config!, problemReporter);
1442+
let parseResult = TaskConfig.parse(workspaceFolder, this._workspace, taskSystemInfo ? taskSystemInfo.platform : Platform.platform, workspaceFolderConfiguration.config!, problemReporter, TaskConfig.TaskConfigSource.TasksJson);
14331443
let hasErrors = false;
14341444
if (!parseResult.validationStatus.isOK()) {
14351445
hasErrors = true;
@@ -1456,25 +1466,121 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
14561466
});
14571467
}
14581468

1469+
private testParseExternalConfig(config: TaskConfig.ExternalTaskRunnerConfiguration | undefined, location: string): { config: TaskConfig.ExternalTaskRunnerConfiguration | undefined, hasParseErrors: boolean } {
1470+
if (!config) {
1471+
return { config: undefined, hasParseErrors: false };
1472+
}
1473+
let parseErrors: string[] = (config as any).$parseErrors;
1474+
if (parseErrors) {
1475+
let isAffected = false;
1476+
for (const parseError of parseErrors) {
1477+
if (/tasks\.json$/.test(parseError)) {
1478+
isAffected = true;
1479+
break;
1480+
}
1481+
}
1482+
if (isAffected) {
1483+
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));
1484+
this.showOutput();
1485+
return { config, hasParseErrors: true };
1486+
}
1487+
}
1488+
return { config, hasParseErrors: false };
1489+
}
1490+
1491+
private async computeWorkspaceFileTasks(workspaceFolder: IWorkspaceFolder, runSource: TaskRunSource = TaskRunSource.User): Promise<WorkspaceFolderTaskResult> {
1492+
if (this.executionEngine === ExecutionEngine.Process) {
1493+
return this.emptyWorkspaceTaskResults(workspaceFolder);
1494+
}
1495+
const configuration = this.testParseExternalConfig(this.configurationService.inspect<TaskConfig.ExternalTaskRunnerConfiguration>('tasks').workspace, nls.localize('TasksSystem.locationWorkspaceConfig', 'workspace file'));
1496+
let customizedTasks: { byIdentifier: IStringDictionary<ConfiguringTask>; } = {
1497+
byIdentifier: Object.create(null)
1498+
};
1499+
1500+
const custom: CustomTask[] = [];
1501+
await this.computeTasksForSingleConfig(workspaceFolder, configuration.config, runSource, custom, customizedTasks.byIdentifier, TaskConfig.TaskConfigSource.WorkspaceFile);
1502+
const engine = configuration.config ? TaskConfig.ExecutionEngine.from(configuration.config) : ExecutionEngine.Terminal;
1503+
if (engine === ExecutionEngine.Process) {
1504+
this.notificationService.warn(nls.localize('TaskSystem.versionWorkspaceFile', 'Only tasks version 2.0.0 permitted in .codeworkspace.'));
1505+
return this.emptyWorkspaceTaskResults(workspaceFolder);
1506+
}
1507+
return { workspaceFolder, set: { tasks: custom }, configurations: customizedTasks, hasErrors: configuration.hasParseErrors };
1508+
}
1509+
1510+
private async computeUserTasks(workspaceFolder: IWorkspaceFolder, runSource: TaskRunSource = TaskRunSource.User): Promise<WorkspaceFolderTaskResult> {
1511+
if (this.executionEngine === ExecutionEngine.Process) {
1512+
return this.emptyWorkspaceTaskResults(workspaceFolder);
1513+
}
1514+
const configuration = this.testParseExternalConfig(this.configurationService.inspect<TaskConfig.ExternalTaskRunnerConfiguration>('tasks').user, nls.localize('TasksSystem.locationUserConfig', 'user settings'));
1515+
let customizedTasks: { byIdentifier: IStringDictionary<ConfiguringTask>; } = {
1516+
byIdentifier: Object.create(null)
1517+
};
1518+
1519+
const custom: CustomTask[] = [];
1520+
await this.computeTasksForSingleConfig(workspaceFolder, configuration.config, runSource, custom, customizedTasks.byIdentifier, TaskConfig.TaskConfigSource.User);
1521+
const engine = configuration.config ? TaskConfig.ExecutionEngine.from(configuration.config) : ExecutionEngine.Terminal;
1522+
if (engine === ExecutionEngine.Process) {
1523+
this.notificationService.warn(nls.localize('TaskSystem.versionSettings', 'Only tasks version 2.0.0 permitted in user settings.'));
1524+
return this.emptyWorkspaceTaskResults(workspaceFolder);
1525+
}
1526+
return { workspaceFolder, set: { tasks: custom }, configurations: customizedTasks, hasErrors: configuration.hasParseErrors };
1527+
}
1528+
1529+
private emptyWorkspaceTaskResults(workspaceFolder: IWorkspaceFolder): WorkspaceFolderTaskResult {
1530+
return { workspaceFolder, set: undefined, configurations: undefined, hasErrors: false };
1531+
}
1532+
1533+
private async computeTasksForSingleConfig(workspaceFolder: IWorkspaceFolder, config: TaskConfig.ExternalTaskRunnerConfiguration | undefined, runSource: TaskRunSource, custom: CustomTask[], customized: IStringDictionary<ConfiguringTask>, source: TaskConfig.TaskConfigSource): Promise<boolean> {
1534+
if (!config) {
1535+
return false;
1536+
}
1537+
let taskSystemInfo: TaskSystemInfo | undefined = workspaceFolder ? this._taskSystemInfos.get(workspaceFolder.uri.scheme) : undefined;
1538+
let problemReporter = new ProblemReporter(this._outputChannel);
1539+
let parseResult = TaskConfig.parse(workspaceFolder, this._workspace, taskSystemInfo ? taskSystemInfo.platform : Platform.platform, config, problemReporter, source);
1540+
let hasErrors = false;
1541+
if (!parseResult.validationStatus.isOK()) {
1542+
this.showOutput(runSource);
1543+
hasErrors = true;
1544+
}
1545+
if (problemReporter.status.isFatal()) {
1546+
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.'));
1547+
return hasErrors;
1548+
}
1549+
if (parseResult.configured && parseResult.configured.length > 0) {
1550+
for (let task of parseResult.configured) {
1551+
customized[task.configures._key] = task;
1552+
}
1553+
}
1554+
if (!(await this._areJsonTasksSupportedPromise) && (parseResult.custom.length > 0)) {
1555+
console.warn('Custom workspace tasks are not supported.');
1556+
} else {
1557+
for (let task of parseResult.custom) {
1558+
custom.push(task);
1559+
}
1560+
}
1561+
return hasErrors;
1562+
}
1563+
14591564
private computeConfiguration(workspaceFolder: IWorkspaceFolder): Promise<WorkspaceFolderConfigurationResult> {
14601565
let { config, hasParseErrors } = this.getConfiguration(workspaceFolder);
14611566
return Promise.resolve<WorkspaceFolderConfigurationResult>({ workspaceFolder, config, hasErrors: hasParseErrors });
14621567
}
14631568

14641569
protected abstract computeLegacyConfiguration(workspaceFolder: IWorkspaceFolder): Promise<WorkspaceFolderConfigurationResult>;
14651570

1466-
private computeWorkspaceFolderSetup(): [IWorkspaceFolder[], IWorkspaceFolder[], ExecutionEngine, JsonSchemaVersion] {
1571+
private computeWorkspaceFolderSetup(): [IWorkspaceFolder[], IWorkspaceFolder[], ExecutionEngine, JsonSchemaVersion, IWorkspace | undefined] {
14671572
let workspaceFolders: IWorkspaceFolder[] = [];
14681573
let ignoredWorkspaceFolders: IWorkspaceFolder[] = [];
14691574
let executionEngine = ExecutionEngine.Terminal;
14701575
let schemaVersion = JsonSchemaVersion.V2_0_0;
1471-
1576+
let workspace: IWorkspace | undefined;
14721577
if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) {
14731578
let workspaceFolder: IWorkspaceFolder = this.contextService.getWorkspace().folders[0];
14741579
workspaceFolders.push(workspaceFolder);
14751580
executionEngine = this.computeExecutionEngine(workspaceFolder);
14761581
schemaVersion = this.computeJsonSchemaVersion(workspaceFolder);
14771582
} else if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
1583+
workspace = this.contextService.getWorkspace();
14781584
for (let workspaceFolder of this.contextService.getWorkspace().folders) {
14791585
if (schemaVersion === this.computeJsonSchemaVersion(workspaceFolder)) {
14801586
workspaceFolders.push(workspaceFolder);
@@ -1487,7 +1593,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
14871593
}
14881594
}
14891595
}
1490-
return [workspaceFolders, ignoredWorkspaceFolders, executionEngine, schemaVersion];
1596+
return [workspaceFolders, ignoredWorkspaceFolders, executionEngine, schemaVersion, workspace];
14911597
}
14921598

14931599
private computeExecutionEngine(workspaceFolder: IWorkspaceFolder): ExecutionEngine {
@@ -1508,7 +1614,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
15081614

15091615
protected getConfiguration(workspaceFolder: IWorkspaceFolder): { config: TaskConfig.ExternalTaskRunnerConfiguration | undefined; hasParseErrors: boolean } {
15101616
let result = this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY
1511-
? Objects.deepClone(this.configurationService.getValue<TaskConfig.ExternalTaskRunnerConfiguration>('tasks', { resource: workspaceFolder.uri }))
1617+
? Objects.deepClone(this.configurationService.inspect<TaskConfig.ExternalTaskRunnerConfiguration>('tasks', { resource: workspaceFolder.uri }).workspaceFolder)
15121618
: undefined;
15131619
if (!result) {
15141620
return { config: undefined, hasParseErrors: false };
@@ -1660,7 +1766,11 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
16601766
}
16611767
const TaskQuickPickEntry = (task: Task): TaskQuickPickEntry => {
16621768
let description: string | undefined;
1663-
if (this.needsFolderQualification()) {
1769+
if (task._source.kind === TaskSourceKind.User) {
1770+
description = nls.localize('taskQuickPick.userSettings', 'User Settings');
1771+
} else if (task._source.kind === TaskSourceKind.WorkspaceFile) {
1772+
description = task.getWorkspaceFileName();
1773+
} else if (this.needsFolderQualification()) {
16641774
let workspaceFolder = task.getWorkspaceFolder();
16651775
if (workspaceFolder) {
16661776
description = workspaceFolder.name;

src/vs/workbench/contrib/tasks/browser/task.contribution.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -255,9 +255,8 @@ const actionBarRegistry = Registry.as<IActionBarRegistry>(ActionBarExtensions.Ac
255255
actionBarRegistry.registerActionBarContributor(Scope.VIEWER, QuickOpenActionContributor);
256256

257257
// tasks.json validation
258-
let schemaId = 'vscode://schemas/tasks';
259258
let schema: IJSONSchema = {
260-
id: schemaId,
259+
id: tasksSchemaId,
261260
description: 'Task definition file',
262261
type: 'object',
263262
allowTrailingCommas: true,
@@ -283,16 +282,17 @@ let schema: IJSONSchema = {
283282
import schemaVersion1 from '../common/jsonSchema_v1';
284283
import schemaVersion2, { updateProblemMatchers } from '../common/jsonSchema_v2';
285284
import { AbstractTaskService, ConfigureTaskAction } from 'vs/workbench/contrib/tasks/browser/abstractTaskService';
285+
import { tasksSchemaId } from 'vs/workbench/services/configuration/common/configuration';
286286
schema.definitions = {
287287
...schemaVersion1.definitions,
288288
...schemaVersion2.definitions,
289289
};
290290
schema.oneOf = [...(schemaVersion2.oneOf || []), ...(schemaVersion1.oneOf || [])];
291291

292292
let jsonRegistry = <jsonContributionRegistry.IJSONContributionRegistry>Registry.as(jsonContributionRegistry.Extensions.JSONContribution);
293-
jsonRegistry.registerSchema(schemaId, schema);
293+
jsonRegistry.registerSchema(tasksSchemaId, schema);
294294

295295
ProblemMatcherRegistry.onMatcherChanged(() => {
296296
updateProblemMatchers();
297-
jsonRegistry.notifySchemaChanged(schemaId);
297+
jsonRegistry.notifySchemaChanged(tasksSchemaId);
298298
});

0 commit comments

Comments
 (0)