Skip to content
Merged
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
23 changes: 17 additions & 6 deletions src/features/signatureHelpProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@

import AbstractSupport from './abstractProvider';
import * as serverUtils from '../omnisharp/utils';
import {extractSummaryText} from './documentation';
import {createRequest} from '../omnisharp/typeConvertion';
import {SignatureHelpProvider, SignatureHelp, SignatureInformation, ParameterInformation, CancellationToken, TextDocument, Position} from 'vscode';
import { createRequest } from '../omnisharp/typeConvertion';
import { SignatureHelpProvider, SignatureHelp, SignatureInformation, ParameterInformation, CancellationToken, TextDocument, Position } from 'vscode';
import { MarkdownString } from 'vscode';
import { SignatureHelpParameter } from '../omnisharp/protocol';

export default class OmniSharpSignatureHelpProvider extends AbstractSupport implements SignatureHelpProvider {

Expand All @@ -18,7 +19,7 @@ export default class OmniSharpSignatureHelpProvider extends AbstractSupport impl
let req = createRequest(document, position);

return serverUtils.signatureHelp(this._server, req, token).then(res => {

if (!res) {
return undefined;
}
Expand All @@ -29,13 +30,13 @@ export default class OmniSharpSignatureHelpProvider extends AbstractSupport impl

for (let signature of res.Signatures) {

let signatureInfo = new SignatureInformation(signature.Label, extractSummaryText(signature.Documentation));
let signatureInfo = new SignatureInformation(signature.Label, signature.StructuredDocumentation.SummaryText);
ret.signatures.push(signatureInfo);

for (let parameter of signature.Parameters) {
let parameterInfo = new ParameterInformation(
parameter.Label,
extractSummaryText(parameter.Documentation));
this.GetParameterDocumentation(parameter));

signatureInfo.parameters.push(parameterInfo);
}
Expand All @@ -44,4 +45,14 @@ export default class OmniSharpSignatureHelpProvider extends AbstractSupport impl
return ret;
});
}

private GetParameterDocumentation(parameter: SignatureHelpParameter) {
let summary = parameter.Documentation;
if (summary.length > 0) {
let paramText = `**${parameter.Name}**: ${summary}`;
return new MarkdownString(paramText);
}

return "";
}
}
1 change: 1 addition & 0 deletions src/omnisharp/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ export interface SignatureHelpItem {
Label: string;
Documentation: string;
Parameters: SignatureHelpParameter[];
StructuredDocumentation: DocumentationComment;
}

