Skip to content

Commit

Permalink
Support plugin tasks using 'TaskScope.Workspace'
Browse files Browse the repository at this point in the history
* Aligns the TaskScope enum definition with vscode
see issue [1] and corresponding schemas for Theia [2] and vscode [3]

* Supports processing of task configurations provided by plugins with
the scope set to TaskScope.Workspace.

* Ignores processing of task configurations provided by plugins with
the scope set to TaskScope.Global i.e. User tasks.

[1] eclipse-theia#7931
[2] https://github.com/eclipse-theia/theia/blob/2aa2fa1ab091ec36ef851c4e364b322301cddb40/packages/plugin/src/theia.d.ts#L8637
[3] https://github.com/microsoft/vscode/blob/50f907f0ba9b0c799a7c1d2f28a625bf30041636/src/vs/vscode.d.ts#L5923

Signed-off-by: Alvaro Sanchez-Leon <[email protected]>
  • Loading branch information
alvsan09 committed Feb 9, 2021
1 parent f1d4690 commit e9e23f5
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 72 deletions.
2 changes: 1 addition & 1 deletion packages/plugin/src/theia.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9233,7 +9233,7 @@ declare module '@theia/plugin' {
}

export enum TaskScope {
/** The task is a global task */
/** The task is a global task. Global tasks are currently not supported. */
Global = 1,

/** The task is a workspace task */
Expand Down
9 changes: 7 additions & 2 deletions packages/task/src/browser/provided-task-configurations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import { inject, injectable } from 'inversify';
import { TaskProviderRegistry } from './task-contribution';
import { TaskDefinitionRegistry } from './task-definition-registry';
import { TaskConfiguration, TaskCustomization, TaskOutputPresentation, TaskConfigurationScope } from '../common';
import { TaskConfiguration, TaskCustomization, TaskOutputPresentation, TaskConfigurationScope, TaskScope } from '../common';

@injectable()
export class ProvidedTaskConfigurations {
Expand Down Expand Up @@ -61,6 +61,8 @@ export class ProvidedTaskConfigurations {
const providers = await this.taskProviderRegistry.getProviders();
const providedTasks: TaskConfiguration[] = (await Promise.all(providers.map(p => p.provideTasks())))
.reduce((acc, taskArray) => acc.concat(taskArray), [])
// Global/User tasks from providers are not supported
.filter(task => task.scope !== TaskScope.Global)
.map(providedTask => {
const originalPresentation = providedTask.presentation || {};
return {
Expand Down Expand Up @@ -116,11 +118,14 @@ export class ProvidedTaskConfigurations {
}
}

// Tasks with scope set to 'Workspace' can be customized in a workspace root, and will not match
// providers scope 'TaskScope.Workspace' unless specifically included as below.
const scopes = [scope, TaskScope.Workspace];
// find the task that matches the `customization`.
// The scenario where more than one match is found should not happen unless users manually enter multiple customizations for one type of task
// If this does happen, return the first match
const matchedTask = matchedTasks.filter(t =>
scope === t._scope && definition.properties.all.every(p => t[p] === customization[p])
scopes.some(scp => scp === t._scope) && definition.properties.all.every(p => t[p] === customization[p])
)[0];
return matchedTask;
}
Expand Down
119 changes: 59 additions & 60 deletions packages/task/src/browser/quick-open-task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,78 +234,33 @@ export class QuickOpenTask implements QuickOpenModel, QuickOpenHandler {
async configure(): Promise<void> {
this.items = [];
this.actionProvider = undefined;
const isMulti: boolean = this.workspaceService.isMultiRootWorkspaceOpened;

const token: number = this.taskService.startUserAction();

const configuredTasks = await this.taskService.getConfiguredTasks(token);
const providedTasks = await this.taskService.getProvidedTasks(token);

// check if tasks.json exists. If not, display "Create tasks.json file from template"
// If tasks.json exists and empty, display 'Open tasks.json file'
let isFirstGroup = true;
const isMulti: boolean = this.workspaceService.isMultiRootWorkspaceOpened;

const { filteredConfiguredTasks, filteredProvidedTasks } = this.getFilteredTasks([], configuredTasks, providedTasks);
const groupedTasks = this.getGroupedTasksByWorkspaceFolder([...filteredConfiguredTasks, ...filteredProvidedTasks]);
if (groupedTasks.has(TaskScope.Global.toString())) {
const configs = groupedTasks.get(TaskScope.Global.toString())!;
this.items.push(
...configs.map(taskConfig => {
const item = new TaskConfigureQuickOpenItem(
token,
taskConfig,
this.taskService,
this.taskNameResolver,
this.workspaceService,
isMulti,
{ showBorder: false }
);
item['taskDefinitionRegistry'] = this.taskDefinitionRegistry;
return item;
})
);
isFirstGroup = false;
}

// 1) Add Items by taskScope
Object.values(TaskScope).forEach(value => {
addTaskItemByTaskScope(this, value.toString());
});

// 2) Adding items by provided workspace folder scope
const rootUris = (await this.workspaceService.roots).map(rootStat => rootStat.resource.toString());
for (const rootFolder of rootUris) {
const folderName = new URI(rootFolder).displayName;
if (groupedTasks.has(rootFolder)) {
const configs = groupedTasks.get(rootFolder.toString())!;
this.items.push(
...configs.map((taskConfig, index) => {
const item = new TaskConfigureQuickOpenItem(
token,
taskConfig,
this.taskService,
this.taskNameResolver,
this.workspaceService,
isMulti,
{
groupLabel: index === 0 && isMulti ? folderName : '',
showBorder: !isFirstGroup && index === 0
}
);
item['taskDefinitionRegistry'] = this.taskDefinitionRegistry;
return item;
})
);
} else {
const { configUri } = this.preferences.resolve('tasks', [], rootFolder);
const existTaskConfigFile = !!configUri;
this.items.push(new QuickOpenGroupItem({
label: existTaskConfigFile ? 'Open tasks.json file' : 'Create tasks.json file from template',
run: (mode: QuickOpenMode): boolean => {
if (mode !== QuickOpenMode.OPEN) {
return false;
}
setTimeout(() => this.taskConfigurationManager.openConfiguration(rootFolder));
return true;
},
showBorder: !isFirstGroup,
groupLabel: isMulti ? folderName : ''
}));

if (addTaskItemByTaskScope(this, rootFolder, folderName)) {
continue;
}
isFirstGroup = false;

// check if tasks.json exists. If not, display "Create tasks.json file from template"
// If tasks.json exists and empty, display 'Open tasks.json file'
addTasksJsonItem(this, rootFolder, folderName);
}

if (this.items.length === 0) {
Expand All @@ -320,6 +275,50 @@ export class QuickOpenTask implements QuickOpenModel, QuickOpenHandler {
fuzzyMatchLabel: true,
fuzzySort: false
});

function addTaskItemByTaskScope(quickOpenTask: QuickOpenTask, taskScopeString: string, folderName?: string): boolean {
if (!groupedTasks.has(taskScopeString)) {
return false;
}

const configs = groupedTasks.get(taskScopeString)!;
quickOpenTask.items.push(
...configs.map((taskConfig, index) => {
const item = new TaskConfigureQuickOpenItem(
token,
taskConfig,
quickOpenTask.taskService,
quickOpenTask.taskNameResolver,
quickOpenTask.workspaceService,
isMulti,
{
groupLabel: folderName && isMulti && index === 0 ? folderName : '',
showBorder: quickOpenTask.items.length > 0 && index === 0
}
);
item['taskDefinitionRegistry'] = quickOpenTask.taskDefinitionRegistry;
return item;
})
);
return true;
}

function addTasksJsonItem(quickOpenTask: QuickOpenTask, rootFolder: string, folderName: string): void {
const { configUri } = quickOpenTask.preferences.resolve('tasks', [], rootFolder);
const existTaskConfigFile = !!configUri;
quickOpenTask.items.push(new QuickOpenGroupItem({
label: existTaskConfigFile ? 'Open tasks.json file' : 'Create tasks.json file from template',
run: (mode: QuickOpenMode): boolean => {
if (mode !== QuickOpenMode.OPEN) {
return false;
}
setTimeout(() => quickOpenTask.taskConfigurationManager.openConfiguration(rootFolder));
return true;
},
showBorder: quickOpenTask.items.length !== 0,
groupLabel: isMulti ? folderName : ''
}));
}
}

async runBuildOrTestTask(buildOrTestType: 'build' | 'test'): Promise<void> {
Expand Down
5 changes: 4 additions & 1 deletion packages/task/src/browser/task-configuration-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,10 @@ export class TaskConfigurationManager {
return undefined;
}
for (const model of this.models.values()) {
if (model.scope === scope) {
// Provided task configurations at the workspace level can be applied to any workspace root.
// Ideally, they should apply to the Workspace file (if available i.e. multi root projects),
// however this follows the current behavior in vscode.
if (model.scope === scope || scope === TaskScope.Workspace) {
return model;
}
}
Expand Down
4 changes: 0 additions & 4 deletions packages/task/src/browser/task-configurations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,10 +304,6 @@ export class TaskConfigurations implements Disposable {
const scope = task._scope;
if (scope === TaskScope.Global) {
return this.openUserTasks();
} else if (typeof scope !== 'string') {
console.error('Global task cannot be customized');
// TODO detected tasks of scope workspace or user could be customized in those preferences.
return;
}

const workspace = this.workspaceService.workspace;
Expand Down
4 changes: 2 additions & 2 deletions packages/task/src/browser/task-definition-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ export class TaskDefinitionRegistry {
}
const def = this.getDefinition(one);
if (def) {
// scope is either a string or an enum value. Anyway...the must exactly match
return def.properties.all.every(p => p === 'type' || one[p] === other[p]) && one._scope === other._scope;
// scope is either a string or an enum value. Anyway...they must exactly match
return def.properties.all.every(p => p === 'type' || one[p] === other[p]) && one.scope === other.scope;
}
return one.label === other.label && one._source === other._source;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/task/src/common/task-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ export namespace TaskCustomization {
}

export enum TaskScope {
Workspace,
Global
Global = 1,
Workspace = 2
}

export type TaskConfigurationScope = string | TaskScope.Workspace | TaskScope.Global;
Expand Down

0 comments on commit e9e23f5

Please sign in to comment.