Skip to content

Commit

Permalink
support presentation.echo in task config
Browse files Browse the repository at this point in the history
- add the suppport of having `echo` property in the `presentation`
object in the task configuration. When `echo` is false, Theia does not print the executed command in the terminal.

- resolves #5332

Signed-off-by: Liang Huang <lhuang4@ualberta.ca>
  • Loading branch information
elaihau committed Apr 17, 2020
1 parent 2c244ad commit ef0eba6
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 20 deletions.
6 changes: 6 additions & 0 deletions packages/task/src/browser/task-schema-updater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,7 @@ const problemMatcher = {
const presentation: IJSONSchema = {
type: 'object',
default: {
echo: true,
reveal: 'always',
focus: false,
panel: 'shared',
Expand All @@ -549,6 +550,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,
Expand Down
3 changes: 1 addition & 2 deletions packages/task/src/browser/task-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1004,7 +1004,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(),
Expand All @@ -1017,7 +1016,7 @@ export class TaskService implements TaskConfigurationClient {
taskId,
widgetOptions: { area: 'bottom' },
mode: widgetOpenMode,
taskConfig: taskInfo ? taskInfo.config : undefined
taskInfo
});
widget.start(terminalId);
}
Expand Down
38 changes: 26 additions & 12 deletions packages/task/src/browser/task-terminal-widget-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -38,20 +39,27 @@ export namespace TaskTerminalWidget {

export interface TaskTerminalWidgetOpenerOptions extends WidgetOpenerOptions {
taskId: number;
taskConfig?: TaskConfiguration;
taskInfo?: TaskInfo;
}
export namespace TaskTerminalWidgetOpenerOptions {
export function isDedicatedTerminal(options: TaskTerminalWidgetOpenerOptions): boolean {
return !!options.taskConfig && !!options.taskConfig.presentation && options.taskConfig.presentation.panel === PanelKind.Dedicated;
const taskConfig = options.taskInfo ? options.taskInfo.config : undefined;
return !!taskConfig && !!taskConfig.presentation && taskConfig.presentation.panel === PanelKind.Dedicated;
}

export function isNewTerminal(options: TaskTerminalWidgetOpenerOptions): boolean {
return !!options.taskConfig && !!options.taskConfig.presentation && options.taskConfig.presentation.panel === PanelKind.New;
const taskConfig = options.taskInfo ? options.taskInfo.config : undefined;
return !!taskConfig && !!taskConfig.presentation && taskConfig.presentation.panel === PanelKind.New;
}

export function isSharedTerminal(options: TaskTerminalWidgetOpenerOptions): boolean {
return !!options.taskConfig &&
(options.taskConfig.presentation === undefined || options.taskConfig.presentation.panel === undefined || options.taskConfig.presentation.panel === PanelKind.Shared);
const taskConfig = options.taskInfo ? options.taskInfo.config : undefined;
return !!taskConfig && (taskConfig.presentation === undefined || taskConfig.presentation.panel === undefined || taskConfig.presentation.panel === PanelKind.Shared);
}

export function echoExecutedCommand(options: TaskTerminalWidgetOpenerOptions): boolean {
const taskConfig = options.taskInfo ? options.taskInfo.config : undefined;
return !!taskConfig && (taskConfig.presentation === undefined || taskConfig.presentation.echo === undefined || taskConfig.presentation.echo);
}
}

Expand Down Expand Up @@ -120,8 +128,8 @@ export class TaskTerminalWidgetManager {

async open(factoryOptions: TerminalWidgetFactoryOptions, openerOptions: TaskTerminalWidgetOpenerOptions): Promise<TerminalWidget> {
const dedicated = TaskTerminalWidgetOpenerOptions.isDedicatedTerminal(openerOptions);
if (dedicated && !openerOptions.taskConfig) {
throw new Error('"taskConfig" must be included as part of the "option" if "isDedicated" is true');
if (dedicated && (!openerOptions.taskInfo || !openerOptions.taskInfo.config)) {
throw new Error('"taskConfig" must be included as part of the "option.taskInfo" if "isDedicated" is true');
}

const { isNew, widget } = await this.getWidgetToRunTask(factoryOptions, openerOptions);
Expand All @@ -132,12 +140,18 @@ export class TaskTerminalWidgetManager {
if (factoryOptions.title) {
widget.setTitle(factoryOptions.title);
}
if (openerOptions.taskConfig && TaskOutputPresentation.shouldClearTerminalBeforeRun(openerOptions.taskConfig)) {
const taskConfig = openerOptions.taskInfo ? openerOptions.taskInfo.config : undefined;
if (taskConfig && TaskOutputPresentation.shouldClearTerminalBeforeRun(taskConfig)) {
widget.clearOutput();
}
}
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;
}

Expand All @@ -151,8 +165,8 @@ export class TaskTerminalWidgetManager {
// 1) dedicated, 2) idle, 3) the one that ran the same task
if (widget.dedicated &&
!widget.busy &&
widget.taskConfig && openerOptions.taskConfig &&
this.taskDefinitionRegistry.compareTasks(openerOptions.taskConfig, widget.taskConfig)) {
widget.taskConfig && openerOptions.taskInfo &&
this.taskDefinitionRegistry.compareTasks(openerOptions.taskInfo.taskConfig, widget.taskConfig)) {

reusableTerminalWidget = widget;
break;
Expand Down
6 changes: 3 additions & 3 deletions packages/task/src/common/process/task-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

Expand Down
3 changes: 3 additions & 0 deletions packages/task/src/common/task-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export enum PanelKind {
}

export interface TaskOutputPresentation {
echo?: boolean;
focus?: boolean;
reveal?: RevealKind;
panel?: PanelKind;
Expand All @@ -51,6 +52,7 @@ export interface TaskOutputPresentation {
export namespace TaskOutputPresentation {
export function getDefault(): TaskOutputPresentation {
return {
echo: true,
reveal: RevealKind.Always,
focus: false,
panel: PanelKind.Shared,
Expand Down Expand Up @@ -83,6 +85,7 @@ export namespace TaskOutputPresentation {
}
outputPresentation = {
...outputPresentation,
echo: task.presentation.echo === undefined || task.presentation.echo,
focus: shouldSetFocusToTerminal(task),
showReuseMessage: shouldShowReuseMessage(task),
clear: shouldClearTerminalBeforeRun(task)
Expand Down
19 changes: 16 additions & 3 deletions packages/task/src/node/process/process-task-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand All @@ -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}`);
Expand Down Expand Up @@ -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.
*
Expand Down
6 changes: 6 additions & 0 deletions packages/task/src/node/process/process-task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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,
Expand Down Expand Up @@ -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}`);
}

Expand Down Expand Up @@ -128,6 +133,7 @@ export class ProcessTask extends Task {
config: this.options.config,
terminalId: this.process.id,
processId: this.process.id,
command: this.command
};
}

Expand Down

0 comments on commit ef0eba6

Please sign in to comment.