Skip to content
This repository has been archived by the owner on Jan 16, 2022. It is now read-only.

Commit

Permalink
Add SuggestionProvider for method parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
cezarypiatek committed Aug 15, 2019
1 parent 7a603f5 commit a89ebff
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="2.8.2" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Features" Version="2.0.0" />
<PackageReference Include="Pluralize.NET" Version="0.1.84" />
<PackageReference Update="NETStandard.Library" PrivateAssets="all" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using System.Linq;
using System.Threading.Tasks;
using MappingGenerator.MethodHelpers;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;

namespace MappingGenerator
{
[ExportCompletionProvider(nameof(MethodParametersSuggestionProvider), LanguageNames.CSharp)]
public class MethodParametersSuggestionProvider : CompletionProvider
{
private CompletionItemRules _preselectCompletionRules;

public MethodParametersSuggestionProvider()
{
_preselectCompletionRules = CompletionItemRules.Default.WithMatchPriority(MatchPriority.Preselect).WithSelectionBehavior(CompletionItemSelectionBehavior.SoftSelection);
}

public override async Task ProvideCompletionsAsync(CompletionContext context)
{
if (!context.Document.SupportsSemanticModel) return;

var syntaxRoot = await context.Document.GetSyntaxRootAsync();
var semanticModel = await context.Document.GetSemanticModelAsync();

var tokenAtCursor = SuggestionHelpers.GetCurrentArgumentListSyntaxToken(syntaxRoot, context.Position);
if (!tokenAtCursor.IsKind(SyntaxKind.OpenParenToken)) return;

var callbackArgumentList = tokenAtCursor.Parent as ArgumentListSyntax;

if (callbackArgumentList==null || callbackArgumentList.Arguments.Any()) return;

var expression = callbackArgumentList.Parent.FindNearestContainer<InvocationExpressionSyntax, ObjectCreationExpressionSyntax>();
if (expression != null)
{
if (expression is InvocationExpressionSyntax invocationExpression)
{
if (invocationExpression.ArgumentList.Arguments.Count == 0)
{
var invocation = new MethodInvocation(invocationExpression);
SuggestMethodParameters(context, invocation, semanticModel);
}
}
else if (expression is ObjectCreationExpressionSyntax creationExpression)
{
if (creationExpression.ArgumentList?.Arguments.Count == 0)
{
var invocation = new ConstructorInvocation(creationExpression);
SuggestMethodParameters(context, invocation, semanticModel);
}
}
}
}

private void SuggestMethodParameters(CompletionContext context, IInvocation invocation, SemanticModel semanticModel)
{
var argumentList = GetArgumentListWithLocalVariables(context.Document, invocation, false, semanticModel);
if (argumentList != null)
{
context.AddItem(CompletionItem.Create(argumentList, rules: _preselectCompletionRules));
}

var argumentListWithParameterNames = GetArgumentListWithLocalVariables(context.Document, invocation, true, semanticModel);
if (argumentListWithParameterNames != null)
{
context.AddItem(CompletionItem.Create(argumentListWithParameterNames, rules: _preselectCompletionRules));
}
}

private string GetArgumentListWithLocalVariables(Document document, IInvocation invocation, bool generateNamedParameters, SemanticModel semanticModel)
{
var mappingSourceFinder = new LocalScopeMappingSourceFinder(semanticModel, invocation.SourceNode);
var syntaxGenerator = SyntaxGenerator.GetGenerator(document);
var overloadParameterSets = invocation.GetOverloadParameterSets(semanticModel);
if (overloadParameterSets != null)
{
var contextAssembly = semanticModel.FindContextAssembly(invocation.SourceNode);
var mappingEngine = new MappingEngine(semanticModel, syntaxGenerator, contextAssembly);
var parametersMatch = MethodHelper.FindBestParametersMatch(mappingSourceFinder, overloadParameterSets);
if (parametersMatch != null)
{
var argumentList = parametersMatch.ToArgumentListSyntax(mappingEngine, generateNamedParameters);
var chunks = argumentList.Arguments.Select(a => a.ToString());
return string.Join(", ", chunks);
}
}

return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace MappingGenerator
{
internal class SuggestionHelpers
{

internal static SyntaxToken GetCurrentArgumentListSyntaxToken(SyntaxNode node, int currentPosition)
{
var allArgumentLists = node.DescendantNodes(n => n.FullSpan.Contains(currentPosition - 1)).OfType<ArgumentListSyntax>().OrderBy(n => n.FullSpan.Length);
return allArgumentLists.SelectMany(n => n.ChildTokens()
.Where(t => t.IsKind(SyntaxKind.OpenParenToken) || t.IsKind(SyntaxKind.CommaToken))
.Where(t => t.FullSpan.Contains(currentPosition - 1))).FirstOrDefault();
}
}
}

0 comments on commit a89ebff

Please sign in to comment.