Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discover and execute (both run and debug) xunit test in VS Code #382

Merged
merged 10 commits into from
May 31, 2016
127 changes: 65 additions & 62 deletions src/features/codeLensProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<CodeLens[]> {

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<CodeLens> {
if (codeLens instanceof OmniSharpCodeLens) {

let req = <protocol.FindUsagesRequest>{
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<CodeLens[]> {
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<CodeLens> {
if (codeLens instanceof OmniSharpCodeLens) {

let req = <protocol.FindUsagesRequest>{
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;
});
}
}
}
209 changes: 107 additions & 102 deletions src/features/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<any>;
label: string;
description: string;
execute(): Thenable<any>;
}

function projectsToCommands(projects: protocol.DotNetProject[]): Promise<Command>[] {
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<cp.ChildProcess>((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<cp.ChildProcess>((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);
}
});
});
}
Loading