diff --git a/CHANGELOG.md b/CHANGELOG.md index c1a5984d9ebaf..bc7aa320226de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ - [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/) +## v1.32.0 + +- [plugin] added `Task#runOptions` field and `RunOptions` interface [#11759](https://github.com/eclipse-theia/theia/pull/11759) - Contributed on behalf of STMicroelectronics +- [tasks] added support for `reevaluateOnRerun` run option [#11759](https://github.com/eclipse-theia/theia/pull/11759) - Contributed on behalf of STMicroelectronics + +[Breaking Changes:](#breaking_changes_1.32.0) + +- [tasks] if the variables of a task should be reevaluated on a rerun (this was the behavior until now) the `reevaluateOnRerun` run option in the task description needs to be set to `true` from now on [#11759](https://github.com/eclipse-theia/theia/pull/11759) - Contributed on behalf of STMicroelectronics + ## v1.31.0 - 10/27/2022 - [debug] added confirmation message for debug exit [#11546](https://github.com/eclipse-theia/theia/pull/11546) diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index cc04bd3b288c5..aaaf79f10c844 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -1456,10 +1456,15 @@ export interface TaskDto { group?: string; detail?: string; presentation?: TaskPresentationOptionsDTO; + runOptions?: RunOptionsDTO; // eslint-disable-next-line @typescript-eslint/no-explicit-any [key: string]: any; } +export interface RunOptionsDTO { + reevaluateOnRerun?: boolean; +} + export interface TaskPresentationOptionsDTO { reveal?: number; focus?: boolean; diff --git a/packages/plugin-ext/src/main/browser/tasks-main.ts b/packages/plugin-ext/src/main/browser/tasks-main.ts index cf64f49bce811..95310e5bd8c0c 100644 --- a/packages/plugin-ext/src/main/browser/tasks-main.ts +++ b/packages/plugin-ext/src/main/browser/tasks-main.ts @@ -202,7 +202,7 @@ export class TasksMainImpl implements TasksMain, Disposable { } protected toTaskConfiguration(taskDto: TaskDto): TaskConfiguration { - const { group, presentation, scope, source, ...common } = taskDto ?? {}; + const { group, presentation, scope, source, runOptions, ...common } = taskDto ?? {}; const partialConfig: Partial = {}; if (presentation) { partialConfig.presentation = this.convertTaskPresentation(presentation); @@ -213,6 +213,7 @@ export class TasksMainImpl implements TasksMain, Disposable { return { ...common, ...partialConfig, + runOptions, _scope: scope, _source: source, }; diff --git a/packages/plugin-ext/src/plugin/type-converters.spec.ts b/packages/plugin-ext/src/plugin/type-converters.spec.ts index a76b3de3fa7f2..215872b8ea479 100644 --- a/packages/plugin-ext/src/plugin/type-converters.spec.ts +++ b/packages/plugin-ext/src/plugin/type-converters.spec.ts @@ -202,7 +202,10 @@ describe('Type converters:', () => { reveal: 3, focus: true }, - group: groupDto + group: groupDto, + runOptions: { + reevaluateOnRerun: false + } }; const shellTaskDtoWithCommandLine: TaskDto = { @@ -213,7 +216,10 @@ describe('Type converters:', () => { scope: 2, command: commandLine, options: { cwd }, - additionalProperty + additionalProperty, + runOptions: { + reevaluateOnRerun: false + } }; const shellPluginTask: theia.Task = { @@ -235,6 +241,9 @@ describe('Type converters:', () => { options: { cwd } + }, + runOptions: { + reevaluateOnRerun: false } }; @@ -264,6 +273,9 @@ describe('Type converters:', () => { options: { cwd } + }, + runOptions: { + reevaluateOnRerun: false } }; @@ -291,6 +303,9 @@ describe('Type converters:', () => { options: { cwd } + }, + runOptions: { + reevaluateOnRerun: false } }; diff --git a/packages/plugin-ext/src/plugin/type-converters.ts b/packages/plugin-ext/src/plugin/type-converters.ts index d3f1ae6e47607..9126224e0df44 100644 --- a/packages/plugin-ext/src/plugin/type-converters.ts +++ b/packages/plugin-ext/src/plugin/type-converters.ts @@ -816,6 +816,8 @@ export function fromTask(task: theia.Task): TaskDto | undefined { taskDto.label = task.name; taskDto.source = task.source; + taskDto.runOptions = { reevaluateOnRerun: task.runOptions.reevaluateOnRerun }; + if ((task as types.Task).hasProblemMatchers) { taskDto.problemMatcher = task.problemMatchers; } @@ -877,10 +879,11 @@ export function toTask(taskDto: TaskDto): theia.Task { throw new Error('Task should be provided for converting'); } - const { type, taskType, label, source, scope, problemMatcher, detail, command, args, options, group, presentation, ...properties } = taskDto; + const { type, taskType, label, source, scope, problemMatcher, detail, command, args, options, group, presentation, runOptions, ...properties } = taskDto; const result = {} as theia.Task; result.name = label; result.source = source; + result.runOptions = runOptions ?? {}; if (detail) { result.detail = detail; } diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts index cf9eea96158cb..b990a737b52e4 100644 --- a/packages/plugin-ext/src/plugin/types-impl.ts +++ b/packages/plugin-ext/src/plugin/types-impl.ts @@ -2057,6 +2057,7 @@ export class Task { private taskSource: string; private taskGroup: TaskGroup | undefined; private taskPresentationOptions: theia.TaskPresentationOptions; + private taskRunOptions: theia.RunOptions; constructor( taskDefinition: theia.TaskDefinition, scope: theia.WorkspaceFolder | theia.TaskScope.Global | theia.TaskScope.Workspace, @@ -2229,6 +2230,17 @@ export class Task { } this.taskPresentationOptions = value; } + + get runOptions(): theia.RunOptions { + return this.taskRunOptions; + } + + set runOptions(value: theia.RunOptions) { + if (value === null || value === undefined) { + value = Object.create(null); + } + this.taskRunOptions = value; + } } @es5ClassCompat diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index 1de28bef1e37e..fab66987e8ab0 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -11532,6 +11532,16 @@ export module '@theia/plugin' { clear?: boolean; } + /** + * Run options for a task. + */ + export interface RunOptions { + /** + * Controls whether task variables are re-evaluated on rerun. + */ + reevaluateOnRerun?: boolean; + } + export class Task { /** @@ -11618,6 +11628,11 @@ export module '@theia/plugin' { * array. */ problemMatchers?: string[]; + + /** + * Run options for the task + */ + runOptions: RunOptions; } /** diff --git a/packages/task/src/browser/task-configurations.ts b/packages/task/src/browser/task-configurations.ts index 8bc5d5d4e2065..f44d4046c412f 100644 --- a/packages/task/src/browser/task-configurations.ts +++ b/packages/task/src/browser/task-configurations.ts @@ -328,7 +328,7 @@ export class TaskConfigurations implements Disposable { console.error('Detected / Contributed tasks should have a task definition.'); return; } - const customization: TaskCustomization = { type: task.type }; + const customization: TaskCustomization = { type: task.type, runOptions: task.runOptions }; definition.properties.all.forEach(p => { if (task[p] !== undefined) { customization[p] = task[p]; diff --git a/packages/task/src/browser/task-service.ts b/packages/task/src/browser/task-service.ts index f03dfe9c74d8d..0a97486aabd03 100644 --- a/packages/task/src/browser/task-service.ts +++ b/packages/task/src/browser/task-service.ts @@ -87,13 +87,18 @@ export interface TaskEndedInfo { value: number | boolean | undefined } +export interface LastRunTaskInfo { + resolvedTask?: TaskConfiguration; + option?: RunTaskOption +} + @injectable() export class TaskService implements TaskConfigurationClient { /** * The last executed task. */ - protected lastTask: { source: string, taskLabel: string, scope: TaskConfigurationScope } | undefined = undefined; + protected lastTask: LastRunTaskInfo = {resolvedTask: undefined, option: undefined}; protected cachedRecentTasks: TaskConfiguration[] = []; protected runningTasks = new Map, @@ -470,7 +475,7 @@ export class TaskService implements TaskConfigurationClient { * * @returns the last executed task or `undefined`. */ - getLastTask(): { source: string, taskLabel: string, scope: TaskConfigurationScope } | undefined { + getLastTask(): LastRunTaskInfo { return this.lastTask; } @@ -496,11 +501,14 @@ export class TaskService implements TaskConfigurationClient { * @param token The cache token for the user interaction in progress */ async runLastTask(token: number): Promise { - if (!this.lastTask) { + if (!this.lastTask?.resolvedTask) { return; } - const { source, taskLabel, scope } = this.lastTask; - return this.run(token, source, taskLabel, scope); + if (!this.lastTask.resolvedTask.runOptions?.reevaluateOnRerun) { + return this.runResolvedTask(this.lastTask.resolvedTask, this.lastTask.option); + } + const { _source, label, _scope } = this.lastTask.resolvedTask; + return this.run(token, _source, label, _scope); } /** @@ -737,7 +745,7 @@ export class TaskService implements TaskConfigurationClient { try { // resolve problemMatchers if (!option && task.problemMatcher) { - const customizationObject: TaskCustomization = { type: task.taskType, problemMatcher: task.problemMatcher }; + const customizationObject: TaskCustomization = { type: task.taskType, problemMatcher: task.problemMatcher, runOptions: task.runOptions }; const resolvedMatchers = await this.resolveProblemMatchers(task, customizationObject); option = { customization: { ...customizationObject, ...{ problemMatcher: resolvedMatchers } } @@ -949,7 +957,7 @@ export class TaskService implements TaskConfigurationClient { } protected async getTaskCustomization(task: TaskConfiguration): Promise { - const customizationObject: TaskCustomization = { type: '', _scope: task._scope }; + const customizationObject: TaskCustomization = { type: '', _scope: task._scope, runOptions: task.runOptions }; const customizationFound = this.taskConfigurations.getCustomizationForTask(task); if (customizationFound) { Object.assign(customizationObject, customizationFound); @@ -982,12 +990,11 @@ export class TaskService implements TaskConfigurationClient { * @param option options to run the resolved task */ protected async runResolvedTask(resolvedTask: TaskConfiguration, option?: RunTaskOption): Promise { - const source = resolvedTask._source; const taskLabel = resolvedTask.label; let taskInfo: TaskInfo | undefined; try { taskInfo = await this.taskServer.run(resolvedTask, this.getContext(), option); - this.lastTask = { source, taskLabel, scope: resolvedTask._scope }; + this.lastTask = {resolvedTask, option }; this.logger.debug(`Task created. Task id: ${taskInfo.taskId}`); /** diff --git a/packages/task/src/common/task-protocol.ts b/packages/task/src/common/task-protocol.ts index de2ce5415e8d7..1fc7894da579f 100644 --- a/packages/task/src/common/task-protocol.ts +++ b/packages/task/src/common/task-protocol.ts @@ -127,6 +127,8 @@ export interface TaskCustomization { /** The order the dependsOn tasks should be executed in. */ dependsOrder?: DependsOrder; + runOptions?: RunOptions; + // eslint-disable-next-line @typescript-eslint/no-explicit-any [name: string]: any; } @@ -228,6 +230,10 @@ export interface RunTaskOption { customization?: TaskCustomizationData; } +export interface RunOptions { + reevaluateOnRerun?: boolean; +} + /** Event sent when a task has concluded its execution */ export interface TaskExitedEvent { readonly taskId: number;