export interface SignatureHelpParameter {
Expand Down
93 changes: 93 additions & 0 deletions test/integrationTests/signatureHelp.integration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*---------------------------------------------------------------------------------------------
* 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 poll from './poll';
import { should, expect } from 'chai';
import testAssetWorkspace from './testAssets/testAssetWorkspace';
import { omnisharp } from '../../src/omnisharp/extension';

const chai = require('chai');
chai.use(require('chai-arrays'));
chai.use(require('chai-fs'));

suite(`SignatureHelp: ${testAssetWorkspace.description}`, function () {
let fileUri: vscode.Uri;
suiteSetup(async function () {
should();

let csharpExtension = vscode.extensions.getExtension("ms-vscode.csharp");
if (!csharpExtension.isActive) {
await csharpExtension.activate();
}

await csharpExtension.exports.initializationFinished;

let fileName = 'sigHelp.cs';
let dir = path.dirname(testAssetWorkspace.projects[0].projectDirectoryPath);
let loc = path.join(dir, fileName);
fileUri = vscode.Uri.file(loc);
await omnisharp.waitForEmptyEventQueue();
await vscode.commands.executeCommand("vscode.open", fileUri);
});


test("Returns response with documentation as undefined when method does not have documentation", async function () {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two types of tests we should be thinking about:

  • Unit tests, which test the specifics of the C# extension's signature help implementation. These tests should theoretically be able to run outside of VS Code and shouldn't need to send VS Code commands
  • Integration tests, which verify that the C# extension's signature help is hooked up in VS Code and validate any nuances there (eg, trigger characters, etc). By definition, these tests run in VS Code.

Today, we can only test features by writing "integration" tests. At some point, we should think about what we'll need to do to write "unit" tests to test these kinds of implementation details. Not blocking your PR on this, but something to consider (cc @TheRealPiotrP ).

let c = <vscode.SignatureHelp>await vscode.commands.executeCommand("vscode.executeSignatureHelpProvider", fileUri, new vscode.Position(19, 23));
expect(c.signatures[0].documentation).to.be.undefined;
});

test("Returns label when method does not have documentation", async function () {
let c = <vscode.SignatureHelp>await vscode.commands.executeCommand("vscode.executeSignatureHelpProvider", fileUri, new vscode.Position(19, 23));
let answer = `void sigHelp.noDocMethod()`;
expect(c.signatures[0].label).to.equal(answer);
});

test("Returns summary as documentation for the method", async function () {
let c = <vscode.SignatureHelp>await vscode.commands.executeCommand("vscode.executeSignatureHelpProvider", fileUri, new vscode.Position(18, 18));
let answer = `DoWork is some method.`;
expect(c.signatures[0].documentation).to.equal(answer);
});

test("Returns label for the method", async function () {
let c = <vscode.SignatureHelp>await vscode.commands.executeCommand("vscode.executeSignatureHelpProvider", fileUri, new vscode.Position(18, 18));
let answer = `void sigHelp.DoWork(int Int1, float Float1)`;
expect(c.signatures[0].label).to.equal(answer);
});

test("Returns label for the parameters", async function () {
let c = <vscode.SignatureHelp>await vscode.commands.executeCommand("vscode.executeSignatureHelpProvider", fileUri, new vscode.Position(18, 18));
let param1 = `int Int1`;
let param2 = `float Float1`;
expect(c.signatures[0].parameters[0].label).to.equal(param1);
expect(c.signatures[0].parameters[1].label).to.equal(param2);
});

test("Returns documentation for the parameters", async function () {
let c = <vscode.SignatureHelp>await vscode.commands.executeCommand("vscode.executeSignatureHelpProvider", fileUri, new vscode.Position(18, 18));
let param1 = `**Int1**: Used to indicate status.`;
let param2 = `**Float1**: Used to specify context.`;
expect((<vscode.MarkdownString> c.signatures[0].parameters[0].documentation).value).to.equal(param1);
expect((<vscode.MarkdownString> c.signatures[0].parameters[1].documentation).value).to.equal(param2);
});

test("Signature Help identifies active parameter if there is no comma", async function () {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! BTW, where did these tests come from?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wrote them myself.

let c = <vscode.SignatureHelp>await vscode.commands.executeCommand("vscode.executeSignatureHelpProvider", fileUri, new vscode.Position(18, 18));
let answer = `int Int1`;
expect(c.signatures[0].parameters[c.activeParameter].label).to.equal(answer);
});

test("Signature Help identifies active parameter based on comma", async function () {
let c = <vscode.SignatureHelp>await vscode.commands.executeCommand("vscode.executeSignatureHelpProvider", fileUri, new vscode.Position(18, 20));
let answer = `float Float1`;
expect(c.signatures[0].parameters[c.activeParameter].label).to.equal(answer);
});

suiteTeardown(async () => {
await testAssetWorkspace.cleanupWorkspace();
});
});
Original file line number Diff line number Diff line change
@@ -1 +1 @@
fe2f654e9c48e6a308b6a264aa38a2b3defe5730
a42255fc669f86623d73c2e9ad48ccb8310c65c0
23 changes: 23 additions & 0 deletions test/integrationTests/testAssets/singleCsproj/sigHelp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
namespace Test
{
class sigHelp
{
///<summary>DoWork is some method.</summary>
/// <param name="Int1">Used to indicate status.</param>
/// <param name="Float1">Used to specify context.</param>
public static void DoWork(int Int1, float Float1)
{
}

public static void noDocMethod()
{
}

public static void main()
{
DoWork(4, 4.0f);
noDocMethod();
}
}
}
23 changes: 23 additions & 0 deletions test/integrationTests/testAssets/slnWithCsproj/src/app/sigHelp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
namespace Test
{
class sigHelp
{
///<summary>DoWork is some method.</summary>
/// <param name="Int1">Used to indicate status.</param>
/// <param name="Float1">Used to specify context.</param>
public static void DoWork(int Int1, float Float1)
{
}

public static void noDocMethod()
{
}

public static void main()
{
DoWork(4, 4.0f);
noDocMethod();
}
}
}