From d858f08c5576da4b8787c70084bf8c2c4e6f16ae Mon Sep 17 00:00:00 2001 From: Liang Huang Date: Mon, 4 Nov 2019 21:08:58 -0500 Subject: [PATCH] update task schema for Theia extensions & plugins - With this change, Theia processes task definitions contributed by extensions and plugins, and updates the task schema. - resolves #6485 Signed-off-by: Liang Huang --- packages/cpp/src/browser/cpp-task-provider.ts | 11 +- .../src/hosted/node/scanners/scanner-theia.ts | 3 +- .../browser/task-definition-registry.spec.ts | 21 ++- .../task/src/browser/task-schema-updater.ts | 175 ++++++++++-------- packages/task/src/common/task-protocol.ts | 2 + 5 files changed, 132 insertions(+), 80 deletions(-) diff --git a/packages/cpp/src/browser/cpp-task-provider.ts b/packages/cpp/src/browser/cpp-task-provider.ts index 094bea434b198..c983365734cdb 100644 --- a/packages/cpp/src/browser/cpp-task-provider.ts +++ b/packages/cpp/src/browser/cpp-task-provider.ts @@ -174,7 +174,16 @@ export class CppTaskProvider implements TaskContribution, TaskProvider, TaskReso source: 'cpp', properties: { required: ['label'], - all: ['label'] + all: ['label'], + schema: { + type: CPP_BUILD_TASK_TYPE_KEY, + required: ['label'], + properties: { + label: { + type: 'string' + } + } + } } }); } diff --git a/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts b/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts index 034a896963745..6c906383d04e5 100644 --- a/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts +++ b/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts @@ -450,7 +450,8 @@ export class TheiaPluginScanner implements PluginScanner { source: pluginName, properties: { required: definitionContribution.required, - all: propertyKeys + all: propertyKeys, + schema: definitionContribution } }; } diff --git a/packages/task/src/browser/task-definition-registry.spec.ts b/packages/task/src/browser/task-definition-registry.spec.ts index de00d2021e3eb..06248e5e4ff06 100644 --- a/packages/task/src/browser/task-definition-registry.spec.ts +++ b/packages/task/src/browser/task-definition-registry.spec.ts @@ -26,7 +26,15 @@ describe('TaskDefinitionRegistry', () => { required: ['extensionType'], properties: { required: ['extensionType'], - all: ['extensionType', 'taskLabel'] + all: ['extensionType', 'taskLabel'], + schema: { + type: 'extA', + required: ['extensionType'], + properties: { + extensionType: {}, + taskLabel: {} + } + } } }; const definitonContributionB = { @@ -34,7 +42,16 @@ describe('TaskDefinitionRegistry', () => { source: 'extA', properties: { required: ['extensionType', 'taskLabel', 'taskDetailedLabel'], - all: ['extensionType', 'taskLabel', 'taskDetailedLabel'] + all: ['extensionType', 'taskLabel', 'taskDetailedLabel'], + schema: { + type: 'extA', + required: ['extensionType', 'taskLabel', 'taskDetailedLabel'], + properties: { + extensionType: {}, + taskLabel: {}, + taskDetailedLabel: {} + } + } } }; diff --git a/packages/task/src/browser/task-schema-updater.ts b/packages/task/src/browser/task-schema-updater.ts index 8600b7b2a3ca2..b65316f1200a8 100644 --- a/packages/task/src/browser/task-schema-updater.ts +++ b/packages/task/src/browser/task-schema-updater.ts @@ -75,9 +75,41 @@ export class TaskSchemaUpdater { return Array.from(allTypes.values()).sort(); } + private updateSchemasForRegisteredTasks(): void { + customizedDetectedTasks.length = 0; + const definitions = this.taskDefinitionRegistry.getAll(); + definitions.forEach(def => { + const customizedDetectedTask = { + type: 'object', + required: ['type'], + properties: {} + } as IJSONSchema; + const taskType = { + ...defaultTaskType, + enum: [def.taskType], + default: def.taskType, + description: 'The task type to customize' + }; + customizedDetectedTask.properties!.type = taskType; + def.properties.all.forEach(taskProp => { + if (!!def.properties.required.find(requiredProp => requiredProp === taskProp)) { // property is mandatory + customizedDetectedTask.required!.push(taskProp); + } + customizedDetectedTask.properties![taskProp] = { ...def.properties.schema.properties![taskProp] }; + }); + customizedDetectedTask.properties!.problemMatcher = problemMatcher; + customizedDetectedTask.properties!.options = commandOptionsSchema; + customizedDetectedTasks.push(customizedDetectedTask); + }); + + taskConfigurationSchema.oneOf!.length = 1; + taskConfigurationSchema.oneOf!.push(...customizedDetectedTasks); + } + /** Returns the task's JSON schema */ getTaskSchema(): IJSONSchema { return { + type: 'object', properties: { tasks: { type: 'array', @@ -103,11 +135,8 @@ export class TaskSchemaUpdater { this.update(); } - /** Gets the most up-to-date names of task types Theia supports from the registry and update the task schema */ private async updateSupportedTaskTypes(): Promise { - const allTypes = await this.getRegisteredTaskTypes(); - supportedTaskTypes.length = 0; - supportedTaskTypes.push(...allTypes); + this.updateSchemasForRegisteredTasks(); this.update(); } } @@ -160,80 +189,74 @@ const commandOptionsSchema: IJSONSchema = { }; const problemMatcherNames: string[] = []; -const supportedTaskTypes = ['shell', 'process']; // default types that Theia supports -const taskConfigurationSchema: IJSONSchema = { - $id: taskSchemaId, +const defaultTaskTypes = ['shell', 'process']; +const supportedTaskTypes = [...defaultTaskTypes]; +const taskLabel = { + type: 'string', + description: 'A unique string that identifies the task that is also used as task\'s user interface label' +}; +const defaultTaskType = { + type: 'string', + enum: supportedTaskTypes, + default: defaultTaskTypes[0], + description: 'Determines what type of process will be used to execute the task. Only shell types will have output shown on the user interface' +}; +const commandAndArgs = { + command: commandSchema, + args: commandArgSchema, + options: commandOptionsSchema +}; +const problemMatcher = { oneOf: [ { - allOf: [ - { - type: 'object', - required: ['type'], - properties: { - label: { - type: 'string', - description: 'A unique string that identifies the task that is also used as task\'s user interface label' - }, - type: { - type: 'string', - enum: supportedTaskTypes, - default: 'shell', - description: 'Determines what type of process will be used to execute the task. Only shell types will have output shown on the user interface' - }, - command: commandSchema, - args: commandArgSchema, - options: commandOptionsSchema, - windows: { - type: 'object', - description: 'Windows specific command configuration that overrides the command, args, and options', - properties: { - command: commandSchema, - args: commandArgSchema, - options: commandOptionsSchema - } - }, - osx: { - type: 'object', - description: 'MacOS specific command configuration that overrides the command, args, and options', - properties: { - command: commandSchema, - args: commandArgSchema, - options: commandOptionsSchema - } - }, - linux: { - type: 'object', - description: 'Linux specific command configuration that overrides the default command, args, and options', - properties: { - command: commandSchema, - args: commandArgSchema, - options: commandOptionsSchema - } - }, - problemMatcher: { - oneOf: [ - { - type: 'string', - description: 'Name of the problem matcher to parse the output of the task', - enum: problemMatcherNames - }, - { - type: 'object', - description: 'User defined problem matcher(s) to parse the output of the task', - }, - { - type: 'array', - description: 'Name(s) of the problem matcher(s) to parse the output of the task', - items: { - type: 'string', - enum: problemMatcherNames - } - } - ] - } - } - } - ] + type: 'string', + description: 'Name of the problem matcher to parse the output of the task', + enum: problemMatcherNames + }, + { + type: 'object', + description: 'User defined problem matcher(s) to parse the output of the task', + }, + { + type: 'array', + description: 'Name(s) of the problem matcher(s) to parse the output of the task', + items: { + type: 'string', + enum: problemMatcherNames + } } ] }; + +const processTaskConfigurationSchema: IJSONSchema = { + type: 'object', + required: ['type', 'label', 'command'], + properties: { + label: taskLabel, + type: defaultTaskType, + ...commandAndArgs, + windows: { + type: 'object', + description: 'Windows specific command configuration that overrides the command, args, and options', + properties: commandAndArgs + }, + osx: { + type: 'object', + description: 'MacOS specific command configuration that overrides the command, args, and options', + properties: commandAndArgs + }, + linux: { + type: 'object', + description: 'Linux specific command configuration that overrides the default command, args, and options', + properties: commandAndArgs + }, + problemMatcher + } +}; + +const customizedDetectedTasks: IJSONSchema[] = []; + +const taskConfigurationSchema: IJSONSchema = { + $id: taskSchemaId, + oneOf: [processTaskConfigurationSchema, ...customizedDetectedTasks] +}; diff --git a/packages/task/src/common/task-protocol.ts b/packages/task/src/common/task-protocol.ts index 0955f1d42a322..ac8d4cd2432e8 100644 --- a/packages/task/src/common/task-protocol.ts +++ b/packages/task/src/common/task-protocol.ts @@ -15,6 +15,7 @@ ********************************************************************************/ import { JsonRpcServer } from '@theia/core/lib/common/messaging/proxy-factory'; +import { IJSONSchema } from '@theia/core/lib/common/json-schema'; import { ProblemMatcher, ProblemMatch, WatchingPattern } from './problem-matcher-protocol'; export const taskPath = '/services/task'; @@ -133,6 +134,7 @@ export interface TaskDefinition { properties: { required: string[]; all: string[]; + schema: IJSONSchema; } }