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

Add support to focus terminal and to control terminal instance reuse

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