未验证 提交 6bb77146 编写于 作者: A Alex Ross 提交者: GitHub

Add a rerun last task command (#62645)

Fixes #25310
上级 47b32383
......@@ -1255,6 +1255,30 @@ declare module 'vscode' {
*/
clear?: boolean;
}
export enum RerunBehavior {
reevaluate = 1,
useEvaluated = 2,
}
export interface RunOptions {
/**
* Controls the behavior of a task when it is rerun.
*/
rerunBehavior?: RerunBehavior;
}
/**
* A task to execute
*/
export class Task2 extends Task {
/**
* Run options for the task
*/
runOptions: RunOptions;
}
//#endregion
}
......@@ -17,7 +17,7 @@ import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspac
import {
ContributedTask, ExtensionTaskSourceTransfer, KeyedTaskIdentifier, TaskExecution, Task, TaskEvent, TaskEventKind,
PresentationOptions, CommandOptions, CommandConfiguration, RuntimeType, CustomTask, TaskScope, TaskSource, TaskSourceKind, ExtensionTaskSource, RevealKind, PanelKind
PresentationOptions, CommandOptions, CommandConfiguration, RuntimeType, CustomTask, TaskScope, TaskSource, TaskSourceKind, ExtensionTaskSource, RevealKind, PanelKind, RunOptions
} from 'vs/workbench/parts/tasks/common/tasks';
......@@ -30,7 +30,8 @@ import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostC
import { ExtHostContext, MainThreadTaskShape, ExtHostTaskShape, MainContext, IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
import {
TaskDefinitionDTO, TaskExecutionDTO, ProcessExecutionOptionsDTO, TaskPresentationOptionsDTO,
ProcessExecutionDTO, ShellExecutionDTO, ShellExecutionOptionsDTO, TaskDTO, TaskSourceDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO
ProcessExecutionDTO, ShellExecutionDTO, ShellExecutionOptionsDTO, TaskDTO, TaskSourceDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO,
RunOptionsDTO
} from 'vs/workbench/api/shared/tasks';
namespace TaskExecutionDTO {
......@@ -99,6 +100,15 @@ namespace TaskPresentationOptionsDTO {
}
}
namespace RunOptionsDTO {
export function from(value: RunOptions): RunOptionsDTO {
if (value === void 0 || value === null) {
return undefined;
}
return Objects.assign(Object.create(null), value);
}
}
namespace ProcessExecutionOptionsDTO {
export function from(value: CommandOptions): ProcessExecutionOptionsDTO {
if (value === void 0 || value === null) {
......@@ -289,7 +299,8 @@ namespace TaskDTO {
presentationOptions: task.command ? TaskPresentationOptionsDTO.from(task.command.presentation) : undefined,
isBackground: task.isBackground,
problemMatchers: [],
hasDefinedMatchers: ContributedTask.is(task) ? task.hasDefinedMatchers : false
hasDefinedMatchers: ContributedTask.is(task) ? task.hasDefinedMatchers : false,
runOptions: RunOptionsDTO.from(task.runOptions),
};
if (task.group) {
result.group = task.group;
......@@ -344,7 +355,8 @@ namespace TaskDTO {
command: command,
isBackground: !!task.isBackground,
problemMatchers: task.problemMatchers.slice(),
hasDefinedMatchers: task.hasDefinedMatchers
hasDefinedMatchers: task.hasDefinedMatchers,
runOptions: task.runOptions,
};
return result;
}
......
......@@ -784,6 +784,7 @@ export function createApiFactory(
QuickInputButtons: extHostTypes.QuickInputButtons,
Range: extHostTypes.Range,
RelativePattern: extHostTypes.RelativePattern,
RerunBehavior: extHostTypes.RerunBehavior,
Selection: extHostTypes.Selection,
ShellExecution: extHostTypes.ShellExecution,
ShellQuoting: extHostTypes.ShellQuoting,
......@@ -797,6 +798,7 @@ export function createApiFactory(
SymbolInformation: extHostTypes.SymbolInformation,
SymbolKind: extHostTypes.SymbolKind,
Task: extHostTypes.Task,
Task2: extHostTypes.Task,
TaskGroup: extHostTypes.TaskGroup,
TaskPanelKind: extHostTypes.TaskPanelKind,
TaskRevealKind: extHostTypes.TaskRevealKind,
......
......@@ -403,7 +403,8 @@ namespace Tasks {
command: command,
isBackground: !!task.isBackground,
problemMatchers: task.problemMatchers.slice(),
hasDefinedMatchers: (task as types.Task).hasDefinedMatchers
hasDefinedMatchers: (task as types.Task).hasDefinedMatchers,
runOptions: (<vscode.Task2>task).runOptions ? (<vscode.Task2>task).runOptions : { rerunBehavior: tasks.RerunBehavior.reevaluate },
};
return result;
}
......@@ -629,7 +630,8 @@ namespace TaskDTO {
group: group,
presentationOptions: TaskPresentationOptionsDTO.from(value.presentationOptions),
problemMatchers: value.problemMatchers,
hasDefinedMatchers: (value as types.Task).hasDefinedMatchers
hasDefinedMatchers: (value as types.Task).hasDefinedMatchers,
runOptions: (<vscode.Task2>value).runOptions ? (<vscode.Task2>value).runOptions : { rerunBehavior: tasks.RerunBehavior.reevaluate },
};
return result;
}
......
......@@ -1591,7 +1591,12 @@ export enum TaskScope {
Workspace = 2
}
export class Task implements vscode.Task {
export enum RerunBehavior {
reevaluate = 1,
useEvaluated = 2,
}
export class Task implements vscode.Task2 {
private __id: string;
......@@ -1605,6 +1610,7 @@ export class Task implements vscode.Task {
private _source: string;
private _group: TaskGroup;
private _presentationOptions: vscode.TaskPresentationOptions;
private _runOptions: vscode.RunOptions;
constructor(definition: vscode.TaskDefinition, name: string, source: string, execution?: ProcessExecution | ShellExecution, problemMatchers?: string | string[]);
constructor(definition: vscode.TaskDefinition, scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder, name: string, source: string, execution?: ProcessExecution | ShellExecution, problemMatchers?: string | string[]);
......@@ -1782,6 +1788,18 @@ export class Task implements vscode.Task {
this.clear();
this._presentationOptions = value;
}
get runOptions(): vscode.RunOptions {
return this._runOptions;
}
set runOptions(value: vscode.RunOptions) {
if (value === null) {
value = undefined;
}
this.clear();
this._runOptions = value;
}
}
......
......@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { UriComponents } from 'vs/base/common/uri';
import { RerunBehavior } from 'vs/workbench/parts/tasks/common/tasks';
export interface TaskDefinitionDTO {
type: string;
......@@ -19,6 +20,10 @@ export interface TaskPresentationOptionsDTO {
clear?: boolean;
}
export interface RunOptionsDTO {
rerunBehavior?: RerunBehavior;
}
export interface ExecutionOptionsDTO {
cwd?: string;
env?: { [key: string]: string };
......@@ -82,6 +87,7 @@ export interface TaskDTO {
presentationOptions: TaskPresentationOptionsDTO;
problemMatchers: string[];
hasDefinedMatchers: boolean;
runOptions: RunOptionsDTO;
}
export interface TaskExecutionDTO {
......
......@@ -9,9 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { TerminateResponse } from 'vs/base/common/processes';
import { Event } from 'vs/base/common/event';
import { Platform } from 'vs/base/common/platform';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { Task, TaskEvent, KeyedTaskIdentifier } from './tasks';
export const enum TaskErrors {
......@@ -85,6 +83,7 @@ export const enum TaskExecuteKind {
export interface ITaskExecuteResult {
kind: TaskExecuteKind;
promise: TPromise<ITaskSummary>;
task: Task;
started?: {
restartOnFileChanges?: string;
};
......@@ -130,6 +129,7 @@ export interface TaskSystemInfoResovler {
export interface ITaskSystem {
onDidStateChange: Event<TaskEvent>;
run(task: Task, resolver: ITaskResolver): ITaskExecuteResult;
rerun(): ITaskExecuteResult | undefined;
isActive(): TPromise<boolean>;
isActiveSync(): boolean;
getActiveTasks(): Task[];
......
......@@ -176,6 +176,21 @@ export namespace PanelKind {
}
}
export namespace RerunBehavior {
export function fromString(value: string | undefined): RerunBehavior {
if (!value) {
return RerunBehavior.reevaluate;
}
switch (value.toLowerCase()) {
case 'useevaluated':
return RerunBehavior.useEvaluated;
case 'reevaulate':
default:
return RerunBehavior.reevaluate;
}
}
}
export interface PresentationOptions {
/**
* Controls whether the task output is reveal in the user interface.
......@@ -421,6 +436,14 @@ export interface ConfigurationProperties {
problemMatchers?: (string | ProblemMatcher)[];
}
export enum RerunBehavior {
reevaluate = 1,
useEvaluated = 2,
}
export interface RunOptions {
rerunBehavior?: RerunBehavior;
}
export interface CommonTask {
/**
......@@ -434,6 +457,8 @@ export interface CommonTask {
_label: string;
type: string;
runOptions: RunOptions;
}
export interface CustomTask extends CommonTask, ConfigurationProperties {
......
......@@ -281,6 +281,20 @@ const identifier: IJSONSchema = {
deprecationMessage: nls.localize('JsonSchema.tasks.identifier.deprecated', 'User defined identifiers are deprecated. For custom task use the name as a reference and for tasks provided by extensions use their defined task identifier.')
};
const rerunBehavior: IJSONSchema = {
type: 'string',
enum: ['reevauate', 'useEvaluated'],
description: nls.localize('JsonSchema.tasks.rerunBehavior', 'The task\'s behavior on rerun')
};
const runOptions: IJSONSchema = {
type: 'object',
properties: {
rerunBehavior: Objects.deepClone(rerunBehavior),
},
description: nls.localize('JsonSchema.tasks.runOptions', 'The task\'s run related options')
};
const options: IJSONSchema = Objects.deepClone(commonSchema.definitions.options);
options.properties.shell = Objects.deepClone(commonSchema.definitions.shellConfiguration);
......@@ -314,7 +328,8 @@ let taskConfiguration: IJSONSchema = {
problemMatcher: {
$ref: '#/definitions/problemMatcherType',
description: nls.localize('JsonSchema.tasks.matchers', 'The problem matcher(s) to use. Can either be a string or a problem matcher definition or an array of strings and problem matchers.')
}
},
runOptions: Objects.deepClone(runOptions),
}
};
......@@ -362,6 +377,7 @@ taskDescription.properties.type = Objects.deepClone(taskType);
taskDescription.properties.presentation = Objects.deepClone(presentation);
taskDescription.properties.terminal = terminal;
taskDescription.properties.group = Objects.deepClone(group);
taskDescription.properties.runOptions = Objects.deepClone(runOptions);
taskDescription.properties.taskName.deprecationMessage = nls.localize(
'JsonSchema.tasks.taskName.deprecated',
'The task\'s name property is deprecated. Use the label property instead.'
......
......@@ -70,11 +70,11 @@ import { Scope, IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs
import { ITerminalService } from 'vs/workbench/parts/terminal/common/terminal';
import { ITaskSystem, ITaskResolver, ITaskSummary, TaskExecuteKind, TaskError, TaskErrors, TaskTerminateResponse, TaskSystemInfo } from 'vs/workbench/parts/tasks/common/taskSystem';
import { ITaskSystem, ITaskResolver, ITaskSummary, TaskExecuteKind, TaskError, TaskErrors, TaskTerminateResponse, TaskSystemInfo, ITaskExecuteResult } from 'vs/workbench/parts/tasks/common/taskSystem';
import {
Task, CustomTask, ConfiguringTask, ContributedTask, InMemoryTask, TaskEvent,
TaskEventKind, TaskSet, TaskGroup, GroupType, ExecutionEngine, JsonSchemaVersion, TaskSourceKind,
TaskSorter, TaskIdentifier, KeyedTaskIdentifier, TASK_RUNNING_STATE
TaskSorter, TaskIdentifier, KeyedTaskIdentifier, TASK_RUNNING_STATE, RerunBehavior
} from 'vs/workbench/parts/tasks/common/tasks';
import { ITaskService, ITaskProvider, RunOptions, CustomizationProperties, TaskFilter } from 'vs/workbench/parts/tasks/common/taskService';
import { getTemplates as getTaskTemplates } from 'vs/workbench/parts/tasks/common/taskTemplates';
......@@ -555,6 +555,10 @@ class TaskService extends Disposable implements ITaskService {
this.runTaskCommand(arg);
});
CommandsRegistry.registerCommand('workbench.action.tasks.reRunTask', (accessor, arg) => {
this.reRunTaskCommand(arg);
});
CommandsRegistry.registerCommand('workbench.action.tasks.restartTask', (accessor, arg) => {
this.runRestartTaskCommand(arg);
});
......@@ -1174,7 +1178,8 @@ class TaskService extends Disposable implements ITaskService {
type: 'inMemory',
name: id,
identifier: id,
dependsOn: extensionTasks.map((task) => { return { workspaceFolder: Task.getWorkspaceFolder(task), task: task._id }; })
dependsOn: extensionTasks.map((task) => { return { workspaceFolder: Task.getWorkspaceFolder(task), task: task._id }; }),
runOptions: { rerunBehavior: RerunBehavior.reevaluate },
};
return { task, resolver };
}
......@@ -1223,39 +1228,43 @@ class TaskService extends Disposable 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);
let key = Task.getRecentlyUsedKey(task);
if (key) {
this.getRecentlyUsedTasks().set(key, key, Touch.AsOld);
}
if (executeResult.kind === TaskExecuteKind.Active) {
let active = executeResult.active;
if (active.same) {
let message;
if (active.background) {
message = nls.localize('TaskSystem.activeSame.background', 'The task \'{0}\' is already active and in background mode.', Task.getQualifiedLabel(task));
} else {
message = nls.localize('TaskSystem.activeSame.noBackground', 'The task \'{0}\' is already active.', Task.getQualifiedLabel(task));
}
this.notificationService.prompt(Severity.Info, message,
[{
label: nls.localize('terminateTask', "Terminate Task"),
run: () => this.terminate(task)
},
{
label: nls.localize('restartTask', "Restart Task"),
run: () => this.restart(task)
}],
{ sticky: true }
);
} 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);
}
}
return executeResult.promise;
return this.handleExecuteResult(executeResult);
});
});
}
private handleExecuteResult(executeResult: ITaskExecuteResult): TPromise<ITaskSummary> {
let key = Task.getRecentlyUsedKey(executeResult.task);
if (key) {
this.getRecentlyUsedTasks().set(key, key, Touch.AsOld);
}
if (executeResult.kind === TaskExecuteKind.Active) {
let active = executeResult.active;
if (active.same) {
let message;
if (active.background) {
message = nls.localize('TaskSystem.activeSame.background', 'The task \'{0}\' is already active and in background mode.', Task.getQualifiedLabel(executeResult.task));
} else {
message = nls.localize('TaskSystem.activeSame.noBackground', 'The task \'{0}\' is already active.', Task.getQualifiedLabel(executeResult.task));
}
this.notificationService.prompt(Severity.Info, message,
[{
label: nls.localize('terminateTask', "Terminate Task"),
run: () => this.terminate(executeResult.task)
},
{
label: nls.localize('restartTask', "Restart Task"),
run: () => this.restart(executeResult.task)
}],
{ sticky: true }
);
} 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);
}
}
return executeResult.promise;
}
public restart(task: Task): void {
if (!this._taskSystem) {
return;
......@@ -1981,6 +1990,24 @@ class TaskService extends Disposable implements ITaskService {
});
}
private reRunTaskCommand(arg?: any): void {
if (!this.canRunCommand()) {
return;
}
ProblemMatcherRegistry.onReady().then(() => {
return this.textFileService.saveAll().then((value) => { // make sure all dirty files are saved
let executeResult = this.getTaskSystem().rerun();
if (executeResult) {
return this.handleExecuteResult(executeResult);
} else {
this.doRunTaskCommand();
return undefined;
}
});
});
}
private splitPerGroupType(tasks: Task[]): { none: Task[], defaults: Task[], users: Task[] } {
let none: Task[] = [];
let defaults: Task[] = [];
......@@ -2529,6 +2556,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, {
MenuRegistry.addCommand({ id: ConfigureTaskAction.ID, title: { value: ConfigureTaskAction.TEXT, original: 'Configure Task' }, category: { value: tasksCategory, original: 'Tasks' } });
MenuRegistry.addCommand({ id: 'workbench.action.tasks.showLog', title: { value: nls.localize('ShowLogAction.label', "Show Task Log"), original: 'Show Task Log' }, category: { value: tasksCategory, original: 'Tasks' } });
MenuRegistry.addCommand({ id: 'workbench.action.tasks.runTask', title: { value: nls.localize('RunTaskAction.label', "Run Task"), original: 'Run Task' }, category: { value: tasksCategory, original: 'Tasks' } });
MenuRegistry.addCommand({ id: 'workbench.action.tasks.reRunTask', title: { value: nls.localize('ReRunTaskAction.label', "Rerun Last Task"), original: 'Rerun Last Task' }, category: { value: tasksCategory, original: 'Tasks' } });
MenuRegistry.addCommand({ id: 'workbench.action.tasks.restartTask', title: { value: nls.localize('RestartTaskAction.label', "Restart Running Task"), original: 'Restart Running Task' }, category: { value: tasksCategory, original: 'Tasks' } });
MenuRegistry.addCommand({ id: 'workbench.action.tasks.showTasks', title: { value: nls.localize('ShowTasksAction.label', "Show Running Tasks"), original: 'Show Running Tasks' }, category: { value: tasksCategory, original: 'Tasks' } });
MenuRegistry.addCommand({ id: 'workbench.action.tasks.terminate', title: { value: nls.localize('TerminateAction.label', "Terminate Task"), original: 'Terminate Task' }, category: { value: tasksCategory, original: 'Tasks' } });
......
......@@ -33,11 +33,11 @@ import { IOutputService, IOutputChannel } from 'vs/workbench/parts/output/common
import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEventKind } from 'vs/workbench/parts/tasks/common/problemCollectors';
import {
Task, CustomTask, ContributedTask, RevealKind, CommandOptions, ShellConfiguration, RuntimeType, PanelKind,
TaskEvent, TaskEventKind, ShellQuotingOptions, ShellQuoting, CommandString, CommandConfiguration
TaskEvent, TaskEventKind, ShellQuotingOptions, ShellQuoting, CommandString, CommandConfiguration, RerunBehavior
} from 'vs/workbench/parts/tasks/common/tasks';
import {
ITaskSystem, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, ITaskResolver,
TelemetryEvent, Triggers, TaskTerminateResponse, TaskSystemInfoResovler, TaskSystemInfo, ResolveSet
TelemetryEvent, Triggers, TaskTerminateResponse, TaskSystemInfoResovler, TaskSystemInfo, ResolveSet, ResolvedVariables
} from 'vs/workbench/parts/tasks/common/taskSystem';
interface TerminalData {
......@@ -69,6 +69,35 @@ class VariableResolver {
}
}
export class VerifiedTask {
readonly task: Task;
readonly resolver: ITaskResolver;
readonly trigger: string;
resolvedVariables?: ResolvedVariables;
systemInfo?: TaskSystemInfo;
workspaceFolder?: IWorkspaceFolder;
shellLaunchConfig?: IShellLaunchConfig;
constructor(task: Task, resolver: ITaskResolver, trigger: string) {
this.task = task;
this.resolver = resolver;
this.trigger = trigger;
}
public verify(): boolean {
return this.trigger && this.resolvedVariables && this.workspaceFolder && (this.shellLaunchConfig !== undefined);
}
public getVerifiedTask(): { task: Task, resolver: ITaskResolver, trigger: string, resolvedVariables: ResolvedVariables, systemInfo: TaskSystemInfo, workspaceFolder: IWorkspaceFolder, shellLaunchConfig: IShellLaunchConfig } {
if (this.verify()) {
return { task: this.task, resolver: this.resolver, trigger: this.trigger, resolvedVariables: this.resolvedVariables, systemInfo: this.systemInfo, workspaceFolder: this.workspaceFolder, shellLaunchConfig: this.shellLaunchConfig };
} else {
throw new Error('VerifiedTask was not checked. verify must be checked before getVerifiedTask.');
}
}
}
export class TerminalTaskSystem implements ITaskSystem {
public static TelemetryEventName: string = 'taskService';
......@@ -117,6 +146,9 @@ export class TerminalTaskSystem implements ITaskSystem {
private idleTaskTerminals: LinkedMap<string, string>;
private sameTaskTerminals: IStringDictionary<string>;
private taskSystemInfoResolver: TaskSystemInfoResovler;
private lastTask: VerifiedTask;
private currentTask: VerifiedTask;
private isRerun: boolean;
private readonly _onDidStateChange: Emitter<TaskEvent>;
......@@ -151,6 +183,7 @@ export class TerminalTaskSystem implements ITaskSystem {
}
public run(task: Task, resolver: ITaskResolver, trigger: string = Triggers.command): ITaskExecuteResult {
this.currentTask = new VerifiedTask(task, resolver, trigger);
let terminalData = this.activeTasks[Task.getMapKey(task)];
if (terminalData && terminalData.promise) {
let reveal = RevealKind.Always;
......@@ -163,11 +196,14 @@ export class TerminalTaskSystem implements ITaskSystem {
this.terminalService.setActiveInstance(terminalData.terminal);
this.terminalService.showPanel(focus);
}
return { kind: TaskExecuteKind.Active, active: { same: true, background: task.isBackground }, promise: terminalData.promise };
this.lastTask = this.currentTask;
return { kind: TaskExecuteKind.Active, task, active: { same: true, background: task.isBackground }, promise: terminalData.promise };
}
try {
return { kind: TaskExecuteKind.Started, started: {}, promise: this.executeTask(task, resolver, trigger) };
const executeResult = { kind: TaskExecuteKind.Started, task, started: {}, promise: this.executeTask(task, resolver, trigger) };
this.lastTask = this.currentTask;
return executeResult;
} catch (error) {
if (error instanceof TaskError) {
throw error;
......@@ -181,6 +217,18 @@ export class TerminalTaskSystem implements ITaskSystem {
}
}
public rerun(): ITaskExecuteResult | undefined {
if (this.lastTask && this.lastTask.verify()) {
if (this.lastTask.task.runOptions.rerunBehavior === RerunBehavior.useEvaluated) {
this.isRerun = true;
}
const result = this.run(this.lastTask.task, this.lastTask.resolver);
this.isRerun = false;
return result;
} else {
return undefined;
}
}
public revealTask(task: Task): boolean {
let terminalData = this.activeTasks[Task.getMapKey(task)];
......@@ -284,7 +332,11 @@ export class TerminalTaskSystem implements ITaskSystem {
return { exitCode: summary.exitCode };
}
}
return this.executeCommand(task, trigger);
if (this.isRerun) {
return this.reexecuteCommand(task, trigger);
} else {
return this.executeCommand(task, trigger);
}
});
} else {
return TPromise.join(promises).then((summaries): ITaskSummary => {
......@@ -298,15 +350,7 @@ export class TerminalTaskSystem implements ITaskSystem {
}
}
private executeCommand(task: CustomTask | ContributedTask, trigger: string): TPromise<ITaskSummary> {
let variables = new Set<string>();
this.collectTaskVariables(variables, task);
let workspaceFolder = Task.getWorkspaceFolder(task);
let taskSystemInfo: TaskSystemInfo;
if (workspaceFolder) {
taskSystemInfo = this.taskSystemInfoResolver(workspaceFolder);
}
let variableResolver: TPromise<VariableResolver>;
private resolveVariablesFromSet(taskSystemInfo: TaskSystemInfo, workspaceFolder: IWorkspaceFolder, task: CustomTask | ContributedTask, variables: Set<string>): TPromise<ResolvedVariables> {
let isProcess = task.command && task.command.runtime === RuntimeType.Process;
let options = task.command && task.command.options ? task.command.options : undefined;
let cwd = options ? options.cwd : undefined;
......@@ -321,10 +365,13 @@ export class TerminalTaskSystem implements ITaskSystem {
}
}
}
let resolvedVariables: TPromise<ResolvedVariables>;
if (taskSystemInfo) {
let resolveSet: ResolveSet = {
variables
};
if (taskSystemInfo.platform === Platform.Platform.Windows && isProcess) {
resolveSet.process = { name: CommandString.value(task.command.name) };
if (cwd) {
......@@ -334,16 +381,7 @@ export class TerminalTaskSystem implements ITaskSystem {
resolveSet.process.path = envPath;
}
}
variableResolver = taskSystemInfo.resolveVariables(workspaceFolder, resolveSet).then((resolved) => {
let result = new Map<string, string>();
resolved.variables.forEach(variable => {
result.set(variable, this.configurationResolverService.resolve(workspaceFolder, variable));
});
if (resolved.process !== void 0) {
result.set(TerminalTaskSystem.ProcessVarName, resolved.process);
}
return new VariableResolver(workspaceFolder, taskSystemInfo, result, undefined);
});
resolvedVariables = taskSystemInfo.resolveVariables(workspaceFolder, resolveSet);
} else {
let result = new Map<string, string>();
variables.forEach(variable => {
......@@ -362,11 +400,52 @@ export class TerminalTaskSystem implements ITaskSystem {
}
result.set(TerminalTaskSystem.ProcessVarName, processVarValue);
}
variableResolver = TPromise.as(new VariableResolver(workspaceFolder, taskSystemInfo, result, this.configurationResolverService));
let resolvedVariablesResult: ResolvedVariables = {
variables: result,
};
resolvedVariables = TPromise.as(resolvedVariablesResult);
}
return variableResolver.then((resolver) => {
return this.executeInTerminal(task, trigger, resolver);
return resolvedVariables;
}
private executeCommand(task: CustomTask | ContributedTask, trigger: string): TPromise<ITaskSummary> {
this.currentTask.workspaceFolder = Task.getWorkspaceFolder(task);
if (this.currentTask.workspaceFolder) {
this.currentTask.systemInfo = this.taskSystemInfoResolver(this.currentTask.workspaceFolder);
}
let variables = new Set<string>();
this.collectTaskVariables(variables, task);
const resolvedVariables = this.resolveVariablesFromSet(this.currentTask.systemInfo, this.currentTask.workspaceFolder, task, variables);
return resolvedVariables.then((resolvedVariables) => {
this.currentTask.resolvedVariables = resolvedVariables;
return this.executeInTerminal(task, trigger, new VariableResolver(this.currentTask.workspaceFolder, this.currentTask.systemInfo, resolvedVariables.variables, this.configurationResolverService));
});
}
private reexecuteCommand(task: CustomTask | ContributedTask, trigger: string): TPromise<ITaskSummary> {
this.currentTask.workspaceFolder = this.lastTask.workspaceFolder;
let variables = new Set<string>();
this.collectTaskVariables(variables, task);
// Check that the task hasn't changed to include new variables
let hasAllVariables = true;
variables.forEach(value => {
if (value in this.lastTask.getVerifiedTask().resolvedVariables) {
hasAllVariables = false;
}
});
if (!hasAllVariables) {
return this.resolveVariablesFromSet(this.lastTask.getVerifiedTask().systemInfo, this.lastTask.getVerifiedTask().workspaceFolder, task, variables).then((resolvedVariables) => {
this.currentTask.resolvedVariables = resolvedVariables;
return this.executeInTerminal(task, trigger, new VariableResolver(this.lastTask.getVerifiedTask().workspaceFolder, this.lastTask.getVerifiedTask().systemInfo, resolvedVariables.variables, this.configurationResolverService));
});
} else {
this.currentTask.resolvedVariables = this.lastTask.getVerifiedTask().resolvedVariables;
return this.executeInTerminal(task, trigger, new VariableResolver(this.lastTask.getVerifiedTask().workspaceFolder, this.lastTask.getVerifiedTask().systemInfo, this.lastTask.getVerifiedTask().resolvedVariables.variables, this.configurationResolverService));
}
}
private executeInTerminal(task: CustomTask | ContributedTask, trigger: string, resolver: VariableResolver): TPromise<ITaskSummary> {
......@@ -574,27 +653,12 @@ export class TerminalTaskSystem implements ITaskSystem {
});
}
private createTerminal(task: CustomTask | ContributedTask, resolver: VariableResolver): [ITerminalInstance, string, TaskError | undefined] {
let platform = resolver.taskSystemInfo ? resolver.taskSystemInfo.platform : Platform.platform;
let options = this.resolveOptions(resolver, task.command.options);
let originalCommand = task.command.name;
let { command, args } = this.resolveCommandAndArgs(resolver, task.command);
let commandExecutable = CommandString.value(command);
let workspaceFolder = Task.getWorkspaceFolder(task);
let needsFolderQualification = workspaceFolder && this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE;
let terminalName = nls.localize('TerminalTaskSystem.terminalName', 'Task - {0}', needsFolderQualification ? Task.getQualifiedLabel(task) : task.name);
let waitOnExit: boolean | string = false;
if (task.command.presentation.reveal !== RevealKind.Never || !task.isBackground) {
if (task.command.presentation.panel === PanelKind.New) {
waitOnExit = nls.localize('closeTerminal', 'Press any key to close the terminal.');
} else if (task.command.presentation.showReuseMessage) {
waitOnExit = nls.localize('reuseTerminal', 'Terminal will be reused by tasks, press any key to close it.');
} else {
waitOnExit = true;
}
}
private createShellLaunchConfig(task: CustomTask | ContributedTask, variableResolver: VariableResolver, platform: Platform.Platform, options: CommandOptions, command: CommandString, args: CommandString[], waitOnExit: boolean | string): IShellLaunchConfig | undefined {
let shellLaunchConfig: IShellLaunchConfig | undefined = undefined;
let isShellCommand = task.command.runtime === RuntimeType.Shell;
let needsFolderQualification = this.currentTask.workspaceFolder && this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE;
let terminalName = nls.localize('TerminalTaskSystem.terminalName', 'Task - {0}', needsFolderQualification ? Task.getQualifiedLabel(task) : task.name);
let originalCommand = task.command.name;
if (isShellCommand) {
shellLaunchConfig = { name: terminalName, executable: null, args: null, waitOnExit };
this.terminalService.configHelper.mergeDefaultShellPathAndArgs(shellLaunchConfig, platform);
......@@ -602,11 +666,11 @@ export class TerminalTaskSystem implements ITaskSystem {
let shellOptions: ShellConfiguration = task.command.options && task.command.options.shell;
if (shellOptions) {
if (shellOptions.executable) {
shellLaunchConfig.executable = this.resolveVariable(resolver, shellOptions.executable);
shellLaunchConfig.executable = this.resolveVariable(variableResolver, shellOptions.executable);
shellSpecified = true;
}
if (shellOptions.args) {
shellLaunchConfig.args = this.resolveVariables(resolver, shellOptions.args.slice());
shellLaunchConfig.args = this.resolveVariables(variableResolver, shellOptions.args.slice());
} else {
shellLaunchConfig.args = [];
}
......@@ -628,7 +692,7 @@ export class TerminalTaskSystem implements ITaskSystem {
windowsShellArgs = true;
let basename = path.basename(shellLaunchConfig.executable).toLowerCase();
if (basename === 'cmd.exe' && ((options.cwd && TPath.isUNC(options.cwd)) || (!options.cwd && TPath.isUNC(process.cwd())))) {
return [undefined, undefined, new TaskError(Severity.Error, nls.localize('TerminalTaskSystem', 'Can\'t execute a shell command on an UNC drive using cmd.exe.'), TaskErrors.UnknownError)];
return undefined;
}
if (basename === 'powershell.exe' || basename === 'pwsh.exe') {
if (!shellSpecified) {
......@@ -665,14 +729,15 @@ export class TerminalTaskSystem implements ITaskSystem {
shellLaunchConfig.args = windowsShellArgs ? shellArgs.join(' ') : shellArgs;
if (task.command.presentation.echo) {
if (needsFolderQualification) {
shellLaunchConfig.initialText = `\x1b[1m> Executing task in folder ${workspaceFolder.name}: ${commandLine} <\x1b[0m\n`;
shellLaunchConfig.initialText = `\x1b[1m> Executing task in folder ${this.currentTask.workspaceFolder.name}: ${commandLine} <\x1b[0m\n`;
} else {
shellLaunchConfig.initialText = `\x1b[1m> Executing task: ${commandLine} <\x1b[0m\n`;
}
}
} else {
let commandExecutable = CommandString.value(command);
let executable = !isShellCommand
? this.resolveVariable(resolver, TerminalTaskSystem.ProcessVarName)
? this.resolveVariable(variableResolver, TerminalTaskSystem.ProcessVarName)
: commandExecutable;
// When we have a process task there is no need to quote arguments. So we go ahead and take the string value.
......@@ -693,12 +758,13 @@ export class TerminalTaskSystem implements ITaskSystem {
return args.join(' ');
};
if (needsFolderQualification) {
shellLaunchConfig.initialText = `\x1b[1m> Executing task in folder ${workspaceFolder.name}: ${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)} <\x1b[0m\n`;
shellLaunchConfig.initialText = `\x1b[1m> Executing task in folder ${this.currentTask.workspaceFolder.name}: ${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)} <\x1b[0m\n`;
} else {
shellLaunchConfig.initialText = `\x1b[1m> Executing task: ${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)} <\x1b[0m\n`;
}
}
}
if (options.cwd) {
let cwd = options.cwd;
let p: typeof path;
......@@ -722,6 +788,29 @@ export class TerminalTaskSystem implements ITaskSystem {
if (options.env) {
shellLaunchConfig.env = options.env;
}
return shellLaunchConfig;
}
private createTerminal(task: CustomTask | ContributedTask, resolver: VariableResolver): [ITerminalInstance, string, TaskError | undefined] {
let platform = resolver.taskSystemInfo ? resolver.taskSystemInfo.platform : Platform.platform;
let options = this.resolveOptions(resolver, task.command.options);
let waitOnExit: boolean | string = false;
let { command, args } = this.resolveCommandAndArgs(resolver, task.command);
let commandExecutable = CommandString.value(command);
if (task.command.presentation.reveal !== RevealKind.Never || !task.isBackground) {
if (task.command.presentation.panel === PanelKind.New) {
waitOnExit = nls.localize('closeTerminal', 'Press any key to close the terminal.');
} else if (task.command.presentation.showReuseMessage) {
waitOnExit = nls.localize('reuseTerminal', 'Terminal will be reused by tasks, press any key to close it.');
} else {
waitOnExit = true;
}
}
this.currentTask.shellLaunchConfig = this.isRerun ? this.lastTask.getVerifiedTask().shellLaunchConfig : this.createShellLaunchConfig(task, resolver, platform, options, command, args, waitOnExit);
if (this.currentTask.shellLaunchConfig === undefined) {
return [undefined, undefined, new TaskError(Severity.Error, nls.localize('TerminalTaskSystem', 'Can\'t execute a shell command on an UNC drive using cmd.exe.'), TaskErrors.UnknownError)];
}
let prefersSameTerminal = task.command.presentation.panel === PanelKind.Dedicated;
let allowsSharedTerminal = task.command.presentation.panel === PanelKind.Shared;
......@@ -740,14 +829,14 @@ export class TerminalTaskSystem implements ITaskSystem {
}
}
if (terminalToReuse) {
terminalToReuse.terminal.reuseTerminal(shellLaunchConfig);
terminalToReuse.terminal.reuseTerminal(this.currentTask.shellLaunchConfig);
if (task.command.presentation.clear) {
terminalToReuse.terminal.clear();
}
return [terminalToReuse.terminal, commandExecutable, undefined];
}
const result = this.terminalService.createTerminal(shellLaunchConfig);
const result = this.terminalService.createTerminal(this.currentTask.shellLaunchConfig);
const terminalKey = result.id.toString();
result.onDisposed((terminal) => {
let terminalData = this.terminals[terminalKey];
......
......@@ -92,7 +92,7 @@ export class ProcessTaskSystem implements ITaskSystem {
public run(task: Task): ITaskExecuteResult {
if (this.activeTask) {
return { kind: TaskExecuteKind.Active, active: { same: this.activeTask._id === task._id, background: this.activeTask.isBackground }, promise: this.activeTaskPromise };
return { kind: TaskExecuteKind.Active, task, active: { same: this.activeTask._id === task._id, background: this.activeTask.isBackground }, promise: this.activeTaskPromise };
}
return this.executeTask(task);
}
......@@ -194,6 +194,10 @@ export class ProcessTaskSystem implements ITaskSystem {
}
}
public rerun(): ITaskExecuteResult | undefined {
return undefined;
}
private doExecuteTask(task: CustomTask, telemetryEvent: TelemetryEvent): ITaskExecuteResult {
let taskSummary: ITaskSummary = {};
let commandConfig: CommandConfiguration = task.command;
......@@ -301,8 +305,8 @@ export class ProcessTaskSystem implements ITaskSystem {
return this.handleError(task, error);
});
let result: ITaskExecuteResult = (<any>task).tscWatch
? { kind: TaskExecuteKind.Started, started: { restartOnFileChanges: '**/*.ts' }, promise: this.activeTaskPromise }
: { kind: TaskExecuteKind.Started, started: {}, promise: this.activeTaskPromise };
? { kind: TaskExecuteKind.Started, task, started: { restartOnFileChanges: '**/*.ts' }, promise: this.activeTaskPromise }
: { kind: TaskExecuteKind.Started, task, started: {}, promise: this.activeTaskPromise };
return result;
} else {
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Start, task));
......@@ -345,7 +349,7 @@ export class ProcessTaskSystem implements ITaskSystem {
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.End, task));
return this.handleError(task, error);
});
return { kind: TaskExecuteKind.Started, started: {}, promise: this.activeTaskPromise };
return { kind: TaskExecuteKind.Started, task, started: {}, promise: this.activeTaskPromise };
}
}
......
......@@ -119,6 +119,10 @@ export interface PresentationOptions {
clear?: boolean;
}
export interface RunOptions {
rerunBehavior?: string;
}
export interface TaskIdentifier {
type?: string;
[name: string]: any;
......@@ -313,6 +317,11 @@ export interface ConfigurationProperties {
* output.
*/
problemMatcher?: ProblemMatcherConfig.ProblemMatcherType;
/**
* Task run options. Control run related properties.
*/
runOptions?: RunOptions;
}
export interface CustomTask extends CommandProperties, ConfigurationProperties {
......@@ -1285,7 +1294,8 @@ namespace ConfiguringTask {
configures: taskIdentifier,
_id: `${typeDeclaration.extensionId}.${taskIdentifier._key}`,
_source: Objects.assign({}, source, { config: configElement }),
_label: undefined
_label: undefined,
runOptions: { rerunBehavior: external.runOptions ? Tasks.RerunBehavior.fromString(external.runOptions.rerunBehavior) : Tasks.RerunBehavior.reevaluate },
};
let configuration = ConfigurationProperties.from(external, context, true);
if (configuration) {
......@@ -1344,7 +1354,8 @@ namespace CustomTask {
name: taskName,
identifier: taskName,
hasDefinedMatchers: false,
command: undefined
command: undefined,
runOptions: { rerunBehavior: external.runOptions ? Tasks.RerunBehavior.fromString(external.runOptions.rerunBehavior) : Tasks.RerunBehavior.reevaluate }
};
let configuration = ConfigurationProperties.from(external, context, false);
if (configuration) {
......@@ -1417,7 +1428,8 @@ namespace CustomTask {
command: contributedTask.command,
name: configuredProps.name || contributedTask.name,
identifier: configuredProps.identifier || contributedTask.identifier,
hasDefinedMatchers: false
hasDefinedMatchers: false,
runOptions: contributedTask.runOptions,
};
let resultConfigProps: Tasks.ConfigurationProperties = result;
......@@ -1852,6 +1864,7 @@ class ConfigurationParser {
isBackground: isBackground,
problemMatchers: matchers,
hasDefinedMatchers: false,
runOptions: { rerunBehavior: Tasks.RerunBehavior.reevaluate },
};
let value = GroupKind.from(fileConfig.group);
if (value) {
......
......@@ -192,7 +192,8 @@ class CustomTaskBuilder {
isBackground: false,
promptOnClose: true,
problemMatchers: [],
hasDefinedMatchers: false
hasDefinedMatchers: false,
runOptions: { rerunBehavior: Tasks.RerunBehavior.reevaluate },
};
}
......
......@@ -300,6 +300,7 @@ configurationRegistry.registerConfiguration({
'workbench.action.tasks.build',
'workbench.action.tasks.restartTask',
'workbench.action.tasks.runTask',
'workbench.action.tasks.reRunTask',
'workbench.action.tasks.showLog',
'workbench.action.tasks.showTasks',
'workbench.action.tasks.terminate',
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册