diff --git a/packages/task/src/browser/task-schema-updater.ts b/packages/task/src/browser/task-schema-updater.ts index 45d7e83173619..db496d154c5ab 100644 --- a/packages/task/src/browser/task-schema-updater.ts +++ b/packages/task/src/browser/task-schema-updater.ts @@ -539,6 +539,7 @@ const problemMatcher = { const presentation: IJSONSchema = { type: 'object', default: { + echo: true, reveal: 'always', focus: false, panel: 'shared', @@ -548,6 +549,11 @@ const presentation: IJSONSchema = { description: 'Configures the panel that is used to present the task\'s output and reads its input.', additionalProperties: true, properties: { + echo: { + type: 'boolean', + default: true, + description: 'Controls whether the executed command is echoed to the panel. Default is true.' + }, focus: { type: 'boolean', default: false, diff --git a/packages/task/src/browser/task-service.ts b/packages/task/src/browser/task-service.ts index a7061a402f23a..7250add3ff871 100644 --- a/packages/task/src/browser/task-service.ts +++ b/packages/task/src/browser/task-service.ts @@ -999,7 +999,6 @@ export class TaskService implements TaskConfigurationClient { } } } - // Create / find a terminal widget to display an execution output of a task that was launched as a command inside a shell. const widget = await this.taskTerminalWidgetManager.open({ created: new Date().toString(), @@ -1012,7 +1011,8 @@ export class TaskService implements TaskConfigurationClient { taskId, widgetOptions: { area: 'bottom' }, mode: widgetOpenMode, - taskConfig: taskInfo ? taskInfo.config : undefined + taskConfig: taskInfo ? taskInfo.config : undefined, + taskInfo }); widget.start(terminalId); } diff --git a/packages/task/src/browser/task-terminal-widget-manager.ts b/packages/task/src/browser/task-terminal-widget-manager.ts index 971282ca71666..112b4b211166b 100644 --- a/packages/task/src/browser/task-terminal-widget-manager.ts +++ b/packages/task/src/browser/task-terminal-widget-manager.ts @@ -19,7 +19,8 @@ import { ApplicationShell, WidgetOpenerOptions } from '@theia/core/lib/browser'; import { TerminalWidget } from '@theia/terminal/lib/browser/base/terminal-widget'; import { TerminalWidgetFactoryOptions } from '@theia/terminal/lib/browser/terminal-widget-impl'; import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service'; -import { PanelKind, TaskConfiguration, TaskWatcher, TaskExitedEvent, TaskServer, TaskOutputPresentation } from '../common'; +import { PanelKind, TaskConfiguration, TaskWatcher, TaskExitedEvent, TaskServer, TaskOutputPresentation, TaskInfo } from '../common'; +import { ProcessTaskInfo } from '../common/process/task-protocol'; import { TaskDefinitionRegistry } from './task-definition-registry'; import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service'; @@ -39,6 +40,7 @@ export namespace TaskTerminalWidget { export interface TaskTerminalWidgetOpenerOptions extends WidgetOpenerOptions { taskId: number; taskConfig?: TaskConfiguration; + taskInfo?: TaskInfo; } export namespace TaskTerminalWidgetOpenerOptions { export function isDedicatedTerminal(options: TaskTerminalWidgetOpenerOptions): boolean { @@ -53,6 +55,11 @@ export namespace TaskTerminalWidgetOpenerOptions { return !!options.taskConfig && (options.taskConfig.presentation === undefined || options.taskConfig.presentation.panel === undefined || options.taskConfig.presentation.panel === PanelKind.Shared); } + + export function echoExecutedCommand(options: TaskTerminalWidgetOpenerOptions): boolean { + return !!options.taskConfig && + (options.taskConfig.presentation === undefined || options.taskConfig.presentation.echo === undefined || options.taskConfig.presentation.echo); + } } @injectable() @@ -137,7 +144,12 @@ export class TaskTerminalWidgetManager { } } this.terminalService.open(widget, openerOptions); - + const taskInfo = openerOptions.taskInfo; + if (TaskTerminalWidgetOpenerOptions.echoExecutedCommand(openerOptions) && + taskInfo && ProcessTaskInfo.is(taskInfo) && taskInfo.command && taskInfo.command.length > 0 + ) { + widget.writeLine(`\x1b[1m> Executing task: ${taskInfo.command} <\x1b[0m\n`); + } return widget; } diff --git a/packages/task/src/common/process/task-protocol.ts b/packages/task/src/common/process/task-protocol.ts index 441c192c46ba4..999063eae563c 100644 --- a/packages/task/src/common/process/task-protocol.ts +++ b/packages/task/src/common/process/task-protocol.ts @@ -78,14 +78,14 @@ export interface ProcessTaskConfiguration extends TaskConfiguration, CommandProp } export interface ProcessTaskInfo extends TaskInfo { - /** terminal id. Defined if task is run as a terminal process */ - readonly terminalId?: number; /** process id. Defined if task is run as a process */ readonly processId?: number; + /** process task command */ + readonly command?: string; } export namespace ProcessTaskInfo { export function is(info: TaskInfo): info is ProcessTaskInfo { - return info['terminalId'] !== undefined || info['processId'] !== undefined; + return info['processId'] !== undefined; } } diff --git a/packages/task/src/common/task-protocol.ts b/packages/task/src/common/task-protocol.ts index fbcb1d3592fa2..0a0069b4b19b9 100644 --- a/packages/task/src/common/task-protocol.ts +++ b/packages/task/src/common/task-protocol.ts @@ -40,6 +40,7 @@ export enum PanelKind { } export interface TaskOutputPresentation { + echo?: boolean; focus?: boolean; reveal?: RevealKind; panel?: PanelKind; @@ -52,6 +53,7 @@ export namespace TaskOutputPresentation { // eslint-disable-next-line @typescript-eslint/no-explicit-any export function fromJson(task: any): TaskOutputPresentation { let outputPresentation = { + echo: true, reveal: RevealKind.Always, focus: false, panel: PanelKind.Shared, @@ -79,6 +81,7 @@ export namespace TaskOutputPresentation { } outputPresentation = { ...outputPresentation, + echo: task.presentation.echo === undefined || task.presentation.echo, focus: shouldSetFocusToTerminal(task), showReuseMessage: shouldShowReuseMessage(task), clear: shouldClearTerminalBeforeRun(task) diff --git a/packages/task/src/node/process/process-task-runner.ts b/packages/task/src/node/process/process-task-runner.ts index 094318e1c4480..5f348af90eaa8 100644 --- a/packages/task/src/node/process/process-task-runner.ts +++ b/packages/task/src/node/process/process-task-runner.ts @@ -72,7 +72,8 @@ export class ProcessTaskRunner implements TaskRunner { // way the command is passed: // - process: directly look for an executable and pass a specific set of arguments/options. // - shell: defer the spawning to a shell that will evaluate a command line with our executable. - const terminal: Process = this.terminalProcessFactory(this.getResolvedCommand(taskConfig)); + const terminalProcessOptions = this.getResolvedCommand(taskConfig); + const terminal: Process = this.terminalProcessFactory(terminalProcessOptions); // Wait for the confirmation that the process is successfully started, or has failed to start. await new Promise((resolve, reject) => { @@ -82,12 +83,14 @@ export class ProcessTaskRunner implements TaskRunner { }); }); + const processType = taskConfig.type as 'process' | 'shell'; return this.taskFactory({ label: taskConfig.label, process: terminal, - processType: taskConfig.type as 'process' | 'shell', + processType, context: ctx, - config: taskConfig + config: taskConfig, + command: this.getCommand(processType, terminalProcessOptions) }); } catch (error) { this.logger.error(`Error occurred while creating task: ${error}`); @@ -249,6 +252,16 @@ export class ProcessTaskRunner implements TaskRunner { return { command, args, commandLine, options }; } + private getCommand(processType: 'process' | 'shell', terminalProcessOptions: TerminalProcessOptions): string | undefined { + if (terminalProcessOptions.args) { + if (processType === 'shell') { + return terminalProcessOptions.args[terminalProcessOptions.args.length - 1]; + } else if (processType === 'process') { + return `${terminalProcessOptions.command} ${terminalProcessOptions.args.join(' ')}`; + } + } + } + /** * This is task specific, to align with VS Code's behavior. * diff --git a/packages/task/src/node/process/process-task.ts b/packages/task/src/node/process/process-task.ts index 3117ff40d3bee..d5295881cb1ed 100644 --- a/packages/task/src/node/process/process-task.ts +++ b/packages/task/src/node/process/process-task.ts @@ -48,6 +48,7 @@ export const TaskProcessOptions = Symbol('TaskProcessOptions'); export interface TaskProcessOptions extends TaskOptions { process: Process; processType: ProcessType; + command?: string; } export const TaskFactory = Symbol('TaskFactory'); @@ -57,6 +58,8 @@ export type TaskFactory = (options: TaskProcessOptions) => ProcessTask; @injectable() export class ProcessTask extends Task { + protected command: string | undefined; + constructor( @inject(TaskManager) protected readonly taskManager: TaskManager, @inject(ILogger) @named('task') protected readonly logger: ILogger, @@ -92,6 +95,8 @@ export class ProcessTask extends Task { }); } }); + + this.command = this.options.command; this.logger.info(`Created new task, id: ${this.id}, process id: ${this.options.process.id}, OS PID: ${this.process.pid}, context: ${this.context}`); } @@ -128,6 +133,7 @@ export class ProcessTask extends Task { config: this.options.config, terminalId: this.process.id, processId: this.process.id, + command: this.command }; }