Skip to content

Commit a46b54a

Browse files
Automatically add launch.json if necessary
This change refactors that code that adds or updates the user's tasks.json file to include a build task, to also add a launch.json file if it is missing.
1 parent 5c0cf12 commit a46b54a

File tree

3 files changed

+285
-94
lines changed

3 files changed

+285
-94
lines changed

src/assets.ts

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
'use strict';
7+
8+
import * as fs from 'fs-extra-promise';
9+
import * as path from 'path';
10+
import * as vscode from 'vscode';
11+
import * as tasks from 'vscode-tasks';
12+
13+
interface DebugConfiguration {
14+
name: string,
15+
type: string,
16+
request: string,
17+
}
18+
19+
interface ConsoleLaunchConfiguration extends DebugConfiguration {
20+
preLaunchTask: string,
21+
program: string,
22+
args: string[],
23+
cwd: string,
24+
stopAtEntry: boolean
25+
}
26+
27+
interface CommandLine {
28+
command: string,
29+
args?: string
30+
}
31+
32+
interface LaunchBrowserConfiguration {
33+
enabled: boolean,
34+
args: string,
35+
windows?: CommandLine,
36+
osx: CommandLine,
37+
linux: CommandLine
38+
}
39+
40+
interface WebLaunchConfiguration extends ConsoleLaunchConfiguration {
41+
launchBrowser: LaunchBrowserConfiguration
42+
}
43+
44+
interface AttachConfiguration extends DebugConfiguration {
45+
processName: string
46+
}
47+
48+
interface Paths {
49+
vscodeFolder: string;
50+
tasksJsonPath: string;
51+
launchJsonPath: string;
52+
}
53+
54+
function getPaths(): Paths {
55+
const vscodeFolder = path.join(vscode.workspace.rootPath, '.vscode');
56+
57+
return {
58+
vscodeFolder: vscodeFolder,
59+
tasksJsonPath: path.join(vscodeFolder, 'tasks.json'),
60+
launchJsonPath: path.join(vscodeFolder, 'launch.json')
61+
}
62+
}
63+
64+
interface Operations {
65+
addTasksJson?: boolean,
66+
updateTasksJson?: boolean,
67+
addLaunchJson?: boolean
68+
}
69+
70+
function hasOperations(operations: Operations) {
71+
return operations.addLaunchJson ||
72+
operations.updateTasksJson ||
73+
operations.addLaunchJson;
74+
}
75+
76+
function getOperations() {
77+
const paths = getPaths();
78+
79+
return getBuildOperations(paths.tasksJsonPath).then(operations =>
80+
getLaunchOperations(paths.launchJsonPath, operations));
81+
}
82+
83+
function getBuildOperations(tasksJsonPath: string) {
84+
return new Promise<Operations>((resolve, reject) => {
85+
return fs.existsAsync(tasksJsonPath).then(exists => {
86+
if (exists) {
87+
fs.readFileAsync(tasksJsonPath).then(buffer => {
88+
const text = buffer.toString();
89+
const tasksJson: tasks.TaskConfiguration = JSON.parse(text);
90+
const buildTask = tasksJson.tasks.find(td => td.taskName === 'build');
91+
92+
resolve({ updateTasksJson: (buildTask === undefined) });
93+
});
94+
}
95+
else {
96+
resolve({ addTasksJson: true });
97+
}
98+
});
99+
});
100+
}
101+
102+
function getLaunchOperations(launchJsonPath: string, operations: Operations) {
103+
return new Promise<Operations>((resolve, reject) => {
104+
return fs.existsAsync(launchJsonPath).then(exists => {
105+
if (exists) {
106+
resolve(operations);
107+
}
108+
else {
109+
operations.addLaunchJson = true;
110+
resolve(operations);
111+
}
112+
});
113+
});
114+
}
115+
116+
function promptToAddAssets() {
117+
return new Promise<boolean>((resolve, reject) => {
118+
const item = { title: 'Yes' }
119+
120+
vscode.window.showInformationMessage('Required assets to build and debug are missing from your project. Add them?', item).then(selection => {
121+
return selection
122+
? resolve(true)
123+
: resolve(false);
124+
});
125+
});
126+
}
127+
128+
function createLaunchConfiguration(): ConsoleLaunchConfiguration {
129+
return {
130+
name: '.NET Core Launch (console)',
131+
type: 'coreclr',
132+
request: 'launch',
133+
preLaunchTask: 'build',
134+
program: '${workspaceRoot}/bin/Debug/<target-framework>/<project-name.dll>',
135+
args: [],
136+
cwd: '${workspaceRoot}',
137+
stopAtEntry: false
138+
}
139+
}
140+
141+
function createWebLaunchConfiguration(): WebLaunchConfiguration {
142+
return {
143+
name: '.NET Core Launch (web)',
144+
type: 'coreclr',
145+
request: 'launch',
146+
preLaunchTask: 'build',
147+
program: '${workspaceRoot}/bin/Debug/<target-framework>/<project-name.dll>',
148+
args: [],
149+
cwd: '${workspaceRoot}',
150+
stopAtEntry: false,
151+
launchBrowser: {
152+
enabled: true,
153+
args: '${auto-detect-url}',
154+
windows: {
155+
command: 'cmd.exe',
156+
args: '/C start ${auto-detect-url}'
157+
},
158+
osx: {
159+
command: 'open'
160+
},
161+
linux: {
162+
command: 'xdg-open'
163+
}
164+
}
165+
}
166+
}
167+
168+
function createAttachConfiguration(): AttachConfiguration {
169+
return {
170+
name: '.NET Core Attack',
171+
type: 'coreclr',
172+
request: 'attach',
173+
processName: '<example>'
174+
}
175+
}
176+
177+
function createLaunchJson(): any {
178+
return {
179+
version: '0.2.0',
180+
configurations: [
181+
createLaunchConfiguration(),
182+
createWebLaunchConfiguration(),
183+
createAttachConfiguration()
184+
]
185+
}
186+
}
187+
188+
function createBuildTaskDescription(): tasks.TaskDescription {
189+
return {
190+
taskName: 'build',
191+
args: [],
192+
isBuildCommand: true,
193+
problemMatcher: '$msCompile'
194+
};
195+
}
196+
197+
function createTasksConfiguration(): tasks.TaskConfiguration {
198+
return {
199+
version: '0.1.0',
200+
command: 'dotnet',
201+
isShellCommand: true,
202+
args: [],
203+
tasks: [ createBuildTaskDescription() ]
204+
};
205+
}
206+
207+
function addTasksJsonIfNecessary(paths: Paths, operations: Operations) {
208+
return new Promise<void>((resolve, reject) => {
209+
if (!operations.addTasksJson) {
210+
return resolve();
211+
}
212+
213+
const tasksJson = createTasksConfiguration();
214+
const tasksJsonText = JSON.stringify(tasksJson, null, ' ');
215+
216+
return fs.writeFileAsync(paths.tasksJsonPath, tasksJsonText);
217+
});
218+
}
219+
220+
function updateTasksJsonIfNecesssary(paths: Paths, operations: Operations) {
221+
return new Promise<void>((resolve, reject) => {
222+
if (!operations.updateTasksJson) {
223+
return resolve();
224+
}
225+
226+
return fs.readFileAsync(paths.tasksJsonPath).then(buffer => {
227+
const bufferText = buffer.toString();
228+
const tasksJson: tasks.TaskConfiguration = JSON.parse(bufferText);
229+
tasksJson.tasks.push(createBuildTaskDescription());
230+
const tasksJsonText = JSON.stringify(tasksJson, null, ' ');
231+
232+
return fs.writeFileAsync(paths.tasksJsonPath, tasksJsonText);
233+
});
234+
});
235+
}
236+
237+
function addLaunchJsonIfNecessary(paths: Paths, operations: Operations) {
238+
return new Promise<void>((resolve, reject) => {
239+
if (!operations.addLaunchJson) {
240+
return resolve();
241+
}
242+
243+
const launchJson = createLaunchJson();
244+
const launchJsonText = JSON.stringify(launchJson, null, ' ');
245+
246+
return fs.writeFileAsync(paths.launchJsonPath, launchJsonText);
247+
});
248+
}
249+
250+
export function addAssetsIfNecessary() {
251+
if (!vscode.workspace.rootPath) {
252+
return;
253+
}
254+
255+
// If there is no project.json, we won't bother to prompt the user for tasks.json.
256+
const projectJsonPath = path.join(vscode.workspace.rootPath, 'project.json');
257+
if (!fs.existsSync(projectJsonPath)) {
258+
return;
259+
}
260+
261+
return getOperations().then(operations => {
262+
if (!hasOperations(operations)) {
263+
return;
264+
}
265+
266+
promptToAddAssets().then(addAssets => {
267+
if (!addAssets) {
268+
return;
269+
}
270+
271+
const paths = getPaths();
272+
273+
return fs.ensureDirAsync(paths.vscodeFolder).then(() => {
274+
return Promise.all([
275+
addTasksJsonIfNecessary(paths, operations),
276+
updateTasksJsonIfNecesssary(paths, operations),
277+
addLaunchJsonIfNecessary(paths, operations)
278+
]);
279+
});
280+
});
281+
});
282+
}

src/omnisharpMain.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {StdioOmnisharpServer} from './omnisharpServer';
2323
import forwardChanges from './features/changeForwarding';
2424
import reportStatus from './features/omnisharpStatus';
2525
import {installCoreClrDebug} from './coreclr-debug';
26-
import {promptToAddBuildTaskIfNecessary} from './tasks';
26+
import {addAssetsIfNecessary} from './assets';
2727
import * as vscode from 'vscode';
2828
import TelemetryReporter from 'vscode-extension-telemetry';
2929

@@ -86,8 +86,8 @@ export function activate(context: vscode.ExtensionContext): any {
8686
server.stop();
8787
}));
8888

89-
// Check to see if there is a tasks.json with a "build" task and prompt the user to add it if missing.
90-
promptToAddBuildTaskIfNecessary();
89+
// Update or add tasks.json and launch.json
90+
addAssetsIfNecessary();
9191

9292
// install coreclr-debug
9393
installCoreClrDebug(context, reporter);

src/tasks.ts

Lines changed: 0 additions & 91 deletions
This file was deleted.

0 commit comments

Comments
 (0)