Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,16 @@
"default": null,
"description": "Specifies the full path to the OmniSharp server."
},
"omnisharp.useLatestExperimentalBuild": {
"type": "boolean",
"default": false,
"description": "Specifies whether the latest experimental omnisharp build must be used"
},
"omnisharp.alternateVersion": {
"type": "string",
"default": null,
"description": "Specifies which alternate omnisharp version must be used"
},
"omnisharp.useMono": {
"type": "boolean",
"default": false,
Expand Down Expand Up @@ -2125,4 +2135,4 @@
}
]
}
}
}
28 changes: 28 additions & 0 deletions src/omnisharp/latestVersionSelector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as fs from 'fs';
import * as semver from 'semver';

export function GetLatestInstalledExperimentalVersion(basePath: string) {
let latestVersion: string = '';
if (fs.existsSync(basePath)) {
let installedItems = fs.readdirSync(basePath);
if (installedItems && installedItems.length > 0) {
let validVersions = installedItems.filter(value => semver.valid(value));
if (validVersions && validVersions.length > 0) {
latestVersion = validVersions.reduce((latestTillNow, element) => {
if (semver.gt(latestTillNow, element)) {
return latestTillNow;
}

return element;
});
}
}
}

return latestVersion;
}
65 changes: 43 additions & 22 deletions src/omnisharp/launcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import * as path from 'path';
import * as vscode from 'vscode';
import * as util from '../common';
import { Options } from './options';
import { GetLatestInstalledExperimentalVersion } from './latestVersionSelector';

