Skip to content

Commit 3907ecd

Browse files
Merge pull request #2221 from DustinCampbell/fix-omnisharp-launch
Ensure that OmniSharp is launched on global mono even when a version or 'latest' specifed
2 parents 8130fb7 + 3e3b234 commit 3907ecd

File tree

7 files changed

+158
-166
lines changed

7 files changed

+158
-166
lines changed

src/observers/OmnisharpLoggerObserver.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ export class OmnisharpLoggerObserver extends BaseLoggerObserver {
5454
}
5555

5656
private handleOmnisharpLaunch(event: OmnisharpLaunch) {
57-
if (event.usingMono) {
58-
this.logger.appendLine(`OmniSharp server started with Mono`);
57+
if (event.monoVersion) {
58+
this.logger.appendLine(`OmniSharp server started with Mono ${event.monoVersion}`);
5959
}
6060
else {
6161
this.logger.appendLine(`OmniSharp server started`);

src/omnisharp/OmnisharpManager.ts

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,60 +9,77 @@ import * as util from '../common';
99
import { OmnisharpDownloader } from './OmnisharpDownloader';
1010
import { PlatformInformation } from '../platform';
1111

12+
export interface LaunchInfo {
13+
LaunchPath: string;
14+
MonoLaunchPath?: string;
15+
}
16+
1217
export class OmnisharpManager {
1318
public constructor(
1419
private downloader: OmnisharpDownloader,
1520
private platformInfo: PlatformInformation) {
1621
}
1722

18-
public async GetOmnisharpPath(omnisharpPath: string, useMono: boolean, serverUrl: string, latestVersionFileServerPath: string, installPath: string, extensionPath: string): Promise<string> {
23+
public async GetOmniSharpLaunchInfo(omnisharpPath: string, serverUrl: string, latestVersionFileServerPath: string, installPath: string, extensionPath: string): Promise<LaunchInfo> {
24+
if (!omnisharpPath) {
25+
// If omnisharpPath was not specified, return the default path.
26+
let basePath = path.resolve(extensionPath, '.omnisharp');
27+
return this.GetLaunchInfo(this.platformInfo, basePath);
28+
}
29+
1930
// Looks at the options path, installs the dependencies and returns the path to be loaded by the omnisharp server
2031
if (path.isAbsolute(omnisharpPath)) {
21-
if (await util.fileExists(omnisharpPath)) {
22-
return omnisharpPath;
23-
}
24-
else {
32+
if (!await util.fileExists(omnisharpPath)) {
2533
throw new Error('The system could not find the specified path');
2634
}
35+
36+
return {
37+
LaunchPath: omnisharpPath
38+
};
2739
}
28-
else if (omnisharpPath == "latest") {
29-
return await this.InstallLatestAndReturnLaunchPath(useMono, serverUrl, latestVersionFileServerPath, installPath, extensionPath);
40+
else if (omnisharpPath === 'latest') {
41+
return await this.InstallLatestAndReturnLaunchInfo(serverUrl, latestVersionFileServerPath, installPath, extensionPath);
3042
}
3143

32-
//If the path is neither a valid path on disk not the string "latest", treat it as a version
33-
return await this.InstallVersionAndReturnLaunchPath(omnisharpPath, useMono, serverUrl, installPath, extensionPath);
44+
// If the path is neither a valid path on disk not the string "latest", treat it as a version
45+
return await this.InstallVersionAndReturnLaunchInfo(omnisharpPath, serverUrl, installPath, extensionPath);
3446
}
3547

36-
private async InstallLatestAndReturnLaunchPath(useMono: boolean, serverUrl: string, latestVersionFileServerPath: string, installPath: string, extensionPath: string) {
48+
private async InstallLatestAndReturnLaunchInfo(serverUrl: string, latestVersionFileServerPath: string, installPath: string, extensionPath: string): Promise<LaunchInfo> {
3749
let version = await this.downloader.GetLatestVersion(serverUrl, latestVersionFileServerPath);
38-
return await this.InstallVersionAndReturnLaunchPath(version, useMono, serverUrl, installPath, extensionPath);
50+
return await this.InstallVersionAndReturnLaunchInfo(version, serverUrl, installPath, extensionPath);
3951
}
4052

41-
private async InstallVersionAndReturnLaunchPath(version: string, useMono: boolean, serverUrl: string, installPath: string, extensionPath: string) {
53+
private async InstallVersionAndReturnLaunchInfo(version: string, serverUrl: string, installPath: string, extensionPath: string): Promise<LaunchInfo> {
4254
if (semver.valid(version)) {
4355
await this.downloader.DownloadAndInstallOmnisharp(version, serverUrl, installPath);
44-
return GetLaunchPathForVersion(this.platformInfo, version, installPath, extensionPath, useMono);
56+
return this.GetLaunchPathForVersion(this.platformInfo, version, installPath, extensionPath);
4557
}
4658
else {
47-
throw new Error(`Invalid omnisharp version - ${version}`);
59+
throw new Error(`Invalid OmniSharp version - ${version}`);
4860
}
4961
}
50-
}
5162

52-
function GetLaunchPathForVersion(platformInfo: PlatformInformation, version: string, installPath: string, extensionPath: string, useMono: boolean) {
53-
if (!version) {
54-
throw new Error('Invalid Version');
55-
}
63+
private GetLaunchPathForVersion(platformInfo: PlatformInformation, version: string, installPath: string, extensionPath: string): LaunchInfo {
64+
if (!version) {
65+
throw new Error('Invalid Version');
66+
}
5667

57-
let basePath = path.resolve(extensionPath, installPath, version);
68+
let basePath = path.resolve(extensionPath, installPath, version);
5869

59-
if (platformInfo.isWindows()) {
60-
return path.join(basePath, 'OmniSharp.exe');
61-
}
62-
if (useMono) {
63-
return path.join(basePath, 'omnisharp', 'OmniSharp.exe');
70+
return this.GetLaunchInfo(platformInfo, basePath);
6471
}
6572

66-
return path.join(basePath, 'run');
67-
}
73+
private GetLaunchInfo(platformInfo: PlatformInformation, basePath: string): LaunchInfo {
74+
if (platformInfo.isWindows()) {
75+
return {
76+
LaunchPath: path.join(basePath, 'OmniSharp.exe')
77+
};
78+
}
6879

80+
return {
81+
LaunchPath: path.join(basePath, 'run'),
82+
MonoLaunchPath: path.join(basePath, 'omnisharp', 'OmniSharp.exe')
83+
};
84+
}
85+
}

src/omnisharp/launcher.ts

Lines changed: 55 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import { satisfies } from 'semver';
88
import { PlatformInformation } from '../platform';
99
import * as path from 'path';
1010
import * as vscode from 'vscode';
11-
import * as util from '../common';
1211
import { Options } from './options';
12+
import { LaunchInfo } from './OmnisharpManager';
1313

1414
export enum LaunchTargetKind {
1515
Solution,
@@ -201,12 +201,12 @@ function isCake(resource: vscode.Uri): boolean {
201201
export interface LaunchResult {
202202
process: ChildProcess;
203203
command: string;
204-
usingMono: boolean;
204+
monoVersion?: string;
205205
}
206206

207-
export async function launchOmniSharp(cwd: string, args: string[], launchPath: string): Promise<LaunchResult> {
207+
export async function launchOmniSharp(cwd: string, args: string[], launchInfo: LaunchInfo): Promise<LaunchResult> {
208208
return new Promise<LaunchResult>((resolve, reject) => {
209-
launch(cwd, args, launchPath)
209+
launch(cwd, args, launchInfo)
210210
.then(result => {
211211
// async error - when target not not ENEOT
212212
result.process.on('error', err => {
@@ -222,50 +222,42 @@ export async function launchOmniSharp(cwd: string, args: string[], launchPath: s
222222
});
223223
}
224224

225-
async function launch(cwd: string, args: string[], launchPath: string): Promise<LaunchResult> {
226-
return PlatformInformation.GetCurrent().then(platformInfo => {
227-
const options = Options.Read();
228-
229-
if (options.useEditorFormattingSettings) {
230-
let globalConfig = vscode.workspace.getConfiguration();
231-
let csharpConfig = vscode.workspace.getConfiguration('[csharp]');
225+
async function launch(cwd: string, args: string[], launchInfo: LaunchInfo): Promise<LaunchResult> {
226+
const platformInfo = await PlatformInformation.GetCurrent();
227+
const options = Options.Read();
232228

233-
args.push(`formattingOptions:useTabs=${!getConfigurationValue(globalConfig, csharpConfig, 'editor.insertSpaces', true)}`);
234-
args.push(`formattingOptions:tabSize=${getConfigurationValue(globalConfig, csharpConfig, 'editor.tabSize', 4)}`);
235-
args.push(`formattingOptions:indentationSize=${getConfigurationValue(globalConfig, csharpConfig, 'editor.tabSize', 4)}`);
236-
}
229+
if (options.useEditorFormattingSettings) {
230+
let globalConfig = vscode.workspace.getConfiguration();
231+
let csharpConfig = vscode.workspace.getConfiguration('[csharp]');
237232

238-
// If the user has provided an absolute path or the specified version has been installed successfully, we'll use the path.
239-
if (launchPath) {
240-
if (platformInfo.isWindows()) {
241-
return launchWindows(launchPath, cwd, args);
242-
}
233+
args.push(`formattingOptions:useTabs=${!getConfigurationValue(globalConfig, csharpConfig, 'editor.insertSpaces', true)}`);
234+
args.push(`formattingOptions:tabSize=${getConfigurationValue(globalConfig, csharpConfig, 'editor.tabSize', 4)}`);
235+
args.push(`formattingOptions:indentationSize=${getConfigurationValue(globalConfig, csharpConfig, 'editor.tabSize', 4)}`);
236+
}
243237

244-
// If we're launching on macOS/Linux, we have two possibilities:
245-
// 1. Launch using Mono
246-
// 2. Launch process directly (e.g. a 'run' script)
247-
return options.useMono
248-
? launchNixMono(launchPath, cwd, args)
249-
: launchNix(launchPath, cwd, args);
250-
}
238+
if (platformInfo.isWindows()) {
239+
return launchWindows(launchInfo.LaunchPath, cwd, args);
240+
}
251241

252-
// If the user has not provided a path, we'll use the locally-installed OmniSharp
253-
const basePath = path.resolve(util.getExtensionPath(), '.omnisharp');
242+
let monoVersion = await getMonoVersion();
243+
let isValidMonoAvailable = await satisfies(monoVersion, '>=5.2.0');
254244

255-
if (platformInfo.isWindows()) {
256-
return launchWindows(path.join(basePath, 'OmniSharp.exe'), cwd, args);
245+
// If the user specifically said that they wanted to launch on Mono, respect their wishes.
246+
if (options.useMono) {
247+
if (!isValidMonoAvailable) {
248+
throw new Error('Cannot start OmniSharp because Mono version >=5.2.0 is required.');
257249
}
258250

259-
// If it's possible to launch on a global Mono, we'll do that. Otherwise, run with our
260-
// locally installed Mono runtime.
261-
return canLaunchMono()
262-
.then(async () => {
263-
return launchNixMono(path.join(basePath, 'omnisharp', 'OmniSharp.exe'), cwd, args);
264-
})
265-
.catch(_ => {
266-
return launchNix(path.join(basePath, 'run'), cwd, args);
267-
});
268-
});
251+
return launchNixMono(launchInfo.LaunchPath, monoVersion, cwd, args);
252+
}
253+
254+
// If we can launch on the global Mono, do so; otherwise, launch directly;
255+
if (isValidMonoAvailable && launchInfo.MonoLaunchPath) {
256+
return launchNixMono(launchInfo.MonoLaunchPath, monoVersion, cwd, args);
257+
}
258+
else {
259+
return launchNix(launchInfo.LaunchPath, cwd, args);
260+
}
269261
}
270262

271263
function getConfigurationValue(globalConfig: vscode.WorkspaceConfiguration, csharpConfig: vscode.WorkspaceConfiguration,
@@ -303,7 +295,6 @@ function launchWindows(launchPath: string, cwd: string, args: string[]): LaunchR
303295
return {
304296
process,
305297
command: launchPath,
306-
usingMono: false
307298
};
308299
}
309300

@@ -315,58 +306,41 @@ function launchNix(launchPath: string, cwd: string, args: string[]): LaunchResul
315306

316307
return {
317308
process,
318-
command: launchPath,
319-
usingMono: true
309+
command: launchPath
320310
};
321311
}
322312

323-
async function launchNixMono(launchPath: string, cwd: string, args: string[]): Promise<LaunchResult> {
324-
return canLaunchMono()
325-
.then(() => {
326-
let argsCopy = args.slice(0); // create copy of details args
327-
argsCopy.unshift(launchPath);
328-
argsCopy.unshift("--assembly-loader=strict");
329-
330-
let process = spawn('mono', argsCopy, {
331-
detached: false,
332-
cwd: cwd
333-
});
334-
335-
return {
336-
process,
337-
command: launchPath,
338-
usingMono: true
339-
};
340-
});
341-
}
313+
function launchNixMono(launchPath: string, monoVersion: string, cwd: string, args: string[]): LaunchResult {
314+
let argsCopy = args.slice(0); // create copy of details args
315+
argsCopy.unshift(launchPath);
316+
argsCopy.unshift("--assembly-loader=strict");
342317

343-
async function canLaunchMono(): Promise<void> {
344-
return new Promise<void>((resolve, reject) => {
345-
hasMono('>=5.2.0').then(success => {
346-
if (success) {
347-
resolve();
348-
}
349-
else {
350-
reject(new Error('Cannot start Omnisharp because Mono version >=5.2.0 is required.'));
351-
}
352-
});
318+
let process = spawn('mono', argsCopy, {
319+
detached: false,
320+
cwd: cwd
353321
});
322+
323+
return {
324+
process,
325+
command: launchPath,
326+
monoVersion,
327+
};
354328
}
355329

356-
export async function hasMono(range?: string): Promise<boolean> {
330+
async function getMonoVersion(): Promise<string> {
357331
const versionRegexp = /(\d+\.\d+\.\d+)/;
358332

359-
return new Promise<boolean>((resolve, reject) => {
333+
return new Promise<string>((resolve, reject) => {
360334
let childprocess: ChildProcess;
361335
try {
362336
childprocess = spawn('mono', ['--version']);
363337
}
364338
catch (e) {
365-
return resolve(false);
339+
return resolve(undefined);
366340
}
367341

368342
childprocess.on('error', function (err: any) {
369-
resolve(false);
343+
resolve(undefined);
370344
});
371345

372346
let stdout = '';
@@ -375,20 +349,14 @@ export async function hasMono(range?: string): Promise<boolean> {
375349
});
376350

377351
childprocess.stdout.on('close', () => {
378-
let match = versionRegexp.exec(stdout),
379-
ret: boolean;
352+
let match = versionRegexp.exec(stdout);
380353

381-
if (!match) {
382-
ret = false;
383-
}
384-
else if (!range) {
385-
ret = true;
354+
if (match && match.length > 1) {
355+
resolve(match[1]);
386356
}
387357
else {
388-
ret = satisfies(match[1], range);
358+
resolve(undefined);
389359
}
390-
391-
resolve(ret);
392360
});
393361
});
394362
}

src/omnisharp/loggingEvents.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export class OmnisharpInitialisation implements BaseEvent {
2727
}
2828

2929
export class OmnisharpLaunch implements BaseEvent {
30-
constructor(public usingMono: boolean, public command: string, public pid: number) { }
30+
constructor(public monoVersion: string, public command: string, public pid: number) { }
3131
}
3232

3333
export class PackageInstallation implements BaseEvent {

0 commit comments

Comments
 (0)