diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index 651b18ea31f5e..337c8b2e39676 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -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 */ diff --git a/packages/task/src/browser/provided-task-configurations.ts b/packages/task/src/browser/provided-task-configurations.ts index ad625a4287140..419bf0aa6134f 100644 --- a/packages/task/src/browser/provided-task-configurations.ts +++ b/packages/task/src/browser/provided-task-configurations.ts @@ -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 { @@ -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 { @@ -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; } diff --git a/packages/task/src/browser/quick-open-task.ts b/packages/task/src/browser/quick-open-task.ts index 6486205fde951..9296827500d21 100644 --- a/packages/task/src/browser/quick-open-task.ts +++ b/packages/task/src/browser/quick-open-task.ts @@ -234,78 +234,33 @@ export class QuickOpenTask implements QuickOpenModel, QuickOpenHandler { async configure(): Promise { 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) { @@ -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 { diff --git a/packages/task/src/browser/task-configuration-manager.ts b/packages/task/src/browser/task-configuration-manager.ts index 06fd156fb5fbe..0cd79975e0247 100644 --- a/packages/task/src/browser/task-configuration-manager.ts +++ b/packages/task/src/browser/task-configuration-manager.ts @@ -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; } } diff --git a/packages/task/src/browser/task-configurations.ts b/packages/task/src/browser/task-configurations.ts index 10c9efc117a22..19d47ee290f31 100644 --- a/packages/task/src/browser/task-configurations.ts +++ b/packages/task/src/browser/task-configurations.ts @@ -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; diff --git a/packages/task/src/browser/task-definition-registry.ts b/packages/task/src/browser/task-definition-registry.ts index 26e7beb927a8b..69e76fd63891c 100644 --- a/packages/task/src/browser/task-definition-registry.ts +++ b/packages/task/src/browser/task-definition-registry.ts @@ -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; } diff --git a/packages/task/src/common/task-protocol.ts b/packages/task/src/common/task-protocol.ts index 682f0668d5c98..b2a537a33b838 100644 --- a/packages/task/src/common/task-protocol.ts +++ b/packages/task/src/common/task-protocol.ts @@ -149,8 +149,8 @@ export namespace TaskCustomization { } export enum TaskScope { - Workspace, - Global + Global = 1, + Workspace = 2 } export type TaskConfigurationScope = string | TaskScope.Workspace | TaskScope.Global;