提交 23c63187 编写于 作者: D Dirk Baeumer

Add support to focus terminal and to control terminal instance reuse

上级 972b9a12
......@@ -28,6 +28,28 @@ declare module 'vscode' {
Never = 3
}
/**
* Controls how the task channel is used between tasks
*/
export enum TaskInstanceKind {
/**
* Shares a channel with other tasks. This is the default.
*/
Shared = 1,
/**
* Uses the same task channel for every run if possible. The task channel is not
* shared with other tasks.
*/
Same = 2,
/**
* Creates a new task channel whenever that task is executed
*/
New = 3
}
/**
* Controls terminal specific behavior.
*/
......@@ -42,6 +64,16 @@ declare module 'vscode' {
* Controls whether the command is echoed in the terminal or not.
*/
echo?: boolean;
/**
* Controls whether the task pane takes focus when the task is executed
*/
focus?: boolean;
/**
* Controls in which pane the task is executed.
*/
instance?: TaskInstanceKind;
}
export interface ProcessTaskOptions {
......
......@@ -489,6 +489,7 @@ export function createApiFactory(
ThemeColor: extHostTypes.ThemeColor,
// functions
TaskRevealKind: extHostTypes.TaskRevealKind,
TaskInstanceKind: extHostTypes.TaskInstanceKind,
TaskGroup: extHostTypes.TaskGroup,
ShellTask: extHostTypes.ShellTask,
ProcessTask: extHostTypes.ProcessTask
......
......@@ -207,12 +207,33 @@ namespace TaskRevealKind {
}
}
namespace TaskInstanceKind {
export function from(value: vscode.TaskInstanceKind): TaskSystem.InstanceKind {
if (value === void 0 || value === null) {
return TaskSystem.InstanceKind.Shared;
}
switch (value) {
case types.TaskInstanceKind.Same:
return TaskSystem.InstanceKind.Same;
case types.TaskInstanceKind.New:
return TaskSystem.InstanceKind.New;
default:
return TaskSystem.InstanceKind.Shared;
}
}
}
namespace TerminalBehaviour {
export function from(value: vscode.TaskTerminalBehavior): TaskSystem.TerminalBehavior {
if (value === void 0 || value === null) {
return { reveal: TaskSystem.RevealKind.Always, echo: false };
return { reveal: TaskSystem.RevealKind.Always, echo: true, focus: false, instance: TaskSystem.InstanceKind.Shared };
}
return { reveal: TaskRevealKind.from(value.reveal), echo: !!value.echo };
return {
reveal: TaskRevealKind.from(value.reveal),
echo: value.echo === void 0 ? true : !!value.echo,
focus: !!value.focus,
instance: TaskInstanceKind.from(value.instance)
};
}
}
......
......@@ -1037,6 +1037,14 @@ export enum TaskRevealKind {
Never = 3
}
export enum TaskInstanceKind {
Shared = 1,
Same = 2,
New = 3
}
export class BaseTask {
private _name: string;
......
......@@ -16,15 +16,20 @@ import { ITaskService } from 'vs/workbench/parts/tasks/common/taskService';
import * as base from './quickOpen';
class TaskEntry extends base.TaskEntry {
constructor(taskService: ITaskService, task: Task, highlights: Model.IHighlight[] = []) {
super(taskService, task, highlights);
constructor(taskService: ITaskService, quickOpenService: IQuickOpenService, task: Task, highlights: Model.IHighlight[] = []) {
super(taskService, quickOpenService, task, highlights);
}
public run(mode: QuickOpen.Mode, context: Model.IContext): boolean {
if (mode === QuickOpen.Mode.PREVIEW) {
return false;
}
this.taskService.run(this._task);
let task = this._task;
this.taskService.run(task);
if (task.command.terminalBehavior.focus) {
this.quickOpenService.close();
return false;
}
return true;
}
}
......@@ -45,8 +50,8 @@ export class QuickOpenHandler extends base.QuickOpenHandler {
return this.taskService.getTasksForGroup(TaskGroup.Build);
}
protected createEntry(taskService: ITaskService, task: Task, highlights: Model.IHighlight[]): base.TaskEntry {
return new TaskEntry(taskService, task, highlights);
protected createEntry(task: Task, highlights: Model.IHighlight[]): base.TaskEntry {
return new TaskEntry(this.taskService, this.quickOpenService, task, highlights);
}
public getEmptyLabel(searchString: string): string {
......
......@@ -21,7 +21,7 @@ import { ActionBarContributor, ContributableActionProvider } from 'vs/workbench/
export class TaskEntry extends Model.QuickOpenEntry {
constructor(protected taskService: ITaskService, protected _task: Task, highlights: Model.IHighlight[] = []) {
constructor(protected taskService: ITaskService, protected quickOpenService: IQuickOpenService, protected _task: Task, highlights: Model.IHighlight[] = []) {
super(highlights);
}
......@@ -75,7 +75,8 @@ export abstract class QuickOpenHandler extends Quickopen.QuickOpenHandler {
}
let recentlyUsedTasks = this.taskService.getRecentlyUsedTasks();
let recent: Task[] = [];
let others: Task[] = [];
let configured: Task[] = [];
let detected: Task[] = [];
let taskMap: IStringDictionary<Task> = Object.create(null);
tasks.forEach(task => taskMap[task.identifier] = task);
recentlyUsedTasks.keys().forEach(key => {
......@@ -86,37 +87,43 @@ export abstract class QuickOpenHandler extends Quickopen.QuickOpenHandler {
});
for (let task of tasks) {
if (!recentlyUsedTasks.has(task.identifier)) {
others.push(task);
}
}
others = others.sort((a, b) => a._source.label.localeCompare(b._source.label));
let sortedTasks = recent.concat(others);
let recentlyUsed: boolean = recentlyUsedTasks.has(tasks[0].identifier);
let otherTasks: boolean = !recentlyUsedTasks.has(tasks[tasks.length - 1].identifier);
let hasRecentlyUsed: boolean = false;
for (let task of sortedTasks) {
let highlights = Filters.matchesContiguousSubString(input, task._label);
if (!highlights) {
continue;
}
if (recentlyUsed) {
recentlyUsed = false;
hasRecentlyUsed = true;
entries.push(new TaskGroupEntry(this.createEntry(this.taskService, task, highlights), nls.localize('recentlyUsed', 'recently used'), false));
} else if (!recentlyUsedTasks.has(task.identifier) && otherTasks) {
otherTasks = false;
entries.push(new TaskGroupEntry(this.createEntry(this.taskService, task, highlights), nls.localize('other tasks', 'other tasks'), hasRecentlyUsed));
} else {
entries.push(this.createEntry(this.taskService, task, highlights));
if (task._source.kind === TaskSourceKind.Workspace) {
configured.push(task);
} else {
detected.push(task);
}
}
}
let hasRecentlyUsed: boolean = recent.length > 0;
this.fillEntries(entries, input, recent, nls.localize('recentlyUsed', 'recently used tasks'));
configured = configured.sort((a, b) => a._label.localeCompare(b._label));
let hasConfigured = configured.length > 0;
this.fillEntries(entries, input, configured, nls.localize('configured', 'configured tasks'), hasRecentlyUsed);
detected = detected.sort((a, b) => a._label.localeCompare(b._label));
this.fillEntries(entries, input, detected, nls.localize('detected', 'detected tasks'), hasRecentlyUsed || hasConfigured);
return new Model.QuickOpenModel(entries, new ContributableActionProvider());
});
}
private fillEntries(entries: Model.QuickOpenEntry[], input: string, tasks: Task[], groupLabel: string, withBorder: boolean = false) {
let first = true;
for (let task of tasks) {
let highlights = Filters.matchesContiguousSubString(input, task._label);
if (!highlights) {
continue;
}
if (first) {
first = false;
entries.push(new TaskGroupEntry(this.createEntry(task, highlights), groupLabel, withBorder));
} else {
entries.push(this.createEntry(task, highlights));
}
}
}
protected abstract getTasks(): TPromise<Task[]>;
protected abstract createEntry(taskService: ITaskService, task: Task, highlights: Model.IHighlight[]): TaskEntry;
protected abstract createEntry(task: Task, highlights: Model.IHighlight[]): TaskEntry;
public getAutoFocus(input: string): QuickOpen.IAutoFocus {
return {
......
......@@ -16,8 +16,8 @@ import { ITaskService } from 'vs/workbench/parts/tasks/common/taskService';
import * as base from './quickOpen';
class TaskEntry extends base.TaskEntry {
constructor(taskService: ITaskService, task: Task, highlights: Model.IHighlight[] = []) {
super(taskService, task, highlights);
constructor(taskService: ITaskService, quickOpenService: IQuickOpenService, task: Task, highlights: Model.IHighlight[] = []) {
super(taskService, quickOpenService, task, highlights);
}
public run(mode: QuickOpen.Mode, context: Model.IContext): boolean {
......@@ -45,8 +45,8 @@ export class QuickOpenHandler extends base.QuickOpenHandler {
return this.taskService.getActiveTasks();
}
protected createEntry(taskService: ITaskService, task: Task, highlights: Model.IHighlight[]): base.TaskEntry {
return new TaskEntry(taskService, task, highlights);
protected createEntry(task: Task, highlights: Model.IHighlight[]): base.TaskEntry {
return new TaskEntry(this.taskService, this.quickOpenService, task, highlights);
}
public getEmptyLabel(searchString: string): string {
......
......@@ -18,15 +18,20 @@ import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import * as base from './quickOpen';
class TaskEntry extends base.TaskEntry {
constructor(taskService: ITaskService, task: Task, highlights: Model.IHighlight[] = []) {
super(taskService, task, highlights);
constructor(taskService: ITaskService, quickOpenService: IQuickOpenService, task: Task, highlights: Model.IHighlight[] = []) {
super(taskService, quickOpenService, task, highlights);
}
public run(mode: QuickOpen.Mode, context: Model.IContext): boolean {
if (mode === QuickOpen.Mode.PREVIEW) {
return false;
}
this.taskService.run(this._task);
let task = this._task;
this.taskService.run(task);
if (task.command.terminalBehavior.focus) {
this.quickOpenService.close();
return false;
}
return true;
}
}
......@@ -53,8 +58,8 @@ export class QuickOpenHandler extends base.QuickOpenHandler {
});
}
protected createEntry(taskService: ITaskService, task: Task, highlights: Model.IHighlight[]): base.TaskEntry {
return new TaskEntry(taskService, task, highlights);
protected createEntry(task: Task, highlights: Model.IHighlight[]): base.TaskEntry {
return new TaskEntry(this.taskService, this.quickOpenService, task, highlights);
}
public getEmptyLabel(searchString: string): string {
......
......@@ -16,8 +16,8 @@ import { ITaskService } from 'vs/workbench/parts/tasks/common/taskService';
import * as base from './quickOpen';
class TaskEntry extends base.TaskEntry {
constructor(taskService: ITaskService, task: Task, highlights: Model.IHighlight[] = []) {
super(taskService, task, highlights);
constructor(taskService: ITaskService, quickOpenService: IQuickOpenService, task: Task, highlights: Model.IHighlight[] = []) {
super(taskService, quickOpenService, task, highlights);
}
public run(mode: QuickOpen.Mode, context: Model.IContext): boolean {
......@@ -45,8 +45,8 @@ export class QuickOpenHandler extends base.QuickOpenHandler {
return this.taskService.getActiveTasks();
}
protected createEntry(taskService: ITaskService, task: Task, highlights: Model.IHighlight[]): base.TaskEntry {
return new TaskEntry(taskService, task, highlights);
protected createEntry(task: Task, highlights: Model.IHighlight[]): base.TaskEntry {
return new TaskEntry(this.taskService, this.quickOpenService, task, highlights);
}
public getEmptyLabel(searchString: string): string {
......
......@@ -16,15 +16,20 @@ import { ITaskService } from 'vs/workbench/parts/tasks/common/taskService';
import * as base from './quickOpen';
class TaskEntry extends base.TaskEntry {
constructor(taskService: ITaskService, task: Task, highlights: Model.IHighlight[] = []) {
super(taskService, task, highlights);
constructor(taskService: ITaskService, quickOpenService: IQuickOpenService, task: Task, highlights: Model.IHighlight[] = []) {
super(taskService, quickOpenService, task, highlights);
}
public run(mode: QuickOpen.Mode, context: Model.IContext): boolean {
if (mode === QuickOpen.Mode.PREVIEW) {
return false;
}
this.taskService.run(this._task);
let task = this._task;
this.taskService.run(task);
if (task.command.terminalBehavior.focus) {
this.quickOpenService.close();
return false;
}
return true;
}
}
......@@ -45,8 +50,8 @@ export class QuickOpenHandler extends base.QuickOpenHandler {
return this.taskService.getTasksForGroup(TaskGroup.Test);
}
protected createEntry(taskService: ITaskService, task: Task, highlights: Model.IHighlight[]): base.TaskEntry {
return new TaskEntry(taskService, task, highlights);
protected createEntry(task: Task, highlights: Model.IHighlight[]): base.TaskEntry {
return new TaskEntry(this.taskService, this.quickOpenService, task, highlights);
}
public getEmptyLabel(searchString: string): string {
......
......@@ -355,6 +355,134 @@ function fillProperty<T, K extends keyof T>(target: T, source: T, key: K) {
}
interface ParserType<T> {
isEmpty(value: T): boolean;
assignProperties(target: T, source: T): T;
fillProperties(target: T, source: T): T;
fillDefaults(value: T, context: ParseContext): T;
freeze(value: T): Readonly<T>;
}
interface MetaData<T, U> {
property: keyof T;
type?: ParserType<U>;
}
function _isEmpty<T>(this: void, value: T, properties: MetaData<T, any>[]): boolean {
if (value === void 0 || value === null) {
return true;
}
for (let meta of properties) {
let property = value[meta.property];
if (property !== void 0 && property !== null) {
if (meta.type !== void 0 && !meta.type.isEmpty(property)) {
return false;
} else if (!Array.isArray(property) || property.length > 0) {
return false;
}
}
}
return true;
}
function _assignProperties<T>(this: void, target: T, source: T, properties: MetaData<T, any>[]): T {
if (_isEmpty(source, properties)) {
return target;
}
if (_isEmpty(target, properties)) {
return source;
}
for (let meta of properties) {
let property = meta.property;
let value: any;
if (meta.type !== void 0) {
value = meta.type.assignProperties(target[property], source[property]);
} else {
value = source[property];
}
if (value !== void 0 && value !== null) {
target[property] = value;
}
}
return target;
}
function _fillProperties<T>(this: void, target: T, source: T, properties: MetaData<T, any>[]): T {
if (_isEmpty(source, properties)) {
return target;
}
if (_isEmpty(target, properties)) {
return source;
}
for (let meta of properties) {
let property = meta.property;
if (target[property] !== void 0) {
continue;
}
let value: any;
if (meta.type) {
value = meta.type.fillProperties(target[property], source[property]);
} else {
value = source[property];
}
if (value !== void 0 && value !== null) {
target[property] = value;
}
}
return target;
}
function _fillDefaults<T>(this: void, target: T, defaults: T, properties: MetaData<T, any>[], context: ParseContext): T {
if (target && Object.isFrozen(target)) {
return target;
}
if (target === void 0 || target === null) {
if (defaults !== void 0 && defaults !== null) {
return Objects.deepClone(defaults);
} else {
return undefined;
}
}
for (let meta of properties) {
let property = meta.property;
if (target[property] !== void 0) {
continue;
}
let value: any;
if (meta.type) {
value = meta.type.fillDefaults(target[property], context);
} else {
value = defaults[property];
}
if (value !== void 0 && value !== null) {
target[property] = value;
}
}
return target;
}
function _freeze<T>(this: void, target: T, properties: MetaData<T, any>[]): Readonly<T> {
if (target === void 0 || target === null) {
return undefined;
}
if (Object.isFrozen(target)) {
return target;
}
for (let meta of properties) {
if (meta.type) {
let value = target[meta.property];
if (value) {
meta.type.freeze(value);
}
}
}
Object.freeze(target);
return target;
}
interface ParseContext {
problemReporter: IProblemReporter;
namedProblemMatchers: IStringDictionary<NamedProblemMatcher>;
......@@ -363,7 +491,11 @@ interface ParseContext {
schemaVersion: Tasks.JsonSchemaVersion;
}
namespace ShellConfiguration {
const properties: MetaData<Tasks.ShellConfiguration, void>[] = [{ property: 'executable' }, { property: 'args' }];
export function is(value: any): value is ShellConfiguration {
let candidate: ShellConfiguration = value;
return candidate && Types.isString(candidate.executable) && (candidate.args === void 0 || Types.isStringArray(candidate.args));
......@@ -380,46 +512,35 @@ namespace ShellConfiguration {
return result;
}
export function isEmpty(value: Tasks.ShellConfiguration): boolean {
return !value || value.executable === void 0 && (value.args === void 0 || value.args.length === 0);
export function isEmpty(this: void, value: Tasks.ShellConfiguration): boolean {
return _isEmpty(value, properties);
}
export function assignProperties(target: Tasks.ShellConfiguration, source: Tasks.ShellConfiguration): Tasks.ShellConfiguration {
if (isEmpty(source)) {
return target;
}
if (isEmpty(target)) {
return source;
}
assignProperty(target, source, 'executable');
assignProperty(target, source, 'args');
return target;
export function assignProperties(this: void, target: Tasks.ShellConfiguration, source: Tasks.ShellConfiguration): Tasks.ShellConfiguration {
return _assignProperties(target, source, properties);
}
export function fillProperties(target: Tasks.ShellConfiguration, source: Tasks.ShellConfiguration): Tasks.ShellConfiguration {
if (isEmpty(source)) {
return target;
}
if (isEmpty(target)) {
return source;
}
fillProperty(target, source, 'executable');
fillProperty(target, source, 'args');
return target;
export function fillProperties(this: void, target: Tasks.ShellConfiguration, source: Tasks.ShellConfiguration): Tasks.ShellConfiguration {
return _fillProperties(target, source, properties);
}
export function fillDefaults(value: Tasks.ShellConfiguration): void {
export function fillDefaults(this: void, value: Tasks.ShellConfiguration, context: ParseContext): Tasks.ShellConfiguration {
return value;
}
export function freeze(value: Tasks.ShellConfiguration): void {
export function freeze(this: void, value: Tasks.ShellConfiguration): Readonly<Tasks.ShellConfiguration> {
if (!value) {
return;
return undefined;
}
Object.freeze(value);
return Object.freeze(value);
}
}
namespace CommandOptions {
const properties: MetaData<Tasks.CommandOptions, Tasks.ShellConfiguration>[] = [{ property: 'cwd' }, { property: 'env' }, { property: 'shell', type: ShellConfiguration }];
const defaults: CommandOptions = { cwd: '${workspaceRoot}' };
export function from(this: void, options: CommandOptions, context: ParseContext): Tasks.CommandOptions {
let result: Tasks.CommandOptions = {};
if (options.cwd !== void 0) {
......@@ -437,7 +558,7 @@ namespace CommandOptions {
}
export function isEmpty(value: Tasks.CommandOptions): boolean {
return !value || value.cwd === void 0 && value.env === void 0 && value.shell === void 0;
return _isEmpty(value, properties);
}
export function assignProperties(target: Tasks.CommandOptions, source: Tasks.CommandOptions): Tasks.CommandOptions {
......@@ -461,45 +582,25 @@ namespace CommandOptions {
}
export function fillProperties(target: Tasks.CommandOptions, source: Tasks.CommandOptions): Tasks.CommandOptions {
if (isEmpty(source)) {
return target;
}
if (isEmpty(target)) {
return source;
}
fillProperty(target, source, 'cwd');
fillProperty(target, source, 'env');
target.shell = ShellConfiguration.fillProperties(target.shell, source.shell);
return target;
return _fillProperties(target, source, properties);
}
export function fillDefaults(value: Tasks.CommandOptions): Tasks.CommandOptions {
if (value && Object.isFrozen(value)) {
return value;
}
if (value === void 0) {
value = {};
}
if (value.cwd === void 0) {
value.cwd = '${workspaceRoot}';
}
ShellConfiguration.fillDefaults(value.shell);
return value;
export function fillDefaults(value: Tasks.CommandOptions, context: ParseContext): Tasks.CommandOptions {
return _fillDefaults(value, defaults, properties, context);
}
export function freeze(value: Tasks.CommandOptions): void {
Object.freeze(value);
if (value.env) {
Object.freeze(value.env);
}
ShellConfiguration.freeze(value.shell);
export function freeze(value: Tasks.CommandOptions): Readonly<Tasks.CommandOptions> {
return _freeze(value, properties);
}
}
namespace CommandConfiguration {
interface TerminalBehavior {
echo?: boolean;
reveal?: string;
focus?: boolean;
instance?: string;
}
interface BaseCommandConfiguationShape {
......@@ -522,9 +623,13 @@ namespace CommandConfiguration {
}
export namespace TerminalBehavior {
const properties: MetaData<Tasks.TerminalBehavior, void>[] = [{ property: 'echo' }, { property: 'reveal' }, { property: 'focus' }, { property: 'instance' }];
export function from(this: void, config: BaseCommandConfiguationShape, context: ParseContext): Tasks.TerminalBehavior {
let echo: boolean = undefined;
let reveal: Tasks.RevealKind = undefined;
let echo: boolean;
let reveal: Tasks.RevealKind;
let focus: boolean;
let instance: Tasks.InstanceKind;
if (Types.isBoolean(config.echoCommand)) {
echo = config.echoCommand;
}
......@@ -538,65 +643,47 @@ namespace CommandConfiguration {
if (Types.isString(config.terminal.reveal)) {
reveal = Tasks.RevealKind.fromString(config.terminal.reveal);
}
if (Types.isBoolean(config.terminal.focus)) {
focus = config.terminal.focus;
}
if (Types.isString(config.terminal.instance)) {
instance = Tasks.InstanceKind.fromString(config.terminal.instance);
}
}
if (echo === void 0 && reveal === void 0) {
if (echo === void 0 && reveal === void 0 && focus === void 0 && instance === void 0) {
return undefined;
}
return { echo, reveal };
return { echo, reveal, focus, instance };
}
export function assignProperties(target: Tasks.TerminalBehavior, source: Tasks.TerminalBehavior): Tasks.TerminalBehavior {
if (isEmpty(source)) {
return target;
}
if (isEmpty(target)) {
return source;
}
assignProperty(target, source, 'echo');
assignProperty(target, source, 'reveal');
return target;
return _assignProperties(target, source, properties);
}
export function fillProperties(target: Tasks.TerminalBehavior, source: Tasks.TerminalBehavior): Tasks.TerminalBehavior {
if (isEmpty(source)) {
return target;
}
if (isEmpty(target)) {
return source;
}
fillProperty(target, source, 'echo');
fillProperty(target, source, 'reveal');
return target;
return _fillProperties(target, source, properties);
}
export function fillDefault(value: Tasks.TerminalBehavior, context: ParseContext): Tasks.TerminalBehavior {
if (value && Object.isFrozen(value)) {
return value;
}
if (value === void 0) {
return { echo: false, reveal: Tasks.RevealKind.Always };
}
if (value.echo === void 0) {
value.echo = context.engine === Tasks.ExecutionEngine.Terminal ? true : false;
}
if (value.reveal === void 0) {
value.reveal = Tasks.RevealKind.Always;
}
return value;
export function fillDefaults(value: Tasks.TerminalBehavior, context: ParseContext): Tasks.TerminalBehavior {
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);
}
export function freeze(value: Tasks.TerminalBehavior): void {
if (value === void 0) {
return;
}
Object.freeze(value);
export function freeze(value: Tasks.TerminalBehavior): Readonly<Tasks.TerminalBehavior> {
return _freeze(value, properties);
}
export function isEmpty(this: void, value: Tasks.TerminalBehavior): boolean {
return !value || value.echo === void 0 && value.reveal === void 0;
return _isEmpty(value, properties);
}
}
const properties: MetaData<Tasks.CommandConfiguration, CommandOptions | TerminalBehavior>[] = [
{ property: 'type' }, { property: 'name' }, { property: 'options', type: CommandOptions },
{ property: 'args' }, { property: 'taskSelector' }, { property: 'suppressTaskName' },
{ property: 'terminalBehavior', type: TerminalBehavior }
];
export function from(this: void, config: CommandConfiguationShape, context: ParseContext): Tasks.CommandConfiguration {
let result: Tasks.CommandConfiguration = fromBase(config, context);
......@@ -662,13 +749,7 @@ namespace CommandConfiguration {
}
export function isEmpty(value: Tasks.CommandConfiguration): boolean {
return !value || value.name === void 0
&& value.type === void 0
&& value.args === void 0
&& value.taskSelector === void 0
&& value.suppressTaskName === void 0
&& CommandOptions.isEmpty(value.options)
&& TerminalBehavior.isEmpty(value.terminalBehavior);
return _isEmpty(value, properties);
}
export function onlyTerminalBehaviour(value: Tasks.CommandConfiguration): boolean {
......@@ -739,9 +820,9 @@ namespace CommandConfiguration {
if (value.name !== void 0 && value.type === void 0) {
value.type = Tasks.CommandType.Process;
}
value.terminalBehavior = TerminalBehavior.fillDefault(value.terminalBehavior, context);
value.terminalBehavior = TerminalBehavior.fillDefaults(value.terminalBehavior, context);
if (!isEmpty(value)) {
value.options = CommandOptions.fillDefaults(value.options);
value.options = CommandOptions.fillDefaults(value.options, context);
}
if (value.args === void 0) {
value.args = EMPTY_ARRAY;
......@@ -751,17 +832,8 @@ namespace CommandConfiguration {
}
}
export function freeze(value: Tasks.CommandConfiguration): void {
Object.freeze(value);
if (value.args) {
Object.freeze(value.args);
}
if (value.options) {
CommandOptions.freeze(value.options);
}
if (value.terminalBehavior) {
TerminalBehavior.freeze(value.terminalBehavior);
}
export function freeze(value: Tasks.CommandConfiguration): Readonly<Tasks.CommandConfiguration> {
return _freeze(value, properties);
}
}
......
......@@ -100,7 +100,6 @@ export interface ITaskResolver {
export interface ITaskSystem extends IEventEmitter {
run(task: Task, resolver: ITaskResolver): ITaskExecuteResult;
show(task: Task, forceFocus?: boolean): void;
isActive(): TPromise<boolean>;
isActiveSync(): boolean;
getActiveTasks(): Task[];
......
......@@ -80,6 +80,40 @@ export namespace RevealKind {
}
}
export enum InstanceKind {
/**
* Shares a terminal with other tasks. This is the default.
*/
Shared = 1,
/**
* Uses the same terminal for every run if possible. The terminal is not
* shared with other tasks.
*/
Same = 2,
/**
* Creates a new terminal whenever that task is executed
*/
New = 3
}
export namespace InstanceKind {
export function fromString(value: string): InstanceKind {
switch (value.toLowerCase()) {
case 'shared':
return InstanceKind.Shared;
case 'same':
return InstanceKind.Same;
case 'new':
return InstanceKind.New;
default:
return InstanceKind.Shared;
}
}
}
export interface TerminalBehavior {
/**
* Controls whether the terminal executing a task is brought to front or not.
......@@ -91,6 +125,16 @@ export interface TerminalBehavior {
* Controls whether the executed command is printed to the output window or terminal as well.
*/
echo: boolean;
/**
* Controls whether the terminal is focus when this task is executed
*/
focus: boolean;
/**
* Controls whether the task runs in a new terminal
*/
instance: InstanceKind;
}
export enum CommandType {
......
......@@ -444,9 +444,6 @@ class NullTaskSystem extends EventEmitter implements ITaskSystem {
promise: TPromise.as<ITaskSummary>({})
};
}
public show(task: Task, forceFocus: boolean = false): void {
return;
}
public isActive(): TPromise<boolean> {
return TPromise.as(false);
}
......@@ -930,20 +927,19 @@ class TaskService extends EventEmitter implements ITaskService {
return ProblemMatcherRegistry.onReady().then(() => {
return this.textFileService.saveAll().then((value) => { // make sure all dirty files are saved
let executeResult = this.getTaskSystem().run(task, resolver);
this.getRecentlyUsedTasks().set(task.identifier, task.identifier, Touch.First);
if (executeResult.kind === TaskExecuteKind.Active) {
let active = executeResult.active;
if (active.same) {
if (active.background) {
this.messageService.show(Severity.Info, nls.localize('TaskSystem.activeSame.background', 'The task is already active and in background mode. To terminate the task use `F1 > terminate task`'));
} else {
this.getTaskSystem().show(task, true);
// this.messageService.show(Severity.Info, nls.localize('TaskSystem.activeSame.noBackground', 'The task is already active. To terminate the task use `F1 > terminate task`'));
this.messageService.show(Severity.Info, nls.localize('TaskSystem.activeSame.noBackground', 'The task is already active. To terminate the task use `F1 > terminate task`'));
}
} else {
throw new TaskError(Severity.Warning, nls.localize('TaskSystem.active', 'There is already a task running. Terminate it first before executing another task.'), TaskErrors.RunningTask);
}
}
this.getRecentlyUsedTasks().set(task.identifier, task.identifier, Touch.First);
return executeResult.promise;
});
});
......
......@@ -135,8 +135,10 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem {
let terminalData = this.activeTasks[task._id];
if (terminalData && terminalData.promise) {
let reveal = task.command.terminalBehavior.reveal;
if (reveal === RevealKind.Always) {
terminalData.terminal.setVisible(true);
let focus = task.command.terminalBehavior.focus;
if (reveal === RevealKind.Always || focus) {
this.terminalService.setActiveInstance(terminalData.terminal);
this.terminalService.showPanel(focus);
}
return { kind: TaskExecuteKind.Active, active: { same: true, background: task.isBackground }, promise: terminalData.promise };
}
......@@ -156,15 +158,6 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem {
}
}
public show(task: Task, forceFocus: boolean = false): void {
let terminalData = this.activeTasks[task._id];
if (terminalData === void 0) {
return;
}
this.terminalService.setActiveInstance(terminalData.terminal);
this.terminalService.showPanel(forceFocus);
}
public isActive(): TPromise<boolean> {
return TPromise.as(this.isActiveSync());
}
......@@ -342,7 +335,7 @@ 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(false);
this.terminalService.showPanel(task.command.terminalBehavior.focus);
}
this.activeTasks[task._id] = { terminal, task, promise };
return promise.then((summary) => {
......@@ -432,7 +425,7 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem {
shellArgs.push(commandLine);
shellLaunchConfig.args = Platform.isWindows ? shellArgs.join(' ') : shellArgs;
if (task.command.terminalBehavior.echo) {
shellLaunchConfig.initialText = `\x1b[1m>Executing task: ${commandLine}<\x1b[0m\n`;
shellLaunchConfig.initialText = `\x1b[1m> Executing task: ${commandLine} <\x1b[0m\n`;
}
} else {
let cwd = options && options.cwd ? options.cwd : process.cwd();
......@@ -455,7 +448,7 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem {
}
return args.join(' ');
};
shellLaunchConfig.initialText = `\x1b[1m>Executing task: ${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)}<\x1b[0m\n`;
shellLaunchConfig.initialText = `\x1b[1m> Executing task: ${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)} <\x1b[0m\n`;
}
}
if (options.cwd) {
......
......@@ -89,10 +89,6 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem {
return this.executeTask(task);
}
public show(task: Task, forceFocus: boolean = false): void {
this.outputChannel.show(!focus);
}
public hasErrors(value: boolean): void {
this.errorsShown = !value;
}
......
......@@ -82,7 +82,7 @@ class TerminalBehaviorBuilder {
public result: Tasks.TerminalBehavior;
constructor(public parent: CommandConfigurationBuilder) {
this.result = { echo: false, reveal: Tasks.RevealKind.Always };
this.result = { echo: false, reveal: Tasks.RevealKind.Always, focus: false, instance: Tasks.InstanceKind.Shared };
}
public echo(value: boolean): TerminalBehaviorBuilder {
......@@ -95,6 +95,16 @@ class TerminalBehaviorBuilder {
return this;
}
public focus(value: boolean): TerminalBehaviorBuilder {
this.result.focus = value;
return this;
}
public instance(value: Tasks.InstanceKind): TerminalBehaviorBuilder {
this.result.instance = value;
return this;
}
public done(): void {
}
}
......@@ -1446,7 +1456,8 @@ suite('Tasks version 2.0.0', () => {
builder.task('dir', 'dir').
group(Tasks.TaskGroup.Build).
command().suppressTaskName(true).
type(Tasks.CommandType.Shell);
type(Tasks.CommandType.Shell).
terminal().echo(true);
testConfiguration(external, builder);
});
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册