Skip to content

Commit

Permalink
Input variables in tasks
Browse files Browse the repository at this point in the history
Fixes #4758
  • Loading branch information
alexr00 committed Nov 28, 2018
1 parent 24036d7 commit 1dd3929
Show file tree
Hide file tree
Showing 12 changed files with 501 additions and 96 deletions.
8 changes: 6 additions & 2 deletions src/vs/workbench/api/electron-browser/mainThreadTask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,9 @@ export class MainThreadTask implements MainThreadTaskShape {
if (TaskHandleDTO.is(value)) {
let workspaceFolder = this._workspaceContextServer.getWorkspaceFolder(URI.revive(value.workspaceFolder));
this._taskService.getTask(workspaceFolder, value.id, true).then((task: Task) => {
this._taskService.run(task);
this._taskService.run(task).then(undefined, reason => {
// eat the error, it has already been surfaced to the user and we don't care about it here
});
let result: TaskExecutionDTO = {
id: value.id,
task: TaskDTO.from(task)
Expand All @@ -471,7 +473,9 @@ export class MainThreadTask implements MainThreadTaskShape {
});
} else {
let task = TaskDTO.to(value, this._workspaceContextServer, true);
this._taskService.run(task);
this._taskService.run(task).then(undefined, reason => {
// eat the error, it has already been surfaced to the user and we don't care about it here
});
let result: TaskExecutionDTO = {
id: task._id,
task: TaskDTO.from(task)
Expand Down
4 changes: 2 additions & 2 deletions src/vs/workbench/parts/debug/node/debugger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,10 @@ export class Debugger implements IDebugger {
substituteVariables(folder: IWorkspaceFolder, config: IConfig): Thenable<IConfig> {
if (this.inExtHost()) {
return this.configurationManager.substituteVariables(this.type, folder, config).then(config => {
return this.configurationResolverService.resolveWithCommands(folder, config, this.variables);
return this.configurationResolverService.resolveWithInteraction(folder, config, this.variables);
});
} else {
return this.configurationResolverService.resolveWithCommands(folder, config, this.variables);
return this.configurationResolverService.resolveWithInteraction(folder, config, this.variables);
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/vs/workbench/parts/tasks/browser/quickOpen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ export class TaskEntry extends Model.QuickOpenEntry {
}

protected doRun(task: CustomTask | ContributedTask, options?: ProblemMatcherRunOptions): boolean {
this.taskService.run(task, options);
this.taskService.run(task, options).then(undefined, reason => {
// eat the error, it has already been surfaced to the user and we don't care about it here
});
if (!task.command || task.command.presentation.focus) {
this.quickOpenService.close();
return false;
Expand Down
32 changes: 32 additions & 0 deletions src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,30 @@ const schema: IJSONSchema = {
}
}
},
inputDescription: {
type: 'object',
required: ['label', 'type', 'description'],
additionalProperties: false,
properties: {
label: {
type: 'string',
description: nls.localize('JsonSchema.tasks.inputLabel', "The input\'s label")
},
type: {
type: 'string',
description: nls.localize('JsonSchema.tasks.inputType', 'The input\'s type. Use prompt for free string input and selection for choosing from values'),
enum: ['prompt', 'pick']
},
description: {
type: 'string',
description: nls.localize('JsonSchema.tasks.inputDescription', 'Description to show for for using input.'),
},
default: {
type: 'string',
description: nls.localize('JsonSchema.tasks.inputDefault', 'Default value for the input.'),
}
}
},
taskRunnerConfiguration: {
type: 'object',
required: [],
Expand Down Expand Up @@ -235,6 +259,14 @@ const schema: IJSONSchema = {
type: 'object',
$ref: '#/definitions/taskDescription'
}
},
inputs: {
type: 'array',
description: nls.localize('JsonSchema.inputs', 'The task inputs. Used for getting user input.'),
items: {
type: 'object',
$ref: '#/definitions/inputDescription'
}
}
}
}
Expand Down
18 changes: 18 additions & 0 deletions src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,24 @@ tasks.items = {
oneOf: taskDefinitions
};

let inputDescription: IJSONSchema = definitions.inputDescription;
let inputDefinitions: IJSONSchema[] = [];
let selectionInputConfiguration: IJSONSchema = Objects.deepClone(inputDescription);
selectionInputConfiguration.properties.options = {
type: 'array',
description: nls.localize('JsonSchema.tasks.inputOptions', 'Options to select from.'),
items: {
type: 'string'
}
};
inputDefinitions.push(Objects.deepClone(inputDescription));
inputDefinitions.push(selectionInputConfiguration);

let inputs = definitions.taskRunnerConfiguration.properties.inputs;
inputs.items = {
oneOf: inputDefinitions
};

definitions.commandConfiguration.properties.isShellCommand = Objects.deepClone(shellCommand);
definitions.options.properties.shell = {
$ref: '#/definitions/shellConfiguration'
Expand Down
28 changes: 21 additions & 7 deletions src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1271,7 +1271,9 @@ class TaskService extends Disposable implements ITaskService {
}
this._taskSystem.terminate(task).then((response) => {
if (response.success) {
this.run(task);
this.run(task).then(undefined, reason => {
// eat the error, it has already been surfaced to the user and we don't care about it here
});
} else {
this.notificationService.warn(nls.localize('TaskSystem.restartFailed', 'Failed to terminate and restart task {0}', Types.isString(task) ? task : task.name));
}
Expand Down Expand Up @@ -1955,7 +1957,9 @@ class TaskService extends Disposable implements ITaskService {
for (let folder of folders) {
let task = resolver.resolve(folder, identifier);
if (task) {
this.run(task);
this.run(task).then(undefined, reason => {
// eat the error, it has already been surfaced to the user and we don't care about it here
});
return;
}
}
Expand Down Expand Up @@ -1984,7 +1988,9 @@ class TaskService extends Disposable implements ITaskService {
if (task === null) {
this.runConfigureTasks();
} else {
this.run(task, { attachProblemMatcher: true });
this.run(task, { attachProblemMatcher: true }).then(undefined, reason => {
// eat the error, it has already been surfaced to the user and we don't care about it here
});
}
});
});
Expand Down Expand Up @@ -2040,7 +2046,9 @@ class TaskService extends Disposable implements ITaskService {
if (tasks.length > 0) {
let { defaults, users } = this.splitPerGroupType(tasks);
if (defaults.length === 1) {
this.run(defaults[0]);
this.run(defaults[0]).then(undefined, reason => {
// eat the error, it has already been surfaced to the user and we don't care about it here
});
return;
} else if (defaults.length + users.length > 0) {
tasks = defaults.concat(users);
Expand All @@ -2061,7 +2069,9 @@ class TaskService extends Disposable implements ITaskService {
this.runConfigureDefaultBuildTask();
return;
}
this.run(task, { attachProblemMatcher: true });
this.run(task, { attachProblemMatcher: true }).then(undefined, reason => {
// eat the error, it has already been surfaced to the user and we don't care about it here
});
});
});
});
Expand All @@ -2084,7 +2094,9 @@ class TaskService extends Disposable implements ITaskService {
if (tasks.length > 0) {
let { defaults, users } = this.splitPerGroupType(tasks);
if (defaults.length === 1) {
this.run(defaults[0]);
this.run(defaults[0]).then(undefined, reason => {
// eat the error, it has already been surfaced to the user and we don't care about it here
});
return;
} else if (defaults.length + users.length > 0) {
tasks = defaults.concat(users);
Expand All @@ -2105,7 +2117,9 @@ class TaskService extends Disposable implements ITaskService {
this.runConfigureTasks();
return;
}
this.run(task);
this.run(task).then(undefined, reason => {
// eat the error, it has already been surfaced to the user and we don't care about it here
});
});
});
});
Expand Down
59 changes: 38 additions & 21 deletions src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,30 +386,43 @@ export class TerminalTaskSystem implements ITaskSystem {
}
return Promise.resolve(resolved);
});
return resolvedVariables;
} else {
let result = new Map<string, string>();
variables.forEach(variable => {
result.set(variable, this.configurationResolverService.resolve(workspaceFolder, variable));
});
if (isProcess) {
let processVarValue: string;
if (Platform.isWindows) {
processVarValue = win32.findExecutable(
this.configurationResolverService.resolve(workspaceFolder, CommandString.value(task.command.name)),
cwd ? this.configurationResolverService.resolve(workspaceFolder, cwd) : undefined,
envPath ? envPath.split(path.delimiter).map(p => this.configurationResolverService.resolve(workspaceFolder, p)) : undefined
);
} else {
processVarValue = this.configurationResolverService.resolve(workspaceFolder, CommandString.value(task.command.name));
let variablesArray = new Array<string>();
variables.forEach(variable => variablesArray.push(variable));

return new Promise((resolve, reject) => {
this.configurationResolverService.resolveWithInteraction(workspaceFolder, variablesArray, undefined).then(resolvedVars => {
resolve(resolvedVars);
}, reason => {
reject(reason);
});
}).then(resolvedVars => {
const resolvedVariablesMap = new Map<string, string>();
for (let i = 0; i < variablesArray.length; i++) {
resolvedVariablesMap.set(variablesArray[i], resolvedVars[i]);
}
result.set(TerminalTaskSystem.ProcessVarName, processVarValue);
}
let resolvedVariablesResult: ResolvedVariables = {
variables: result,
};
resolvedVariables = Promise.resolve(resolvedVariablesResult);
if (isProcess) {
let processVarValue: string;
if (Platform.isWindows) {
processVarValue = win32.findExecutable(
this.configurationResolverService.resolve(workspaceFolder, CommandString.value(task.command.name)),
cwd ? this.configurationResolverService.resolve(workspaceFolder, cwd) : undefined,
envPath ? envPath.split(path.delimiter).map(p => this.configurationResolverService.resolve(workspaceFolder, p)) : undefined
);
} else {
processVarValue = this.configurationResolverService.resolve(workspaceFolder, CommandString.value(task.command.name));
}
resolvedVariablesMap.set(TerminalTaskSystem.ProcessVarName, processVarValue);
}
let resolvedVariablesResult: ResolvedVariables = {
variables: resolvedVariablesMap,
};
resolvedVariables = Promise.resolve(resolvedVariablesResult);
return resolvedVariables;
});

}
return resolvedVariables;
}

