Skip to content

Commit ef2b0a4

Browse files
committed
Fixes microsoft#24073: Improve task API and adopt gulp extension to it.
1 parent 0bcc6fc commit ef2b0a4

12 files changed

Lines changed: 480 additions & 362 deletions

File tree

extensions/gulp/package.json

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,23 @@
2626
"onCommand:workbench.action.tasks.runTask",
2727
"onCommand:workbench.action.tasks.build",
2828
"onCommand:workbench.action.tasks.test"
29-
]
29+
],
30+
"contributes": {
31+
"configuration": {
32+
"id": "gulp",
33+
"type": "object",
34+
"title": "Gulp",
35+
"properties": {
36+
"gulp.autoDetect": {
37+
"type": "string",
38+
"enum": [
39+
"off",
40+
"on"
41+
],
42+
"default": "on",
43+
"description": "%config.gulp.autoDetect%"
44+
}
45+
}
46+
}
47+
}
3048
}

extensions/gulp/package.nls.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"config.gulp.autoDetect": "Controls whether auto detection of gulp tasks in on or off. Default is on."
3+
}

extensions/gulp/src/main.ts

Lines changed: 116 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -8,93 +8,138 @@ import * as path from 'path';
88
import * as fs from 'fs';
99
import * as cp from 'child_process';
1010
import * as vscode from 'vscode';
11+
import * as nls from 'vscode-nls';
12+
13+
const localize = nls.config(process.env.VSCODE_NLS_CONFIG)();
14+
15+
type AutoDetect = 'on' | 'off';
16+
let taskProvider: vscode.Disposable | undefined;
1117

