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

Return the SignatureHelp items nearest to the cursor #73606

Merged
merged 6 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.SignatureHelp;
using Microsoft.CodeAnalysis.Options;
using Roslyn.Text.Adornments;
using LSP = Roslyn.LanguageServer.Protocol;

Expand Down Expand Up @@ -53,48 +53,73 @@ internal class SignatureHelpHandler : ILspServiceDocumentRequestHandler<LSP.Text
var triggerInfo = new SignatureHelpTriggerInfo(SignatureHelpTriggerReason.InvokeSignatureHelpCommand);
var options = _globalOptions.GetSignatureHelpOptions(document.Project.Language);

var sigHelpItems = new ArrayBuilder<SignatureHelpItems>();
JoeRobich marked this conversation as resolved.
Show resolved Hide resolved

foreach (var provider in providers)
{
var items = await provider.Value.GetItemsAsync(document, position, triggerInfo, options, cancellationToken).ConfigureAwait(false);

if (items != null)
if (items is null)
{
var sigInfos = new ArrayBuilder<LSP.SignatureInformation>();
continue;
}

foreach (var item in items.Items)
{
LSP.SignatureInformation sigInfo;
if (clientCapabilities.HasVisualStudioLspCapability() == true)
{
sigInfo = new LSP.VSInternalSignatureInformation
{
ColorizedLabel = GetSignatureClassifiedText(item)
};
}
else
{
sigInfo = new LSP.SignatureInformation();
}
sigHelpItems.Add(items);
}

// Multiple providers could be offering items at the current location. As
// there might be an ObjectCreationExpression within a MethodInvocation.
// We want to identify the items nearest to the cursor.

SignatureHelpItems? helpItems = null;
var minDistance = int.MaxValue;
foreach (var items in sigHelpItems.ToArrayAndFree())
JoeRobich marked this conversation as resolved.
Show resolved Hide resolved
{
var distanceFromCursor = position - items.ApplicableSpan.Start;
if (distanceFromCursor < minDistance)
JoeRobich marked this conversation as resolved.
Show resolved Hide resolved
{
minDistance = distanceFromCursor;
helpItems = items;
}
}

if (helpItems is not null)
{
var sigInfos = new ArrayBuilder<LSP.SignatureInformation>();

sigInfo.Label = GetSignatureText(item);
sigInfo.Documentation = new LSP.MarkupContent { Kind = LSP.MarkupKind.PlainText, Value = item.DocumentationFactory(cancellationToken).GetFullText() };
sigInfo.Parameters = item.Parameters.Select(p => new LSP.ParameterInformation
foreach (var item in helpItems.Items)
{
LSP.SignatureInformation sigInfo;
if (clientCapabilities.HasVisualStudioLspCapability() == true)
{
sigInfo = new LSP.VSInternalSignatureInformation
{
Label = p.Name,
Documentation = new LSP.MarkupContent { Kind = LSP.MarkupKind.PlainText, Value = p.DocumentationFactory(cancellationToken).GetFullText() }
}).ToArray();
sigInfos.Add(sigInfo);
ColorizedLabel = GetSignatureClassifiedText(item)
};
}

var sigHelp = new LSP.SignatureHelp
else
{
ActiveSignature = GetActiveSignature(items),
ActiveParameter = items.ArgumentIndex,
Signatures = sigInfos.ToArrayAndFree()
};
sigInfo = new LSP.SignatureInformation();
}

return sigHelp;
sigInfo.Label = GetSignatureText(item);
sigInfo.Documentation = new LSP.MarkupContent { Kind = LSP.MarkupKind.PlainText, Value = item.DocumentationFactory(cancellationToken).GetFullText() };
sigInfo.Parameters = item.Parameters.Select(p => new LSP.ParameterInformation
{
Label = p.Name,
Documentation = new LSP.MarkupContent { Kind = LSP.MarkupKind.PlainText, Value = p.DocumentationFactory(cancellationToken).GetFullText() }
}).ToArray();
sigInfos.Add(sigInfo);
}

var sigHelp = new LSP.SignatureHelp
{
ActiveSignature = GetActiveSignature(helpItems),
ActiveParameter = helpItems.ArgumentIndex,
Signatures = sigInfos.ToArrayAndFree()
};

return sigHelp;
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,29 @@ int M2(string a)
AssertJsonEquals(expected, results);
}

[Theory, CombinatorialData]
public async Task TestGetNestedSignatureHelpAsync(bool mutatingLspWorkspace)
{
var markup =
@"class Foo {
public Foo(int showMe) {}
public static void Do(Foo foo) {
Do(new Foo({|caret:|}
}
}";
await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace);
var expected = new LSP.SignatureHelp()
{
ActiveParameter = 0,
ActiveSignature = 0,
Signatures = [CreateSignatureInformation("Foo(int showMe)", "", "showMe", "")]
};

var results = await RunGetSignatureHelpAsync(testLspServer, testLspServer.GetLocations("caret").Single());
AssertJsonEquals(expected, results);
}

private static async Task<LSP.SignatureHelp?> RunGetSignatureHelpAsync(TestLspServer testLspServer, LSP.Location caret)
{
return await testLspServer.ExecuteRequestAsync<LSP.TextDocumentPositionParams, LSP.SignatureHelp?>(
Expand Down
Loading