private executeCommand(task: CustomTask | ContributedTask, trigger: string): Promise<ITaskSummary> {
Expand All @@ -425,6 +438,8 @@ export class TerminalTaskSystem implements ITaskSystem {
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));
}, reason => {
return Promise.reject(reason);
});
}

Expand All @@ -445,6 +460,8 @@ export class TerminalTaskSystem implements ITaskSystem {
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));
}, reason => {
return Promise.reject(reason);
});
} else {
this.currentTask.resolvedVariables = this.lastTask.getVerifiedTask().resolvedVariables;
Expand Down
6 changes: 6 additions & 0 deletions src/vs/workbench/parts/tasks/node/taskConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import * as Tasks from '../common/tasks';
import { TaskDefinitionRegistry } from '../common/taskDefinitionRegistry';

import { TaskDefinition } from 'vs/workbench/parts/tasks/node/tasks';
import { ConfiguredInput } from 'vs/workbench/services/configurationResolver/common/configurationResolver';

export const enum ShellQuoting {
/**
Expand Down Expand Up @@ -450,6 +451,11 @@ export interface BaseTaskRunnerConfiguration {
* Problem matcher declarations
*/
declares?: ProblemMatcherConfig.NamedProblemMatcher[];

/**
* Optional user input varaibles.
*/
inputs?: ConfiguredInput[];
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,22 @@ export interface IConfigurationResolverService {
resolveAny(folder: IWorkspaceFolder, config: any, commandValueMapping?: IStringDictionary<string>): any;

/**
* Recursively resolves all variables (including commands) in the given config and returns a copy of it with substituted values.
* Recursively resolves all variables (including commands and user input) in the given config and returns a copy of it with substituted values.
* If a "variables" dictionary (with names -> command ids) is given,
* command variables are first mapped through it before being resolved.
*/
resolveWithCommands(folder: IWorkspaceFolder, config: any, variables?: IStringDictionary<string>): TPromise<any>;
resolveWithInteraction(folder: IWorkspaceFolder, config: any, variables?: IStringDictionary<string>): TPromise<any>;
}

export enum ConfiguredInputType {
prompt,
pick
}

export interface ConfiguredInput {
label: string;
description: string;
default?: string;
type: ConfiguredInputType;
options?: string[];
}
Loading

0 comments on commit 1dd3929

Please sign in to comment.