Skip to content

Commit b2581f6

Browse files
Merge pull request #2780 from gregg-miskelly/SimplifyLaunchJson
Improvements to launch.json/tasks.json
2 parents 265499a + 9323811 commit b2581f6

File tree

9 files changed

+373
-506
lines changed

9 files changed

+373
-506
lines changed

gulpfile.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@ require('./tasks/offlinePackagingTasks');
1616
require('./tasks/backcompatTasks');
1717
require('./tasks/coverageTasks');
1818

19-
gulp.task('generateOptionsSchema', () : void => {
19+
// Disable warning about wanting an async function
20+
// tslint:disable-next-line
21+
gulp.task('generateOptionsSchema', () : Promise<void> => {
2022
optionsSchemaGenerator.GenerateOptionsSchema();
23+
return Promise.resolve();
2124
});
2225

2326
// Disable warning about wanting an async function

package.json

Lines changed: 70 additions & 101 deletions
Large diffs are not rendered by default.

src/assets.ts

Lines changed: 201 additions & 179 deletions
Large diffs are not rendered by default.

src/configurationProvider.ts

Lines changed: 73 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ import * as serverUtils from './omnisharp/utils';
99
import * as vscode from 'vscode';
1010
import { ParsedEnvironmentFile } from './coreclr-debug/ParsedEnvironmentFile';
1111

12-
import { AssetGenerator, addTasksJsonIfNecessary, createAttachConfiguration, createLaunchConfiguration, createWebLaunchConfiguration } from './assets';
12+
import { AssetGenerator, AssetOperations, addTasksJsonIfNecessary, createAttachConfiguration, createFallbackLaunchConfiguration, getBuildOperations } from './assets';
1313

1414
import { OmniSharpServer } from './omnisharp/server';
15-
import { containsDotNetCoreProjects } from './omnisharp/protocol';
15+
import { WorkspaceInformationResponse } from './omnisharp/protocol';
1616
import { isSubfolderOf } from './common';
1717
import { parse } from 'jsonc-parser';
1818
import { MessageItem } from './vscodeAdapter';
@@ -30,11 +30,12 @@ export class CSharpConfigurationProvider implements vscode.DebugConfigurationPro
3030
* Note: serverUtils.requestWorkspaceInformation only retrieves one folder for multi-root workspaces. Therefore, generator will be incorrect for all folders
3131
* except the first in a workspace. Currently, this only works if the requested folder is the same as the server's solution path or folder.
3232
*/
33-
private async checkWorkspaceInformationMatchesWorkspaceFolder(folder: vscode.WorkspaceFolder | undefined): Promise<boolean> {
33+
private async checkWorkspaceInformationMatchesWorkspaceFolder(folder: vscode.WorkspaceFolder): Promise<boolean> {
34+
3435
const solutionPathOrFolder: string = this.server.getSolutionPathOrFolder();
3536

3637
// Make sure folder, folder.uri, and solutionPathOrFolder are defined.
37-
if (!folder || !folder.uri || !solutionPathOrFolder)
38+
if (!solutionPathOrFolder)
3839
{
3940
return Promise.resolve(false);
4041
}
@@ -60,46 +61,63 @@ export class CSharpConfigurationProvider implements vscode.DebugConfigurationPro
6061
/**
6162
* Returns a list of initial debug configurations based on contextual information, e.g. package.json or folder.
6263
*/
63-
provideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration[]> {
64-
return serverUtils.requestWorkspaceInformation(this.server).then(async info => {
65-
return this.checkWorkspaceInformationMatchesWorkspaceFolder(folder).then(async workspaceMatches => {
66-
const generator = new AssetGenerator(info);
67-
if (workspaceMatches && containsDotNetCoreProjects(info)) {
68-
const dotVscodeFolder: string = path.join(folder.uri.fsPath, '.vscode');
69-
const tasksJsonPath: string = path.join(dotVscodeFolder, 'tasks.json');
70-
71-
// Make sure .vscode folder exists, addTasksJsonIfNecessary will fail to create tasks.json if the folder does not exist.
72-
return fs.ensureDir(dotVscodeFolder).then(async () => {
73-
// Check to see if tasks.json exists.
74-
return fs.pathExists(tasksJsonPath);
75-
}).then(async tasksJsonExists => {
76-
// Enable addTasksJson if it does not exist.
77-
return addTasksJsonIfNecessary(generator, {addTasksJson: !tasksJsonExists});
78-
}).then(() => {
79-
const isWebProject = generator.hasWebServerDependency();
80-
const launchJson: string = generator.createLaunchJson(isWebProject);
81-
82-
// jsonc-parser's parse function parses a JSON string with comments into a JSON object. However, this removes the comments.
83-
return parse(launchJson);
84-
});
64+
async provideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, token?: vscode.CancellationToken): Promise<vscode.DebugConfiguration[]> {
65+
66+
if (!folder || !folder.uri) {
67+
vscode.window.showErrorMessage("Cannot create .NET debug configurations. No workspace folder was selected.");
68+
return [];
69+
}
70+
71+
if (!this.server.isRunning()) {
72+
vscode.window.showErrorMessage("Cannot create .NET debug configurations. The OmniSharp server is still initializing or has exited unexpectedly.");
73+
return [];
74+
}
75+
76+
try
77+
{
78+
let hasWorkspaceMatches : boolean = await this.checkWorkspaceInformationMatchesWorkspaceFolder(folder);
79+
if (!hasWorkspaceMatches) {
80+
vscode.window.showErrorMessage(`Cannot create .NET debug configurations. The active C# project is not within folder '${folder.uri.fsPath}'.`);
81+
return [];
82+
}
83+
84+
let info: WorkspaceInformationResponse = await serverUtils.requestWorkspaceInformation(this.server);
85+
86+
const generator = new AssetGenerator(info, folder);
87+
if (generator.hasExecutableProjects()) {
88+
89+
if (!await generator.selectStartupProject())
90+
{
91+
return [];
8592
}
93+
94+
// Make sure .vscode folder exists, addTasksJsonIfNecessary will fail to create tasks.json if the folder does not exist.
95+
await fs.ensureDir(generator.vscodeFolder);
96+
97+
// Add a tasks.json
98+
const buildOperations : AssetOperations = await getBuildOperations(generator);
99+
await addTasksJsonIfNecessary(generator, buildOperations);
86100

101+
const isWebProject = generator.hasWebServerDependency();
102+
const launchJson: string = generator.createLaunchJson(isWebProject);
103+
104+
// jsonc-parser's parse function parses a JSON string with comments into a JSON object. However, this removes the comments.
105+
return parse(launchJson);
106+
107+
} else {
87108
// Error to be caught in the .catch() below to write default C# configurations
88109
throw new Error("Does not contain .NET Core projects.");
89-
});
90-
}).catch((err) => {
110+
}
111+
}
112+
catch
113+
{
91114
// Provider will always create an launch.json file. Providing default C# configurations.
92115
// jsonc-parser's parse to convert to JSON object without comments.
93116
return [
94-
parse(createLaunchConfiguration(
95-
"${workspaceFolder}/bin/Debug/<insert-target-framework-here>/<insert-project-name-here>.dll",
96-
'${workspaceFolder}')),
97-
parse(createWebLaunchConfiguration(
98-
"${workspaceFolder}/bin/Debug/<insert-target-framework-here>/<insert-project-name-here>.dll",
99-
'${workspaceFolder}')),
117+
createFallbackLaunchConfiguration(),
100118
parse(createAttachConfiguration())
101119
];
102-
});
120+
}
103121
}
104122

105123
/**
@@ -135,13 +153,28 @@ export class CSharpConfigurationProvider implements vscode.DebugConfigurationPro
135153
*/
136154
resolveDebugConfiguration(folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration, token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration> {
137155

138-
// read from envFile and set config.env
139-
if (config.envFile) {
140-
config = this.parseEnvFile(config.envFile.replace(/\${workspaceFolder}/g, folder.uri.fsPath), config);
156+
if (!config.type)
157+
{
158+
// If the config doesn't look functional force VSCode to open a configuration file https://github.com/Microsoft/vscode/issues/54213
159+
return null;
160+
}
161+
162+
if (config.request === "launch")
163+
{
164+
if (!config.cwd && !config.pipeTransport) {
165+
config.cwd = "${workspaceFolder}";
166+
}
167+
if (!config.internalConsoleOptions) {
168+
config.internalConsoleOptions = "openOnSessionStart";
169+
}
170+
171+
// read from envFile and set config.env
172+
if (config.envFile) {
173+
config = this.parseEnvFile(config.envFile.replace(/\${workspaceFolder}/g, folder.uri.fsPath), config);
174+
}
141175
}
142176

143-
// If the config looks functional return it, otherwise force VSCode to open a configuration file https://github.com/Microsoft/vscode/issues/54213
144-
return config && config.type ? config : null;
177+
return config;
145178
}
146179

147180
private static async showFileWarningAsync(message: string, fileName: string) {

src/omnisharp/protocol.ts

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -778,31 +778,3 @@ export function findExecutableMSBuildProjects(projects: MSBuildProject[]) {
778778

779779
return result;
780780
}
781-
782-
export function findExecutableProjectJsonProjects(projects: DotNetProject[], configurationName: string) {
783-
let result: DotNetProject[] = [];
784-
785-
projects.forEach(project => {
786-
project.Configurations.forEach(configuration => {
787-
if (configuration.Name === configurationName && configuration.EmitEntryPoint === true) {
788-
if (project.Frameworks.length > 0) {
789-
result.push(project);
790-
}
791-
}
792-
});
793-
});
794-
795-
return result;
796-
}
797-
798-
export function containsDotNetCoreProjects(workspaceInfo: WorkspaceInformationResponse) {
799-
if (workspaceInfo.DotNet && findExecutableProjectJsonProjects(workspaceInfo.DotNet.Projects, 'Debug').length > 0) {
800-
return true;
801-
}
802-
803-
if (workspaceInfo.MsBuild && findExecutableMSBuildProjects(workspaceInfo.MsBuild.Projects).length > 0) {
804-
return true;
805-
}
806-
807-
return false;
808-
}

src/omnisharp/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ export class OmniSharpServer {
501501
public async makeRequest<TResponse>(command: string, data?: any, token?: CancellationToken): Promise<TResponse> {
502502

503503
if (!this.isRunning()) {
504-
return Promise.reject<TResponse>('server has been stopped or not started');
504+
return Promise.reject<TResponse>('OmniSharp server is not running.');
505505
}
506506

507507
let startTime: number;

src/tools/OptionsSchema.json

Lines changed: 17 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -176,35 +176,26 @@
176176
},
177177
"LaunchBrowserPlatformOptions": {
178178
"type": "object",
179+
"required": [ "command" ],
179180
"properties": {
180181
"command": {
181182
"type": "string",
182-
"description": "The command to execute for launching the web browser",
183+
"description": "The executable which will start the web browser",
183184
"default": "open"
184185
},
185186
"args": {
186187
"type": "string",
187-
"description": "The arguments to pass to the command to open the browser. Use ${auto-detect-url} to automatically use the address the server is listening to",
188+
"description": "The arguments to pass to the command to open the browser. Use ${auto-detect-url} to automatically use the address the server is listening to.",
188189
"default": "${auto-detect-url}"
189190
}
190191
}
191192
},
192193
"LaunchBrowser": {
193194
"type": "object",
194-
"description": "Describes options to launch a web browser as part of launch",
195+
"required": [ "enabled" ],
196+
"description": "Configures starting a web browser as part of the launch -- should a web browser be started, and if so, what command should be run to start it. This option can be modified to launch a specific browser.",
195197
"default": {
196-
"enabled": true,
197-
"args": "${auto-detect-url}",
198-
"windows": {
199-
"command": "cmd.exe",
200-
"args": "/C start ${auto-detect-url}"
201-
},
202-
"osx": {
203-
"command": "open"
204-
},
205-
"linux": {
206-
"command": "xdg-open"
207-
}
198+
"enabled": true
208199
},
209200
"properties": {
210201
"enabled": {
@@ -213,39 +204,29 @@
213204
"default": true
214205
},
215206
"args": {
216-
"anyOf": [
217-
{
218-
"type": "array",
219-
"description": "Command line arguments passed to the program.",
220-
"items": {
221-
"type": "string"
222-
},
223-
"default": []
224-
},
225-
{
226-
"type": "string",
227-
"description": "Stringified version of command line arguments passed to the program.",
228-
"default": ""
229-
}
230-
]
207+
"type": "string",
208+
"description": "The arguments to pass to the command to open the browser. This is used only if the platform-specific element (`osx`, `linux` or `windows`) doesn't specify a value for `args`. Use ${auto-detect-url} to automatically use the address the server is listening to.",
209+
"default": "${auto-detect-url}"
231210
},
232211
"osx": {
233212
"$ref": "#/definitions/LaunchBrowserPlatformOptions",
234-
"description": "OSX-specific web launch configuration options",
213+
"description": "OSX-specific web launch configuration options. By default, this will start the browser using `open`.",
235214
"default": {
236-
"command": "open"
215+
"command": "open",
216+
"args": "${auto-detect-url}"
237217
}
238218
},
239219
"linux": {
240220
"$ref": "#/definitions/LaunchBrowserPlatformOptions",
241-
"description": "Linux-specific web launch configuration options",
221+
"description": "Linux-specific web launch configuration options. By default, this will start the browser using `xdg-open`.",
242222
"default": {
243-
"command": "xdg-open"
223+
"command": "xdg-open",
224+
"args": "${auto-detect-url}"
244225
}
245226
},
246227
"windows": {
247228
"$ref": "#/definitions/LaunchBrowserPlatformOptions",
248-
"description": "Windows-specific web launch configuration options",
229+
"description": "Windows-specific web launch configuration options. By default, this will start the browser using `cmd /c start`.",
249230
"default": {
250231
"command": "cmd.exe",
251232
"args": "/C start ${auto-detect-url}"
@@ -295,18 +276,7 @@
295276
"$ref": "#/definitions/LaunchBrowser",
296277
"description": "Describes options to launch a web browser as part of launch",
297278
"default": {
298-
"enabled": true,
299-
"args": "${auto-detect-url}",
300-
"windows": {
301-
"command": "cmd.exe",
302-
"args": "/C start ${auto-detect-url}"
303-
},
304-
"osx": {
305-
"command": "open"
306-
},
307-
"linux": {
308-
"command": "xdg-open"
309-
}
279+
"enabled": true
310280
}
311281
},
312282
"env": {

src/tools/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
OptionsSchema.json defines the type for Launch/Attach options.
33

44
# GenerateOptionsSchema
5-
If there are any modifications to the OptionsSchema.json file. Please run `gulp generateOptionsSchema` at the repo root.
5+
If there are any modifications to the OptionsSchema.json file. Please run `npm run gulp generateOptionsSchema` at the repo root.
66
This will call GenerateOptionsSchema and update the package.json file.
77

88
### Important notes:

0 commit comments

Comments
 (0)