Implemented latest feedback from #28235

This commit is contained in:
Dirk Baeumer 2017-06-21 23:44:32 +02:00
parent c9cf1f51b3
commit 2ef8e48de4
21 changed files with 1121 additions and 592 deletions

73
.vscode/tasks.json vendored
View file

@ -1,23 +1,14 @@
{
"version": "0.1.0",
"windows": {
"command": ".\\node_modules\\.bin\\gulp"
},
"osx": {
"command": "./node_modules/.bin/gulp"
},
"linux": {
"command": "./node_modules/.bin/gulp"
},
"isShellCommand": true,
"version": "2.0.0",
"tasks": [
{
"taskName": "watch",
"args": [
"--no-color"
],
"isBuildCommand": true,
"customize": "vscode.npm.run watch",
"taskName": "Build VS Code",
"group": "build",
"isBackground": true,
"terminal": {
"reveal": "never"
},
"problemMatcher": {
"owner": "typescript",
"applyTo": "closedDocuments",
@ -37,38 +28,34 @@
}
},
{
"taskName": "tslint",
"args": [],
"problemMatcher": {
"owner": "tslint",
"fileLocation": [
"relative",
"${workspaceRoot}"
],
"severity": "warning",
"pattern": {
"regexp": "(.*)\\[(\\d+),\\s(\\d+)\\]:\\s(.*)$", // (.*)\[(\d+), (\d+)\]: (.*)
"file": 1,
"line": 2,
"column": 3,
"message": 4
}
"customize": "gulp.tslint",
"taskName": "Run tslint",
"problemMatcher": ["$tslint4"]
},
{
"taskName": "Run tests",
"type": "shell",
"command": "./scripts/test.sh",
"windows": {
"command": ".\\scripts\\test.bat"
},
"group": "test",
"terminal": {
"echo": true,
"reveal": "always"
}
},
{
"taskName": "test",
"args": [
"--no-color"
],
"showOutput": "always",
"isTestCommand": true
"taskName": "Run Dev",
"type": "shell",
"command": "./scripts/code.sh",
"windows": {
"command": ".\\scripts\\code.bat"
}
},
{
"taskName": "electron",
"args": [
"--no-color"
],
"showOutput": "never"
"customize": "gulp.electron",
"taskName": "Download electron"
}
]
}

View file

@ -41,6 +41,22 @@
"description": "%config.grunt.autoDetect%"
}
}
}
},
"taskTypes": [
{
"taskType": "grunt",
"required": ["task"],
"properties": {
"task": {
"type": "string",
"description": "The Grunt task to customize"
},
"file": {
"type": "string",
"description": "The Grunt file that provides the task. Can be omitted."
}
}
}
]
}
}

View file

