Skip to content

Commit 969188d

Browse files
Merge pull request #382 from troydai/troy-test
Discover and execute (both run and debug) xunit test in VS Code
2 parents 7de8408 + 62b9fe2 commit 969188d

File tree

6 files changed

+590
-437
lines changed

6 files changed

+590
-437
lines changed

src/features/codeLensProvider.ts

Lines changed: 65 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -8,76 +8,79 @@
88
import {CancellationToken, CodeLens, Range, Uri, TextDocument, CodeLensProvider} from 'vscode';
99
import {toRange, toLocation} from '../typeConvertion';
1010
import AbstractSupport from './abstractProvider';
11+
import {updateCodeLensForTest} from './dotnetTest';
1112
import * as protocol from '../protocol';
1213
import * as serverUtils from '../omnisharpUtils';
1314

1415
class OmniSharpCodeLens extends CodeLens {
1516

16-
fileName: string;
17+
fileName: string;
1718

18-
constructor(fileName: string, range: Range) {
19-
super(range);
20-
this.fileName = fileName;
21-
}
19+
constructor(fileName: string, range: Range) {
20+
super(range);
21+
this.fileName = fileName;
22+
}
2223
}
2324

2425
export default class OmniSharpCodeLensProvider extends AbstractSupport implements CodeLensProvider {
2526

26-
private static filteredSymbolNames: { [name: string]: boolean } = {
27-
'Equals': true,
28-
'Finalize': true,
29-
'GetHashCode': true,
30-
'ToString': true
31-
};
32-
33-
provideCodeLenses(document: TextDocument, token: CancellationToken): CodeLens[] | Thenable<CodeLens[]> {
34-
35-
return serverUtils.currentFileMembersAsTree(this._server, { Filename: document.fileName }, token).then(tree => {
36-
let ret: CodeLens[] = [];
37-
tree.TopLevelTypeDefinitions.forEach(node => OmniSharpCodeLensProvider._convertQuickFix(ret, document.fileName, node));
38-
return ret;
39-
});
40-
}
41-
42-
private static _convertQuickFix(bucket: CodeLens[], fileName:string, node: protocol.Node): void {
43-
44-
if (node.Kind === 'MethodDeclaration' && OmniSharpCodeLensProvider.filteredSymbolNames[node.Location.Text]) {
45-
return;
46-
}
47-
48-
let lens = new OmniSharpCodeLens(fileName, toRange(node.Location));
49-
bucket.push(lens);
50-
51-
for (let child of node.ChildNodes) {
52-
OmniSharpCodeLensProvider._convertQuickFix(bucket, fileName, child);
53-
}
54-
}
55-
56-
resolveCodeLens(codeLens: CodeLens, token: CancellationToken): Thenable<CodeLens> {
57-
if (codeLens instanceof OmniSharpCodeLens) {
58-
59-
let req = <protocol.FindUsagesRequest>{
60-
Filename: codeLens.fileName,
61-
Line: codeLens.range.start.line + 1,
62-
Column: codeLens.range.start.character + 1,
63-
OnlyThisFile: false,
64-
ExcludeDefinition: true
65-
};
66-
67-
return serverUtils.findUsages(this._server, req, token).then(res => {
68-
if (!res || !Array.isArray(res.QuickFixes)) {
69-
return;
70-
}
71-
72-
let len = res.QuickFixes.length;
73-
codeLens.command = {
74-
title: len === 1 ? '1 reference' : `${len} references`,
75-
command: 'editor.action.showReferences',
76-
arguments: [Uri.file(req.Filename), codeLens.range.start, res.QuickFixes.map(toLocation)]
77-
};
78-
79-
return codeLens;
80-
});
81-
}
82-
}
27+
private static filteredSymbolNames: { [name: string]: boolean } = {
28+
'Equals': true,
29+
'Finalize': true,
30+
'GetHashCode': true,
31+
'ToString': true
32+
};
33+
34+
provideCodeLenses(document: TextDocument, token: CancellationToken): CodeLens[] | Thenable<CodeLens[]> {
35+
let request = { Filename: document.fileName };
36+
return serverUtils.currentFileMembersAsTree(this._server, { Filename: document.fileName }, token).then(tree => {
37+
let ret: CodeLens[] = [];
38+
tree.TopLevelTypeDefinitions.forEach(node => this._convertQuickFix(ret, document.fileName, node));
39+
return ret;
40+
});
41+
}
42+
43+
private _convertQuickFix(bucket: CodeLens[], fileName: string, node: protocol.Node): void {
44+
45+
if (node.Kind === 'MethodDeclaration' && OmniSharpCodeLensProvider.filteredSymbolNames[node.Location.Text]) {
46+
return;
47+
}
48+
49+
let lens = new OmniSharpCodeLens(fileName, toRange(node.Location));
50+
bucket.push(lens);
51+
52+
for (let child of node.ChildNodes) {
53+
this._convertQuickFix(bucket, fileName, child);
54+
}
55+
56+
updateCodeLensForTest(bucket, fileName, node, this._server.isDebugEnable());
57+
}
58+
59+
resolveCodeLens(codeLens: CodeLens, token: CancellationToken): Thenable<CodeLens> {
60+
if (codeLens instanceof OmniSharpCodeLens) {
61+
62+
let req = <protocol.FindUsagesRequest>{
63+
Filename: codeLens.fileName,
64+
Line: codeLens.range.start.line + 1,
65+
Column: codeLens.range.start.character + 1,
66+
OnlyThisFile: false,
67+
ExcludeDefinition: true
68+
};
69+
70+
return serverUtils.findUsages(this._server, req, token).then(res => {
71+
if (!res || !Array.isArray(res.QuickFixes)) {
72+
return;
73+
}
74+
75+
let len = res.QuickFixes.length;
76+
codeLens.command = {
77+
title: len === 1 ? '1 reference' : `${len} references`,
78+
command: 'editor.action.showReferences',
79+
arguments: [Uri.file(req.Filename), codeLens.range.start, res.QuickFixes.map(toLocation)]
80+
};
81+
82+
return codeLens;
83+
});
84+
}
85+
}
8386
}

