diff --git a/README.md b/README.md index b5708cd6e1..5d325f42c5 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,18 @@ Please file any issues at https://github.com/OmniSharp/omnisharp-vscode/issues. ### Debugging The C# extension now supports basic debugging capabilities! See http://aka.ms/vscclrdebugger for details. +### What's new in C# extension version 1.1 + +* Preliminary support for `dotnet test` +* Fix for OmniSharp installation problems on networks with an http proxy +* Debugger support for an external console +* Debugger support for environment variables +* Support for debugging .NET Core 1.0.0 post RC2 builds +* Automatic web vs. console debugger configuration detection +* Detach support +* Fix expression evaluation errors when referencing assemblies which aren't currently loaded +* Fix expression evaluation on Windows 7 + ### Development First install: diff --git a/coreclr-debug/NuGet.config b/coreclr-debug/NuGet.config index 5db080cb7d..045c4497c2 100644 --- a/coreclr-debug/NuGet.config +++ b/coreclr-debug/NuGet.config @@ -4,5 +4,7 @@ + + diff --git a/debugger.md b/debugger.md index d548941c09..691c1f5ab9 100644 --- a/debugger.md +++ b/debugger.md @@ -116,9 +116,21 @@ You can optionally configure a file by file mapping by providing map following t "sourceFileMap": { "C:\foo":"/home/me/foo" - } + } #####Symbol Path You can optionally provide paths to symbols following this schema: "symbolPath":"[ \"/Volumes/symbols\"]" + +#####Environment variables +Environment variables may be passed to your program using this schema: + + "env": { + "myVariableName":"theValueGoesHere" + } + +#####External console (terminal) window +The target process can optionally launch into a seperate console window. You will want this if your console app takes console input (ex: Console.ReadLine). This can be enabled with: + + "externalConsole": true diff --git a/package.json b/package.json index 6a270cbeea..3171c4aa97 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "csharp", "publisher": "ms-vscode", - "version": "1.0.12", + "version": "1.1.4", "description": "C# for Visual Studio Code (powered by OmniSharp).", "displayName": "C#", "author": "Microsoft Corporation", @@ -22,7 +22,7 @@ "postinstall": "tsc" }, "dependencies": { - "decompress": "^3.0.0", + "decompress": "^4.0.0", "del": "^2.0.2", "fs-extra-promise": "^0.3.1", "http-proxy-agent": "^1.0.0", @@ -263,6 +263,17 @@ } } }, + "env": { + "type": "object", + "additionalProperties": { "type": "string" }, + "description": "Environment variables passed to the program.", + "default": { } + }, + "externalConsole": { + "type": "boolean", + "description": "If 'true' the debugger should launch the target application into a new external console.", + "default": false + }, "sourceFileMap": { "type": "object", "description": "Optional source file mappings passed to the debug engine. Example: '{ \"C:\\foo\":\"/home/user/foo\" }'", @@ -282,6 +293,98 @@ "type": "string" }, "default": [] + }, + "pipeTransport": { + "type": "object", + "description": "When present, this tells the debugger to connect to a remote computer using another executable as a pipe that will relay standard input/output between VS Code and the .NET Core debugger backend executable (clrdbg).", + "default": { + "pipeProgram": "enter the fully qualified path for the pipe program name, for example 'c:\\tools\\plink.exe'", + "pipeArgs": [] + }, + "properties" : { + "pipeProgram": { + "type": "string", + "description": "The fully qualified pipe command to execute.", + "default": "enter the fully qualified path for the pipe program name, for example 'c:\\tools\\plink.exe'" + }, + "pipeArgs": { + "type": "array", + "description": "Command line arguments passed to the pipe program.", + "items": { + "type": "string" + }, + "default": [] + }, + "windows": { + "type": "object", + "description": "Windows-specific pipe launch configuration options", + "default": { + "pipeProgram": "enter the fully qualified path for the pipe program name, for example 'c:\\tools\\plink.exe'", + "pipeArgs": [] + }, + "properties": { + "pipeProgram": { + "type": "string", + "description": "The fully qualified pipe command to execute.", + "default": "enter the fully qualified path for the pipe program name, for example 'c:\\tools\\plink.exe'" + }, + "pipeArgs": { + "type": "array", + "description": "Command line arguments passed to the pipe program.", + "items": { + "type": "string" + }, + "default": [] + } + } + }, + "osx": { + "type": "object", + "description": "OSX-specific pipe launch configuration options", + "default": { + "pipeProgram": "enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'", + "pipeArgs": [] + }, + "properties": { + "pipeProgram": { + "type": "string", + "description": "The fully qualified pipe command to execute.", + "default": "enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'" + }, + "pipeArgs": { + "type": "array", + "description": "Command line arguments passed to the pipe program.", + "items": { + "type": "string" + }, + "default": [] + } + } + }, + "linux": { + "type": "object", + "description": "Linux-specific pipe launch configuration options", + "default": { + "pipeProgram": "enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'", + "pipeArgs": [] + }, + "properties": { + "pipeProgram": { + "type": "string", + "description": "The fully qualified pipe command to execute.", + "default": "enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'" + }, + "pipeArgs": { + "type": "array", + "description": "Command line arguments passed to the pipe program.", + "items": { + "type": "string" + }, + "default": [] + } + } + } + } } } }, @@ -330,7 +433,8 @@ "program": "${workspaceRoot}/bin/Debug//", "args": [], "cwd": "${workspaceRoot}", - "stopAtEntry": false + "stopAtEntry": false, + "externalConsole": false }, { "name": ".NET Core Launch (web)", @@ -354,6 +458,9 @@ "linux": { "command": "xdg-open" } + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" } }, { diff --git a/src/assets.ts b/src/assets.ts index b921a533ce..29172eaf18 100644 --- a/src/assets.ts +++ b/src/assets.ts @@ -24,7 +24,9 @@ interface ConsoleLaunchConfiguration extends DebugConfiguration { program: string, args: string[], cwd: string, - stopAtEntry: boolean + stopAtEntry: boolean, + env?: any, + externalConsole?: boolean } interface CommandLine { @@ -137,6 +139,7 @@ function createLaunchConfiguration(targetFramework: string, executableName: stri program: '${workspaceRoot}/bin/Debug/' + targetFramework + '/'+ executableName, args: [], cwd: '${workspaceRoot}', + externalConsole: false, stopAtEntry: false } } @@ -164,6 +167,9 @@ function createWebLaunchConfiguration(targetFramework: string, executableName: s linux: { command: 'xdg-open' } + }, + env: { + ASPNETCORE_ENVIRONMENT: "Development" } } } @@ -177,14 +183,25 @@ function createAttachConfiguration(): AttachConfiguration { } } -function createLaunchJson(targetFramework: string, executableName: string): any { - return { - version: '0.2.0', - configurations: [ - createLaunchConfiguration(targetFramework, executableName), - createWebLaunchConfiguration(targetFramework, executableName), - createAttachConfiguration() - ] +function createLaunchJson(targetFramework: string, executableName: string, isWebProject: boolean): any { + let version = '0.2.0'; + if (!isWebProject) { + return { + version: version, + configurations: [ + createLaunchConfiguration(targetFramework, executableName), + createAttachConfiguration() + ] + } + } + else { + return { + version: version, + configurations: [ + createWebLaunchConfiguration(targetFramework, executableName), + createAttachConfiguration() + ] + } } } @@ -220,7 +237,24 @@ function addTasksJsonIfNecessary(info: protocol.DotNetWorkspaceInformation, path }); } -function addLaunchJsonIfNecessary(info: protocol.DotNetWorkspaceInformation, paths: Paths, operations: Operations) { +function hasWebServerDependency(projectJsonPath: string) { + let projectJson = fs.readFileSync(projectJsonPath, 'utf8'); + let projectJsonObject = JSON.parse(projectJson); + + if (projectJsonObject == null) { + return false; + } + + for (var key in projectJsonObject.dependencies) { + if (key.toLowerCase().startsWith("microsoft.aspnetcore.server")) { + return true; + } + } + + return false; +} + +function addLaunchJsonIfNecessary(info: protocol.DotNetWorkspaceInformation, paths: Paths, operations: Operations, projectJsonPath: string) { return new Promise((resolve, reject) => { if (!operations.addLaunchJson) { return resolve(); @@ -248,7 +282,7 @@ function addLaunchJsonIfNecessary(info: protocol.DotNetWorkspaceInformation, pat } } - const launchJson = createLaunchJson(targetFramework, executableName); + const launchJson = createLaunchJson(targetFramework, executableName, hasWebServerDependency(projectJsonPath)); const launchJsonText = JSON.stringify(launchJson, null, ' '); return fs.writeFileAsync(paths.launchJsonPath, launchJsonText); @@ -284,7 +318,7 @@ export function addAssetsIfNecessary(server: OmnisharpServer) { return fs.ensureDirAsync(paths.vscodeFolder).then(() => { return Promise.all([ addTasksJsonIfNecessary(info.DotNet, paths, operations), - addLaunchJsonIfNecessary(info.DotNet, paths, operations) + addLaunchJsonIfNecessary(info.DotNet, paths, operations, projectJsonPath) ]); }); }); diff --git a/src/coreclr-debug/main.ts b/src/coreclr-debug/main.ts index 3ee83a871e..f8c77524c0 100644 --- a/src/coreclr-debug/main.ts +++ b/src/coreclr-debug/main.ts @@ -200,25 +200,29 @@ function isOnPath(command : string) : boolean { return false; } let fileName = command; - let seperatorChar = ':'; if (process.platform == 'win32') { // on Windows, add a '.exe', and the path is semi-colon seperatode fileName = fileName + ".exe"; - seperatorChar = ';'; } - - let pathSegments: string[] = pathValue.split(seperatorChar); + + let pathSegments: string[] = pathValue.split(path.delimiter); for (let segment of pathSegments) { if (segment.length === 0 || !path.isAbsolute(segment)) { continue; } - + const segmentPath = path.join(segment, fileName); - if (CoreClrDebugUtil.existsSync(segmentPath)) { - return true; + + try { + if (CoreClrDebugUtil.existsSync(segmentPath)) { + return true; + } + } catch (err) { + // any error from existsSync can be treated as the command not being on the path + continue; } } - + return false; } @@ -332,9 +336,9 @@ function createProjectJson(targetRuntime: string): any emitEntryPoint: true }, dependencies: { - "Microsoft.VisualStudio.clrdbg": "14.0.25229-preview-2963841", - "Microsoft.VisualStudio.clrdbg.MIEngine": "14.0.30401-preview-1", - "Microsoft.VisualStudio.OpenDebugAD7": "1.0.20405-preview-1", + "Microsoft.VisualStudio.clrdbg": "14.0.25406-preview-3044032", + "Microsoft.VisualStudio.clrdbg.MIEngine": "14.0.30606-preview-1", + "Microsoft.VisualStudio.OpenDebugAD7": "1.0.20527-preview-1", "NETStandard.Library": "1.5.0-rc2-24027", "Newtonsoft.Json": "7.0.1", "Microsoft.VisualStudio.Debugger.Interop.Portable": "1.0.1", diff --git a/src/coreclr-debug/util.ts b/src/coreclr-debug/util.ts index baca1c201c..e17f35af98 100644 --- a/src/coreclr-debug/util.ts +++ b/src/coreclr-debug/util.ts @@ -83,7 +83,7 @@ export default class CoreClrDebugUtil fs.accessSync(path, fs.F_OK); return true; } catch (err) { - if (err.code === 'ENOENT') { + if (err.code === 'ENOENT' || err.code === 'ENOTDIR') { return false; } else { throw Error(err.code); diff --git a/src/features/codeLensProvider.ts b/src/features/codeLensProvider.ts index 496b660d72..1c24c1e1bd 100644 --- a/src/features/codeLensProvider.ts +++ b/src/features/codeLensProvider.ts @@ -8,76 +8,79 @@ import {CancellationToken, CodeLens, Range, Uri, TextDocument, CodeLensProvider} from 'vscode'; import {toRange, toLocation} from '../typeConvertion'; import AbstractSupport from './abstractProvider'; +import {updateCodeLensForTest} from './dotnetTest'; import * as protocol from '../protocol'; import * as serverUtils from '../omnisharpUtils'; class OmniSharpCodeLens extends CodeLens { - fileName: string; + fileName: string; - constructor(fileName: string, range: Range) { - super(range); - this.fileName = fileName; - } + constructor(fileName: string, range: Range) { + super(range); + this.fileName = fileName; + } } export default class OmniSharpCodeLensProvider extends AbstractSupport implements CodeLensProvider { - private static filteredSymbolNames: { [name: string]: boolean } = { - 'Equals': true, - 'Finalize': true, - 'GetHashCode': true, - 'ToString': true - }; - - provideCodeLenses(document: TextDocument, token: CancellationToken): CodeLens[] | Thenable { - - return serverUtils.currentFileMembersAsTree(this._server, { Filename: document.fileName }, token).then(tree => { - let ret: CodeLens[] = []; - tree.TopLevelTypeDefinitions.forEach(node => OmniSharpCodeLensProvider._convertQuickFix(ret, document.fileName, node)); - return ret; - }); - } - - private static _convertQuickFix(bucket: CodeLens[], fileName:string, node: protocol.Node): void { - - if (node.Kind === 'MethodDeclaration' && OmniSharpCodeLensProvider.filteredSymbolNames[node.Location.Text]) { - return; - } - - let lens = new OmniSharpCodeLens(fileName, toRange(node.Location)); - bucket.push(lens); - - for (let child of node.ChildNodes) { - OmniSharpCodeLensProvider._convertQuickFix(bucket, fileName, child); - } - } - - resolveCodeLens(codeLens: CodeLens, token: CancellationToken): Thenable { - if (codeLens instanceof OmniSharpCodeLens) { - - let req = { - Filename: codeLens.fileName, - Line: codeLens.range.start.line + 1, - Column: codeLens.range.start.character + 1, - OnlyThisFile: false, - ExcludeDefinition: true - }; - - return serverUtils.findUsages(this._server, req, token).then(res => { - if (!res || !Array.isArray(res.QuickFixes)) { - return; - } - - let len = res.QuickFixes.length; - codeLens.command = { - title: len === 1 ? '1 reference' : `${len} references`, - command: 'editor.action.showReferences', - arguments: [Uri.file(req.Filename), codeLens.range.start, res.QuickFixes.map(toLocation)] - }; - - return codeLens; - }); - } - } + private static filteredSymbolNames: { [name: string]: boolean } = { + 'Equals': true, + 'Finalize': true, + 'GetHashCode': true, + 'ToString': true + }; + + provideCodeLenses(document: TextDocument, token: CancellationToken): CodeLens[] | Thenable { + let request = { Filename: document.fileName }; + return serverUtils.currentFileMembersAsTree(this._server, { Filename: document.fileName }, token).then(tree => { + let ret: CodeLens[] = []; + tree.TopLevelTypeDefinitions.forEach(node => this._convertQuickFix(ret, document.fileName, node)); + return ret; + }); + } + + private _convertQuickFix(bucket: CodeLens[], fileName: string, node: protocol.Node): void { + + if (node.Kind === 'MethodDeclaration' && OmniSharpCodeLensProvider.filteredSymbolNames[node.Location.Text]) { + return; + } + + let lens = new OmniSharpCodeLens(fileName, toRange(node.Location)); + bucket.push(lens); + + for (let child of node.ChildNodes) { + this._convertQuickFix(bucket, fileName, child); + } + + updateCodeLensForTest(bucket, fileName, node, this._server.isDebugEnable()); + } + + resolveCodeLens(codeLens: CodeLens, token: CancellationToken): Thenable { + if (codeLens instanceof OmniSharpCodeLens) { + + let req = { + Filename: codeLens.fileName, + Line: codeLens.range.start.line + 1, + Column: codeLens.range.start.character + 1, + OnlyThisFile: false, + ExcludeDefinition: true + }; + + return serverUtils.findUsages(this._server, req, token).then(res => { + if (!res || !Array.isArray(res.QuickFixes)) { + return; + } + + let len = res.QuickFixes.length; + codeLens.command = { + title: len === 1 ? '1 reference' : `${len} references`, + command: 'editor.action.showReferences', + arguments: [Uri.file(req.Filename), codeLens.range.start, res.QuickFixes.map(toLocation)] + }; + + return codeLens; + }); + } + } } diff --git a/src/features/commands.ts b/src/features/commands.ts index e3e71241b6..7036e9e494 100644 --- a/src/features/commands.ts +++ b/src/features/commands.ts @@ -13,134 +13,139 @@ import * as fs from 'fs-extra-promise'; import * as path from 'path'; import * as protocol from '../protocol'; import * as vscode from 'vscode'; +import * as dotnetTest from './dotnetTest' let channel = vscode.window.createOutputChannel('.NET'); export default function registerCommands(server: OmnisharpServer, extensionPath: string) { - let d1 = vscode.commands.registerCommand('o.restart', () => server.restart()); - let d2 = vscode.commands.registerCommand('o.pickProjectAndStart', () => pickProjectAndStart(server)); - let d3 = vscode.commands.registerCommand('o.showOutput', () => server.getChannel().show(vscode.ViewColumn.Three)); - let d4 = vscode.commands.registerCommand('dotnet.restore', () => dotnetRestoreAllProjects(server)); - + let d1 = vscode.commands.registerCommand('o.restart', () => server.restart()); + let d2 = vscode.commands.registerCommand('o.pickProjectAndStart', () => pickProjectAndStart(server)); + let d3 = vscode.commands.registerCommand('o.showOutput', () => server.getChannel().show(vscode.ViewColumn.Three)); + let d4 = vscode.commands.registerCommand('dotnet.restore', () => dotnetRestoreAllProjects(server)); + // register empty handler for csharp.installDebugger // running the command activates the extension, which is all we need for installation to kickoff let d5 = vscode.commands.registerCommand('csharp.downloadDebugger', () => { }); - - return vscode.Disposable.from(d1, d2, d3, d4, d5); + + // register two commands for running and debugging xunit tests + let d6 = dotnetTest.registerDotNetTestRunCommand(server); + let d7 = dotnetTest.registerDotNetTestDebugCommand(server); + + return vscode.Disposable.from(d1, d2, d3, d4, d5, d6, d7); } function pickProjectAndStart(server: OmnisharpServer) { - return findLaunchTargets().then(targets => { - - let currentPath = server.getSolutionPathOrFolder(); - if (currentPath) { - for (let target of targets) { - if (target.target.fsPath === currentPath) { - target.label = `\u2713 ${target.label}`; - } - } - } - - return vscode.window.showQuickPick(targets, { - matchOnDescription: true, - placeHolder: `Select 1 of ${targets.length} projects` - }).then(target => { - if (target) { - return server.restart(target.target.fsPath); - } - }); - }); + return findLaunchTargets().then(targets => { + + let currentPath = server.getSolutionPathOrFolder(); + if (currentPath) { + for (let target of targets) { + if (target.target.fsPath === currentPath) { + target.label = `\u2713 ${target.label}`; + } + } + } + + return vscode.window.showQuickPick(targets, { + matchOnDescription: true, + placeHolder: `Select 1 of ${targets.length} projects` + }).then(target => { + if (target) { + return server.restart(target.target.fsPath); + } + }); + }); } interface Command { - label: string; - description: string; - execute(): Thenable; + label: string; + description: string; + execute(): Thenable; } function projectsToCommands(projects: protocol.DotNetProject[]): Promise[] { - return projects.map(project => { - let projectDirectory = project.Path; - - return fs.lstatAsync(projectDirectory).then(stats => { - if (stats.isFile()) { - projectDirectory = path.dirname(projectDirectory); - } - - return { - label: `dotnet restore - (${project.Name || path.basename(project.Path)})`, - description: projectDirectory, - execute() { - return runDotnetRestore(projectDirectory); - } - }; - }); - }); + return projects.map(project => { + let projectDirectory = project.Path; + + return fs.lstatAsync(projectDirectory).then(stats => { + if (stats.isFile()) { + projectDirectory = path.dirname(projectDirectory); + } + + return { + label: `dotnet restore - (${project.Name || path.basename(project.Path)})`, + description: projectDirectory, + execute() { + return runDotnetRestore(projectDirectory); + } + }; + }); + }); } export function dotnetRestoreAllProjects(server: OmnisharpServer) { - if (!server.isRunning()) { - return Promise.reject('OmniSharp server is not running.'); - } - - return serverUtils.requestWorkspaceInformation(server).then(info => { - - if (!('DotNet in info') || info.DotNet.Projects.length < 1) { - return Promise.reject("No .NET Core projects found"); - } - - let commandPromises = projectsToCommands(info.DotNet.Projects); - - return Promise.all(commandPromises).then(commands => { - return vscode.window.showQuickPick(commands); - }).then(command => { - if (command) { - return command.execute(); - } - }); - }); + if (!server.isRunning()) { + return Promise.reject('OmniSharp server is not running.'); + } + + return serverUtils.requestWorkspaceInformation(server).then(info => { + + if (!('DotNet in info') || info.DotNet.Projects.length < 1) { + return Promise.reject("No .NET Core projects found"); + } + + let commandPromises = projectsToCommands(info.DotNet.Projects); + + return Promise.all(commandPromises).then(commands => { + return vscode.window.showQuickPick(commands); + }).then(command => { + if (command) { + return command.execute(); + } + }); + }); } export function dotnetRestoreForProject(server: OmnisharpServer, fileName: string) { - if (!server.isRunning()) { - return Promise.reject('OmniSharp server is not running.'); - } - - return serverUtils.requestWorkspaceInformation(server).then(info => { - - if (!('DotNet in info') || info.DotNet.Projects.length < 1) { - return Promise.reject("No .NET Core projects found"); - } - - let directory = path.dirname(fileName); - - for (let project of info.DotNet.Projects) { - if (project.Path === directory) { - return runDotnetRestore(directory, fileName); - } - } - }); + if (!server.isRunning()) { + return Promise.reject('OmniSharp server is not running.'); + } + + return serverUtils.requestWorkspaceInformation(server).then(info => { + + if (!('DotNet in info') || info.DotNet.Projects.length < 1) { + return Promise.reject("No .NET Core projects found"); + } + + let directory = path.dirname(fileName); + + for (let project of info.DotNet.Projects) { + if (project.Path === directory) { + return runDotnetRestore(directory, fileName); + } + } + }); } function runDotnetRestore(cwd: string, fileName?: string) { - return new Promise((resolve, reject) => { - channel.clear(); - channel.show(); - - let cmd = 'dotnet restore'; - if (fileName) { - cmd = `${cmd} "${fileName}"` - } - - return cp.exec(cmd, {cwd: cwd, env: process.env}, (err, stdout, stderr) => { - channel.append(stdout.toString()); - channel.append(stderr.toString()); - if (err) { - channel.append('ERROR: ' + err); - } - }); - }); + return new Promise((resolve, reject) => { + channel.clear(); + channel.show(); + + let cmd = 'dotnet restore'; + if (fileName) { + cmd = `${cmd} "${fileName}"` + } + + return cp.exec(cmd, { cwd: cwd, env: process.env }, (err, stdout, stderr) => { + channel.append(stdout.toString()); + channel.append(stderr.toString()); + if (err) { + channel.append('ERROR: ' + err); + } + }); + }); } \ No newline at end of file diff --git a/src/features/dotnetTest.ts b/src/features/dotnetTest.ts new file mode 100644 index 0000000000..65e2c7407e --- /dev/null +++ b/src/features/dotnetTest.ts @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import {OmnisharpServer} from '../omnisharpServer'; +import {toRange} from '../typeConvertion'; +import * as vscode from 'vscode'; +import * as serverUtils from "../omnisharpUtils"; +import * as protocol from '../protocol'; + +let _testOutputChannel: vscode.OutputChannel = undefined; + +function getTestOutputChannel(): vscode.OutputChannel { + if (_testOutputChannel == undefined) { + _testOutputChannel = vscode.window.createOutputChannel(".NET Test Log"); + } + + return _testOutputChannel; +} + +export function registerDotNetTestRunCommand(server: OmnisharpServer): vscode.Disposable { + return vscode.commands.registerCommand( + 'dotnet.test.run', + (testMethod, fileName) => runDotnetTest(testMethod, fileName, server)); +} + +export function registerDotNetTestDebugCommand(server: OmnisharpServer): vscode.Disposable { + return vscode.commands.registerCommand( + 'dotnet.test.debug', + (testMethod, fileName) => debugDotnetTest(testMethod, fileName, server)); +} + +// Run test through dotnet-test command. This function can be moved to a separate structure +export function runDotnetTest(testMethod: string, fileName: string, server: OmnisharpServer) { + getTestOutputChannel().show(); + getTestOutputChannel().appendLine('Running test ' + testMethod + '...'); + serverUtils + .runDotNetTest(server, { FileName: fileName, MethodName: testMethod }) + .then( + response => { + if (response.Pass) { + getTestOutputChannel().appendLine('Test passed \n'); + } + else { + getTestOutputChannel().appendLine('Test failed \n'); + } + }, + reason => { + vscode.window.showErrorMessage(`Failed to run test because ${reason}.`); + }); +} + +// Run test through dotnet-test command with debugger attached +export function debugDotnetTest(testMethod: string, fileName: string, server: OmnisharpServer) { + serverUtils.getTestStartInfo(server, { FileName: fileName, MethodName: testMethod }).then(response => { + vscode.commands.executeCommand( + 'vscode.startDebug', { + "name": ".NET test launch", + "type": "coreclr", + "request": "launch", + "program": response.Executable, + "args": response.Argument.split(' '), + "cwd": "${workspaceRoot}", + "stopAtEntry": false + } + ).then( + response => { }, + reason => { vscode.window.showErrorMessage(`Failed to start debugger on test because ${reason}.`) }); + }); +} + +export function updateCodeLensForTest(bucket: vscode.CodeLens[], fileName: string, node: protocol.Node, isDebugEnable: boolean) { + // backward compatible check: Features property doesn't present on older version OmniSharp + if (node.Features == undefined) { + return; + } + + let testFeature = node.Features.find(value => value.Name == 'XunitTestMethod'); + if (testFeature) { + // this test method has a test feature + + bucket.push(new vscode.CodeLens( + toRange(node.Location), + { title: "run test", command: 'dotnet.test.run', arguments: [testFeature.Data, fileName] })); + + if (isDebugEnable) { + bucket.push(new vscode.CodeLens( + toRange(node.Location), + { title: "debug test", command: 'dotnet.test.debug', arguments: [testFeature.Data, fileName] })); + } + } +} \ No newline at end of file diff --git a/src/omnisharpDownload.ts b/src/omnisharpDownload.ts index 4b42cf4a58..dbb407c08e 100644 --- a/src/omnisharpDownload.ts +++ b/src/omnisharpDownload.ts @@ -13,29 +13,30 @@ import * as tmp from 'tmp'; import {parse} from 'url'; import {SupportedPlatform, getSupportedPlatform} from './utils'; import {getProxyAgent} from './proxy'; +import {OutputChannel} from 'vscode'; -const Decompress = require('decompress'); +const decompress = require('decompress'); const BaseDownloadUrl = 'https://vscodeoscon.blob.core.windows.net/ext'; const DefaultInstallLocation = path.join(__dirname, '../.omnisharp'); -const ApiToken = '18a6f5ecea711220d4f433d4fd41062d479fda1d'; +const OmniSharpVersion = '1.9-beta4'; tmp.setGracefulCleanup(); function getOmnisharpAssetName(): string { switch (getSupportedPlatform()) { case SupportedPlatform.Windows: - return 'omnisharp-win-x64-net451.zip'; + return `omnisharp-${OmniSharpVersion}-win-x64-net451.zip`; case SupportedPlatform.OSX: - return 'omnisharp-osx-x64-netcoreapp1.0.tar.gz'; + return `omnisharp-${OmniSharpVersion}-osx-x64-netcoreapp1.0.tar.gz`; case SupportedPlatform.CentOS: - return 'omnisharp-centos-x64-netcoreapp1.0.tar.gz'; + return `omnisharp-${OmniSharpVersion}-centos-x64-netcoreapp1.0.tar.gz`; case SupportedPlatform.Debian: - return 'omnisharp-debian-x64-netcoreapp1.0.tar.gz'; + return `omnisharp-${OmniSharpVersion}-debian-x64-netcoreapp1.0.tar.gz`; case SupportedPlatform.RHEL: - return 'omnisharp-rhel-x64-netcoreapp1.0.tar.gz'; + return `omnisharp-${OmniSharpVersion}-rhel-x64-netcoreapp1.0.tar.gz`; case SupportedPlatform.Ubuntu: - return 'omnisharp-ubuntu-x64-netcoreapp1.0.tar.gz'; + return `omnisharp-${OmniSharpVersion}-ubuntu-x64-netcoreapp1.0.tar.gz`; default: if (process.platform === 'linux') { @@ -73,14 +74,14 @@ function download(urlString: string): Promise { }); } -export function downloadOmnisharp(): Promise { +export function downloadOmnisharp(output: OutputChannel): Promise { return new Promise((resolve, reject) => { - console.log(`[OmniSharp]: Installing to ${DefaultInstallLocation}`); + output.appendLine(`[INFO] Installing to ${DefaultInstallLocation}`); const assetName = getOmnisharpAssetName(); const urlString = `${BaseDownloadUrl}/${assetName}`; - console.log(`[OmniSharp] Attempting to download ${assetName}...`); + output.appendLine(`[INFO] Attempting to download ${assetName}...`); return download(urlString) .then(inStream => { @@ -89,7 +90,7 @@ export function downloadOmnisharp(): Promise { return reject(err); } - console.log(`[OmniSharp] Downloading to ${tmpPath}...`); + output.appendLine(`[INFO] Downloading to ${tmpPath}...`); const outStream = fs.createWriteStream(null, { fd: fd }); @@ -99,30 +100,18 @@ export function downloadOmnisharp(): Promise { outStream.once('finish', () => { // At this point, the asset has finished downloading. - console.log(`[OmniSharp] Download complete!`); + output.appendLine(`[INFO] Download complete!`); + output.appendLine(`[INFO] Decompressing...`); - let decompress = new Decompress() - .src(tmpPath) - .dest(DefaultInstallLocation); - - if (path.extname(assetName).toLowerCase() === '.zip') { - decompress = decompress.use(Decompress.zip()); - console.log(`[OmniSharp] Unzipping...`); - } - else { - decompress = decompress.use(Decompress.targz()); - console.log(`[OmniSharp] Untaring...`); - } - - decompress.run((err, files) => { - if (err) { + return decompress(tmpPath, DefaultInstallLocation) + .then(files => { + output.appendLine(`[INFO] Done! ${files.length} files unpacked.`) + return resolve(true); + }) + .error(err => { + output.appendLine(`[ERROR] ${err}`); return reject(err); - } - - console.log(`[OmniSharp] Done! ${files.length} files unpacked.`) - - return resolve(true); - }); + }); }); inStream.pipe(outStream); diff --git a/src/omnisharpServer.ts b/src/omnisharpServer.ts index 96d6c946e1..33bf072240 100644 --- a/src/omnisharpServer.ts +++ b/src/omnisharpServer.ts @@ -14,6 +14,7 @@ import {Disposable, CancellationToken, OutputChannel, workspace, window} from 'v import {ErrorMessage, UnresolvedDependenciesMessage, MSBuildProjectDiagnostics, ProjectInformationResponse} from './protocol'; import getLaunchTargets, {LaunchTarget} from './launchTargetFinder'; import TelemetryReporter from 'vscode-extension-telemetry'; +import * as vscode from 'vscode' enum ServerState { Starting, @@ -31,29 +32,29 @@ interface Request { module Events { export const StateChanged = 'stateChanged'; - + export const StdOut = 'stdout'; export const StdErr = 'stderr'; - + export const Error = 'Error'; export const ServerError = 'ServerError'; - + export const UnresolvedDependencies = 'UnresolvedDependencies'; export const PackageRestoreStarted = 'PackageRestoreStarted'; export const PackageRestoreFinished = 'PackageRestoreFinished'; - + export const ProjectChanged = 'ProjectChanged'; export const ProjectAdded = 'ProjectAdded'; export const ProjectRemoved = 'ProjectRemoved'; - + export const MsBuildProjectDiagnostics = 'MsBuildProjectDiagnostics'; - + export const BeforeServerStart = 'BeforeServerStart'; export const ServerStart = 'ServerStart'; export const ServerStop = 'ServerStop'; - + export const MultipleLaunchTargets = 'server:MultipleLaunchTargets'; - + export const Started = 'started'; } @@ -65,7 +66,7 @@ class Delays { idleDelays: number = 0; // 501-1500 milliseconds nonFocusDelays: number = 0; // 1501-3000 milliseconds bigDelays: number = 0; // 3000+ milliseconds - + public report(elapsedTime: number) { if (elapsedTime <= 25) { this.immediateDelays += 1; @@ -89,8 +90,8 @@ class Delays { this.bigDelays += 1; } } - - public toMeasures(): {[key: string]: number} { + + public toMeasures(): { [key: string]: number } { return { immedateDelays: this.immediateDelays, nearImmediateDelays: this.nearImmediateDelays, @@ -115,6 +116,8 @@ export abstract class OmnisharpServer { private _isProcessingQueue = false; private _channel: OutputChannel; + private _isDebugEnable: boolean = false; + protected _serverProcess: ChildProcess; protected _extraArgv: string[]; @@ -132,28 +135,28 @@ export abstract class OmnisharpServer { return this._state; } - private _setState(value: ServerState) : void { + private _setState(value: ServerState): void { if (typeof value !== 'undefined' && value !== this._state) { this._state = value; this._fireEvent(Events.StateChanged, this._state); } } - + private _recordRequestDelay(requestName: string, elapsedTime: number) { let delays = this._requestDelays[requestName]; if (!delays) { delays = new Delays(); this._requestDelays[requestName] = delays; } - + delays.report(elapsedTime); } - + public reportAndClearTelemetry() { for (var path in this._requestDelays) { const eventName = 'omnisharp' + path; const measures = this._requestDelays[path].toMeasures(); - + this._reporter.sendTelemetryEvent(eventName, null, measures); } @@ -168,6 +171,10 @@ export abstract class OmnisharpServer { return this._channel; } + public isDebugEnable(): boolean { + return this._isDebugEnable; + } + // --- eventing public onStdout(listener: (e: string) => any, thisArg?: any) { @@ -186,7 +193,7 @@ export abstract class OmnisharpServer { return this._addListener(Events.ServerError, listener, thisArg); } - public onUnresolvedDependencies(listener: (e: UnresolvedDependenciesMessage) => any, thisArg?:any) { + public onUnresolvedDependencies(listener: (e: UnresolvedDependenciesMessage) => any, thisArg?: any) { return this._addListener(Events.UnresolvedDependencies, listener, thisArg); } @@ -214,7 +221,7 @@ export abstract class OmnisharpServer { return this._addListener(Events.MsBuildProjectDiagnostics, listener, thisArg); } - public onBeforeServerStart(listener: (e:string) => any) { + public onBeforeServerStart(listener: (e: string) => any) { return this._addListener(Events.BeforeServerStart, listener); } @@ -271,6 +278,13 @@ export abstract class OmnisharpServer { this._setState(ServerState.Started); this._fireEvent(Events.ServerStart, solutionPath); return this._doConnect(); + }).then(_ => { + return vscode.commands.getCommands() + .then(commands => { + if (commands.find(c => c == "vscode.startDebug")) { + this._isDebugEnable = true; + } + }); }).then(_ => { this._processQueue(); }, err => { @@ -300,7 +314,7 @@ export abstract class OmnisharpServer { return reject(err); } }); - + killer.on('exit', resolve); killer.on('error', reject); }); @@ -309,7 +323,7 @@ export abstract class OmnisharpServer { this._serverProcess.kill('SIGTERM'); ret = Promise.resolve(undefined); } - + return ret.then(_ => { this._start = null; this._serverProcess = null; @@ -327,7 +341,7 @@ export abstract class OmnisharpServer { } } - public autoStart(preferredPath:string): Thenable { + public autoStart(preferredPath: string): Thenable { return getLaunchTargets().then(targets => { if (targets.length === 0) { return new Promise((resolve, reject) => { @@ -368,13 +382,13 @@ export abstract class OmnisharpServer { if (this._getState() !== ServerState.Started) { return Promise.reject('server has been stopped or not started'); } - + let startTime: number; let request: Request; - + let promise = new Promise((resolve, reject) => { startTime = Date.now(); - + request = { path, data, @@ -382,9 +396,9 @@ export abstract class OmnisharpServer { onError: err => reject(err), _enqueued: Date.now() }; - + this._queue.push(request); - + if (this._getState() === ServerState.Started && !this._isProcessingQueue) { this._processQueue(); } @@ -406,7 +420,7 @@ export abstract class OmnisharpServer { let endTime = Date.now(); let elapsedTime = endTime - startTime; this._recordRequestDelay(path, elapsedTime); - + return response; }); } @@ -504,19 +518,17 @@ export class StdioOmnisharpServer extends OmnisharpServer { // timeout logic const handle = setTimeout(() => { - if (listener) - { + if (listener) { listener.dispose(); } - + reject(new Error('Failed to start OmniSharp')); }, StdioOmnisharpServer.StartupTimeout); // handle started-event listener = this.onOmnisharpStart(() => { - if (listener) - { - listener.dispose(); + if (listener) { + listener.dispose(); } clearTimeout(handle); resolve(this); @@ -552,17 +564,17 @@ export class StdioOmnisharpServer extends OmnisharpServer { switch (packet.Type) { case 'response': - this._handleResponsePacket( packet); + this._handleResponsePacket(packet); break; case 'event': - this._handleEventPacket( packet); + this._handleEventPacket(packet); break; default: console.warn('unknown packet: ', packet); break; } }; - + this._rl.addListener('line', onLineReceived); this._callOnStop.push(() => this._rl.removeListener('line', onLineReceived)); } @@ -590,7 +602,7 @@ export class StdioOmnisharpServer extends OmnisharpServer { if (packet.Event === 'log') { // handle log events - const entry = <{ LogLevel: string; Name: string; Message: string; }> packet.Body; + const entry = <{ LogLevel: string; Name: string; Message: string; }>packet.Body; this._fireEvent(Events.StdOut, `[${entry.LogLevel}:${entry.Name}] ${entry.Message}\n`); return; } else { diff --git a/src/omnisharpServerLauncher.ts b/src/omnisharpServerLauncher.ts index 561c7d805f..b4d0bb87d2 100644 --- a/src/omnisharpServerLauncher.ts +++ b/src/omnisharpServerLauncher.ts @@ -37,7 +37,7 @@ export function installOmnisharpIfNeeded(output: OutputChannel): Promise throw err; } - return downloadOmnisharp().then(_ => { + return downloadOmnisharp(output).then(_ => { return getOmnisharpLaunchFilePath(); }) }); diff --git a/src/omnisharpUtils.ts b/src/omnisharpUtils.ts index 409a51e239..d1b1e88227 100644 --- a/src/omnisharpUtils.ts +++ b/src/omnisharpUtils.ts @@ -73,3 +73,10 @@ export function updateBuffer(server: OmnisharpServer, request: protocol.UpdateBu return server.makeRequest(protocol.Requests.UpdateBuffer, request); } +export function getTestStartInfo(server: OmnisharpServer, request: protocol.V2.GetTestStartInfoRequest) { + return server.makeRequest(protocol.V2.Requests.GetTestStartInfo, request); +} + +export function runDotNetTest(server: OmnisharpServer, request: protocol.V2.RunDotNetTestRequest) { + return server.makeRequest(protocol.V2.Requests.RunDotNetTest, request); +} \ No newline at end of file diff --git a/src/protocol.ts b/src/protocol.ts index 959d05a17d..76bb72bd18 100644 --- a/src/protocol.ts +++ b/src/protocol.ts @@ -23,16 +23,16 @@ export module Requests { export const RemoveFromProject = '/removefromproject'; export const Rename = '/rename'; export const RunCodeAction = '/runcodeaction'; - export const SignatureHelp = '/signatureHelp'; + export const SignatureHelp = '/signatureHelp'; export const TypeLookup = '/typelookup'; export const UpdateBuffer = '/updatebuffer'; } export interface Request { - Filename: string; - Line?: number; - Column?: number; - Buffer?: string; + Filename: string; + Line?: number; + Column?: number; + Buffer?: string; Changes?: LinePositionSpanTextChange[]; } @@ -49,394 +49,423 @@ export interface UpdateBufferRequest extends Request { } export interface ChangeBufferRequest { - FileName: string; - StartLine: number; - StartColumn: number; - EndLine: number; - EndColumn: number; - NewText: string; + FileName: string; + StartLine: number; + StartColumn: number; + EndLine: number; + EndColumn: number; + NewText: string; } export interface AddToProjectRequest extends Request { - //? + //? } export interface RemoveFromProjectRequest extends Request { - //? + //? } export interface FindUsagesRequest extends Request { - // MaxWidth: number; ? - OnlyThisFile: boolean; - ExcludeDefinition: boolean; + // MaxWidth: number; ? + OnlyThisFile: boolean; + ExcludeDefinition: boolean; } export interface FindSymbolsRequest extends Request { - Filter: string; + Filter: string; } export interface FormatRequest extends Request { - ExpandTab: boolean; + ExpandTab: boolean; } export interface CodeActionRequest extends Request { - CodeAction: number; - WantsTextChanges?: boolean; - SelectionStartColumn?: number; - SelectionStartLine?: number; - SelectionEndColumn?: number; - SelectionEndLine?: number; + CodeAction: number; + WantsTextChanges?: boolean; + SelectionStartColumn?: number; + SelectionStartLine?: number; + SelectionEndColumn?: number; + SelectionEndLine?: number; } export interface FormatResponse { - Buffer: string; + Buffer: string; } export interface TextChange { - NewText: string; - StartLine: number; - StartColumn: number; - EndLine: number; - EndColumn: number; + NewText: string; + StartLine: number; + StartColumn: number; + EndLine: number; + EndColumn: number; } export interface FormatAfterKeystrokeRequest extends Request { - Character: string; + Character: string; } export interface FormatRangeRequest extends Request { - EndLine: number; - EndColumn: number; + EndLine: number; + EndColumn: number; } export interface FormatRangeResponse { - Changes: TextChange[]; + Changes: TextChange[]; } export interface ResourceLocation { - FileName: string; - Line: number; - Column: number; + FileName: string; + Line: number; + Column: number; } export interface Error { - Message: string; - Line: number; - Column: number; - EndLine: number; - EndColumn: number; - FileName: string; + Message: string; + Line: number; + Column: number; + EndLine: number; + EndColumn: number; + FileName: string; } export interface ErrorResponse { - Errors: Error[]; + Errors: Error[]; } export interface QuickFix { - LogLevel: string; - FileName: string; - Line: number; - Column: number; - EndLine: number; - EndColumn: number; - Text: string; - Projects: string[]; + LogLevel: string; + FileName: string; + Line: number; + Column: number; + EndLine: number; + EndColumn: number; + Text: string; + Projects: string[]; } export interface SymbolLocation extends QuickFix { - Kind: string; + Kind: string; } export interface QuickFixResponse { - QuickFixes: QuickFix[]; + QuickFixes: QuickFix[]; } export interface FindSymbolsResponse { - QuickFixes: SymbolLocation[]; + QuickFixes: SymbolLocation[]; } export interface TypeLookupRequest extends Request { - IncludeDocumentation: boolean; + IncludeDocumentation: boolean; } export interface TypeLookupResponse { - Type: string; - Documentation: string; + Type: string; + Documentation: string; } export interface RunCodeActionResponse { - Text: string; - Changes: TextChange[]; + Text: string; + Changes: TextChange[]; } export interface GetCodeActionsResponse { - CodeActions: string[]; + CodeActions: string[]; +} + +export interface SyntaxFeature { + Name: string; + Data: string; } export interface Node { - ChildNodes: Node[]; - Location: QuickFix; - Kind: string; + ChildNodes: Node[]; + Location: QuickFix; + Kind: string; + Features: SyntaxFeature[]; } export interface CurrentFileMembersAsTreeResponse { - TopLevelTypeDefinitions: Node[]; + TopLevelTypeDefinitions: Node[]; } export interface AutoCompleteRequest extends Request { - WordToComplete: string; - WantDocumentationForEveryCompletionResult?: boolean; - WantImportableTypes?: boolean; - WantMethodHeader?: boolean; - WantSnippet?: boolean; - WantReturnType?: boolean; - WantKind?: boolean; + WordToComplete: string; + WantDocumentationForEveryCompletionResult?: boolean; + WantImportableTypes?: boolean; + WantMethodHeader?: boolean; + WantSnippet?: boolean; + WantReturnType?: boolean; + WantKind?: boolean; } export interface AutoCompleteResponse { - CompletionText: string; - Description: string; - DisplayText: string; - RequiredNamespaceImport: string; - MethodHeader: string; - ReturnType: string; - Snippet: string; - Kind: string; + CompletionText: string; + Description: string; + DisplayText: string; + RequiredNamespaceImport: string; + MethodHeader: string; + ReturnType: string; + Snippet: string; + Kind: string; } export interface ProjectInformationResponse { - MsBuildProject: MSBuildProject; - DnxProject: DnxProject; + MsBuildProject: MSBuildProject; + DnxProject: DnxProject; } export interface WorkspaceInformationResponse { - MsBuild: MsBuildWorkspaceInformation; - Dnx: DnxWorkspaceInformation; + MsBuild: MsBuildWorkspaceInformation; + Dnx: DnxWorkspaceInformation; DotNet: DotNetWorkspaceInformation; - ScriptCs: ScriptCsContext; + ScriptCs: ScriptCsContext; } export interface MsBuildWorkspaceInformation { - SolutionPath: string; - Projects: MSBuildProject[]; + SolutionPath: string; + Projects: MSBuildProject[]; } export interface ScriptCsContext { - CsxFiles: { [n: string]: string }; - References: { [n: string]: string }; - Usings: { [n: string]: string }; - ScriptPacks: { [n: string]: string }; - Path: string; + CsxFiles: { [n: string]: string }; + References: { [n: string]: string }; + Usings: { [n: string]: string }; + ScriptPacks: { [n: string]: string }; + Path: string; } export interface MSBuildProject { - ProjectGuid: string; - Path: string; - AssemblyName: string; - TargetPath: string; - TargetFramework: string; - SourceFiles: string[]; + ProjectGuid: string; + Path: string; + AssemblyName: string; + TargetPath: string; + TargetFramework: string; + SourceFiles: string[]; } export interface DnxWorkspaceInformation { - RuntimePath: string; - DesignTimeHostPort: number; - Projects: DnxProject[]; + RuntimePath: string; + DesignTimeHostPort: number; + Projects: DnxProject[]; } export interface DnxProject { - Path: string; - Name: string; - Commands: { [name: string]: string; }; - Configurations: string[]; - ProjectSearchPaths: string[]; - Frameworks: DnxFramework[]; - GlobalJsonPath: string; - SourceFiles: string[]; + Path: string; + Name: string; + Commands: { [name: string]: string; }; + Configurations: string[]; + ProjectSearchPaths: string[]; + Frameworks: DnxFramework[]; + GlobalJsonPath: string; + SourceFiles: string[]; } export interface DnxFramework { - Name: string; - FriendlyName: string; - ShortName: string; + Name: string; + FriendlyName: string; + ShortName: string; } export interface DotNetWorkspaceInformation { - Projects: DotNetProject[]; - RuntimePath: string; + Projects: DotNetProject[]; + RuntimePath: string; } export interface DotNetProject { - Path: string; - Name: string; - ProjectSearchPaths: string[]; - Configurations: DotNetConfiguration[]; - Frameworks: DotNetFramework[]; - SourceFiles: string[]; + Path: string; + Name: string; + ProjectSearchPaths: string[]; + Configurations: DotNetConfiguration[]; + Frameworks: DotNetFramework[]; + SourceFiles: string[]; } export interface DotNetConfiguration { - Name: string; - CompilationOutputPath: string; - CompilationOutputAssemblyFile: string; - CompilationOutputPdbFile: string; - EmitEntryPoint?: boolean; + Name: string; + CompilationOutputPath: string; + CompilationOutputAssemblyFile: string; + CompilationOutputPdbFile: string; + EmitEntryPoint?: boolean; } export interface DotNetFramework { - Name: string; - FriendlyName: string; - ShortName: string; + Name: string; + FriendlyName: string; + ShortName: string; } export interface RenameRequest extends Request { - RenameTo: string; - WantsTextChanges?: boolean; + RenameTo: string; + WantsTextChanges?: boolean; } export interface ModifiedFileResponse { - FileName: string; - Buffer: string; - Changes: TextChange[]; + FileName: string; + Buffer: string; + Changes: TextChange[]; } export interface RenameResponse { - Changes: ModifiedFileResponse[]; + Changes: ModifiedFileResponse[]; } export interface SignatureHelp { - Signatures: SignatureHelpItem[]; - ActiveSignature: number; - ActiveParameter: number; + Signatures: SignatureHelpItem[]; + ActiveSignature: number; + ActiveParameter: number; } export interface SignatureHelpItem { - Name: string; - Label: string; - Documentation: string; - Parameters: SignatureHelpParameter[]; + Name: string; + Label: string; + Documentation: string; + Parameters: SignatureHelpParameter[]; } export interface SignatureHelpParameter { - Name: string; - Label: string; - Documentation: string; + Name: string; + Label: string; + Documentation: string; } export interface MSBuildProjectDiagnostics { - FileName: string; - Warnings: MSBuildDiagnosticsMessage[]; - Errors: MSBuildDiagnosticsMessage[]; + FileName: string; + Warnings: MSBuildDiagnosticsMessage[]; + Errors: MSBuildDiagnosticsMessage[]; } export interface MSBuildDiagnosticsMessage { - LogLevel: string; - FileName: string; - Text: string; - StartLine: number; - StartColumn: number; - EndLine: number; - EndColumn: number; + LogLevel: string; + FileName: string; + Text: string; + StartLine: number; + StartColumn: number; + EndLine: number; + EndColumn: number; } export interface ErrorMessage { - Text: string; - FileName: string; - Line: number; - Column: number; + Text: string; + FileName: string; + Line: number; + Column: number; } export interface PackageRestoreMessage { - FileName: string; - Succeeded: boolean; + FileName: string; + Succeeded: boolean; } export interface UnresolvedDependenciesMessage { - FileName: string; - UnresolvedDependencies: PackageDependency[]; + FileName: string; + UnresolvedDependencies: PackageDependency[]; } export interface PackageDependency { - Name: string; - Version: string; + Name: string; + Version: string; } export namespace V2 { - + export module Requests { export const GetCodeActions = '/v2/getcodeactions'; export const RunCodeAction = '/v2/runcodeaction'; + export const GetTestStartInfo = '/v2/getteststartinfo'; + export const RunDotNetTest = '/v2/runtest'; + } + + export interface Point { + Line: number; + Column: number; + } + + export interface Range { + Start: Point; + End: Point; + } + + export interface GetCodeActionsRequest extends Request { + Selection: Range; } - export interface Point { - Line: number; - Column: number; - } - - export interface Range { - Start: Point; - End: Point; - } - - export interface GetCodeActionsRequest extends Request { - Selection: Range; - } - - export interface OmniSharpCodeAction { - Identifier: string; - Name: string; - } - - export interface GetCodeActionsResponse { - CodeActions: OmniSharpCodeAction[]; - } - - export interface RunCodeActionRequest extends Request { - Identifier: string; - Selection: Range; - WantsTextChanges: boolean; - } - - export interface RunCodeActionResponse { - Changes: ModifiedFileResponse[]; - } - - - export interface MSBuildProjectDiagnostics { - FileName: string; - Warnings: MSBuildDiagnosticsMessage[]; - Errors: MSBuildDiagnosticsMessage[]; - } - - export interface MSBuildDiagnosticsMessage { - LogLevel: string; - FileName: string; - Text: string; - StartLine: number; - StartColumn: number; - EndLine: number; - EndColumn: number; - } - - export interface ErrorMessage { - Text: string; - FileName: string; - Line: number; - Column: number; - } - - export interface PackageRestoreMessage { - FileName: string; - Succeeded: boolean; - } - - export interface UnresolvedDependenciesMessage { - FileName: string; - UnresolvedDependencies: PackageDependency[]; - } - - export interface PackageDependency { - Name: string; - Version: string; - } + export interface OmniSharpCodeAction { + Identifier: string; + Name: string; + } + + export interface GetCodeActionsResponse { + CodeActions: OmniSharpCodeAction[]; + } + + export interface RunCodeActionRequest extends Request { + Identifier: string; + Selection: Range; + WantsTextChanges: boolean; + } + + export interface RunCodeActionResponse { + Changes: ModifiedFileResponse[]; + } + + + export interface MSBuildProjectDiagnostics { + FileName: string; + Warnings: MSBuildDiagnosticsMessage[]; + Errors: MSBuildDiagnosticsMessage[]; + } + + export interface MSBuildDiagnosticsMessage { + LogLevel: string; + FileName: string; + Text: string; + StartLine: number; + StartColumn: number; + EndLine: number; + EndColumn: number; + } + + export interface ErrorMessage { + Text: string; + FileName: string; + Line: number; + Column: number; + } + + export interface PackageRestoreMessage { + FileName: string; + Succeeded: boolean; + } + + export interface UnresolvedDependenciesMessage { + FileName: string; + UnresolvedDependencies: PackageDependency[]; + } + + export interface PackageDependency { + Name: string; + Version: string; + } + + // dotnet-test endpoints + export interface GetTestStartInfoRequest { + FileName: string; + MethodName: string; + } + + export interface GetTestStartInfoResponse { + Executable: string; + Argument: string; + } + + export interface RunDotNetTestRequest { + FileName: string; + MethodName: string; + } + + export interface RunDotNetTestResponse { + Failure: string; + Pass: boolean; + } } \ No newline at end of file