@ -81,6 +81,11 @@ function getOutputChannel(): vscode.OutputChannel {
return _channel;
}
interface GruntTaskIdentifier extends vscode.TaskIdentifier {
task: string;
file?: string;
}
async function getGruntTasks(): Promise<vscode.Task[]> {
let workspaceRoot = vscode.workspace.rootPath;
let emptyTasks: vscode.Task[] = [];
@ -145,10 +150,13 @@ async function getGruntTasks(): Promise<vscode.Task[]> {
let matches = regExp.exec(line);
if (matches && matches.length === 2) {
let taskName = matches[1];
let identifier: GruntTaskIdentifier = {
type: 'grunt',
task: taskName
};
let task = taskName.indexOf(' ') === -1
? new vscode.ShellTask(taskName, `${command} ${taskName}`)
: new vscode.ShellTask(taskName, `${command} "${taskName}"`);
task.identifier = `grunt.${taskName}`;
? new vscode.ShellTask(identifier, taskName, `${command} ${taskName}`)
: new vscode.ShellTask(identifier, taskName, `${command} "${taskName}"`);
result.push(task);
let lowerCaseTaskName = taskName.toLowerCase();
if (lowerCaseTaskName === 'build') {

View file

@ -81,6 +81,11 @@ function getOutputChannel(): vscode.OutputChannel {
return _channel;
}
interface GulpTaskIdentifier extends vscode.TaskIdentifier {
task: string;
file?: string;
}
async function getGulpTasks(): Promise<vscode.Task[]> {
let workspaceRoot = vscode.workspace.rootPath;
let emptyTasks: vscode.Task[] = [];
@ -121,8 +126,11 @@ async function getGulpTasks(): Promise<vscode.Task[]> {
if (line.length === 0) {
continue;
}
let task = new vscode.ShellTask(line, `${gulpCommand} ${line}`);
task.identifier = `gulp.${line}`;
let identifier: GulpTaskIdentifier = {
type: 'gulp',
task: line
};
let task = new vscode.ShellTask(identifier, line, `${gulpCommand} ${line}`);
result.push(task);
let lowerCaseLine = line.toLowerCase();
if (lowerCaseLine === 'build') {

View file

@ -41,6 +41,22 @@
"description": "%config.jake.autoDetect%"
}
}
}
},
"taskTypes": [
{
"taskType": "jake",
"required": ["task"],
"properties": {
"task": {
"type": "string",
"description": "The Jake task to customize"
},
"file": {
"type": "string",
"description": "The Jake file that provides the task. Can be omitted."
}
}
}
]
}
}

View file

@ -81,6 +81,11 @@ function getOutputChannel(): vscode.OutputChannel {
return _channel;
}
interface JakeTaskIdentifier extends vscode.TaskIdentifier {
task: string;
file?: string;
}
async function getJakeTasks(): Promise<vscode.Task[]> {
let workspaceRoot = vscode.workspace.rootPath;
let emptyTasks: vscode.Task[] = [];
@ -125,8 +130,11 @@ async function getJakeTasks(): Promise<vscode.Task[]> {
let matches = regExp.exec(line);
if (matches && matches.length === 2) {
let taskName = matches[1];
let task = new vscode.ShellTask(taskName, `${jakeCommand} ${taskName}`);
task.identifier = `jake.${taskName}`;
let identifier: JakeTaskIdentifier = {
type: 'jake',
task: taskName
};
let task = new vscode.ShellTask(identifier, taskName, `${jakeCommand} ${taskName}`);
result.push(task);
let lowerCaseLine = line.toLowerCase();
if (lowerCaseLine === 'build') {

View file

@ -59,6 +59,12 @@ async function readFile(file: string): Promise<string> {
});
}
interface NpmTaskIdentifier extends vscode.TaskIdentifier {
script: string;
file?: string;
}
async function getNpmScriptsAsTasks(): Promise<vscode.Task[]> {
let workspaceRoot = vscode.workspace.rootPath;
let emptyTasks: vscode.Task[] = [];
@ -81,7 +87,11 @@ async function getNpmScriptsAsTasks(): Promise<vscode.Task[]> {
const result: vscode.Task[] = [];
Object.keys(json.scripts).forEach(each => {
const task = new vscode.ShellTask(`run ${each}`, `npm run ${each}`);
const identifier: NpmTaskIdentifier = {
type: 'npm',
script: each
};
const task = new vscode.ShellTask(identifier, `run ${each}`, `npm run ${each}`);
const lowerCaseTaskName = each.toLowerCase();
if (lowerCaseTaskName === 'build') {
task.group = vscode.TaskGroup.Build;
@ -91,7 +101,7 @@ async function getNpmScriptsAsTasks(): Promise<vscode.Task[]> {
result.push(task);
});
// add some 'well known' npm tasks
result.push(new vscode.ShellTask(`install`, `npm install`));
result.push(new vscode.ShellTask({ type: 'npm', script: 'install' } as NpmTaskIdentifier, `install`, `npm install`));
return Promise.resolve(result);
} catch (e) {
return Promise.resolve(emptyTasks);

View file

@ -21,6 +21,11 @@ const exists = (file: string): Promise<boolean> =>
});
});
interface TypeScriptTaskIdentifier extends vscode.TaskIdentifier {
configFile: string;
}
/**
* Provides tasks for building `tsconfig.json` files in a project.
*/
@ -48,7 +53,8 @@ class TscTaskProvider implements vscode.TaskProvider {
return projects.map(configFile => {
const configFileName = path.relative(rootPath, configFile);
const buildTask = new vscode.ShellTask(`build ${configFileName}`, `${command} -p "${configFile}"`, '$tsc');
const identifier: TypeScriptTaskIdentifier = { type: 'typescript', configFile: configFileName };
const buildTask = new vscode.ShellTask(identifier, `build ${configFileName}`, `${command} -p "${configFile}"`, '$tsc');
buildTask.source = 'tsc';
buildTask.group = vscode.TaskGroup.Build;
return buildTask;

View file

@ -122,6 +122,21 @@ declare module 'vscode' {
export const Test: 'test';
}
/**
* An identifer to uniquely identify a task in the system.
* The value must be JSON-stringifyable.
*/
export interface TaskIdentifier {
/**
* The task type as defined by the extension implementing a
* task provider. Examples are 'grunt', 'npm' or 'tsc'.
* Usually a task provider defines more properties to identify
* a task. They need to be defined in the package.json of the
* extension under the 'taskTypes' extension point.
*/
readonly type: string;
}
/**
* A task that starts an external process.
*/
@ -130,17 +145,19 @@ declare module 'vscode' {
/**
* Creates a process task.
*
* @param identifier: the task's identifier as defined in the 'taskTypes' extension point.
* @param name the task's name. Is presented in the user interface.
* @param process the process to start.
* @param problemMatchers the names of problem matchers to use, like '$tsc'
* or '$eslint'. Problem matchers can be contributed by an extension using
* the `problemMatchers` extension point.
*/
constructor(name: string, process: string, problemMatchers?: string | string[]);
constructor(identifier: TaskIdentifier, name: string, process: string, problemMatchers?: string | string[]);
/**
* Creates a process task.
*
* @param identifier: the task's identifier as defined in the 'taskTypes' extension point.
* @param name the task's name. Is presented in the user interface.
* @param process the process to start.
* @param args arguments to be passed to the process.
@ -148,11 +165,12 @@ declare module 'vscode' {
* or '$eslint'. Problem matchers can be contributed by an extension using
* the `problemMatchers` extension point.
*/
constructor(name: string, process: string, args: string[], problemMatchers?: string | string[]);
constructor(identifier: TaskIdentifier, name: string, process: string, args: string[], problemMatchers?: string | string[]);
/**
* Creates a process task.
*
* @param identifier: the task's identifier as defined in the 'taskTypes' extension point.
* @param name the task's name. Is presented in the user interface.
* @param process the process to start.
* @param args arguments to be passed to the process.
@ -161,18 +179,17 @@ declare module 'vscode' {
* or '$eslint'. Problem matchers can be contributed by an extension using
* the `problemMatchers` extension point.
*/
constructor(name: string, process: string, args: string[], options: ProcessTaskOptions, problemMatchers?: string | string[]);
constructor(identifier: TaskIdentifier, name: string, process: string, args: string[], options: ProcessTaskOptions, problemMatchers?: string | string[]);
/**
* The task's identifier.
*/
identifier: TaskIdentifier;
/**
* The task's name
*/
readonly name: string;
/**
* The task's identifier. If omitted the internal identifier will
* be `${extensionName}:${name}`
*/
identifier: string | undefined;
name: string;
/**
* Whether the task is a background task or not.
@ -182,7 +199,7 @@ declare module 'vscode' {
/**
* The process to be executed.
*/
readonly process: string;
process: string;
/**
* The arguments passed to the process. Defaults to an empty array.
@ -280,17 +297,19 @@ declare module 'vscode' {
/**
* Creates a shell task.
*
* @param identifier: the task's identifier as defined in the 'taskTypes' extension point.
* @param name the task's name. Is presented in the user interface.
* @param commandLine the command line to execute.
* @param problemMatchers the names of problem matchers to use, like '$tsc'
* or '$eslint'. Problem matchers can be contributed by an extension using
* the `problemMatchers` extension point.
*/
constructor(name: string, commandLine: string, problemMatchers?: string | string[]);
constructor(identifier: TaskIdentifier, name: string, commandLine: string, problemMatchers?: string | string[]);
/**
* Creates a shell task.
*
* @param identifier: the task's identifier as defined in the 'taskTypes' extension point.
* @param name the task's name. Is presented in the user interface.
* @param commandLine the command line to execute.
* @param options additional options used when creating the shell.
@ -298,18 +317,17 @@ declare module 'vscode' {
* or '$eslint'. Problem matchers can be contributed by an extension using
* the `problemMatchers` extension point.
*/
constructor(name: string, commandLine: string, options: ShellTaskOptions, problemMatchers?: string | string[]);
constructor(identifier: TaskIdentifier, name: string, commandLine: string, options: ShellTaskOptions, problemMatchers?: string | string[]);
/**
* The task's identifier.
*/
identifier: TaskIdentifier;
/**
* The task's name
*/
readonly name: string;
/**
* The task's identifier. If omitted the internal identifier will
* be `${extensionName}:${name}`
*/
identifier: string | undefined;
name: string;
/**
* Whether the task is a background task or not.
@ -319,7 +337,7 @@ declare module 'vscode' {
/**
* The command line to execute.
*/
readonly commandLine: string;
commandLine: string;
/**
* A human-readable string describing the source of this

View file

@ -7,6 +7,7 @@
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import * as UUID from 'vs/base/common/uuid';
import * as Objects from 'vs/base/common/objects';
import { asWinJsPromise } from 'vs/base/common/async';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
@ -315,7 +316,7 @@ namespace Tasks {
return result;
}
function fromSingle(task: vscode.Task, extension: IExtensionDescription, uuidMap: UUIDMap): TaskSystem.Task {
function fromSingle(task: vscode.Task, extension: IExtensionDescription, uuidMap: UUIDMap): TaskSystem.ContributedTask {
if (typeof task.name !== 'string') {
return undefined;
}
@ -336,12 +337,20 @@ namespace Tasks {
detail: extension.id
};
let label = nls.localize('task.label', '{0}: {1}', source.label, task.name);
let result: TaskSystem.Task = {
_id: uuidMap.getUUID(task.identifier),
let id = `${extension.id}.${task.identifierKey}`;
let identifier: TaskSystem.TaskIdentifier = {
_key: task.identifierKey,
type: task.identifier.type
};
Objects.assign(identifier, task.identifier);
let result: TaskSystem.ContributedTask = {
_id: id, // uuidMap.getUUID(identifier),
_source: source,
_label: label,
type: task.identifier.type,
defines: identifier,
name: task.name,
identifier: task.identifier ? task.identifier : `${extension.id}.${task.name}`,
identifier: label,
group: types.TaskGroup.is(task.group) ? task.group : undefined,
command: command,
isBackground: !!task.isBackground,
@ -357,7 +366,7 @@ namespace Tasks {
let result: TaskSystem.CommandConfiguration = {
name: value.process,
args: Strings.from(value.args),
type: TaskSystem.CommandType.Process,
runtime: TaskSystem.RuntimeType.Process,
suppressTaskName: true,
presentation: PresentationOptions.from(value.presentationOptions)
};
@ -373,7 +382,7 @@ namespace Tasks {
}
let result: TaskSystem.CommandConfiguration = {
name: value.commandLine,
type: TaskSystem.CommandType.Shell,
runtime: TaskSystem.RuntimeType.Shell,
presentation: PresentationOptions.from(value.presentationOptions)
};
if (value.options) {

View file

@ -4,6 +4,8 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as crypto from 'crypto';
import URI from 'vs/base/common/uri';
import { illegalArgument } from 'vs/base/common/errors';
import * as vscode from 'vscode';
@ -1049,40 +1051,53 @@ export class BaseTask {
private _name: string;
private _problemMatchers: string[];
private _identifier: string;
private _identifier: vscode.TaskIdentifier;
private _identifierKey: string;
private _isBackground: boolean;
private _source: string;
private _group: string;
private _presentationOptions: vscode.TaskPresentationOptions;
constructor(name: string, problemMatchers: string[]) {
if (typeof name !== 'string') {
throw illegalArgument('name');
}
this._name = name;
constructor(identifier: vscode.TaskIdentifier, name: string, problemMatchers: string[]) {
this.identifier = identifier;
this.name = name;
this._problemMatchers = problemMatchers || [];
this._isBackground = false;
this._presentationOptions = Object.create(null);
}
get identifier(): string {
get identifier(): vscode.TaskIdentifier {
return this._identifier;
}
set identifier(value: string) {
set identifier(value: vscode.TaskIdentifier) {
if (value === void 0 || value === null) {
this._identifier = undefined;
}
if (typeof value !== 'string' || value.length === 0) {
throw illegalArgument('identifier must be a string of length > 0');
throw illegalArgument('Identifier can\'t be undefined or null');
}
this._identifierKey = undefined;
this._identifier = value;
}
get identifierKey(): string {
if (!this._identifierKey) {
const hash = crypto.createHash('md5');
hash.update(JSON.stringify(this._identifier));
this._identifierKey = hash.digest('hex');
}
return this._identifierKey;
}
get name(): string {
return this._name;
}
set name(value: string) {
if (typeof value !== 'string') {
throw illegalArgument('name');
}
this._name = value;
}
get isBackground(): boolean {
return this._isBackground;
}
@ -1194,9 +1209,9 @@ export class ProcessTask extends BaseTask {
private _args: string[];
private _options: vscode.ProcessTaskOptions;
constructor(name: string, process: string, args?: string[], problemMatchers?: string | string[]);
constructor(name: string, process: string, args: string[] | undefined, options: vscode.ProcessTaskOptions, problemMatchers?: string | string[]);
constructor(name: string, process: string, arg3?: string[], arg4?: vscode.ProcessTaskOptions | string | string[], arg5?: string | string[]) {
constructor(identifier: vscode.TaskIdentifier, name: string, process: string, args?: string[], problemMatchers?: string | string[]);
constructor(identifier: vscode.TaskIdentifier, name: string, process: string, args: string[] | undefined, options: vscode.ProcessTaskOptions, problemMatchers?: string | string[]);
constructor(identifier: vscode.TaskIdentifier, name: string, process: string, varg1?: string[], varg2?: vscode.ProcessTaskOptions | string | string[], varg3?: string | string[]) {
if (typeof process !== 'string') {
throw illegalArgument('process');
}
@ -1204,16 +1219,16 @@ export class ProcessTask extends BaseTask {
let options: vscode.ProcessTaskOptions;
let problemMatchers: string | string[];
args = arg3 || [];
if (arg4) {
if (Array.isArray(arg4) || typeof arg4 === 'string') {
problemMatchers = arg4;
args = varg1 || [];
if (varg2) {
if (Array.isArray(varg2) || typeof varg2 === 'string') {
problemMatchers = varg2;
} else {
options = arg4;
options = varg2;
}
}
if (arg5 && !problemMatchers) {
problemMatchers = arg5;
if (varg3 && !problemMatchers) {
problemMatchers = varg3;
}
let pm: string[];
if (problemMatchers && (typeof problemMatchers === 'string')) {
@ -1222,7 +1237,7 @@ export class ProcessTask extends BaseTask {
pm = problemMatchers;
}
pm = pm || [];
super(name, pm);
super(identifier, name, pm);
this._process = process;
this._args = args;
this._options = options || Object.create(null);
@ -1260,9 +1275,9 @@ export class ShellTask extends BaseTask implements vscode.ShellTask {
private _commandLine: string;
private _options: vscode.ShellTaskOptions;
constructor(name: string, commandLine: string, problemMatchers?: string | string[]);
constructor(name: string, commandLine: string, options: vscode.ShellTaskOptions, problemMatchers?: string | string[]);
constructor(name: string, commandLine: string, optionsOrProblemMatchers?: vscode.ShellTaskOptions | string | string[], problemMatchers?: string | string[]) {
constructor(identifier: vscode.TaskIdentifier, name: string, commandLine: string, problemMatchers?: string | string[]);
constructor(identifier: vscode.TaskIdentifier, name: string, commandLine: string, options: vscode.ShellTaskOptions, problemMatchers?: string | string[]);
constructor(identifier: vscode.TaskIdentifier, name: string, commandLine: string, optionsOrProblemMatchers?: vscode.ShellTaskOptions | string | string[], problemMatchers?: string | string[]) {
if (typeof commandLine !== 'string') {
throw illegalArgument('commandLine');
}
@ -1279,7 +1294,7 @@ export class ShellTask extends BaseTask implements vscode.ShellTask {
pm = problemMatchers;
}
pm = pm || [];
super(name, pm);
super(identifier, name, pm);
this._commandLine = commandLine;
this._options = options || Object.create(null);
}

View file

@ -78,7 +78,7 @@ export abstract class QuickOpenHandler extends Quickopen.QuickOpenHandler {
let configured: Task[] = [];
let detected: Task[] = [];
let taskMap: IStringDictionary<Task> = Object.create(null);
tasks.forEach(task => taskMap[task.identifier] = task);
tasks.forEach(task => taskMap[Task.getKey(task)] = task);
recentlyUsedTasks.keys().forEach(key => {
let task = taskMap[key];
if (task) {
@ -86,7 +86,7 @@ export abstract class QuickOpenHandler extends Quickopen.QuickOpenHandler {
}
});
for (let task of tasks) {
if (!recentlyUsedTasks.has(task.identifier)) {
if (!recentlyUsedTasks.has(Task.getKey(task))) {
if (task._source.kind === TaskSourceKind.Workspace) {
configured.push(task);
} else {
@ -98,7 +98,7 @@ export abstract class QuickOpenHandler extends Quickopen.QuickOpenHandler {
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);
this.fillEntries(entries, input, configured, nls.localize('configured', 'custom 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());

View file

@ -0,0 +1,123 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
import { IStringDictionary } from 'vs/base/common/collections';
import { TPromise } from 'vs/base/common/winjs.base';
import * as Types from 'vs/base/common/types';
import * as Objects from 'vs/base/common/objects';
import { ExtensionsRegistry, ExtensionMessageCollector } from 'vs/platform/extensions/common/extensionsRegistry';
import * as Tasks from 'vs/workbench/parts/tasks/common/tasks';
const taskTypeSchema: IJSONSchema = {
type: 'object',
additionalProperties: false,
properties: {
taskType: {
type: 'string',
description: nls.localize('TaskType.description', 'The actual task type')
},
properties: {
type: 'object',
description: nls.localize('TaskType.properties', 'Additional properties of the task type'),
additionalProperties: {
$ref: 'http://json-schema.org/draft-04/schema#'
}
}
}
};
namespace Configuration {
export interface TaskTypeDescription {
taskType?: string;
required?: string[];
properties?: IJSONSchemaMap;
}
export function from(value: TaskTypeDescription, messageCollector: ExtensionMessageCollector): Tasks.TaskTypeDescription {
if (!value) {
return undefined;
}
let taskType = Types.isString(value.taskType) ? value.taskType : undefined;
if (!taskType || taskType.length === 0) {
messageCollector.error(nls.localize('TaskTypeConfiguration.noType', 'The task type configuration is missing the required \'taskType\' property'));
return undefined;
}
let required: string[] = [];
if (Array.isArray(value.required)) {
for (let element of value.required) {
if (Types.isString(element)) {
required.push(element);
}
}
}
return { taskType, required: required.length >= 0 ? required : undefined, properties: value.properties ? Objects.deepClone(value.properties) : undefined };
}
}
const taskTypesExtPoint = ExtensionsRegistry.registerExtensionPoint<Configuration.TaskTypeDescription[]>('taskTypes', [], {
description: nls.localize('TaskTypeExtPoint', 'Contributes task types'),
type: 'array',
items: taskTypeSchema
});
export interface ITaskTypeRegistry {
onReady(): TPromise<void>;
exists(key: string): boolean;
get(key: string): Tasks.TaskTypeDescription;
all(): Tasks.TaskTypeDescription[];
}
class TaskTypeRegistryImpl implements ITaskTypeRegistry {
private taskTypes: IStringDictionary<Tasks.TaskTypeDescription>;
private readyPromise: TPromise<void>;
constructor() {
this.taskTypes = Object.create(null);
this.readyPromise = new TPromise<void>((resolve, reject) => {
taskTypesExtPoint.setHandler((extensions) => {
try {
extensions.forEach(extension => {
let taskTypes = extension.value;
for (let taskType of taskTypes) {
let type = Configuration.from(taskType, extension.collector);
if (type) {
this.taskTypes[type.taskType] = type;
}
}
});
} catch (error) {
}
resolve(undefined);
});
});
}
public onReady(): TPromise<void> {
return this.readyPromise;
}
public get(key: string): Tasks.TaskTypeDescription {
return this.taskTypes[key];
}
public exists(key: string): boolean {
return !!this.taskTypes[key];
}
public all(): Tasks.TaskTypeDescription[] {
return Object.keys(this.taskTypes).map(key => this.taskTypes[key]);
}
}
export const TaskTypeRegistry: ITaskTypeRegistry = new TaskTypeRegistryImpl();

View file

@ -5,6 +5,7 @@
'use strict';
import * as Types from 'vs/base/common/types';
import { IJSONSchemaMap } from 'vs/base/common/jsonSchema';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ProblemMatcher } from 'vs/platform/markers/common/problemMatcher';
@ -140,20 +141,20 @@ export interface PresentationOptions {
panel: PanelKind;
}
export enum CommandType {
export enum RuntimeType {
Shell = 1,
Process = 2
}
export namespace CommandType {
export function fromString(value: string): CommandType {
export namespace RuntimeType {
export function fromString(value: string): RuntimeType {
switch (value.toLowerCase()) {
case 'shell':
return CommandType.Shell;
return RuntimeType.Shell;
case 'process':
return CommandType.Process;
return RuntimeType.Process;
default:
return CommandType.Process;
return RuntimeType.Process;
}
}
}
@ -163,7 +164,7 @@ export interface CommandConfiguration {
/**
* The task type
*/
type: CommandType;
runtime: RuntimeType;
/**
* The command to execute
@ -225,40 +226,22 @@ export interface TaskSource {
detail?: string;
}
/**
* A task description
*/
export interface Task {
export interface TaskIdentifier {
_key: string;
type: string;
}
/**
* The task's internal id
*/
_id: string;
/**
* The cached label.
*/
_label: string;
/**
* Indicated the source of the task (e.g tasks.json or extension)
*/
_source: TaskSource;
export interface ConfigurationProperties {
/**
* The task's name
*/
name: string;
name?: string;
/**
* The task's identifier.
* The task's name
*/
identifier: string;
/**
* The id of the customized task
*/
customize?: string;
identifier?: string;
/**
* the task's group;
@ -266,9 +249,9 @@ export interface Task {
group?: string;
/**
* The command configuration
* The presentation options
*/
command: CommandConfiguration;
presentation?: PresentationOptions;
/**
* Whether the task is a background task or not.
@ -291,6 +274,89 @@ export interface Task {
problemMatchers?: (string | ProblemMatcher)[];
}
export interface CommonTask {
/**
* The task's internal id
*/
_id: string;
/**
* The cached label.
*/
_label: string;
/**
* Indicated the source of the task (e.g tasks.json or extension)
*/
_source: TaskSource;
type: string;
}
export interface CustomTask extends CommonTask, ConfigurationProperties {
type: 'custom';
name: string;
identifier: string;
/**
* The command configuration
*/
command: CommandConfiguration;
}
export namespace CustomTask {
export function is(value: any): value is CustomTask {
let candidate: CustomTask = value;
return candidate && candidate.type === 'custom';
}
}
export interface ConfiguringTask extends CommonTask, ConfigurationProperties {
configures: TaskIdentifier;
}
export namespace ConfiguringTask {
export function is(value: any): value is ConfiguringTask {
let candidate: ConfiguringTask = value;
return candidate && candidate.configures && Types.isString(candidate.configures.type) && value.command === void 0;
}
}
export interface ContributedTask extends CommonTask, ConfigurationProperties {
defines: TaskIdentifier;
/**
* The command configuration
*/
command: CommandConfiguration;
}
export namespace ContributedTask {
export function is(value: any): value is ContributedTask {
let candidate: ContributedTask = value;
return candidate && candidate.defines && Types.isString(candidate.defines.type) && candidate.command !== void 0;
}
}
export type Task = CustomTask | ContributedTask;
export namespace Task {
export function getKey(task: Task): string {
if (CustomTask.is(task)) {
return task.identifier;
} else {
return task.defines._key;
}
}
}
export enum ExecutionEngine {
Process = 1,
Terminal = 2
@ -304,4 +370,10 @@ export enum JsonSchemaVersion {
export interface TaskSet {
tasks: Task[];
extension?: IExtensionDescription;
}
export interface TaskTypeDescription {
taskType: string;
required: string[];
properties: IJSONSchemaMap;
}

View file

@ -11,6 +11,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema';
import commonSchema from './jsonSchemaCommon';
import { ProblemMatcherRegistry } from 'vs/platform/markers/common/problemMatcher';
import { TaskTypeRegistry } from '../common/taskTypeRegistry';
const shellCommand: IJSONSchema = {
anyOf: [
@ -96,9 +97,42 @@ const version: IJSONSchema = {
description: nls.localize('JsonSchema.version', 'The config\'s version number.')
};
const customize: IJSONSchema = {
type: 'string',
description: nls.localize('JsonSchema.tasks.customize', 'The contributed task to be customized.')
const customizeTaskDescription: IJSONSchema = Objects.deepClone(commonSchema.definitions.taskDescription);
customizeTaskDescription.required = undefined;
customizeTaskDescription.properties.presentation = Objects.deepClone(presentation);
TaskTypeRegistry.onReady().then(() => {
let oneOf: IJSONSchema[] = [];
for (let taskType of TaskTypeRegistry.all()) {
let schema: IJSONSchema = {
type: 'object',
properties: {
}
};
schema.properties.type = {
type: 'string',
description: nls.localize('JsonSchema.customizations.customizes.type', 'The task system to customize'),
enum: [taskType.taskType]
};
if (taskType.required) {
schema.required = taskType.required.slice();
}
for (let key of Object.keys(taskType.properties)) {
let property = taskType.properties[key];
schema.properties[key] = Objects.deepClone(property);
}
oneOf.push(schema);
}
customizeTaskDescription.properties.customizes = {
description: nls.localize('JsonSchema.customizations.customizes', 'The task to be customized'),
oneOf
};
});
const customizations: IJSONSchema = {
type: 'array',
description: nls.localize('JsonSchema.customizations', 'The tasks to be customized'),
items: customizeTaskDescription
};
const schema: IJSONSchema = {
@ -121,7 +155,8 @@ const schema: IJSONSchema = {
linux: {
'$ref': '#/definitions/taskRunnerConfiguration',
'description': nls.localize('JsonSchema.linux', 'Linux specific command configuration')
}
},
customizations: customizations
}
},
{
@ -141,7 +176,6 @@ definitions.taskDescription.properties.dependsOn = dependsOn;
// definitions.taskDescription.properties.echoCommand.deprecationMessage = nls.localize('JsonSchema.tasks.echoCommand.deprecated', 'The property echoCommand is deprecated. Use the terminal property instead.');
// definitions.taskDescription.properties.isBuildCommand.deprecationMessage = nls.localize('JsonSchema.tasks.isBuildCommand.deprecated', 'The property isBuildCommand is deprecated. Use the group property instead.');
// definitions.taskDescription.properties.isTestCommand.deprecationMessage = nls.localize('JsonSchema.tasks.isTestCommand.deprecated', 'The property isTestCommand is deprecated. Use the group property instead.');
definitions.taskDescription.properties.customize = customize;
definitions.taskDescription.properties.type = Objects.deepClone(taskType);
definitions.taskDescription.properties.presentation = presentation;
definitions.taskDescription.properties.group = group;

View file

@ -2,7 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./media/task.contribution';
@ -52,6 +51,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag
import { IModeService } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
import jsonContributionRegistry = require('vs/platform/jsonschemas/common/jsonContributionRegistry');
import { IJSONSchema } from 'vs/base/common/jsonSchema';
@ -75,11 +75,11 @@ import { Scope, IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs
import { ITerminalService } from 'vs/workbench/parts/terminal/common/terminal';
import { ITaskSystem, ITaskResolver, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, TaskSystemEvents, TaskTerminateResponse } from 'vs/workbench/parts/tasks/common/taskSystem';
import { Task, TaskSet, TaskGroup, ExecutionEngine, JsonSchemaVersion, TaskSourceKind } from 'vs/workbench/parts/tasks/common/tasks';
import { Task, CustomTask, ConfiguringTask, ContributedTask, TaskSet, TaskGroup, ExecutionEngine, JsonSchemaVersion, TaskSourceKind, TaskIdentifier } from 'vs/workbench/parts/tasks/common/tasks';
import { ITaskService, TaskServiceEvents, ITaskProvider, TaskEvent } from 'vs/workbench/parts/tasks/common/taskService';
import { templates as taskTemplates } from 'vs/workbench/parts/tasks/common/taskTemplates';
import * as TaskConfig from 'vs/workbench/parts/tasks/common/taskConfiguration';
import * as TaskConfig from '../node/taskConfiguration';
import { ProcessTaskSystem } from 'vs/workbench/parts/tasks/node/processTaskSystem';
import { TerminalTaskSystem } from './terminalTaskSystem';
import { ProcessRunnerDetector } from 'vs/workbench/parts/tasks/node/processRunnerDetector';
@ -513,8 +513,8 @@ class ProblemReporter implements TaskConfig.IProblemReporter {
interface WorkspaceTaskResult {
set: TaskSet;
annotatingTasks: {
byIdentifier: IStringDictionary<Task>;
configurations: {
byIdentifier: IStringDictionary<ConfiguringTask>;
};
hasErrors: boolean;
}
@ -821,7 +821,7 @@ class TaskService extends EventEmitter implements ITaskService {
}
public customize(task: Task, openConfig: boolean = false): TPromise<void> {
if (task._source.kind !== TaskSourceKind.Extension) {
if (!ContributedTask.is(task)) {
return TPromise.as<void>(undefined);
}
let configuration = this.getConfiguration();
@ -830,20 +830,25 @@ class TaskService extends EventEmitter implements ITaskService {
return TPromise.as<void>(undefined);
}
let fileConfig = configuration.config;
let customize: TaskConfig.TaskDescription = { customize: task.identifier, taskName: task._label };
if (task.problemMatchers === void 0) {
customize.problemMatcher = [];
let customizes: TaskConfig.ConfiguringTask = {
};
let identifier: TaskConfig.TaskIdentifier = Objects.assign(Object.create(null), task.defines);
delete identifier['_key'];
Object.keys(identifier).forEach(key => customizes[key] = identifier[key]);
if (task.problemMatchers === void 0 || task.problemMatchers.length === 0) {
customizes.problemMatcher = [];
}
if (!fileConfig) {
fileConfig = {
version: '2.0.0',
tasks: [customize]
tasks: [customizes]
};
} else {
if (Array.isArray(fileConfig.tasks)) {
fileConfig.tasks.push(customize);
fileConfig.tasks.push(customizes);
} else {
fileConfig.tasks = [customize];
fileConfig.tasks = [customizes];
}
};
return this.configurationEditingService.writeConfiguration(ConfigurationTarget.WORKSPACE, { key: 'tasks', value: fileConfig }).then(() => {
@ -861,7 +866,7 @@ class TaskService extends EventEmitter implements ITaskService {
}
private createRunnableTask(sets: TaskSet[], group: TaskGroup): { task: Task; resolver: ITaskResolver } {
let uuidMap: IStringDictionary<Task> = Object.create(null);
let idMap: IStringDictionary<Task> = Object.create(null);
let labelMap: IStringDictionary<Task> = Object.create(null);
let identifierMap: IStringDictionary<Task> = Object.create(null);
@ -869,7 +874,7 @@ class TaskService extends EventEmitter implements ITaskService {
let extensionTasks: Task[] = [];
sets.forEach((set) => {
set.tasks.forEach((task) => {
uuidMap[task._id] = task;
idMap[task._id] = task;
labelMap[task._label] = task;
identifierMap[task.identifier] = task;
if (group && task.group === group) {
@ -883,7 +888,7 @@ class TaskService extends EventEmitter implements ITaskService {
});
let resolver: ITaskResolver = {
resolve: (id: string) => {
return uuidMap[id] || labelMap[id] || identifierMap[id];
return idMap[id] || labelMap[id] || identifierMap[id];
}
};
if (workspaceTasks.length > 0) {
@ -900,10 +905,11 @@ class TaskService extends EventEmitter implements ITaskService {
return { task: extensionTasks[0], resolver };
} else {
let id: string = UUID.generateUuid();
let task: Task = {
let task: CustomTask = {
_id: id,
_source: { kind: TaskSourceKind.Generic, label: 'generic' },
_label: id,
type: 'custom',
name: id,
identifier: id,
dependsOn: extensionTasks.map(task => task._id),
@ -937,7 +943,7 @@ class TaskService extends EventEmitter implements ITaskService {
return ProblemMatcherRegistry.onReady().then(() => {
return this.textFileService.saveAll().then((value) => { // make sure all dirty files are saved
let executeResult = this.getTaskSystem().run(task, resolver);
this.getRecentlyUsedTasks().set(task.identifier, task.identifier, Touch.First);
this.getRecentlyUsedTasks().set(Task.getKey(task), Task.getKey(task), Touch.First);
if (executeResult.kind === TaskExecuteKind.Active) {
let active = executeResult.active;
if (active.same) {
@ -1042,29 +1048,28 @@ class TaskService extends EventEmitter implements ITaskService {
}).then((result) => {
return this.getWorkspaceTasks().then((workspaceTaskResult) => {
let workspaceTasksToDelete: Task[] = [];
let annotatingTasks = workspaceTaskResult.annotatingTasks;
let legacyAnnotatingTasks = workspaceTaskResult.set ? this.getLegacyAnnotatingTasks(workspaceTaskResult.set) : undefined;
if (annotatingTasks || legacyAnnotatingTasks) {
let configurations = workspaceTaskResult.configurations;
let legacyTaskConfigurations = workspaceTaskResult.set ? this.getLegacyTaskConfigurations(workspaceTaskResult.set) : undefined;
if (configurations || legacyTaskConfigurations) {
for (let set of result) {
for (let task of set.tasks) {
if (annotatingTasks) {
let annotatingTask = annotatingTasks.byIdentifier[task.identifier];
if (annotatingTask) {
TaskConfig.mergeTasks(task, annotatingTask);
task.name = annotatingTask.name;
task._label = annotatingTask._label;
task._source.kind = TaskSourceKind.Workspace;
for (let i = 0; i < set.tasks.length; i++) {
let task = set.tasks[i];
if (!ContributedTask.is(task)) {
continue;
}
if (configurations) {
let configuredTask = configurations.byIdentifier[task.defines._key];
if (configuredTask) {
set.tasks[i] = TaskConfig.createCustomTask(task, configuredTask);
continue;
}
}
if (legacyAnnotatingTasks) {
let legacyAnnotatingTask = legacyAnnotatingTasks[task.identifier];
if (legacyAnnotatingTask) {
TaskConfig.mergeTasks(task, legacyAnnotatingTask);
task._source.kind = TaskSourceKind.Workspace;
task.name = legacyAnnotatingTask.name;
task._label = legacyAnnotatingTask._label;
workspaceTasksToDelete.push(legacyAnnotatingTask);
if (legacyTaskConfigurations) {
let configuredTask = legacyTaskConfigurations[task.defines._key];
if (configuredTask) {
set.tasks[i] = TaskConfig.createCustomTask(task, configuredTask);
workspaceTasksToDelete.push(configuredTask);
set.tasks[i] = configuredTask;
continue;
}
}
@ -1096,7 +1101,7 @@ class TaskService extends EventEmitter implements ITaskService {
});
}
private getLegacyAnnotatingTasks(workspaceTasks: TaskSet): IStringDictionary<Task> {
private getLegacyTaskConfigurations(workspaceTasks: TaskSet): IStringDictionary<Task> {
let result: IStringDictionary<Task>;
function getResult() {
if (result) {
@ -1106,11 +1111,17 @@ class TaskService extends EventEmitter implements ITaskService {
return result;
}
for (let task of workspaceTasks.tasks) {
let commandName = task.command && task.command.name;
// This is for backwards compatibility with the 0.1.0 task annotation code
// if we had a gulp, jake or grunt command a task specification was a annotation
if (commandName === 'gulp' || commandName === 'grunt' || commandName === 'jake') {
getResult()[`${commandName}.${task.name}`] = task;
if (CustomTask.is(task)) {
let commandName = task.command && task.command.name;
// This is for backwards compatibility with the 0.1.0 task annotation code
// if we had a gulp, jake or grunt command a task specification was a annotation
if (commandName === 'gulp' || commandName === 'grunt' || commandName === 'jake') {
let identifier: TaskIdentifier = TaskConfig.getTaskIdentifier({
type: commandName,
task: task.name
} as TaskConfig.TaskIdentifier);
getResult()[identifier._key] = task;
}
}
}
return result;
@ -1139,7 +1150,7 @@ class TaskService extends EventEmitter implements ITaskService {
{
let { config, hasParseErrors } = this.getConfiguration();
if (hasParseErrors) {
return TPromise.as({ set: undefined, hasErrors: true, annotatingTasks: undefined });
return TPromise.as({ set: undefined, hasErrors: true, configurations: undefined });
}
let engine = TaskConfig.ExecutionEngine._default;
if (config) {
@ -1153,7 +1164,7 @@ class TaskService extends EventEmitter implements ITaskService {
return { config, hasErrors };
}
let result: TaskConfig.ExternalTaskRunnerConfiguration = Objects.clone(config);
let configuredTasks: IStringDictionary<TaskConfig.TaskDescription> = Object.create(null);
let configuredTasks: IStringDictionary<TaskConfig.CustomTask> = Object.create(null);
if (!result.tasks) {
if (detectedConfig.tasks) {
result.tasks = detectedConfig.tasks;
@ -1188,7 +1199,7 @@ class TaskService extends EventEmitter implements ITaskService {
return configPromise.then((resolved) => {
return ProblemMatcherRegistry.onReady().then((): WorkspaceTaskResult => {
if (!resolved || !resolved.config) {
return { set: undefined, annotatingTasks: undefined, hasErrors: resolved !== void 0 ? resolved.hasErrors : false };
return { set: undefined, configurations: undefined, hasErrors: resolved !== void 0 ? resolved.hasErrors : false };
}
let problemReporter = new ProblemReporter(this._outputChannel);
let parseResult = TaskConfig.parse(resolved.config, problemReporter);
@ -1199,18 +1210,18 @@ class TaskService extends EventEmitter implements ITaskService {
}
if (problemReporter.status.isFatal()) {
problemReporter.fatal(nls.localize('TaskSystem.configurationErrors', 'Error: the provided task configuration has validation errors and can\'t not be used. Please correct the errors first.'));
return { set: undefined, annotatingTasks: undefined, hasErrors };
return { set: undefined, configurations: undefined, hasErrors };
}
let annotatingTasks: { byIdentifier: IStringDictionary<Task>; };
if (parseResult.annotatingTasks && parseResult.annotatingTasks.length > 0) {
annotatingTasks = {
let customizedTasks: { byIdentifier: IStringDictionary<ConfiguringTask>; };
if (parseResult.configured && parseResult.configured.length > 0) {
customizedTasks = {
byIdentifier: Object.create(null)
};
for (let task of parseResult.annotatingTasks) {
annotatingTasks.byIdentifier[task.customize] = task;
for (let task of parseResult.configured) {
customizedTasks.byIdentifier[task.configures._key] = task;
}
}
return { set: { tasks: parseResult.tasks }, annotatingTasks: annotatingTasks, hasErrors };
return { set: { tasks: parseResult.custom }, configurations: customizedTasks, hasErrors };
});
});
}
@ -1623,4 +1634,4 @@ schema.oneOf = [...schemaVersion1.oneOf, ...schemaVersion2.oneOf];
let jsonRegistry = <jsonContributionRegistry.IJSONContributionRegistry>Registry.as(jsonContributionRegistry.Extensions.JSONContribution);
jsonRegistry.registerSchema(schemaId, schema);
jsonRegistry.registerSchema(schemaId, schema);

View file

@ -33,7 +33,7 @@ import { IConfigurationResolverService } from 'vs/workbench/services/configurati
import { ITerminalService, ITerminalInstance, IShellLaunchConfig } from 'vs/workbench/parts/terminal/common/terminal';
import { IOutputService, IOutputChannel } from 'vs/workbench/parts/output/common/output';
import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEvents } from 'vs/workbench/parts/tasks/common/problemCollectors';
import { Task, RevealKind, CommandOptions, ShellConfiguration, CommandType, PanelKind } from 'vs/workbench/parts/tasks/common/tasks';
import { Task, RevealKind, CommandOptions, ShellConfiguration, RuntimeType, PanelKind } from 'vs/workbench/parts/tasks/common/tasks';
import {
ITaskSystem, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, ITaskResolver,
TelemetryEvent, Triggers, TaskSystemEvents, TaskEvent, TaskType, TaskTerminateResponse
@ -406,7 +406,7 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem {
waitOnExit = nls.localize('reuseTerminal', 'Terminal will be reused by tasks, press any key to close it.');
};
let shellLaunchConfig: IShellLaunchConfig = undefined;
let isShellCommand = task.command.type === CommandType.Shell;
let isShellCommand = task.command.runtime === RuntimeType.Shell;
if (isShellCommand) {
if (Platform.isWindows && ((options.cwd && TPath.isUNC(options.cwd)) || (!options.cwd && TPath.isUNC(process.cwd())))) {
throw new TaskError(Severity.Error, nls.localize('TerminalTaskSystem', 'Can\'t execute a shell command on an UNC drive.'), TaskErrors.UnknownError);

View file

@ -20,7 +20,8 @@ import { IConfigurationResolverService } from 'vs/workbench/services/configurati
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import * as TaskConfig from '../common/taskConfiguration';
import * as Tasks from '../common/tasks';
import * as TaskConfig from './taskConfiguration';
let build: string = 'build';
let test: string = 'test';
@ -313,8 +314,8 @@ export class ProcessRunnerDetector {
});
}
private createTaskDescriptions(tasks: string[], problemMatchers: string[], list: boolean): TaskConfig.TaskDescription[] {
let taskConfigs: TaskConfig.TaskDescription[] = [];
private createTaskDescriptions(tasks: string[], problemMatchers: string[], list: boolean): TaskConfig.CustomTask[] {
let taskConfigs: TaskConfig.CustomTask[] = [];
if (list) {
tasks.forEach((task) => {
taskConfigs.push({
@ -337,7 +338,7 @@ export class ProcessRunnerDetector {
taskConfigs.push({
taskName: name,
args: [],
isBuildCommand: true,
group: Tasks.TaskGroup.Build,
problemMatcher: problemMatchers
});
}
@ -347,7 +348,7 @@ export class ProcessRunnerDetector {
taskConfigs.push({
taskName: name,
args: [],
isTestCommand: true
group: Tasks.TaskGroup.Test,
});
}
}

View file

@ -30,7 +30,7 @@ import {
ITaskSystem, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, TelemetryEvent, Triggers,
TaskSystemEvents, TaskEvent, TaskType, TaskTerminateResponse
} from 'vs/workbench/parts/tasks/common/taskSystem';
import { Task, CommandOptions, RevealKind, CommandConfiguration, CommandType } from 'vs/workbench/parts/tasks/common/tasks';
import { Task, CommandOptions, RevealKind, CommandConfiguration, RuntimeType } from 'vs/workbench/parts/tasks/common/tasks';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
@ -171,7 +171,7 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem {
let args: string[] = commandConfig.args ? commandConfig.args.slice() : [];
args = this.resolveVariables(args);
let command: string = this.resolveVariable(commandConfig.name);
this.childProcess = new LineProcess(command, args, commandConfig.type === CommandType.Shell, this.resolveOptions(commandConfig.options));
this.childProcess = new LineProcess(command, args, commandConfig.runtime === RuntimeType.Shell, this.resolveOptions(commandConfig.options));
telemetryEvent.command = this.childProcess.getSanitizedCommand();
// we have no problem matchers defined. So show the output log
let reveal = task.command.presentation.reveal;

View file

@ -13,7 +13,7 @@ import { ValidationStatus } from 'vs/base/common/parsers';
import { ProblemMatcher, FileLocationKind, ProblemPattern, ApplyToKind } from 'vs/platform/markers/common/problemMatcher';
import * as Tasks from 'vs/workbench/parts/tasks/common/tasks';
import { parse, ParseResult, IProblemReporter, ExternalTaskRunnerConfiguration } from 'vs/workbench/parts/tasks/common/taskConfiguration';
import { parse, ParseResult, IProblemReporter, ExternalTaskRunnerConfiguration, CustomTask } from 'vs/workbench/parts/tasks/node/taskConfiguration';
class ProblemReporter implements IProblemReporter {
@ -56,15 +56,15 @@ class ProblemReporter implements IProblemReporter {
class ConfiguationBuilder {
public result: Tasks.Task[];
private builders: TaskBuilder[];
private builders: CustomTaskBuilder[];
constructor() {
this.result = [];
this.builders = [];
}
public task(name: string, command: string): TaskBuilder {
let builder = new TaskBuilder(this, name, command);
public task(name: string, command: string): CustomTaskBuilder {
let builder = new CustomTaskBuilder(this, name, command);
this.builders.push(builder);
this.result.push(builder.result);
return builder;
@ -114,11 +114,11 @@ class CommandConfigurationBuilder {
private presentationBuilder: PresentationBuilder;
constructor(public parent: TaskBuilder, command: string) {
constructor(public parent: CustomTaskBuilder, command: string) {
this.presentationBuilder = new PresentationBuilder(this);
this.result = {
name: command,
type: Tasks.CommandType.Process,
runtime: Tasks.RuntimeType.Process,
args: [],
options: {
cwd: '${workspaceRoot}'
@ -133,8 +133,8 @@ class CommandConfigurationBuilder {
return this;
}
public type(value: Tasks.CommandType): CommandConfigurationBuilder {
this.result.type = value;
public runtime(value: Tasks.RuntimeType): CommandConfigurationBuilder {
this.result.runtime = value;
return this;
}
@ -168,9 +168,9 @@ class CommandConfigurationBuilder {
}
}
class TaskBuilder {
class CustomTaskBuilder {
public result: Tasks.Task;
public result: Tasks.CustomTask;
private commandBuilder: CommandConfigurationBuilder;
constructor(public parent: ConfiguationBuilder, name: string, command: string) {
@ -179,6 +179,7 @@ class TaskBuilder {
_id: name,
_source: { kind: Tasks.TaskSourceKind.Workspace, label: 'workspace' },
_label: name,
type: 'custom',
identifier: name,
name: name,
command: this.commandBuilder.result,
@ -188,22 +189,22 @@ class TaskBuilder {
};
}
public identifier(value: string): TaskBuilder {
public identifier(value: string): CustomTaskBuilder {
this.result.identifier = value;
return this;
}
public group(value: Tasks.TaskGroup): TaskBuilder {
public group(value: Tasks.TaskGroup): CustomTaskBuilder {
this.result.group = value;
return this;
}
public isBackground(value: boolean): TaskBuilder {
public isBackground(value: boolean): CustomTaskBuilder {
this.result.isBackground = value;
return this;
}
public promptOnClose(value: boolean): TaskBuilder {
public promptOnClose(value: boolean): CustomTaskBuilder {
this.result.promptOnClose = value;
return this;
}
@ -229,7 +230,7 @@ class ProblemMatcherBuilder {
public result: ProblemMatcher;
constructor(public parent: TaskBuilder) {
constructor(public parent: CustomTaskBuilder) {
this.result = {
owner: ProblemMatcherBuilder.DEFAULT_UUID,
applyTo: ApplyToKind.allDocuments,
@ -342,8 +343,8 @@ function testDefaultProblemMatcher(external: ExternalTaskRunnerConfiguration, re
let reporter = new ProblemReporter();
let result = parse(external, reporter);
assert.ok(!reporter.receivedMessage);
assert.strictEqual(result.tasks.length, 1);
let task = result.tasks[0];
assert.strictEqual(result.custom.length, 1);
let task = result.custom[0];
assert.ok(task);
assert.strictEqual(task.problemMatchers.length, resolved);
}
@ -401,7 +402,7 @@ class TaskGroupMap {
function assertConfiguration(result: ParseResult, expected: Tasks.Task[]): void {
assert.ok(result.validationStatus.isOK());
let actual = result.tasks;
let actual = result.custom;
assert.strictEqual(typeof actual, typeof expected);
if (!actual) {
return;
@ -460,7 +461,7 @@ function assertCommandConfiguration(actual: Tasks.CommandConfiguration, expected
if (actual && expected) {
assertPresentation(actual.presentation, expected.presentation);
assert.strictEqual(actual.name, expected.name, 'name');
assert.strictEqual(actual.type, expected.type, 'task type');
assert.strictEqual(actual.runtime, expected.runtime, 'runtime type');
assert.strictEqual(actual.suppressTaskName, expected.suppressTaskName, 'suppressTaskName');
assert.strictEqual(actual.taskSelector, expected.taskSelector, 'taskSelector');
assert.deepEqual(actual.args, expected.args, 'args');
@ -558,7 +559,7 @@ suite('Tasks version 0.1.0', () => {
builder.task('tsc', 'tsc').
group(Tasks.TaskGroup.Build).
command().suppressTaskName(true).
type(Tasks.CommandType.Shell);
runtime(Tasks.RuntimeType.Shell);
testConfiguration(
{
version: '0.1.0',
@ -753,7 +754,7 @@ suite('Tasks version 0.1.0', () => {
task(name, name).
group(Tasks.TaskGroup.Build).
command().suppressTaskName(true).
type(Tasks.CommandType.Shell);
runtime(Tasks.RuntimeType.Shell);
let external: ExternalTaskRunnerConfiguration = {
version: '0.1.0',
command: 'tsc',
@ -877,7 +878,7 @@ suite('Tasks version 0.1.0', () => {
{
taskName: 'taskName',
isBuildCommand: true
}
} as CustomTask
]
};
let builder = new ConfiguationBuilder();
@ -908,7 +909,7 @@ suite('Tasks version 0.1.0', () => {
{
taskName: 'taskName',
isTestCommand: true
}
} as CustomTask
]
};
let builder = new ConfiguationBuilder();
@ -942,7 +943,7 @@ suite('Tasks version 0.1.0', () => {
echoCommand: true,
args: ['--p'],
isWatching: true
}
} as CustomTask
]
};
let builder = new ConfiguationBuilder();
@ -1166,7 +1167,7 @@ suite('Tasks version 0.1.0', () => {
{
taskName: 'taskName',
isWatching: true
}
} as CustomTask
]
};
let builder = new ConfiguationBuilder();
@ -1222,7 +1223,7 @@ suite('Tasks version 0.1.0', () => {
{
taskName: 'taskName',
suppressTaskName: true
}
} as CustomTask
]
};
let builder = new ConfiguationBuilder();
@ -1319,12 +1320,12 @@ suite('Tasks version 0.1.0', () => {
env: 'env'
}
}
}
} as CustomTask
]
};
let builder = new ConfiguationBuilder();
builder.task('taskNameOne', 'tsc').command().suppressTaskName(true).
type(Tasks.CommandType.Shell).args(['arg']).options({ cwd: 'cwd', env: { env: 'env' } });
runtime(Tasks.RuntimeType.Shell).args(['arg']).options({ cwd: 'cwd', env: { env: 'env' } });
testConfiguration(external, builder);
});
@ -1395,11 +1396,11 @@ suite('Tasks version 0.1.0', () => {
{
taskName: 'taskNameOne',
isShellCommand: true,
}
} as CustomTask
]
};
let builder = new ConfiguationBuilder();
builder.task('taskNameOne', 'tsc').command().type(Tasks.CommandType.Shell).args(['$name']);
builder.task('taskNameOne', 'tsc').command().runtime(Tasks.RuntimeType.Shell).args(['$name']);
testConfiguration(external, builder);
});
@ -1456,7 +1457,7 @@ suite('Tasks version 2.0.0', () => {
builder.task('dir', 'dir').
group(Tasks.TaskGroup.Build).
command().suppressTaskName(true).
type(Tasks.CommandType.Shell).
runtime(Tasks.RuntimeType.Shell).
presentation().echo(true);
testConfiguration(external, builder);
});
@ -1490,7 +1491,7 @@ suite('Bugs / regression tests', () => {
isBuildCommand: false,
showOutput: 'always',
echoCommand: true
}
} as CustomTask
]
},
osx: {
@ -1508,7 +1509,7 @@ suite('Bugs / regression tests', () => {
],
isBuildCommand: false,
showOutput: 'always'
}
} as CustomTask
]
}
};