src/features/commands.ts

Lines changed: 107 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -13,134 +13,139 @@ import * as fs from 'fs-extra-promise';
1313
import * as path from 'path';
1414
import * as protocol from '../protocol';
1515
import * as vscode from 'vscode';
16+
import * as dotnetTest from './dotnetTest'
1617

1718
let channel = vscode.window.createOutputChannel('.NET');
1819

1920
export default function registerCommands(server: OmnisharpServer, extensionPath: string) {
20-
let d1 = vscode.commands.registerCommand('o.restart', () => server.restart());
21-
let d2 = vscode.commands.registerCommand('o.pickProjectAndStart', () => pickProjectAndStart(server));
22-
let d3 = vscode.commands.registerCommand('o.showOutput', () => server.getChannel().show(vscode.ViewColumn.Three));
23-
let d4 = vscode.commands.registerCommand('dotnet.restore', () => dotnetRestoreAllProjects(server));
24-
21+
let d1 = vscode.commands.registerCommand('o.restart', () => server.restart());
22+
let d2 = vscode.commands.registerCommand('o.pickProjectAndStart', () => pickProjectAndStart(server));
23+
let d3 = vscode.commands.registerCommand('o.showOutput', () => server.getChannel().show(vscode.ViewColumn.Three));
24+
let d4 = vscode.commands.registerCommand('dotnet.restore', () => dotnetRestoreAllProjects(server));
25+
2526
// register empty handler for csharp.installDebugger
2627
// running the command activates the extension, which is all we need for installation to kickoff
2728
let d5 = vscode.commands.registerCommand('csharp.downloadDebugger', () => { });
28-
29-
return vscode.Disposable.from(d1, d2, d3, d4, d5);
29+
30+
// register two commands for running and debugging xunit tests
31+
let d6 = dotnetTest.registerDotNetTestRunCommand(server);
32+
let d7 = dotnetTest.registerDotNetTestDebugCommand(server);
33+
34+
return vscode.Disposable.from(d1, d2, d3, d4, d5, d6, d7);
3035
}
3136

3237
function pickProjectAndStart(server: OmnisharpServer) {
3338

34-
return findLaunchTargets().then(targets => {
35-
36-
let currentPath = server.getSolutionPathOrFolder();
37-
if (currentPath) {
38-
for (let target of targets) {
39-
if (target.target.fsPath === currentPath) {
40-
target.label = `\u2713 ${target.label}`;
41-
}
42-
}
43-
}
44-
45-
return vscode.window.showQuickPick(targets, {
46-
matchOnDescription: true,
47-
placeHolder: `Select 1 of ${targets.length} projects`
48-
}).then(target => {
49-
if (target) {
50-
return server.restart(target.target.fsPath);
51-
}
52-
});
53-
});
39+
return findLaunchTargets().then(targets => {
40+
41+
let currentPath = server.getSolutionPathOrFolder();
42+
if (currentPath) {
43+
for (let target of targets) {
44+
if (target.target.fsPath === currentPath) {
45+
target.label = `\u2713 ${target.label}`;
46+
}
47+
}
48+
}
49+
50+
return vscode.window.showQuickPick(targets, {
51+
matchOnDescription: true,
52+
placeHolder: `Select 1 of ${targets.length} projects`
53+
}).then(target => {
54+
if (target) {
55+
return server.restart(target.target.fsPath);
56+
}
57+
});
58+
});
5459
}
5560

5661
interface Command {
57-
label: string;
58-
description: string;
59-
execute(): Thenable<any>;
62+
label: string;
63+
description: string;
64+
execute(): Thenable<any>;
6065
}
6166