export enum LaunchTargetKind {
Solution,
Expand Down Expand Up @@ -46,7 +47,7 @@ export function findLaunchTargets(): Thenable<LaunchTarget[]> {
const options = Options.Read();

return vscode.workspace.findFiles(
/*include*/ '{**/*.sln,**/*.csproj,**/project.json,**/*.csx,**/*.cake}',
/*include*/ '{**/*.sln,**/*.csproj,**/project.json,**/*.csx,**/*.cake}',
/*exclude*/ '{**/node_modules/**,**/.git/**,**/bower_components/**}',
/*maxResults*/ options.maxProjectResults)
.then(resourcesToLaunchTargets);
Expand All @@ -61,7 +62,7 @@ function resourcesToLaunchTargets(resources: vscode.Uri[]): LaunchTarget[] {
//
// TODO:
// * It should be possible to choose a .csproj as a launch target
// * It should be possible to choose a .sln file even when no .csproj files are found
// * It should be possible to choose a .sln file even when no .csproj files are found
// within the root.

if (!Array.isArray(resources)) {
Expand All @@ -88,8 +89,7 @@ function resourcesToLaunchTargets(resources: vscode.Uri[]): LaunchTarget[] {

let targets: LaunchTarget[] = [];

workspaceFolderToUriMap.forEach((resources, folderIndex) =>
{
workspaceFolderToUriMap.forEach((resources, folderIndex) => {
let hasCsProjFiles = false,
hasSlnFile = false,
hasProjectJson = false,
Expand All @@ -98,15 +98,15 @@ function resourcesToLaunchTargets(resources: vscode.Uri[]): LaunchTarget[] {
hasCake = false;

hasCsProjFiles = resources.some(isCSharpProject);

let folder = vscode.workspace.workspaceFolders[folderIndex];
let folderPath = folder.uri.fsPath;

resources.forEach(resource => {
// Add .sln files if there are .csproj files
if (hasCsProjFiles && isSolution(resource)) {
hasSlnFile = true;

targets.push({
label: path.basename(resource.fsPath),
description: vscode.workspace.asRelativePath(path.dirname(resource.fsPath)),
Expand All @@ -115,13 +115,13 @@ function resourcesToLaunchTargets(resources: vscode.Uri[]): LaunchTarget[] {
kind: LaunchTargetKind.Solution
});
}

// Add project.json files
if (isProjectJson(resource)) {
const dirname = path.dirname(resource.fsPath);
hasProjectJson = true;
hasProjectJsonAtRoot = hasProjectJsonAtRoot || dirname === folderPath;

targets.push({
label: path.basename(resource.fsPath),
description: vscode.workspace.asRelativePath(path.dirname(resource.fsPath)),
Expand All @@ -130,18 +130,18 @@ function resourcesToLaunchTargets(resources: vscode.Uri[]): LaunchTarget[] {
kind: LaunchTargetKind.ProjectJson
});
}

// Discover if there is any CSX file
if (!hasCSX && isCsx(resource)) {
hasCSX = true;
}

// Discover if there is any Cake file
if (!hasCake && isCake(resource)) {
hasCake = true;
}
});

// Add the root folder under the following circumstances:
// * If there are .csproj files, but no .sln file, and none in the root.
// * If there are project.json files, but none in the root.
Expand All @@ -154,7 +154,7 @@ function resourcesToLaunchTargets(resources: vscode.Uri[]): LaunchTarget[] {
kind: LaunchTargetKind.Folder
});
}

// if we noticed any CSX file(s), add a single CSX-specific target pointing at the root folder
if (hasCSX) {
targets.push({
Expand All @@ -165,7 +165,7 @@ function resourcesToLaunchTargets(resources: vscode.Uri[]): LaunchTarget[] {
kind: LaunchTargetKind.Csx
});
}

// if we noticed any Cake file(s), add a single Cake-specific target pointing at the root folder
if (hasCake) {
targets.push({
Expand Down Expand Up @@ -229,8 +229,7 @@ function launch(cwd: string, args: string[]): Promise<LaunchResult> {
return PlatformInformation.GetCurrent().then(platformInfo => {
const options = Options.Read();

if (options.useEditorFormattingSettings)
{
if (options.useEditorFormattingSettings) {
let globalConfig = vscode.workspace.getConfiguration();
let csharpConfig = vscode.workspace.getConfiguration('[csharp]');

Expand All @@ -253,8 +252,18 @@ function launch(cwd: string, args: string[]): Promise<LaunchResult> {
: launchNix(options.path, cwd, args);
}

// If the user has not provided a path, we'll use the locally-installed OmniSharp
const basePath = path.resolve(util.getExtensionPath(), '.omnisharp');
let extensionPath = util.getExtensionPath();
let basePath: string;
if (options.alternateVersion) {
basePath = path.resolve(extensionPath,`.omnisharp/experimental/${options.alternateVersion}`);
}
else if (options.useLatestExperimentalBuild) {
basePath = getLatestExperimentalBuildPath(extensionPath);
}
else {
// If the user has neither provided a path not set any options for using other versions, we'll use the locally-installed OmniSharp
basePath = path.resolve(extensionPath, '.omnisharp');
}

if (platformInfo.isWindows()) {
return launchWindows(path.join(basePath, 'OmniSharp.exe'), cwd, args);
Expand All @@ -272,13 +281,25 @@ function launch(cwd: string, args: string[]): Promise<LaunchResult> {
});
}

function getLatestExperimentalBuildPath(extensionPath: string) {
let dirPath = path.resolve(extensionPath, `.omnisharp/experimental`);
let latestVersion = GetLatestInstalledExperimentalVersion(dirPath);
if (latestVersion && latestVersion.length>0) {
return path.resolve(extensionPath, `.omnisharp/experimental/${latestVersion}`);
}
else {
//If there is no latest version present in experimental folder, use the release version
return path.resolve(extensionPath, `.omnisharp`);
}
}

function getConfigurationValue(globalConfig: vscode.WorkspaceConfiguration, csharpConfig: vscode.WorkspaceConfiguration,
configurationPath: string, defaultValue: any): any {

if (csharpConfig[configurationPath] != undefined) {
return csharpConfig[configurationPath];
}

return globalConfig.get(configurationPath, defaultValue);
}

Expand All @@ -287,7 +308,7 @@ function launchWindows(launchPath: string, cwd: string, args: string[]): LaunchR
const hasSpaceWithoutQuotes = /^[^"].* .*[^"]/;
return hasSpaceWithoutQuotes.test(arg)
? `"${arg}"`
: arg.replace("&","^&");
: arg.replace("&", "^&");
}

let argsCopy = args.slice(0); // create copy of args
Expand Down Expand Up @@ -395,4 +416,4 @@ export function hasMono(range?: string): Promise<boolean> {
resolve(ret);
});
});
}
}
25 changes: 16 additions & 9 deletions src/omnisharp/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ export class Options {
public useFormatting?: boolean,
public showReferencesCodeLens?: boolean,
public showTestsCodeLens?: boolean,
public disableCodeActions?: boolean) { }
public disableCodeActions?: boolean,
public alternateVersion?: string,
public useLatestExperimentalBuild?: boolean) { }

public static Read(): Options {
// Extra effort is taken below to ensure that legacy versions of options
Expand Down Expand Up @@ -59,17 +61,22 @@ export class Options {

const disableCodeActions = csharpConfig.get<boolean>('disableCodeActions', false);

return new Options(path,
useMono,
const alternateVersion = omnisharpConfig.get<string>('alternateVersion');
const useLatestExperimentalBuild = omnisharpConfig.get<boolean>('useLatestExperimentalBuild', false);

return new Options(path,
useMono,
waitForDebugger,
loggingLevel,
autoStart,
projectLoadTimeout,
maxProjectResults,
useEditorFormattingSettings,
loggingLevel,
autoStart,
projectLoadTimeout,
maxProjectResults,
useEditorFormattingSettings,
useFormatting,
showReferencesCodeLens,
showTestsCodeLens,
disableCodeActions);
disableCodeActions,
alternateVersion,
useLatestExperimentalBuild);
}
}
85 changes: 85 additions & 0 deletions test/unitTests/latestVersionSelector.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import * as path from 'path';
import * as fs from 'fs';
import { should } from 'chai';
import { GetLatestInstalledExperimentalVersion } from '../../src/omnisharp/latestVersionSelector';

const tmp = require('tmp');

suite("Experimental Omnisharp - Latest Version", () => {
suiteSetup(() => should());

test('Returns latest version', () => {
let versions: string[] = ["1.28.0", "1.27.0", "1.26.0"];
let latestVersion = GetLatestVersion(versions);
latestVersion.should.equal("1.28.0");
});

test('Ignores unparseable strings', () => {
let versions: string[] = ["1.28.0", "1.27.0", "1.26.0", "a.b.c"];
let latestVersion = GetLatestVersion(versions);
latestVersion.should.equal("1.28.0");
});

test('Returns pre-release versions if they are the latest', () => {
let versions: string[] = ["1.28.0", "1.27.0", "1.26.0", "1.29.0-beta1"];
let latestVersion = GetLatestVersion(versions);
latestVersion.should.equal("1.29.0-beta1");
});

test('Returns the latest pre-release version', () => {
let versions: string[] = ["1.28.0", "1.27.0", "1.29.0-beta2", "1.29.0-beta1"];
let latestVersion = GetLatestVersion(versions);
latestVersion.should.equal("1.29.0-beta2");
});

test('Returns the prod version over pre-release version', () => {
let versions: string[] = ["1.28.0", "1.27.0", "1.29.0", "1.29.0-beta1"];
let latestVersion = GetLatestVersion(versions);
latestVersion.should.equal("1.29.0");
});

test('Returns undefined if no valid version exists', () => {
let versions: string[] = ["a.b.c"];
let latestVersion = GetLatestVersion(versions);
latestVersion.should.equal("");
});

test('Returns empty if folder is empty', () => {
let versions: string[] = [];
let latestVersion = GetLatestVersion(versions);
latestVersion.should.equal("");
});

test('Returns empty if experimental folder doesnot exist', () => {
let latestVersion = GetLatestInstalledExperimentalVersion("");
latestVersion.should.equal("");
});
});

function GetLatestVersion(versions: string[]): string {
let tmpDir = tmp.dirSync();
let dirPath = tmpDir.name;
AddVersionsToDirectory(dirPath, versions);
let latestVersion = GetLatestInstalledExperimentalVersion(dirPath);
CleanUpDirectory(dirPath);
tmpDir.removeCallback();
return latestVersion;
}

function AddVersionsToDirectory(dirPath: string, versions: string[]) {
for (let version of versions) {
fs.mkdirSync(`${dirPath}/${version}`);
}
}

function CleanUpDirectory(dirPath: string) {
let installedVersions = fs.readdirSync(dirPath);
for (let version of installedVersions) {
fs.rmdirSync(`${dirPath}/${version}`);
}
}