From b502bb614ba32cd3f09b944862aba883d03662d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Tue, 20 Apr 2021 11:52:53 +0200 Subject: [PATCH] Clean up handling of 'taskType' field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Mäder --- .../plugin-ext/src/common/plugin-api-rpc.ts | 2 + .../plugin-ext/src/main/browser/tasks-main.ts | 2 +- packages/plugin-ext/src/plugin/tasks/tasks.ts | 75 ++++++--------- .../src/plugin/type-converters.spec.ts | 2 + .../plugin-ext/src/plugin/type-converters.ts | 15 +-- packages/plugin-ext/src/plugin/types-impl.ts | 74 +------------- .../process/process-task-contribution.ts | 4 +- .../task/src/browser/task-configurations.ts | 11 +-- .../task/src/browser/task-contribution.ts | 77 +++++++++++++-- .../src/browser/task-definition-registry.ts | 6 +- packages/task/src/browser/task-service.ts | 96 ++++++------------- .../src/node/process/process-task-runner.ts | 2 +- 12 files changed, 155 insertions(+), 211 deletions(-) diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index 8bd3bb5904b35..7cb367bc37329 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -1354,6 +1354,8 @@ export interface CommandProperties { export interface TaskDto { type: string; + taskType?: 'shell' | 'process' | 'customExecution'; // the task execution type + executionId?: string, label: string; source?: string; scope: string | number; diff --git a/packages/plugin-ext/src/main/browser/tasks-main.ts b/packages/plugin-ext/src/main/browser/tasks-main.ts index 113f2562b5539..7b688a25417fd 100644 --- a/packages/plugin-ext/src/main/browser/tasks-main.ts +++ b/packages/plugin-ext/src/main/browser/tasks-main.ts @@ -109,7 +109,7 @@ export class TasksMainImpl implements TasksMain, Disposable { const toDispose = new DisposableCollection( this.taskProviderRegistry.register(type, taskProvider, handle), - this.taskResolverRegistry.register(type, taskResolver), + this.taskResolverRegistry.registerTaskResolver(type, taskResolver), Disposable.create(() => this.taskProviders.delete(handle)) ); this.taskProviders.set(handle, toDispose); diff --git a/packages/plugin-ext/src/plugin/tasks/tasks.ts b/packages/plugin-ext/src/plugin/tasks/tasks.ts index 98e46bf3334a8..013ec3ab928a7 100644 --- a/packages/plugin-ext/src/plugin/tasks/tasks.ts +++ b/packages/plugin-ext/src/plugin/tasks/tasks.ts @@ -28,16 +28,19 @@ import { RPCProtocol, ConnectionClosedError } from '../../common/rpc-protocol'; import { TaskProviderAdapter } from './task-provider'; import { Emitter, Event } from '@theia/core/lib/common/event'; import { TerminalServiceExtImpl } from '../terminal-ext'; +import { UUID } from '@theia/core/shared/@phosphor/coreutils'; +type ExecutionCallback = (resolvedDefintion: theia.TaskDefinition) => Thenable; export class TasksExtImpl implements TasksExt { private proxy: TasksMain; private callId = 0; private adaptersMap = new Map(); private executions = new Map(); - protected providedCustomExecutions: Map; - protected notProvidedCustomExecutions: Set; - protected activeCustomExecutions: Map; + protected callbackIdBase: string = UUID.uuid4(); + protected callbackId: number; + protected customExecutionIds: Map = new Map(); + protected customExecutionFunctions: Map = new Map(); protected lastStartedTask: number | undefined; private readonly onDidExecuteTask: Emitter = new Emitter(); @@ -49,9 +52,6 @@ export class TasksExtImpl implements TasksExt { constructor(rpc: RPCProtocol, readonly terminalExt: TerminalServiceExtImpl) { this.proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.TASKS_MAIN); - this.providedCustomExecutions = new Map(); - this.notProvidedCustomExecutions = new Set(); - this.activeCustomExecutions = new Map(); this.fetchTaskExecutions(); } @@ -68,16 +68,10 @@ export class TasksExtImpl implements TasksExt { } async $onDidStartTask(execution: TaskExecutionDto, terminalId: number): Promise { - const customExecution: CustomExecution | undefined = this.providedCustomExecutions.get(execution.task.id); + const customExecution = this.customExecutionFunctions.get(execution.task.executionId || ''); if (customExecution) { - if (this.activeCustomExecutions.get(execution.id) !== undefined) { - throw new Error('We should not be trying to start the same custom task executions twice.'); - } - - // Clone the custom execution to keep the original untouched. This is important for multiple runs of the same task. - this.activeCustomExecutions.set(execution.id, customExecution); const taskDefinition = converter.toTask(execution.task).definition; - const pty = await customExecution.callback(taskDefinition); + const pty = await customExecution(taskDefinition); this.terminalExt.attachPtyToTerminal(terminalId, pty); if (pty.onDidClose) { const disposable = pty.onDidClose((e: number | void = undefined) => { @@ -105,7 +99,6 @@ export class TasksExtImpl implements TasksExt { } this.executions.delete(id); - this.customExecutionComplete(id); this.onDidTerminateTask.fire({ execution: taskExecution @@ -159,7 +152,7 @@ export class TasksExtImpl implements TasksExt { // in the provided custom execution map that is cleaned up after the // task is executed. if (CustomExecution.is(task.execution!)) { - this.addCustomExecution(taskDto, false); + taskDto.executionId = this.addCustomExecution(task.execution!.callback); } const executionDto = await this.proxy.$executeTask(taskDto); if (executionDto) { @@ -177,8 +170,9 @@ export class TasksExtImpl implements TasksExt { return adapter.provideTasks(token).then(tasks => { if (tasks) { for (const task of tasks) { - if (task.type === 'customExecution' || task.taskType === 'customExecution') { - this.addCustomExecution(task, true); + if (task.taskType === 'customExecution') { + task.executionId = this.addCustomExecution(task.callback); + task.callback = undefined; } } } @@ -192,7 +186,13 @@ export class TasksExtImpl implements TasksExt { $resolveTask(handle: number, task: TaskDto, token: theia.CancellationToken): Promise { const adapter = this.adaptersMap.get(handle); if (adapter) { - return adapter.resolveTask(task, token); + return adapter.resolveTask(task, token).then(resolvedTask => { + if (resolvedTask && resolvedTask.taskType === 'customExecution') { + resolvedTask.executionId = this.addCustomExecution(resolvedTask.callback); + resolvedTask.callback = undefined; + } + return resolvedTask; + }); } else { return Promise.reject(new Error('No adapter found to resolve task')); } @@ -244,36 +244,17 @@ export class TasksExtImpl implements TasksExt { return result; } - private addCustomExecution(taskDto: TaskDto, isProvided: boolean): void { - const taskId = taskDto.id; - if (!isProvided && !this.providedCustomExecutions.has(taskId)) { - this.notProvidedCustomExecutions.add(taskId); + private addCustomExecution(callback: ExecutionCallback): string { + let id = this.customExecutionIds.get(callback); + if (!id) { + id = this.nextCallbackId(); + this.customExecutionIds.set(callback, id); + this.customExecutionFunctions.set(id, callback); } - this.providedCustomExecutions.set(taskDto.id, new CustomExecution(taskDto.callback)); + return id; } - private customExecutionComplete(id: number): void { - const extensionCallback2: CustomExecution | undefined = this.activeCustomExecutions.get(id); - if (extensionCallback2) { - this.activeCustomExecutions.delete(id); - } - - // Technically we don't really need to do this, however, if an extension - // is executing a task through "executeTask" over and over again - // with different properties in the task definition, then the map of executions - // could grow indefinitely, something we don't want. - if (this.notProvidedCustomExecutions.has(id) && (this.lastStartedTask !== id)) { - this.providedCustomExecutions.delete(id); - this.notProvidedCustomExecutions.delete(id); - } - const iterator = this.notProvidedCustomExecutions.values(); - let iteratorResult = iterator.next(); - while (!iteratorResult.done) { - if (!this.activeCustomExecutions.has(iteratorResult.value) && (this.lastStartedTask !== iteratorResult.value)) { - this.providedCustomExecutions.delete(iteratorResult.value); - this.notProvidedCustomExecutions.delete(iteratorResult.value); - } - iteratorResult = iterator.next(); - } + private nextCallbackId(): string { + return this.callbackIdBase + this.callbackId++; } } diff --git a/packages/plugin-ext/src/plugin/type-converters.spec.ts b/packages/plugin-ext/src/plugin/type-converters.spec.ts index 356437cab58cb..122ab2d6f33ec 100644 --- a/packages/plugin-ext/src/plugin/type-converters.spec.ts +++ b/packages/plugin-ext/src/plugin/type-converters.spec.ts @@ -187,6 +187,7 @@ describe('Type converters:', () => { const shellTaskDto: TaskDto = { type: shellType, + taskType: shellType, label, source, scope: 1, @@ -203,6 +204,7 @@ describe('Type converters:', () => { const shellTaskDtoWithCommandLine: TaskDto = { type: shellType, + taskType: shellType, label, source, scope: 2, diff --git a/packages/plugin-ext/src/plugin/type-converters.ts b/packages/plugin-ext/src/plugin/type-converters.ts index f89ad40443d73..22c0ad4694032 100644 --- a/packages/plugin-ext/src/plugin/type-converters.ts +++ b/packages/plugin-ext/src/plugin/type-converters.ts @@ -750,16 +750,16 @@ export function fromTask(task: theia.Task): TaskDto | undefined { return taskDto; } - if (taskDefinition.taskType === 'shell' || types.ShellExecution.is(execution)) { - return fromShellExecution(execution, taskDto); + if (types.ShellExecution.is(execution)) { + return fromShellExecution(execution, taskDto); } - if (taskDefinition.taskType === 'process' || types.ProcessExecution.is(execution)) { - return fromProcessExecution(execution, taskDto); + if (types.ProcessExecution.is(execution)) { + return fromProcessExecution(execution, taskDto); } - if (taskDefinition.taskType === 'customExecution' || types.CustomExecution.is(execution)) { - return fromCustomExecution(execution, taskDto); + if (types.CustomExecution.is(execution)) { + return fromCustomExecution(execution, taskDto); } return taskDto; @@ -839,6 +839,7 @@ export function toTask(taskDto: TaskDto): theia.Task { } export function fromProcessExecution(execution: theia.ProcessExecution, taskDto: TaskDto): TaskDto { + taskDto.taskType = 'process'; taskDto.command = execution.process; taskDto.args = execution.args; @@ -850,6 +851,7 @@ export function fromProcessExecution(execution: theia.ProcessExecution, taskDto: } export function fromShellExecution(execution: theia.ShellExecution, taskDto: TaskDto): TaskDto { + taskDto.taskType = 'shell'; const options = execution.options; if (options) { taskDto.options = getShellExecutionOptions(options); @@ -872,6 +874,7 @@ export function fromShellExecution(execution: theia.ShellExecution, taskDto: Tas } export function fromCustomExecution(execution: theia.CustomExecution, taskDto: TaskDto): TaskDto { + taskDto.taskType = 'customExecution'; const callback = execution.callback; if (callback) { taskDto.callback = callback; diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts index 5b039ddca5652..47b2c880f0c33 100644 --- a/packages/plugin-ext/src/plugin/types-impl.ts +++ b/packages/plugin-ext/src/plugin/types-impl.ts @@ -1466,14 +1466,6 @@ export enum ProgressLocation { Notification = 15 } -function computeTaskExecutionId(values: string[]): string { - let id: string = ''; - for (let i = 0; i < values.length; i++) { - id += values[i].replace(/,/g, ',,') + ','; - } - return id; -} - export class ProcessExecution { private executionProcess: string; private arguments: string[]; @@ -1529,21 +1521,7 @@ export class ProcessExecution { this.executionOptions = value; } - public computeId(): string { - const props: string[] = []; - props.push('process'); - if (this.executionProcess !== undefined) { - props.push(this.executionProcess); - } - if (this.arguments && this.arguments.length > 0) { - for (const arg of this.arguments) { - props.push(arg); - } - } - return computeTaskExecutionId(props); - } - - public static is(value: theia.ShellExecution | theia.ProcessExecution | theia.CustomExecution): boolean { + public static is(value: theia.ShellExecution | theia.ProcessExecution | theia.CustomExecution): value is ProcessExecution { const candidate = value as ProcessExecution; return candidate && !!candidate.process; } @@ -1634,24 +1612,7 @@ export class ShellExecution { this.shellOptions = value; } - public computeId(): string { - const props: string[] = []; - props.push('shell'); - if (this.shellCommandLine !== undefined) { - props.push(this.shellCommandLine); - } - if (this.shellCommand !== undefined) { - props.push(typeof this.shellCommand === 'string' ? this.shellCommand : this.shellCommand.value); - } - if (this.arguments && this.arguments.length > 0) { - for (const arg of this.arguments) { - props.push(typeof arg === 'string' ? arg : arg.value); - } - } - return computeTaskExecutionId(props); - } - - public static is(value: theia.ShellExecution | theia.ProcessExecution | theia.CustomExecution): boolean { + public static is(value: theia.ShellExecution | theia.ProcessExecution | theia.CustomExecution): value is ShellExecution { const candidate = value as ShellExecution; return candidate && (!!candidate.commandLine || !!candidate.command); } @@ -1662,9 +1623,6 @@ export class CustomExecution { constructor(callback: (resolvedDefintion: theia.TaskDefinition) => Thenable) { this._callback = callback; } - public computeId(): string { - return 'customExecution' + UUID.uuid4(); - } public set callback(value: (resolvedDefintion: theia.TaskDefinition) => Thenable) { this._callback = value; @@ -1674,7 +1632,7 @@ export class CustomExecution { return this._callback; } - public static is(value: theia.ShellExecution | theia.ProcessExecution | theia.CustomExecution): boolean { + public static is(value: theia.ShellExecution | theia.ProcessExecution | theia.CustomExecution): value is CustomExecution { const candidate = value as CustomExecution; return candidate && (!!candidate._callback); } @@ -1842,7 +1800,6 @@ export class Task { value = undefined; } this.taskExecution = value; - this.updateDefinitionBasedOnExecution(); } get problemMatchers(): string[] { @@ -1907,31 +1864,6 @@ export class Task { } this.taskPresentationOptions = value; } - - private updateDefinitionBasedOnExecution(): void { - if (this.taskExecution instanceof ProcessExecution) { - Object.assign(this.taskDefinition, { - id: this.taskExecution.computeId(), - taskType: 'process' - }); - } else if (this.taskExecution instanceof ShellExecution) { - Object.assign(this.taskDefinition, { - id: this.taskExecution.computeId(), - taskType: 'shell' - }); - } else if (this.taskExecution instanceof CustomExecution) { - Object.assign(this.taskDefinition, { - id: this.taskDefinition.id ? this.taskDefinition.id : this.taskExecution.computeId(), - taskType: 'customExecution' - }); - } else { - Object.assign(this.taskDefinition, { - type: '$empty', - id: UUID.uuid4(), - taskType: this.taskDefinition!.type - }); - } - } } export class Task2 extends Task { diff --git a/packages/task/src/browser/process/process-task-contribution.ts b/packages/task/src/browser/process/process-task-contribution.ts index 16c4ab2ec44ec..71dd591215700 100644 --- a/packages/task/src/browser/process/process-task-contribution.ts +++ b/packages/task/src/browser/process/process-task-contribution.ts @@ -25,7 +25,7 @@ export class ProcessTaskContribution implements TaskContribution { protected readonly processTaskResolver: ProcessTaskResolver; registerResolvers(resolvers: TaskResolverRegistry): void { - resolvers.register('process', this.processTaskResolver); - resolvers.register('shell', this.processTaskResolver); + resolvers.registerExecutionResolver('process', this.processTaskResolver); + resolvers.registerExecutionResolver('shell', this.processTaskResolver); } } diff --git a/packages/task/src/browser/task-configurations.ts b/packages/task/src/browser/task-configurations.ts index 54a6717695603..9897a64d5c9f1 100644 --- a/packages/task/src/browser/task-configurations.ts +++ b/packages/task/src/browser/task-configurations.ts @@ -244,7 +244,7 @@ export class TaskConfigurations implements Disposable { return undefined; } - const customizationByType = this.getTaskCustomizations(taskConfig.taskType || taskConfig.type, taskConfig._scope) || []; + const customizationByType = this.getTaskCustomizations(taskConfig.type, taskConfig._scope) || []; const hasCustomization = customizationByType.length > 0; if (hasCustomization) { const taskDefinition = this.taskDefinitionRegistry.getDefinition(taskConfig); @@ -329,7 +329,7 @@ export class TaskConfigurations implements Disposable { console.error('Detected / Contributed tasks should have a task definition.'); return; } - const customization: TaskCustomization = { type: task.taskType || task.type }; + const customization: TaskCustomization = { type: task.type }; definition.properties.all.forEach(p => { if (task[p] !== undefined) { customization[p] = task[p]; @@ -457,7 +457,7 @@ export class TaskConfigurations implements Disposable { const jsonTasks = this.taskConfigurationManager.getTasks(scope); if (jsonTasks) { const ind = jsonTasks.findIndex((t: TaskCustomization | TaskConfiguration) => { - if (t.type !== (task.taskType || task.type)) { + if (t.type !== (task.type)) { return false; } const def = this.taskDefinitionRegistry.getDefinition(t); @@ -503,9 +503,6 @@ export class TaskConfigurations implements Disposable { } private getTaskDefinition(task: TaskCustomization): TaskDefinition | undefined { - return this.taskDefinitionRegistry.getDefinition({ - ...task, - type: typeof task.taskType === 'string' ? task.taskType : task.type - }); + return this.taskDefinitionRegistry.getDefinition(task); } } diff --git a/packages/task/src/browser/task-contribution.ts b/packages/task/src/browser/task-contribution.ts index 4dfcccaa389e8..9b0dea3422056 100644 --- a/packages/task/src/browser/task-contribution.ts +++ b/packages/task/src/browser/task-contribution.ts @@ -96,38 +96,99 @@ export class TaskResolverRegistry { */ readonly onWillProvideTaskResolver = this.onWillProvideTaskResolverEmitter.event; - protected resolvers: Map; + protected taskResolvers: Map = new Map(); + protected executionResolvers: Map = new Map(); - @postConstruct() - protected init(): void { - this.resolvers = new Map(); + /** + * Registers the given {@link TaskResolver} to resolve the `TaskConfiguration` of the specified type. + * If there is already a `TaskResolver` registered for the specified type the registration will + * be overwritten with the new value. + * + * @deprecated since 1.12.0 use `registerTaskResolver` instead. + * + * @param type the task configuration type for which the given resolver should be registered. + * @param resolver the task resolver that should be registered. + * + * @returns a `Disposable` that can be invoked to unregister the given resolver + */ + register(type: string, resolver: TaskResolver): Disposable { + return this.registerTaskResolver(type, resolver); } /** * Registers the given {@link TaskResolver} to resolve the `TaskConfiguration` of the specified type. * If there is already a `TaskResolver` registered for the specified type the registration will * be overwritten with the new value. + * * @param type the task configuration type for which the given resolver should be registered. * @param resolver the task resolver that should be registered. * * @returns a `Disposable` that can be invoked to unregister the given resolver */ - register(type: string, resolver: TaskResolver): Disposable { - this.resolvers.set(type, resolver); + + registerTaskResolver(type: string, resolver: TaskResolver): Disposable { + if (this.taskResolvers.has(type)) { + console.warn(`Overriding task resolver for ${type}`); + } + this.taskResolvers.set(type, resolver); return { - dispose: () => this.resolvers.delete(type) + dispose: () => this.taskResolvers.delete(type) }; } /** * Retrieves the {@link TaskResolver} registered for the given type task configuration type. + * + * @deprecated since 1.12.0 use `getTaskResolver()` instead. + * * @param type the task configuration type * * @returns a promise of the registered `TaskResolver` or `undefined` if no resolver is registered for the given type. */ async getResolver(type: string): Promise { + return this.getTaskResolver(type); + } + + /** + * Retrieves the {@link TaskResolver} registered for the given type task configuration type. + * @param type the task configuration type + * + * @returns a promise of the registered `TaskResolver` or `undefined` if no resolver is registered for the given type. + */ + async getTaskResolver(type: string): Promise { await WaitUntilEvent.fire(this.onWillProvideTaskResolverEmitter, {}); - return this.resolvers.get(type); + return this.taskResolvers.get(type); + } + + /** + * Registers the given {@link TaskResolver} to resolve the `TaskConfiguration` for the + * specified type of execution ('shell', 'process' or 'customExecution'). + * If there is already a `TaskResolver` registered for the specified type the registration will + * be overwritten with the new value. + * + * @param type the task execution type for which the given resolver should be registered. + * @param resolver the task resolver that should be registered. + * + * @returns a `Disposable` that can be invoked to unregister the given resolver + */ + registerExecutionResolver(type: string, resolver: TaskResolver): Disposable { + if (this.executionResolvers.has(type)) { + console.warn(`Overriding execution resolver for ${type}`); + } + this.executionResolvers.set(type, resolver); + return { + dispose: () => this.executionResolvers.delete(type) + }; + } + + /** + * Retrieves the {@link TaskResolver} registered for the given type of execution ('shell', 'process' or 'customExecution').. + * @param type the task configuration type + * + * @returns a promise of the registered `TaskResolver` or `undefined` if no resolver is registered for the given type. + */ + getExecutionResolver(executionType: string): TaskResolver | undefined { + return this.executionResolvers.get(executionType); } } diff --git a/packages/task/src/browser/task-definition-registry.ts b/packages/task/src/browser/task-definition-registry.ts index c80ebdbfaa80d..06d5ae63bf269 100644 --- a/packages/task/src/browser/task-definition-registry.ts +++ b/packages/task/src/browser/task-definition-registry.ts @@ -66,7 +66,7 @@ export class TaskDefinitionRegistry { * @return the task definition for the task configuration. If the task definition is not found, `undefined` is returned. */ getDefinition(taskConfiguration: TaskConfiguration | TaskCustomization): TaskDefinition | undefined { - const definitions = this.getDefinitions(taskConfiguration.taskType || taskConfiguration.type); + const definitions = this.getDefinitions(taskConfiguration.type); let matchedDefinition: TaskDefinition | undefined; let highest = -1; for (const def of definitions) { @@ -107,8 +107,8 @@ export class TaskDefinitionRegistry { } compareTasks(one: TaskConfiguration | TaskCustomization, other: TaskConfiguration | TaskCustomization): boolean { - const oneType = one.taskType || one.type; - const otherType = other.taskType || other.type; + const oneType = one.type; + const otherType = other.type; if (oneType !== otherType) { return false; } diff --git a/packages/task/src/browser/task-service.ts b/packages/task/src/browser/task-service.ts index 7fb2e214c4a5c..91f40a23e273e 100644 --- a/packages/task/src/browser/task-service.ts +++ b/packages/task/src/browser/task-service.ts @@ -43,7 +43,6 @@ import { TaskConfiguration, TaskConfigurationScope, TaskCustomization, - TaskDefinition, TaskExitedEvent, TaskIdentifier, TaskInfo, @@ -697,18 +696,12 @@ export class TaskService implements TaskConfigurationClient { // TaskIdentifier object does not support tasks of type 'shell' (The same behavior as in VS Code). // So if we want the 'dependsOn' property to include tasks of type 'shell', // then we must mention their labels (in the 'dependsOn' property) and not to create a task identifier object for them. - const taskDefinition = this.taskDefinitionRegistry.getDefinition(taskIdentifier); - if (taskDefinition) { - currentTaskChildConfiguration = this.getTaskByTaskIdentifierAndTaskDefinition(taskDefinition, taskIdentifier, tasks); - if (!currentTaskChildConfiguration.type) { - this.messageService.error(notEnoughDataError); - throw new Error(notEnoughDataError); - } - return currentTaskChildConfiguration; - } else { + currentTaskChildConfiguration = this.getTaskByTaskIdentifier(taskIdentifier, tasks); + if (!currentTaskChildConfiguration.type) { this.messageService.error(notEnoughDataError); throw new Error(notEnoughDataError); } + return currentTaskChildConfiguration; } else { currentTaskChildConfiguration = tasks.filter(t => taskIdentifier === this.taskNameResolver.resolve(t))[0]; return currentTaskChildConfiguration; @@ -716,41 +709,17 @@ export class TaskService implements TaskConfigurationClient { } /** - * Gets the matched task from an array of task configurations by TaskDefinition and TaskIdentifier. + * Gets the matched task from an array of task configurations by TaskIdentifier. * In case that more than one task configuration matches, we returns the first one. * - * @param taskDefinition The task definition for the task configuration. * @param taskIdentifier The task label (string) or a JSON object which represents a TaskIdentifier (e.g. {"type":"npm", "script":"script1"}) * @param tasks An array of task configurations. - * @returns The correct TaskConfiguration object which matches the taskDefinition and taskIdentifier. + * @returns The correct TaskConfiguration object which matches the taskIdentifier. */ - getTaskByTaskIdentifierAndTaskDefinition(taskDefinition: TaskDefinition | undefined, taskIdentifier: TaskIdentifier, tasks: TaskConfiguration[]): TaskConfiguration { - const identifierProperties: string[] = []; - let relevantTasks = tasks.filter(t => - taskDefinition && t.hasOwnProperty('taskType') && - taskDefinition['taskType'] === t['taskType'] && - t.hasOwnProperty('source') && - taskDefinition['source'] === t['source']); - - Object.keys(taskIdentifier).forEach(key => { - identifierProperties.push(key); - }); - - identifierProperties.forEach(key => { - if (key === 'type' || key === 'taskType') { - relevantTasks = relevantTasks.filter(t => (t.hasOwnProperty('type') || t.hasOwnProperty('taskType')) && - ((taskIdentifier[key] === t['type']) || (taskIdentifier[key] === t['taskType']))); - } else { - relevantTasks = relevantTasks.filter(t => t.hasOwnProperty(key) && taskIdentifier[key] === t[key]); - } - }); - - if (relevantTasks.length > 0) { - return relevantTasks[0]; - } else { - // return empty TaskConfiguration - return { 'label': '', '_scope': '', 'type': '' }; - } + getTaskByTaskIdentifier(taskIdentifier: TaskIdentifier, tasks: TaskConfiguration[]): TaskConfiguration { + const requiredProperties = Object.keys(taskIdentifier); + const taskWithAllProperties = tasks.find(task => requiredProperties.every(property => task.hasOwnProperty(property) && task[property] === taskIdentifier[property])); + return taskWithAllProperties ?? { label: '', _scope: '', type: '' }; // Fall back to empty TaskConfiguration } async runTask(task: TaskConfiguration, option?: RunTaskOption): Promise { @@ -804,24 +773,36 @@ export class TaskService implements TaskConfigurationClient { } protected async doRunTask(task: TaskConfiguration, option?: RunTaskOption): Promise { + let overridePropertiesFunction: (task: TaskConfiguration) => void = () => { }; if (option && option.customization) { const taskDefinition = this.taskDefinitionRegistry.getDefinition(task); if (taskDefinition) { // use the customization object to override the task config - Object.keys(option.customization).forEach(customizedProperty => { - // properties used to define the task cannot be customized - if (customizedProperty !== 'type' && !taskDefinition.properties.all.some(pDefinition => pDefinition === customizedProperty)) { - task[customizedProperty] = option.customization![customizedProperty]; - } - }); + overridePropertiesFunction = tsk => { + Object.keys(option.customization!).forEach(customizedProperty => { + // properties used to define the task cannot be customized + if (customizedProperty !== 'type' && !taskDefinition.properties.all.some(pDefinition => pDefinition === customizedProperty)) { + tsk[customizedProperty] = option.customization![customizedProperty]; + } + }); + }; } } + overridePropertiesFunction(task); + this.addRecentTasks(task); + try { + const resolver = await this.taskResolverRegistry.getTaskResolver(task.type); + const resolvedTask = resolver ? await resolver.resolveTask(task) : task; + const executionResolver = this.taskResolverRegistry.getExecutionResolver(resolvedTask.taskType || resolvedTask.type); + overridePropertiesFunction(resolvedTask); + const taskToRun = executionResolver ? await executionResolver.resolveTask(resolvedTask) : resolvedTask; - const resolvedTask = await this.getResolvedTask(task); - if (resolvedTask) { - // remove problem markers from the same source before running the task await this.removeProblemMarkers(option); - return this.runResolvedTask(resolvedTask, option); + return this.runResolvedTask(taskToRun, option); + } catch (error) { + const errMessage = `Error resolving task '${task.label}': ${error}`; + this.logger.error(errMessage); } + return undefined; } /** @@ -963,21 +944,6 @@ export class TaskService implements TaskConfigurationClient { } } - protected async getResolvedTask(task: TaskConfiguration): Promise { - let resolver = undefined; - let resolvedTask: TaskConfiguration; - try { - resolver = await this.taskResolverRegistry.getResolver(task.type); - resolvedTask = resolver ? await resolver.resolveTask(task) : task; - } catch (error) { - const errMessage = `Error resolving task '${task.label}': ${error}`; - this.logger.error(errMessage); - resolvedTask = task; - } - this.addRecentTasks(task); - return resolvedTask; - } - /** * Runs the resolved task and opens terminal widget if the task is based on a terminal process * @param resolvedTask the resolved task diff --git a/packages/task/src/node/process/process-task-runner.ts b/packages/task/src/node/process/process-task-runner.ts index 8bf1b23d29d3b..0b2979d31c8a7 100644 --- a/packages/task/src/node/process/process-task-runner.ts +++ b/packages/task/src/node/process/process-task-runner.ts @@ -83,7 +83,7 @@ export class ProcessTaskRunner implements TaskRunner { }); }); - const processType = taskConfig.type as 'process' | 'shell'; + const processType = (taskConfig.taskType || taskConfig.type) as 'process' | 'shell'; return this.taskFactory({ label: taskConfig.label, process: terminal,