Skip to content

Commit

Permalink
Make changes to tasks to allow tasks from User Settings and workspace…
Browse files Browse the repository at this point in the history
… file (#81469)

Still need to make configuration changes.
Part of #1435
  • Loading branch information
alexr00 authored Sep 26, 2019
1 parent 3f38682 commit 328869d
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 46 deletions.
128 changes: 119 additions & 9 deletions src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import Constants from 'vs/workbench/contrib/markers/browser/constants';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace';

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

private updateSetup(setup?: [IWorkspaceFolder[], IWorkspaceFolder[], ExecutionEngine, JsonSchemaVersion]): void {
private updateSetup(setup?: [IWorkspaceFolder[], IWorkspaceFolder[], ExecutionEngine, JsonSchemaVersion, IWorkspace | undefined]): void {
if (!setup) {
setup = this.computeWorkspaceFolderSetup();
}
Expand All @@ -447,6 +448,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
this._ignoredWorkspaceFolders = setup[1];
this._executionEngine = setup[2];
this._schemaVersion = setup[3];
this._workspace = setup[4];
}

protected showOutput(runSource: TaskRunSource = TaskRunSource.User): void {
Expand Down Expand Up @@ -1402,13 +1404,21 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
for (let folder of this.workspaceFolders) {
promises.push(this.computeWorkspaceFolderTasks(folder, runSource).then((value) => value, () => undefined));
}
return Promise.all(promises).then((values) => {
return Promise.all(promises).then(async (values) => {
let result = new Map<string, WorkspaceFolderTaskResult>();
for (let value of values) {
if (value) {
result.set(value.workspaceFolder.uri.toString(), value);
}
}
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);
}
return result;
});
}
Expand All @@ -1429,7 +1439,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
return ProblemMatcherRegistry.onReady().then(async (): Promise<WorkspaceFolderTaskResult> => {
let taskSystemInfo: TaskSystemInfo | undefined = this._taskSystemInfos.get(workspaceFolder.uri.scheme);
let problemReporter = new ProblemReporter(this._outputChannel);
let parseResult = TaskConfig.parse(workspaceFolder, taskSystemInfo ? taskSystemInfo.platform : Platform.platform, workspaceFolderConfiguration.config!, problemReporter);
let parseResult = TaskConfig.parse(workspaceFolder, this._workspace, taskSystemInfo ? taskSystemInfo.platform : Platform.platform, workspaceFolderConfiguration.config!, problemReporter, TaskConfig.TaskConfigSource.TasksJson);
let hasErrors = false;
if (!parseResult.validationStatus.isOK()) {
hasErrors = true;
Expand All @@ -1456,25 +1466,121 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
});
}

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;
}

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

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

private computeWorkspaceFolderSetup(): [IWorkspaceFolder[], IWorkspaceFolder[], ExecutionEngine, JsonSchemaVersion] {
private computeWorkspaceFolderSetup(): [IWorkspaceFolder[], IWorkspaceFolder[], ExecutionEngine, JsonSchemaVersion, IWorkspace | undefined] {
let workspaceFolders: IWorkspaceFolder[] = [];
let ignoredWorkspaceFolders: IWorkspaceFolder[] = [];
let executionEngine = ExecutionEngine.Terminal;
let schemaVersion = JsonSchemaVersion.V2_0_0;

let workspace: IWorkspace | undefined;
if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) {
let workspaceFolder: IWorkspaceFolder = this.contextService.getWorkspace().folders[0];
workspaceFolders.push(workspaceFolder);
executionEngine = this.computeExecutionEngine(workspaceFolder);
schemaVersion = this.computeJsonSchemaVersion(workspaceFolder);
} else if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
workspace = this.contextService.getWorkspace();
for (let workspaceFolder of this.contextService.getWorkspace().folders) {
if (schemaVersion === this.computeJsonSchemaVersion(workspaceFolder)) {
workspaceFolders.push(workspaceFolder);
Expand All @@ -1487,7 +1593,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
}
}
}
return [workspaceFolders, ignoredWorkspaceFolders, executionEngine, schemaVersion];
return [workspaceFolders, ignoredWorkspaceFolders, executionEngine, schemaVersion, workspace];
}

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

protected getConfiguration(workspaceFolder: IWorkspaceFolder): { config: TaskConfig.ExternalTaskRunnerConfiguration | undefined; hasParseErrors: boolean } {
let result = this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY
? Objects.deepClone(this.configurationService.getValue<TaskConfig.ExternalTaskRunnerConfiguration>('tasks', { resource: workspaceFolder.uri }))
? Objects.deepClone(this.configurationService.inspect<TaskConfig.ExternalTaskRunnerConfiguration>('tasks', { resource: workspaceFolder.uri }).workspaceFolder)
: undefined;
if (!result) {
return { config: undefined, hasParseErrors: false };
Expand Down Expand Up @@ -1660,7 +1766,11 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
}
const TaskQuickPickEntry = (task: Task): TaskQuickPickEntry => {
let description: string | undefined;
if (this.needsFolderQualification()) {
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()) {
let workspaceFolder = task.getWorkspaceFolder();
if (workspaceFolder) {
description = workspaceFolder.name;
Expand Down
8 changes: 4 additions & 4 deletions src/vs/workbench/contrib/tasks/browser/task.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,9 +255,8 @@ const actionBarRegistry = Registry.as<IActionBarRegistry>(ActionBarExtensions.Ac
actionBarRegistry.registerActionBarContributor(Scope.VIEWER, QuickOpenActionContributor);

// tasks.json validation
let schemaId = 'vscode://schemas/tasks';
let schema: IJSONSchema = {
id: schemaId,
id: tasksSchemaId,
description: 'Task definition file',
type: 'object',
allowTrailingCommas: true,
Expand All @@ -283,16 +282,17 @@ let schema: IJSONSchema = {
import schemaVersion1 from '../common/jsonSchema_v1';
import schemaVersion2, { updateProblemMatchers } from '../common/jsonSchema_v2';
import { AbstractTaskService, ConfigureTaskAction } from 'vs/workbench/contrib/tasks/browser/abstractTaskService';
import { tasksSchemaId } from 'vs/workbench/services/configuration/common/configuration';
schema.definitions = {
...schemaVersion1.definitions,
...schemaVersion2.definitions,
};
schema.oneOf = [...(schemaVersion2.oneOf || []), ...(schemaVersion1.oneOf || [])];

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

ProblemMatcherRegistry.onMatcherChanged(() => {
updateProblemMatchers();
jsonRegistry.notifySchemaChanged(schemaId);
jsonRegistry.notifySchemaChanged(tasksSchemaId);
});
Loading

0 comments on commit 328869d

Please sign in to comment.