diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index 9b5804ee96f21ee28fd95e46c1401b8939ea7428..55ad91bee3e617117ae6e57e130a5d03048c2839 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -485,14 +485,18 @@ export class LinkedMap { } public delete(key: K): boolean { + return !!this.remove(key); + } + + public remove(key: K): V | undefined { const item = this._map.get(key); if (!item) { - return false; + return undefined; } this._map.delete(key); this.removeItem(item); this._size--; - return true; + return item.value; } public shift(): V | undefined { diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 29bde57d3183827c8577dc9278174e4d00c9dc44..fecdcb110fc06a7d1dcbadfb2ab9e878b0bf0850 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -31,49 +31,52 @@ declare module 'vscode' { /** * Controls how the task channel is used between tasks */ - export enum TaskInstanceKind { + export enum TaskPanelKind { /** - * Shares a channel with other tasks. This is the default. + * Shares a panel with other tasks. This is the default. */ Shared = 1, /** - * Uses the same task channel for every run if possible. The task channel is not + * Uses a dedicated panel for this tasks. The panel is not * shared with other tasks. */ - Same = 2, + Dedicated = 2, /** - * Creates a new task channel whenever that task is executed + * Creates a new panel whenever this task is executed. */ New = 3 } /** - * Controls terminal specific behavior. + * Controls how the task is presented in the UI. */ - export interface TaskTerminalBehavior { + export interface TaskPresentationOptions { /** - * Controls whether the terminal executing a task is brought to front or not. + * Controls whether the task output is reveal in the user interface. * Defaults to `RevealKind.Always`. */ reveal?: TaskRevealKind; /** - * Controls whether the command is echoed in the terminal or not. + * Controls whether the command associated with the task is echoed + * in the user interface. */ echo?: boolean; /** - * Controls whether the task pane takes focus when the task is executed + * Controls whether the panel showing the task output is taking focus. */ focus?: boolean; /** - * Controls in which pane the task is executed. + * Controls if the task panel is used for this task only (dedicated), + * shared between tasks (shared) or if a new panel is created on + * every task execution (new). Defaults to `TaskInstanceKind.Shared` */ - instance?: TaskInstanceKind; + panel?: TaskPanelKind; } export interface ProcessTaskOptions { @@ -200,9 +203,9 @@ declare module 'vscode' { options: ProcessTaskOptions; /** - * The terminal behavior. Defaults to an empty object literal. + * The presentation options. Defaults to an empty literal. */ - terminalBehavior: TaskTerminalBehavior; + presentationOptions: TaskPresentationOptions; /** * The problem matchers attached to the task. Defaults to an empty @@ -332,9 +335,9 @@ declare module 'vscode' { options: ShellTaskOptions; /** - * The terminal behavior. Defaults to an empty object literal. + * The presentation options. Defaults to an empty literal. */ - terminalBehavior: TaskTerminalBehavior; + presentationOptions: TaskPresentationOptions; /** * The problem matchers attached to the task. Defaults to an empty diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 7c4e72c853daaf1f7997a5cd530486ebcc43ce0f..fbaaaaa5897eb8a270ef11993016d2ea9190e4ff 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -489,7 +489,7 @@ export function createApiFactory( ThemeColor: extHostTypes.ThemeColor, // functions TaskRevealKind: extHostTypes.TaskRevealKind, - TaskInstanceKind: extHostTypes.TaskInstanceKind, + TaskPanelKind: extHostTypes.TaskPanelKind, TaskGroup: extHostTypes.TaskGroup, ShellTask: extHostTypes.ShellTask, ProcessTask: extHostTypes.ProcessTask diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index 28721295f7df1c2f8cb8d365854e9247d5f35d06..6fbc70d641ea8cef848489f107a990c0239a91b5 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -207,32 +207,32 @@ namespace TaskRevealKind { } } -namespace TaskInstanceKind { - export function from(value: vscode.TaskInstanceKind): TaskSystem.InstanceKind { +namespace TaskPanelKind { + export function from(value: vscode.TaskPanelKind): TaskSystem.PanelKind { if (value === void 0 || value === null) { - return TaskSystem.InstanceKind.Shared; + return TaskSystem.PanelKind.Shared; } switch (value) { - case types.TaskInstanceKind.Same: - return TaskSystem.InstanceKind.Same; - case types.TaskInstanceKind.New: - return TaskSystem.InstanceKind.New; + case types.TaskPanelKind.Dedicated: + return TaskSystem.PanelKind.Dedicated; + case types.TaskPanelKind.New: + return TaskSystem.PanelKind.New; default: - return TaskSystem.InstanceKind.Shared; + return TaskSystem.PanelKind.Shared; } } } -namespace TerminalBehaviour { - export function from(value: vscode.TaskTerminalBehavior): TaskSystem.TerminalBehavior { +namespace PresentationOptions { + export function from(value: vscode.TaskPresentationOptions): TaskSystem.PresentationOptions { if (value === void 0 || value === null) { - return { reveal: TaskSystem.RevealKind.Always, echo: true, focus: false, instance: TaskSystem.InstanceKind.Shared }; + return { reveal: TaskSystem.RevealKind.Always, echo: true, focus: false, panel: TaskSystem.PanelKind.Shared }; } return { reveal: TaskRevealKind.from(value.reveal), echo: value.echo === void 0 ? true : !!value.echo, focus: !!value.focus, - instance: TaskInstanceKind.from(value.instance) + panel: TaskPanelKind.from(value.panel) }; } } @@ -359,7 +359,7 @@ namespace Tasks { args: Strings.from(value.args), type: TaskSystem.CommandType.Process, suppressTaskName: true, - terminalBehavior: TerminalBehaviour.from(value.terminalBehavior) + presentation: PresentationOptions.from(value.presentationOptions) }; if (value.options) { result.options = CommandOptions.from(value.options); @@ -374,7 +374,7 @@ namespace Tasks { let result: TaskSystem.CommandConfiguration = { name: value.commandLine, type: TaskSystem.CommandType.Shell, - terminalBehavior: TerminalBehaviour.from(value.terminalBehavior) + presentation: PresentationOptions.from(value.presentationOptions) }; if (value.options) { result.options = CommandOptions.from(value.options); diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index 5b60f3cff85b38401612e770d0bbcef64794b573..ed530ee907a43c0127f16e8f76f6fa8fb855670f 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -1037,10 +1037,10 @@ export enum TaskRevealKind { Never = 3 } -export enum TaskInstanceKind { +export enum TaskPanelKind { Shared = 1, - Same = 2, + Dedicated = 2, New = 3 } @@ -1053,7 +1053,7 @@ export class BaseTask { private _isBackground: boolean; private _source: string; private _group: string; - private _terminalBehavior: vscode.TaskTerminalBehavior; + private _presentationOptions: vscode.TaskPresentationOptions; constructor(name: string, problemMatchers: string[]) { if (typeof name !== 'string') { @@ -1062,7 +1062,7 @@ export class BaseTask { this._name = name; this._problemMatchers = problemMatchers || []; this._isBackground = false; - this._terminalBehavior = Object.create(null); + this._presentationOptions = Object.create(null); } get identifier(): string { @@ -1124,15 +1124,15 @@ export class BaseTask { this._group = value; } - get terminalBehavior(): vscode.TaskTerminalBehavior { - return this._terminalBehavior; + get presentationOptions(): vscode.TaskPresentationOptions { + return this._presentationOptions; } - set terminalBehavior(value: vscode.TaskTerminalBehavior) { + set presentationOptions(value: vscode.TaskPresentationOptions) { if (value === void 0 || value === null) { value = Object.create(null); } - this._terminalBehavior = value; + this._presentationOptions = value; } get problemMatchers(): string[] { diff --git a/src/vs/workbench/parts/tasks/browser/buildQuickOpen.ts b/src/vs/workbench/parts/tasks/browser/buildQuickOpen.ts index 0ef0d65fb3e0f94036f1f9f2d4296ae4ce889cf6..22e0bd0cd00048a37b0086e8c1e8c3ef6d5ce5d6 100644 --- a/src/vs/workbench/parts/tasks/browser/buildQuickOpen.ts +++ b/src/vs/workbench/parts/tasks/browser/buildQuickOpen.ts @@ -26,7 +26,7 @@ class TaskEntry extends base.TaskEntry { } let task = this._task; this.taskService.run(task); - if (task.command.terminalBehavior.focus) { + if (task.command.presentation.focus) { this.quickOpenService.close(); return false; } diff --git a/src/vs/workbench/parts/tasks/browser/taskQuickOpen.ts b/src/vs/workbench/parts/tasks/browser/taskQuickOpen.ts index 544826907d49127661898ca4ef8f5b12761d7611..e48d1d7d55cb094ad0f3ebf47f6014222717e2b7 100644 --- a/src/vs/workbench/parts/tasks/browser/taskQuickOpen.ts +++ b/src/vs/workbench/parts/tasks/browser/taskQuickOpen.ts @@ -28,7 +28,7 @@ class TaskEntry extends base.TaskEntry { } let task = this._task; this.taskService.run(task); - if (task.command.terminalBehavior.focus) { + if (task.command.presentation.focus) { this.quickOpenService.close(); return false; } diff --git a/src/vs/workbench/parts/tasks/browser/testQuickOpen.ts b/src/vs/workbench/parts/tasks/browser/testQuickOpen.ts index ff9b506b36ec5e869c9df6a8ffd80c7d61c2b029..4c7f59844b0caca7426d5034d61daab8c7369940 100644 --- a/src/vs/workbench/parts/tasks/browser/testQuickOpen.ts +++ b/src/vs/workbench/parts/tasks/browser/testQuickOpen.ts @@ -26,7 +26,7 @@ class TaskEntry extends base.TaskEntry { } let task = this._task; this.taskService.run(task); - if (task.command.terminalBehavior.focus) { + if (task.command.presentation.focus) { this.quickOpenService.close(); return false; } diff --git a/src/vs/workbench/parts/tasks/common/taskConfiguration.ts b/src/vs/workbench/parts/tasks/common/taskConfiguration.ts index 9b07f84f16fa951b9ef46badbf743abef6055828..75422d4ef674b93f7c8cbe244dfce64ba9ee1176 100644 --- a/src/vs/workbench/parts/tasks/common/taskConfiguration.ts +++ b/src/vs/workbench/parts/tasks/common/taskConfiguration.ts @@ -234,16 +234,27 @@ export interface BaseTaskRunnerConfiguration { /** * Controls the behavior of the used terminal */ - terminal?: { + presentation?: { /** - * The terminal should echo the run command. + * Controls whether the terminal executing a task is brought to front or not. + * Defaults to `RevealKind.Always`. + */ + reveal?: string; + + /** + * Controls whether the executed command is printed to the output window or terminal as well. */ echo?: boolean; + /** - * Controls whether or not the terminal is reveal if a task - * is executed. + * Controls whether the terminal is focus when this task is executed */ - reveal?: string; + focus?: boolean; + + /** + * Controls whether the task runs in a new terminal + */ + panel?: string; }; /** @@ -596,11 +607,11 @@ namespace CommandOptions { namespace CommandConfiguration { - interface TerminalBehavior { + interface PresentationOptions { echo?: boolean; reveal?: string; focus?: boolean; - instance?: string; + panel?: string; } interface BaseCommandConfiguationShape { @@ -611,7 +622,11 @@ namespace CommandConfiguration { options?: CommandOptions; echoCommand?: boolean; showOutput?: string; - terminal?: TerminalBehavior; + /** + * @deprecated Use panel instead. + */ + terminal?: PresentationOptions; + presentation?: PresentationOptions; taskSelector?: string; suppressTaskName?: boolean; } @@ -622,66 +637,67 @@ namespace CommandConfiguration { linux?: BaseCommandConfiguationShape; } - export namespace TerminalBehavior { - const properties: MetaData[] = [{ property: 'echo' }, { property: 'reveal' }, { property: 'focus' }, { property: 'instance' }]; + export namespace PresentationOptions { + const properties: MetaData[] = [{ property: 'echo' }, { property: 'reveal' }, { property: 'focus' }, { property: 'panel' }]; - export function from(this: void, config: BaseCommandConfiguationShape, context: ParseContext): Tasks.TerminalBehavior { + export function from(this: void, config: BaseCommandConfiguationShape, context: ParseContext): Tasks.PresentationOptions { let echo: boolean; let reveal: Tasks.RevealKind; let focus: boolean; - let instance: Tasks.InstanceKind; + let panel: Tasks.PanelKind; if (Types.isBoolean(config.echoCommand)) { echo = config.echoCommand; } if (Types.isString(config.showOutput)) { reveal = Tasks.RevealKind.fromString(config.showOutput); } - if (config.terminal) { - if (Types.isBoolean(config.terminal.echo)) { - echo = config.terminal.echo; + let presentation = config.presentation || config.terminal; + if (presentation) { + if (Types.isBoolean(presentation.echo)) { + echo = presentation.echo; } - if (Types.isString(config.terminal.reveal)) { - reveal = Tasks.RevealKind.fromString(config.terminal.reveal); + if (Types.isString(presentation.reveal)) { + reveal = Tasks.RevealKind.fromString(presentation.reveal); } - if (Types.isBoolean(config.terminal.focus)) { - focus = config.terminal.focus; + if (Types.isBoolean(presentation.focus)) { + focus = presentation.focus; } - if (Types.isString(config.terminal.instance)) { - instance = Tasks.InstanceKind.fromString(config.terminal.instance); + if (Types.isString(presentation.panel)) { + panel = Tasks.PanelKind.fromString(presentation.panel); } } - if (echo === void 0 && reveal === void 0 && focus === void 0 && instance === void 0) { + if (echo === void 0 && reveal === void 0 && focus === void 0 && panel === void 0) { return undefined; } - return { echo, reveal, focus, instance }; + return { echo, reveal, focus, panel }; } - export function assignProperties(target: Tasks.TerminalBehavior, source: Tasks.TerminalBehavior): Tasks.TerminalBehavior { + export function assignProperties(target: Tasks.PresentationOptions, source: Tasks.PresentationOptions): Tasks.PresentationOptions { return _assignProperties(target, source, properties); } - export function fillProperties(target: Tasks.TerminalBehavior, source: Tasks.TerminalBehavior): Tasks.TerminalBehavior { + export function fillProperties(target: Tasks.PresentationOptions, source: Tasks.PresentationOptions): Tasks.PresentationOptions { return _fillProperties(target, source, properties); } - export function fillDefaults(value: Tasks.TerminalBehavior, context: ParseContext): Tasks.TerminalBehavior { + export function fillDefaults(value: Tasks.PresentationOptions, context: ParseContext): Tasks.PresentationOptions { let defaultEcho = context.engine === Tasks.ExecutionEngine.Terminal ? true : false; - return _fillDefaults(value, { echo: defaultEcho, reveal: Tasks.RevealKind.Always, focus: false, instance: Tasks.InstanceKind.Shared }, properties, context); + return _fillDefaults(value, { echo: defaultEcho, reveal: Tasks.RevealKind.Always, focus: false, panel: Tasks.PanelKind.Shared }, properties, context); } - export function freeze(value: Tasks.TerminalBehavior): Readonly { + export function freeze(value: Tasks.PresentationOptions): Readonly { return _freeze(value, properties); } - export function isEmpty(this: void, value: Tasks.TerminalBehavior): boolean { + export function isEmpty(this: void, value: Tasks.PresentationOptions): boolean { return _isEmpty(value, properties); } } - const properties: MetaData[] = [ + const properties: MetaData[] = [ { property: 'type' }, { property: 'name' }, { property: 'options', type: CommandOptions }, { property: 'args' }, { property: 'taskSelector' }, { property: 'suppressTaskName' }, - { property: 'terminalBehavior', type: TerminalBehavior } + { property: 'presentation', type: PresentationOptions } ]; export function from(this: void, config: CommandConfiguationShape, context: ParseContext): Tasks.CommandConfiguration { @@ -705,7 +721,7 @@ namespace CommandConfiguration { let result: Tasks.CommandConfiguration = { name: undefined, type: undefined, - terminalBehavior: undefined + presentation: undefined }; if (Types.isString(config.command)) { result.name = config.command; @@ -735,9 +751,9 @@ namespace CommandConfiguration { } } } - let terminal = TerminalBehavior.from(config, context); - if (terminal) { - result.terminalBehavior = terminal; + let panel = PresentationOptions.from(config, context); + if (panel) { + result.presentation = panel; } if (Types.isString(config.taskSelector)) { result.taskSelector = config.taskSelector; @@ -754,7 +770,7 @@ namespace CommandConfiguration { export function onlyTerminalBehaviour(value: Tasks.CommandConfiguration): boolean { return value && - value.terminalBehavior && (value.terminalBehavior.echo !== void 0 || value.terminalBehavior.reveal !== void 0) && + value.presentation && (value.presentation.echo !== void 0 || value.presentation.reveal !== void 0) && value.name === void 0 && value.type === void 0 && value.args === void 0 && CommandOptions.isEmpty(value.options); } @@ -776,7 +792,7 @@ namespace CommandConfiguration { target.args = target.args.concat(source.args); } } - target.terminalBehavior = TerminalBehavior.assignProperties(target.terminalBehavior, source.terminalBehavior); + target.presentation = PresentationOptions.assignProperties(target.presentation, source.presentation); target.options = CommandOptions.assignProperties(target.options, source.options); return target; } @@ -788,14 +804,14 @@ namespace CommandConfiguration { target = target || { name: undefined, type: undefined, - terminalBehavior: undefined + presentation: undefined }; fillProperty(target, source, 'name'); fillProperty(target, source, 'type'); fillProperty(target, source, 'taskSelector'); fillProperty(target, source, 'suppressTaskName'); - target.terminalBehavior = TerminalBehavior.fillProperties(target.terminalBehavior, source.terminalBehavior); + target.presentation = PresentationOptions.fillProperties(target.presentation, source.presentation); target.options = CommandOptions.fillProperties(target.options, source.options); let args: string[] = source.args ? source.args.slice() : []; @@ -820,7 +836,7 @@ namespace CommandConfiguration { if (value.name !== void 0 && value.type === void 0) { value.type = Tasks.CommandType.Process; } - value.terminalBehavior = TerminalBehavior.fillDefaults(value.terminalBehavior, context); + value.presentation = PresentationOptions.fillDefaults(value.presentation, context); if (!isEmpty(value)) { value.options = CommandOptions.fillDefaults(value.options, context); } @@ -1415,7 +1431,7 @@ class ConfigurationParser { command: { name: undefined, type: undefined, - terminalBehavior: undefined, + presentation: undefined, suppressTaskName: true }, isBackground: isBackground, diff --git a/src/vs/workbench/parts/tasks/common/tasks.ts b/src/vs/workbench/parts/tasks/common/tasks.ts index ca4b93f5422794444c0d9735149b52bd8d80646e..d84a4ce1f1422f1f3b8770c217cab5c37fa729d8 100644 --- a/src/vs/workbench/parts/tasks/common/tasks.ts +++ b/src/vs/workbench/parts/tasks/common/tasks.ts @@ -80,61 +80,64 @@ export namespace RevealKind { } } -export enum InstanceKind { +export enum PanelKind { /** - * Shares a terminal with other tasks. This is the default. + * Shares a panel with other tasks. This is the default. */ Shared = 1, /** - * Uses the same terminal for every run if possible. The terminal is not + * Uses a dedicated panel for this tasks. The panel is not * shared with other tasks. */ - Same = 2, + Dedicated = 2, /** - * Creates a new terminal whenever that task is executed + * Creates a new panel whenever this task is executed. */ New = 3 } -export namespace InstanceKind { - export function fromString(value: string): InstanceKind { +export namespace PanelKind { + export function fromString(value: string): PanelKind { switch (value.toLowerCase()) { case 'shared': - return InstanceKind.Shared; - case 'same': - return InstanceKind.Same; + return PanelKind.Shared; + case 'dedicated': + return PanelKind.Dedicated; case 'new': - return InstanceKind.New; + return PanelKind.New; default: - return InstanceKind.Shared; + return PanelKind.Shared; } } } -export interface TerminalBehavior { +export interface PresentationOptions { /** - * Controls whether the terminal executing a task is brought to front or not. + * Controls whether the task output is reveal in the user interface. * Defaults to `RevealKind.Always`. */ reveal: RevealKind; /** - * Controls whether the executed command is printed to the output window or terminal as well. + * Controls whether the command associated with the task is echoed + * in the user interface. */ echo: boolean; /** - * Controls whether the terminal is focus when this task is executed + * Controls whether the panel showing the task output is taking focus. */ focus: boolean; /** - * Controls whether the task runs in a new terminal + * Controls if the task panel is used for this task only (dedicated), + * shared between tasks (shared) or if a new panel is created on + * every task execution (new). Defaults to `TaskInstanceKind.Shared` */ - instance: InstanceKind; + panel: PanelKind; } export enum CommandType { @@ -189,9 +192,9 @@ export interface CommandConfiguration { suppressTaskName?: boolean; /** - * Describes how the terminal is supposed to behave. + * Describes how the task is presented in the UI. */ - terminalBehavior: TerminalBehavior; + presentation: PresentationOptions; } export namespace TaskGroup { diff --git a/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts b/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts index 554f6a9bc39586dd8ffff95a69c09e24044b92ea..f9c2f251a895163b91b13597a5622f6c6500902c 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts @@ -43,23 +43,35 @@ const dependsOn: IJSONSchema = { ] }; -const terminal: IJSONSchema = { +const presentation: IJSONSchema = { type: 'object', default: { reveal: 'always' }, - description: nls.localize('JsonSchema.tasks.terminal', 'Configures the terminal that is used to execute the task.'), + description: nls.localize('JsonSchema.tasks.terminal', 'Configures the panel that is used to present the task\'s ouput and reads its input.'), + additionalProperties: false, properties: { echo: { + type: 'boolean', + default: true, + description: nls.localize('JsonSchema.tasks.terminal.echo', 'Controls whether the executed command is echoed to the panel. Default is true.') + }, + focus: { type: 'boolean', default: false, - description: nls.localize('JsonSchema.tasks.terminal.echo', 'Controls whether the executed command is echoed to the terminal. Default is false.') + description: nls.localize('JsonSchema.tasks.terminal.focus', 'Controls whether the panel takes focus. Default is false. If set to true the panel is revealed as well.') }, reveal: { type: 'string', enum: ['always', 'silent', 'never'], default: 'always', - description: nls.localize('JsonSchema.tasks.terminal.reveals', 'Controls whether the terminal running the task is revealed or not. Default is \"always\".') + description: nls.localize('JsonSchema.tasks.terminal.reveals', 'Controls whether the panel running the task is revealed or not. Default is \"always\".') + }, + panel: { + type: 'string', + enum: ['shared', 'dedicated', 'new'], + default: 'shared', + description: nls.localize('JsonSchema.tasks.terminal.instance', 'Controls if the panel is shared between tasks, dedicated to this task or a new one is created on every run.') } } }; @@ -131,7 +143,7 @@ definitions.taskDescription.properties.dependsOn = dependsOn; // definitions.taskDescription.properties.isTestCommand.deprecationMessage = nls.localize('JsonSchema.tasks.isTestCommand.deprecated', 'The property isTestCommand is deprecated. Use the group property instead.'); definitions.taskDescription.properties.customize = customize; definitions.taskDescription.properties.type = Objects.deepClone(taskType); -definitions.taskDescription.properties.terminal = terminal; +definitions.taskDescription.properties.presentation = presentation; definitions.taskDescription.properties.group = group; definitions.options.properties.shell = { $ref: '#/definitions/shellConfiguration' diff --git a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts index 8f43026ea2186a0491290e76e075527d6a235d15..30ff0d963aef954676ca39366f9281b0b1c10475 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts @@ -15,6 +15,7 @@ import * as Platform from 'vs/base/common/platform'; import * as Async from 'vs/base/common/async'; import { TPromise } from 'vs/base/common/winjs.base'; import { IStringDictionary } from 'vs/base/common/collections'; +import { LinkedMap, Touch } from 'vs/base/common/map'; import Severity from 'vs/base/common/severity'; import { EventEmitter } from 'vs/base/common/eventEmitter'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -33,7 +34,7 @@ import { IConfigurationResolverService } from 'vs/workbench/services/configurati import { ITerminalService, ITerminalInstance, IShellLaunchConfig } from 'vs/workbench/parts/terminal/common/terminal'; import { IOutputService, IOutputChannel } from 'vs/workbench/parts/output/common/output'; import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEvents } from 'vs/workbench/parts/tasks/common/problemCollectors'; -import { Task, RevealKind, CommandOptions, ShellConfiguration, CommandType } from 'vs/workbench/parts/tasks/common/tasks'; +import { Task, RevealKind, CommandOptions, ShellConfiguration, CommandType, PanelKind } from 'vs/workbench/parts/tasks/common/tasks'; import { ITaskSystem, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, ITaskResolver, TelemetryEvent, Triggers, TaskSystemEvents, TaskEvent, TaskType @@ -105,9 +106,9 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem { private outputChannel: IOutputChannel; private activeTasks: IStringDictionary; - private primaryTerminal: PrimaryTerminal; private terminals: IStringDictionary; - private idleTaskTerminals: IStringDictionary; + private idleTaskTerminals: LinkedMap; + private sameTaskTerminals: IStringDictionary; constructor(private terminalService: ITerminalService, private outputService: IOutputService, private markerService: IMarkerService, private modelService: IModelService, @@ -120,7 +121,8 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem { this.outputChannel = this.outputService.getChannel(outputChannelId); this.activeTasks = Object.create(null); this.terminals = Object.create(null); - this.idleTaskTerminals = Object.create(null); + this.idleTaskTerminals = new LinkedMap(); + this.sameTaskTerminals = Object.create(null); } public log(value: string): void { @@ -134,8 +136,8 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem { public run(task: Task, resolver: ITaskResolver, trigger: string = Triggers.command): ITaskExecuteResult { let terminalData = this.activeTasks[task._id]; if (terminalData && terminalData.promise) { - let reveal = task.command.terminalBehavior.reveal; - let focus = task.command.terminalBehavior.focus; + let reveal = task.command.presentation.reveal; + let focus = task.command.presentation.focus; if (reveal === RevealKind.Always || focus) { this.terminalService.setActiveInstance(terminalData.terminal); this.terminalService.showPanel(focus); @@ -275,10 +277,14 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem { onData.dispose(); onExit.dispose(); delete this.activeTasks[task._id]; - if (this.primaryTerminal && this.primaryTerminal.terminal === terminal) { - this.primaryTerminal.busy = false; + switch (task.command.presentation.panel) { + case PanelKind.Dedicated: + this.sameTaskTerminals[task._id] = terminal.id.toString(); + break; + case PanelKind.Shared: + this.idleTaskTerminals.set(task._id, terminal.id.toString(), Touch.First); + break; } - this.idleTaskTerminals[task._id] = terminal.id.toString(); let remaining = decoder.end(); if (remaining) { watchingProblemMatcher.processLine(remaining); @@ -291,7 +297,7 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem { this.emit(TaskSystemEvents.Inactive, event); } eventCounter = 0; - let reveal = task.command.terminalBehavior.reveal; + let reveal = task.command.presentation.reveal; if (exitCode && exitCode === 1 && watchingProblemMatcher.numberOfMatches === 0 && reveal !== RevealKind.Never) { this.terminalService.setActiveInstance(terminal); this.terminalService.showPanel(false); @@ -317,10 +323,14 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem { onData.dispose(); onExit.dispose(); delete this.activeTasks[task._id]; - if (this.primaryTerminal && this.primaryTerminal.terminal === terminal) { - this.primaryTerminal.busy = false; + switch (task.command.presentation.panel) { + case PanelKind.Dedicated: + this.sameTaskTerminals[task._id] = terminal.id.toString(); + break; + case PanelKind.Shared: + this.idleTaskTerminals.set(task._id, terminal.id.toString(), Touch.First); + break; } - this.idleTaskTerminals[task._id] = terminal.id.toString(); let remaining = decoder.end(); if (remaining) { startStopProblemMatcher.processLine(remaining); @@ -334,8 +344,8 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem { }); } this.terminalService.setActiveInstance(terminal); - if (task.command.terminalBehavior.reveal === RevealKind.Always || (task.command.terminalBehavior.reveal === RevealKind.Silent && task.problemMatchers.length === 0)) { - this.terminalService.showPanel(task.command.terminalBehavior.focus); + if (task.command.presentation.reveal === RevealKind.Always || (task.command.presentation.reveal === RevealKind.Silent && task.problemMatchers.length === 0)) { + this.terminalService.showPanel(task.command.presentation.focus); } this.activeTasks[task._id] = { terminal, task, promise }; return promise.then((summary) => { @@ -371,7 +381,7 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem { let { command, args } = this.resolveCommandAndArgs(task); let terminalName = nls.localize('TerminalTaskSystem.terminalName', 'Task - {0}', task.name); let waitOnExit: boolean | string = false; - if (task.command.terminalBehavior.reveal !== RevealKind.Never || !task.isBackground) { + if (task.command.presentation.reveal !== RevealKind.Never || !task.isBackground) { waitOnExit = nls.localize('reuseTerminal', 'Terminal will be reused by tasks, press any key to close it.'); }; let shellLaunchConfig: IShellLaunchConfig = undefined; @@ -424,7 +434,7 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem { }); shellArgs.push(commandLine); shellLaunchConfig.args = Platform.isWindows ? shellArgs.join(' ') : shellArgs; - if (task.command.terminalBehavior.echo) { + if (task.command.presentation.echo) { shellLaunchConfig.initialText = `\x1b[1m> Executing task: ${commandLine} <\x1b[0m\n`; } } else { @@ -438,7 +448,7 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem { args, waitOnExit }; - if (task.command.terminalBehavior.echo) { + if (task.command.presentation.echo) { let getArgsToEcho = (args: string | string[]): string => { if (!args || args.length === 0) { return ''; @@ -464,41 +474,38 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem { }); shellLaunchConfig.env = env; } - let terminalId = this.idleTaskTerminals[task._id]; - if (terminalId) { - let taskTerminal = this.terminals[terminalId]; - if (taskTerminal) { - delete this.idleTaskTerminals[task._id]; - taskTerminal.terminal.reuseTerminal(shellLaunchConfig); - return [taskTerminal.terminal, command]; + let prefersSameTerminal = task.command.presentation.panel === PanelKind.Dedicated; + let allowsSharedTerminal = task.command.presentation.panel === PanelKind.Shared; + + let terminalToReuse: TerminalData; + if (prefersSameTerminal) { + let terminalId = this.sameTaskTerminals[task._id]; + if (terminalId) { + terminalToReuse = this.terminals[terminalId]; + delete this.sameTaskTerminals[task._id]; } - } - if (this.primaryTerminal && !this.primaryTerminal.busy) { - // We reuse the primary terminal. Make sure the last running task isn't referenced in the idle terminals - let terminalData = this.terminals[this.primaryTerminal.terminal.id.toString()]; - if (terminalData) { - delete this.idleTaskTerminals[terminalData.lastTask]; + } else if (allowsSharedTerminal) { + let terminalId = this.idleTaskTerminals.remove(task._id) || this.idleTaskTerminals.shift(); + if (terminalId) { + terminalToReuse = this.terminals[terminalId]; } - this.primaryTerminal.terminal.reuseTerminal(shellLaunchConfig); - this.primaryTerminal.busy = true; - return [this.primaryTerminal.terminal, command]; } + if (terminalToReuse) { + terminalToReuse.terminal.reuseTerminal(shellLaunchConfig); + return [terminalToReuse.terminal, command]; + } + const result = this.terminalService.createInstance(shellLaunchConfig); const key = result.id.toString(); result.onDisposed((terminal) => { let terminalData = this.terminals[key]; if (terminalData) { delete this.terminals[key]; - delete this.idleTaskTerminals[terminalData.lastTask]; - } - if (this.primaryTerminal && this.primaryTerminal.terminal === terminal) { - this.primaryTerminal = undefined; + delete this.sameTaskTerminals[terminalData.lastTask]; + this.idleTaskTerminals.delete(terminalData.lastTask); } }); this.terminals[key] = { terminal: result, lastTask: task._id }; - if (!task.isBackground && !this.primaryTerminal) { - this.primaryTerminal = { terminal: result, busy: true }; - } return [result, command]; } diff --git a/src/vs/workbench/parts/tasks/node/processTaskSystem.ts b/src/vs/workbench/parts/tasks/node/processTaskSystem.ts index 0db8946a2d29db3ff1400e63a6104adf559e3497..56bb36e37456c3d736fc5dc75514135e8234901a 100644 --- a/src/vs/workbench/parts/tasks/node/processTaskSystem.ts +++ b/src/vs/workbench/parts/tasks/node/processTaskSystem.ts @@ -167,12 +167,12 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem { this.childProcess = new LineProcess(command, args, commandConfig.type === CommandType.Shell, this.resolveOptions(commandConfig.options)); telemetryEvent.command = this.childProcess.getSanitizedCommand(); // we have no problem matchers defined. So show the output log - let reveal = task.command.terminalBehavior.reveal; + let reveal = task.command.presentation.reveal; if (reveal === RevealKind.Always || (reveal === RevealKind.Silent && task.problemMatchers.length === 0)) { this.showOutput(); } - if (commandConfig.terminalBehavior.echo) { + if (commandConfig.presentation.echo) { let prompt: string = Platform.isWindows ? '>' : '$'; this.log(`running command${prompt} ${command} ${args.join(' ')}`); } diff --git a/src/vs/workbench/parts/tasks/test/node/configuration.test.ts b/src/vs/workbench/parts/tasks/test/node/configuration.test.ts index 2d60a06a146054d66357fda50ea328a88cb1bdca..9c12f67048d590d182dfc9bcb10c435b300696a1 100644 --- a/src/vs/workbench/parts/tasks/test/node/configuration.test.ts +++ b/src/vs/workbench/parts/tasks/test/node/configuration.test.ts @@ -77,31 +77,31 @@ class ConfiguationBuilder { } } -class TerminalBehaviorBuilder { +class PresentationBuilder { - public result: Tasks.TerminalBehavior; + public result: Tasks.PresentationOptions; constructor(public parent: CommandConfigurationBuilder) { - this.result = { echo: false, reveal: Tasks.RevealKind.Always, focus: false, instance: Tasks.InstanceKind.Shared }; + this.result = { echo: false, reveal: Tasks.RevealKind.Always, focus: false, panel: Tasks.PanelKind.Shared }; } - public echo(value: boolean): TerminalBehaviorBuilder { + public echo(value: boolean): PresentationBuilder { this.result.echo = value; return this; } - public reveal(value: Tasks.RevealKind): TerminalBehaviorBuilder { + public reveal(value: Tasks.RevealKind): PresentationBuilder { this.result.reveal = value; return this; } - public focus(value: boolean): TerminalBehaviorBuilder { + public focus(value: boolean): PresentationBuilder { this.result.focus = value; return this; } - public instance(value: Tasks.InstanceKind): TerminalBehaviorBuilder { - this.result.instance = value; + public instance(value: Tasks.PanelKind): PresentationBuilder { + this.result.panel = value; return this; } @@ -112,10 +112,10 @@ class TerminalBehaviorBuilder { class CommandConfigurationBuilder { public result: Tasks.CommandConfiguration; - private terminalBuilder: TerminalBehaviorBuilder; + private presentationBuilder: PresentationBuilder; constructor(public parent: TaskBuilder, command: string) { - this.terminalBuilder = new TerminalBehaviorBuilder(this); + this.presentationBuilder = new PresentationBuilder(this); this.result = { name: command, type: Tasks.CommandType.Process, @@ -123,7 +123,7 @@ class CommandConfigurationBuilder { options: { cwd: '${workspaceRoot}' }, - terminalBehavior: this.terminalBuilder.result, + presentation: this.presentationBuilder.result, suppressTaskName: false }; } @@ -158,13 +158,13 @@ class CommandConfigurationBuilder { return this; } - public terminal(): TerminalBehaviorBuilder { - return this.terminalBuilder; + public presentation(): PresentationBuilder { + return this.presentationBuilder; } public done(taskName: string): void { this.result.args = this.result.args.map(arg => arg === '$name' ? taskName : arg); - this.terminalBuilder.done(); + this.presentationBuilder.done(); } } @@ -458,7 +458,7 @@ function assertTask(actual: Tasks.Task, expected: Tasks.Task) { function assertCommandConfiguration(actual: Tasks.CommandConfiguration, expected: Tasks.CommandConfiguration) { assert.strictEqual(typeof actual, typeof expected); if (actual && expected) { - assertTerminalBehavior(actual.terminalBehavior, expected.terminalBehavior); + assertPresentation(actual.presentation, expected.presentation); assert.strictEqual(actual.name, expected.name, 'name'); assert.strictEqual(actual.type, expected.type, 'task type'); assert.strictEqual(actual.suppressTaskName, expected.suppressTaskName, 'suppressTaskName'); @@ -475,7 +475,7 @@ function assertCommandConfiguration(actual: Tasks.CommandConfiguration, expected } } -function assertTerminalBehavior(actual: Tasks.TerminalBehavior, expected: Tasks.TerminalBehavior) { +function assertPresentation(actual: Tasks.PresentationOptions, expected: Tasks.PresentationOptions) { assert.strictEqual(typeof actual, typeof expected); if (actual && expected) { assert.strictEqual(actual.echo, expected.echo); @@ -574,7 +574,7 @@ suite('Tasks version 0.1.0', () => { task('tsc', 'tsc'). group(Tasks.TaskGroup.Build). command().suppressTaskName(true). - terminal().reveal(Tasks.RevealKind.Silent); + presentation().reveal(Tasks.RevealKind.Silent); testConfiguration( { version: '0.1.0', @@ -639,7 +639,7 @@ suite('Tasks version 0.1.0', () => { task('tsc', 'tsc'). group(Tasks.TaskGroup.Build). command().suppressTaskName(true). - terminal().reveal(Tasks.RevealKind.Never); + presentation().reveal(Tasks.RevealKind.Never); testConfiguration( { version: '0.1.0', @@ -656,7 +656,7 @@ suite('Tasks version 0.1.0', () => { task('tsc', 'tsc'). group(Tasks.TaskGroup.Build). command().suppressTaskName(true). - terminal(). + presentation(). echo(true); testConfiguration( { @@ -805,7 +805,7 @@ suite('Tasks version 0.1.0', () => { task('tsc', 'tsc'). group(Tasks.TaskGroup.Build). command().suppressTaskName(true). - terminal().reveal(Platform.isWindows ? Tasks.RevealKind.Always : Tasks.RevealKind.Never); + presentation().reveal(Platform.isWindows ? Tasks.RevealKind.Always : Tasks.RevealKind.Never); let external: ExternalTaskRunnerConfiguration = { version: '0.1.0', command: 'tsc', @@ -823,7 +823,7 @@ suite('Tasks version 0.1.0', () => { task('tsc', 'tsc'). group(Tasks.TaskGroup.Build). command().suppressTaskName(true). - terminal(). + presentation(). echo(Platform.isWindows ? false : true); let external: ExternalTaskRunnerConfiguration = { version: '0.1.0', @@ -951,7 +951,7 @@ suite('Tasks version 0.1.0', () => { isBackground(true). promptOnClose(false). command().args(['$name', '--p']). - terminal(). + presentation(). echo(true).reveal(Tasks.RevealKind.Never); testConfiguration(external, builder); @@ -972,7 +972,7 @@ suite('Tasks version 0.1.0', () => { let builder = new ConfiguationBuilder(); builder.task('test', 'tsc'). group(Tasks.TaskGroup.Test). - command().args(['$name']).terminal(). + command().args(['$name']).presentation(). echo(true).reveal(Tasks.RevealKind.Never); testConfiguration(external, builder); @@ -1457,7 +1457,7 @@ suite('Tasks version 2.0.0', () => { group(Tasks.TaskGroup.Build). command().suppressTaskName(true). type(Tasks.CommandType.Shell). - terminal().echo(true); + presentation().echo(true); testConfiguration(external, builder); }); @@ -1518,14 +1518,14 @@ suite('Bugs / regression tests', () => { command().suppressTaskName(true). args(['-ExecutionPolicy', 'RemoteSigned', '.\\dockerTask.ps1', '-ComposeForDebug', '-Environment', 'debug']). options({ cwd: '${workspaceRoot}' }). - terminal().echo(true).reveal(Tasks.RevealKind.Always); + presentation().echo(true).reveal(Tasks.RevealKind.Always); testConfiguration(external, builder); } else if (Platform.isMacintosh) { builder.task('composeForDebug', '/bin/bash'). command().suppressTaskName(true). args(['-c', './dockerTask.sh composeForDebug debug']). options({ cwd: '${workspaceRoot}' }). - terminal().reveal(Tasks.RevealKind.Always); + presentation().reveal(Tasks.RevealKind.Always); testConfiguration(external, builder); } });