From 8a8b052e23e3773b6b077c419fbae92a022a67f9 Mon Sep 17 00:00:00 2001
From: Alvaro Sanchez-Leon <alvaro.sanchez-leon@ericsson.com>
Date: Mon, 1 Feb 2021 12:47:55 -0500
Subject: [PATCH] Support plugin tasks using 'TaskScope.Workspace'

* 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.

Fixes: #7931

[1] https://github.com/eclipse-theia/theia/issues/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 <alvaro.sanchez-leon@ericsson.com>
---
 packages/plugin/src/theia.d.ts                           | 2 +-
 .../task/src/browser/provided-task-configurations.ts     | 9 +++++++--
 packages/task/src/browser/task-configurations.ts         | 8 ++------
 packages/task/src/browser/task-definition-registry.ts    | 4 ++--
 packages/task/src/browser/task-name-resolver.ts          | 4 ++--
 packages/task/src/common/task-protocol.ts                | 4 ++--
 6 files changed, 16 insertions(+), 15 deletions(-)

diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts
index 7919747d9b26f..213b25246af43 100644
--- a/packages/plugin/src/theia.d.ts
+++ b/packages/plugin/src/theia.d.ts
@@ -9336,7 +9336,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..df62dfbdaf02b 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/task-configurations.ts b/packages/task/src/browser/task-configurations.ts
index 925dd8cb7ed41..19d47ee290f31 100644
--- a/packages/task/src/browser/task-configurations.ts
+++ b/packages/task/src/browser/task-configurations.ts
@@ -302,12 +302,8 @@ export class TaskConfigurations implements Disposable {
     /** Adds given task to a config file and opens the file to provide ability to edit task configuration. */
     async configure(token: number, task: TaskConfiguration): Promise<void> {
         const scope = task._scope;
-        if (scope === TaskScope.Global || scope === TaskScope.Workspace) {
-            return this.taskConfigurationManager.openConfiguration(scope);
-        } 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;
+        if (scope === TaskScope.Global) {
+            return this.openUserTasks();
         }
 
         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/browser/task-name-resolver.ts b/packages/task/src/browser/task-name-resolver.ts
index f6ab62683743d..ff628cc567b78 100644
--- a/packages/task/src/browser/task-name-resolver.ts
+++ b/packages/task/src/browser/task-name-resolver.ts
@@ -33,10 +33,10 @@ export class TaskNameResolver {
      */
     resolve(task: TaskConfiguration): string {
         if (this.isDetectedTask(task)) {
-            const scope = task._scope;
+            const scope = task.scope;
             const rawConfigs = this.taskConfigurations.getRawTaskConfigurations(scope);
             const jsonConfig = rawConfigs.find(rawConfig => this.taskDefinitionRegistry.compareTasks({
-                ...rawConfig, _scope: scope
+                ...rawConfig, scope: scope
             }, task));
             // detected task that has a `label` defined in `tasks.json`
             if (jsonConfig && jsonConfig.label) {
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;