6267
function projectsToCommands(projects: protocol.DotNetProject[]): Promise<Command>[] {
63-
return projects.map(project => {
64-
let projectDirectory = project.Path;
65-
66-
return fs.lstatAsync(projectDirectory).then(stats => {
67-
if (stats.isFile()) {
68-
projectDirectory = path.dirname(projectDirectory);
69-
}
70-
71-
return {
72-
label: `dotnet restore - (${project.Name || path.basename(project.Path)})`,
73-
description: projectDirectory,
74-
execute() {
75-
return runDotnetRestore(projectDirectory);
76-
}
77-
};
78-
});
79-
});
68+
return projects.map(project => {
69+
let projectDirectory = project.Path;
70+
71+
return fs.lstatAsync(projectDirectory).then(stats => {
72+
if (stats.isFile()) {
73+
projectDirectory = path.dirname(projectDirectory);
74+
}
75+
76+
return {
77+
label: `dotnet restore - (${project.Name || path.basename(project.Path)})`,
78+
description: projectDirectory,
79+
execute() {
80+
return runDotnetRestore(projectDirectory);
81+
}
82+
};
83+
});
84+
});
8085
}
8186

8287
export function dotnetRestoreAllProjects(server: OmnisharpServer) {
8388

84-
if (!server.isRunning()) {
85-
return Promise.reject('OmniSharp server is not running.');
86-
}
87-
88-
return serverUtils.requestWorkspaceInformation(server).then(info => {
89-
90-
if (!('DotNet in info') || info.DotNet.Projects.length < 1) {
91-
return Promise.reject("No .NET Core projects found");
92-
}
93-
94-
let commandPromises = projectsToCommands(info.DotNet.Projects);
95-
96-
return Promise.all(commandPromises).then(commands => {
97-
return vscode.window.showQuickPick(commands);
98-
}).then(command => {
99-
if (command) {
100-
return command.execute();
101-
}
102-
});
103-
});
89+
if (!server.isRunning()) {
90+
return Promise.reject('OmniSharp server is not running.');
91+
}
92+
93+
return serverUtils.requestWorkspaceInformation(server).then(info => {
94+
95+
if (!('DotNet in info') || info.DotNet.Projects.length < 1) {
96+
return Promise.reject("No .NET Core projects found");
97+
}
98+
99+
let commandPromises = projectsToCommands(info.DotNet.Projects);
100+
101+
return Promise.all(commandPromises).then(commands => {
102+
return vscode.window.showQuickPick(commands);
103+
}).then(command => {
104+
if (command) {
105+
return command.execute();
106+
}
107+
});
108+
});
104109
}
105110

106111
export function dotnetRestoreForProject(server: OmnisharpServer, fileName: string) {
107112

108-
if (!server.isRunning()) {
109-
return Promise.reject('OmniSharp server is not running.');
110-
}
111-
112-
return serverUtils.requestWorkspaceInformation(server).then(info => {
113-
114-
if (!('DotNet in info') || info.DotNet.Projects.length < 1) {
115-
return Promise.reject("No .NET Core projects found");
116-
}
117-
118-
let directory = path.dirname(fileName);
119-
120-
for (let project of info.DotNet.Projects) {
121-
if (project.Path === directory) {
122-
return runDotnetRestore(directory, fileName);
123-
}
124-
}
125-
});
113+
if (!server.isRunning()) {
114+
return Promise.reject('OmniSharp server is not running.');
115+
}
116+
117+
return serverUtils.requestWorkspaceInformation(server).then(info => {
118+
119+
if (!('DotNet in info') || info.DotNet.Projects.length < 1) {
120+
return Promise.reject("No .NET Core projects found");
121+
}
122+
123+
let directory = path.dirname(fileName);
124+
125+
for (let project of info.DotNet.Projects) {
126+
if (project.Path === directory) {
127+
return runDotnetRestore(directory, fileName);
128+
}
129+
}
130+
});
126131
}
127132

128133
function runDotnetRestore(cwd: string, fileName?: string) {
129-
return new Promise<cp.ChildProcess>((resolve, reject) => {
130-
channel.clear();
131-
channel.show();
132-
133-
let cmd = 'dotnet restore';
134-
if (fileName) {
135-
cmd = `${cmd} "${fileName}"`
136-
}
137-
138-
return cp.exec(cmd, {cwd: cwd, env: process.env}, (err, stdout, stderr) => {
139-
channel.append(stdout.toString());
140-
channel.append(stderr.toString());
141-
if (err) {
142-
channel.append('ERROR: ' + err);
143-
}
144-
});
145-
});
134+
return new Promise<cp.ChildProcess>((resolve, reject) => {
135+
channel.clear();
136+
channel.show();
137+
138+
let cmd = 'dotnet restore';
139+
if (fileName) {
140+
cmd = `${cmd} "${fileName}"`
141+
}
142+
143+
return cp.exec(cmd, { cwd: cwd, env: process.env }, (err, stdout, stderr) => {
144+
channel.append(stdout.toString());
145+
channel.append(stderr.toString());
146+
if (err) {
147+
channel.append('ERROR: ' + err);
148+
}
149+
});
150+
});
146151
}

0 commit comments

Comments
 (0)