1218
export function activate(_context: vscode.ExtensionContext): void {
1319
let workspaceRoot = vscode.workspace.rootPath;
1420
if (!workspaceRoot) {
1521
return;
1622
}
17-
let gulpfile = path.join(workspaceRoot, 'gulpfile.js');
18-
let gulpPromise: Thenable<vscode.TaskSet> | undefined = undefined;
19-
let fileWatcher = vscode.workspace.createFileSystemWatcher(gulpfile);
23+
let pattern = path.join(workspaceRoot, 'gulpfile{.babel.js,.js}');
24+
let gulpPromise: Thenable<vscode.Task[]> | undefined = undefined;
25+
let fileWatcher = vscode.workspace.createFileSystemWatcher(pattern);
2026
fileWatcher.onDidChange(() => gulpPromise = undefined);
2127
fileWatcher.onDidCreate(() => gulpPromise = undefined);
2228
fileWatcher.onDidDelete(() => gulpPromise = undefined);
2329

24-
vscode.workspace.registerTaskProvider({
25-
provideTasks: () => {
26-
if (!gulpPromise) {
27-
gulpPromise = getGulpTasks();
28-
}
29-
return gulpPromise;
30+
function onConfigurationChanged() {
31+
let autoDetect = vscode.workspace.getConfiguration('gulp').get<AutoDetect>('autoDetect');
32+
if (taskProvider && autoDetect === 'off') {
33+
gulpPromise = undefined;
34+
taskProvider.dispose();
35+
taskProvider = undefined;
36+
} else if (!taskProvider && autoDetect === 'on') {
37+
taskProvider = vscode.workspace.registerTaskProvider({
38+
provideTasks: () => {
39+
if (!gulpPromise) {
40+
gulpPromise = getGulpTasks();
41+
}
42+
return gulpPromise;
43+
}
44+
});
3045
}
46+
}
47+
vscode.workspace.onDidChangeConfiguration(onConfigurationChanged);
48+
onConfigurationChanged();
49+
}
50+
51+
export function deactivate(): void {
52+
if (taskProvider) {
53+
taskProvider.dispose();
54+
}
55+
}
56+
57+
function exists(file: string): Promise<boolean> {
58+
return new Promise<boolean>((resolve, _reject) => {
59+
fs.exists(file, (value) => {
60+
resolve(value);
61+
});
3162
});
3263
}
3364

34-
function getGulpTasks(): Thenable<vscode.TaskSet> {
35-
return new Promise<vscode.TaskSet>((resolve, _reject) => {
36-
let workspaceRoot = vscode.workspace.rootPath;
37-
let emptyTaskSet = { tasks: [] };
38-
if (!workspaceRoot) {
39-
return resolve(emptyTaskSet);
40-
}
41-
let gulpfile = path.join(workspaceRoot, 'gulpfile.js');
42-
fs.exists(gulpfile, (value) => {
43-
if (!value) {
44-
resolve(emptyTaskSet);
45-
return;
65+
function exec(command: string, options: cp.ExecOptions): Promise<{ stdout: string; stderr: string }> {
66+
return new Promise<{ stdout: string; stderr: string }>((resolve, reject) => {
67+
cp.exec(command, options, (error, stdout, stderr) => {
68+
if (error) {
69+
reject({ error, stdout, stderr });
4670
}
71+
resolve({ stdout, stderr });
72+
});
73+
});
74+
}
4775

48-
let commandLine: string;
49-
let platform = process.platform;
50-
if (platform === 'win32' && fs.existsSync(path.join(workspaceRoot!, 'node_modules', '.bin', 'gulp.cmd'))) {
51-
commandLine = `${path.join('.', 'node_modules', '.bin', 'gulp.cmd')} --tasks-simple --no-color`;
52-
} else if ((platform === 'linux' || platform === 'darwin') && fs.existsSync(path.join(workspaceRoot!, 'node_modules', '.bin', 'gulp.cmd'))) {
53-
commandLine = `${path.join('.', 'node_modules', '.bin', 'gulp')} --tasks-simple --no-color`;
54-
} else {
55-
commandLine = 'gulp --tasks-simple --no-color';
56-
}
57-
cp.exec(commandLine, { cwd: workspaceRoot }, (error, stdout, stderr) => {
58-
let channel = vscode.window.createOutputChannel('tasks');
59-
if (stderr) {
60-
channel.appendLine(stderr);
61-
}
62-
if (error) {
63-
channel.appendLine(`Auto detecting gulp failed with error: ${error ? error.toString() : 'unknown'}`);
64-
resolve(emptyTaskSet);
65-
return;
76+
async function getGulpTasks(): Promise<vscode.Task[]> {
77+
let workspaceRoot = vscode.workspace.rootPath;
78+
let emptyTasks: vscode.Task[] = [];
79+
if (!workspaceRoot) {
80+
return emptyTasks;
81+
}
82+
let gulpfile = path.join(workspaceRoot, 'gulpfile.js');
83+
if (!await exists(gulpfile)) {
84+
gulpfile = path.join(workspaceRoot, 'gulpfile.babel.js');
85+
if (! await exists(gulpfile)) {
86+
return emptyTasks;
87+
}
88+
}
89+
90+
let gulpCommand: string;
91+
let platform = process.platform;
92+
if (platform === 'win32' && await exists(path.join(workspaceRoot!, 'node_modules', '.bin', 'gulp.cmd'))) {
93+
gulpCommand = path.join('.', 'node_modules', '.bin', 'gulp.cmd');
94+
} else if ((platform === 'linux' || platform === 'darwin') && await exists(path.join(workspaceRoot!, 'node_modules', '.bin', 'gulp'))) {
95+
gulpCommand = path.join('.', 'node_modules', '.bin', 'gulp');
96+
} else {
97+
gulpCommand = 'gulp';
98+
}
99+
100+
let commandLine = `${gulpCommand} --tasks-simple --no-color`;
101+
let channel = vscode.window.createOutputChannel('tasks');
102+
try {
103+
let { stdout, stderr } = await exec(commandLine, { cwd: workspaceRoot });
104+
if (stderr) {
105+
channel.appendLine(stderr);
106+
}
107+
let result: vscode.Task[] = [];
108+
if (stdout) {
109+
let buildTask: { task: vscode.Task | undefined, rank: number } = { task: undefined, rank: 0 };
110+
let testTask: { task: vscode.Task | undefined, rank: number } = { task: undefined, rank: 0 };
111+
let lines = stdout.split(/\r{0,1}\n/);
112+
for (let line of lines) {
113+
if (line.length === 0) {
114+
continue;
66115
}
67-
let result: vscode.TaskSet = { tasks: [], buildTasks: [], testTasks: [] };
68-
if (stdout) {
69-
let buildTask: { id: string | undefined, rank: number } = { id: undefined, rank: 0 };
70-
let testTask: { id: string | undefined, rank: number } = { id: undefined, rank: 0 };
71-
let lines = stdout.split(/\r{0,1}\n/);
72-
for (let line of lines) {
73-
if (line.length === 0) {
74-
continue;
75-
}
76-
let task = new vscode.ShellTask(`gulp ${line}`, `gulp ${line}`);
77-
result.tasks.push(task);
78-
let lowerCaseLine = line.toLowerCase();
79-
if (lowerCaseLine === 'build') {
80-
buildTask = { id: line, rank: 2 };
81-
} else if (lowerCaseLine.indexOf('build') !== -1 && buildTask.rank < 1) {
82-
buildTask = { id: line, rank: 1 };
83-
} else if (lowerCaseLine === 'test') {
84-
testTask = { id: line, rank: 2 };
85-
} else if (lowerCaseLine.indexOf('test') !== -1 && testTask.rank < 1) {
86-
testTask = { id: line, rank: 1 };
87-
}
88-
}
89-
if (buildTask.id) {
90-
result.buildTasks!.push(buildTask.id);
91-
}
92-
if (testTask.id) {
93-
result.testTasks!.push(testTask.id);
94-
}
116+
let task = new vscode.ShellTask(`gulp: ${line}`, `${gulpCommand} ${line}`);
117+
task.identifier = `gulp.${line}`;
118+
result.push(task);
119+
let lowerCaseLine = line.toLowerCase();
120+
if (lowerCaseLine === 'build') {
121+
buildTask = { task, rank: 2 };
122+
} else if (lowerCaseLine.indexOf('build') !== -1 && buildTask.rank < 1) {
123+
buildTask = { task, rank: 1 };
124+
} else if (lowerCaseLine === 'test') {
125+
testTask = { task, rank: 2 };
126+
} else if (lowerCaseLine.indexOf('test') !== -1 && testTask.rank < 1) {
127+
testTask = { task, rank: 1 };
95128
}
96-
resolve(result);
97-
});
98-
});
99-
});
129+
}
130+
if (buildTask.task) {
131+
buildTask.task.group = vscode.TaskGroup.Build;
132+
}
133+
if (testTask.task) {
134+
testTask.task.group = vscode.TaskGroup.Test;
135+
}
136+
}
137+
return result;
138+
} catch (err) {
139+
if (err.stderr) {
140+
channel.appendLine(err.stderr);
141+
}
142+
channel.appendLine(localize('execFailed', 'Auto detecting gulp failed with error: {0}', err.error ? err.error.toString() : 'unknown'));
143+
return emptyTasks;
144+
}
100145
}

extensions/gulp/tsconfig.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
"target": "es6",
44
"module": "commonjs",
55
"lib": [
6-
"es6",
7-
"es2015.promise"
6+
"es2016"
87
],
98
"outDir": "./out",
109
"strictNullChecks": true,

src/vs/vscode.proposed.d.ts

Lines changed: 51 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,30 @@ declare module 'vscode' {
267267
env?: { [key: string]: string };
268268
}
269269

270+
export namespace TaskGroup {
271+
/**
272+
* The clean task group
273+
*/
274+
export const Clean: 'clean';
275+
/**
276+
* The build task group
277+
*/
278+
export const Build: 'build';
279+
/**
280+
* The rebuild all task group
281+
*/
282+
export const RebuildAll: 'rebuildAll';
283+
/**
284+
* The test task group
285+
*/
286+
export const Test: 'test';
287+
}
288+
289+
/**
290+
* The supported task groups.
291+
*/
292+
export type TaskGroup = 'clean' | 'build' | 'rebuildAll' | 'test';
293+
270294
/**
271295
* A task that starts an external process.
272296
*/
@@ -328,6 +352,12 @@ declare module 'vscode' {
328352
*/
329353
args: string[];
330354

355+
/**
356+
* The task group this tasks belongs to. Defaults to undefined meaning
357+
* that the task doesn't belong to any special group.
358+
*/
359+
group?: TaskGroup;
360+
331361
/**
332362
* The process options used when the process is executed.
333363
* Defaults to an empty object literal.
@@ -374,14 +404,27 @@ declare module 'vscode' {
374404
* The current working directory of the executed shell.
375405
* If omitted VSCode's current workspace root is used.
376406
*/
377-
cwd?: string;
407+
cwd: string;
378408

379409
/**
380410
* The additional environment of the executed shell. If omitted
381411
* the parent process' environment is used. If provided it is merged with
382412
* the parent process' environment.
383413
*/
384414
env?: { [key: string]: string };
415+
} | {
416+
/**
417+
* The current working directory of the executed shell.
418+
* If omitted VSCode's current workspace root is used.
419+
*/
420+
cwd?: string;
421+
422+
/**
423+
* The additional environment of the executed shell. If omitted
424+
* the parent process' environment is used. If provided it is merged with
425+
* the parent process' environment.
426+
*/
427+
env: { [key: string]: string };
385428
};
386429

387430
/**
@@ -429,6 +472,12 @@ declare module 'vscode' {
429472
*/
430473
readonly commandLine: string;
431474

475+
/**
476+
* The task group this tasks belongs to. Defaults to undefined meaning
477+
* that the task doesn't belong to any special group.
478+
*/
479+
group?: TaskGroup;
480+
432481
/**
433482
* The shell options used when the shell is executed. Defaults to an
434483
* empty object literal.
@@ -449,29 +498,6 @@ declare module 'vscode' {
449498

450499
export type Task = ProcessTask | ShellTask;
451500

452-
/**
453-
* Result return from a task provider.
454-
*/
455-
export interface TaskSet {
456-
/**
457-
* The actual tasks returned.
458-
*/
459-
tasks: Task[];
460-
461-
/**
462-
* The build tasks provided. Tasks must be identified using
463-
* `Task.identifier`
464-
*/
465-
buildTasks?: string[];
466-
467-
/**
468-
* The test tasks provided. Tasks must be identified using
469-
* `Task.identifier`
470-
*/
471-
testTasks?: string[];
472-
}
473-
474-
475501
/**
476502
* A task provider allows to add tasks to the task service.
477503
* A task provider is registerd via #workspace.registerTaskProvider.
@@ -482,7 +508,7 @@ declare module 'vscode' {
482508
* @param token A cancellation token.
483509
* @return a #TaskSet
484510
*/
485-
provideTasks(token: CancellationToken): ProviderResult<TaskSet>;
511+
provideTasks(token: CancellationToken): ProviderResult<Task[]>;
486512
}
487513

488514
export namespace workspace {

src/vs/workbench/api/node/extHost.api.impl.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,7 @@ export function createApiFactory(
529529
FileLocationKind: extHostTypes.FileLocationKind,
530530
ApplyToKind: extHostTypes.ApplyToKind,
531531
RevealKind: extHostTypes.RevealKind,
532+
TaskGroup: extHostTypes.TaskGroup,
532533
ShellTask: extHostTypes.ShellTask,
533534
ProcessTask: extHostTypes.ProcessTask
534535
};

0 commit comments

Comments
 (0)