Skip to content

Commit

Permalink
Merge pull request #2 from MrLuje/feature/nsubstitute
Browse files Browse the repository at this point in the history
Feature/nsubstitute
  • Loading branch information
MrLuje authored Feb 20, 2018
2 parents 1aacb7a + 3b4b89f commit 2bc4356
Show file tree
Hide file tree
Showing 12 changed files with 520 additions and 396 deletions.
1 change: 1 addition & 0 deletions .czrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "path": "cz-conventional-changelog" }
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -->
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\packages\Microsoft.VSSDK.BuildTools\build\Microsoft.VSSDK.BuildTools.props" Condition="Exists('..\..\packages\Microsoft.VSSDK.BuildTools\build\Microsoft.VSSDK.BuildTools.props')" Label="Paket" />
<PropertyGroup>
<MinimumVisualStudioVersion>15.0</MinimumVisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
Expand Down Expand Up @@ -68,6 +69,7 @@
<StartArguments>/rootsuffix Roslyn</StartArguments>
</PropertyGroup>
<ItemGroup>
<None Include="paket.references" />
<None Include="source.extension.vsixmanifest">
<SubType>Designer</SubType>
</None>
Expand Down Expand Up @@ -98,4 +100,5 @@
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" />
<Import Project="..\..\packages\Microsoft.VSSDK.BuildTools\build\Microsoft.VSSDK.BuildTools.targets" Condition="Exists('..\..\packages\Microsoft.VSSDK.BuildTools\build\Microsoft.VSSDK.BuildTools.targets')" Label="Paket" />
</Project>
1 change: 1 addition & 0 deletions Mocking.Helpers/Mocking.Helpers.Vsix/paket.references
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Microsoft.VSSDK.BuildTools
5 changes: 3 additions & 2 deletions Mocking.Helpers/Mocking.Helpers/CompletionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Mocking.Helpers.Interfaces;
using Mocking.Helpers.Moq;
using System;
using System.Linq;
Expand All @@ -15,9 +16,9 @@ public class CompletionService
private CompletionItemRules _defaultCompletions;
private readonly SyntaxToken _token;
private readonly SemanticModel _semanticModel;
private readonly MoqProvider _provider;
private readonly BaseMockingProvider _provider;

public CompletionService(CompletionContext context, SyntaxToken token, SemanticModel semanticModel, MoqProvider provider)
public CompletionService(CompletionContext context, SyntaxToken token, SemanticModel semanticModel, BaseMockingProvider provider)
{
_context = context;
_token = token;
Expand Down
33 changes: 33 additions & 0 deletions Mocking.Helpers/Mocking.Helpers/Core/BaseMockingProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Mocking.Helpers.Interfaces
{
public abstract class BaseMockingProvider
{
public abstract string AssemblyName { get; }
public abstract string MockingMethodName { get; }
public abstract string MockingWildcardMethod { get; }

protected string FormatMockingWildcardMethod(string parameterTypeName)
{
return string.Format(this.MockingWildcardMethod, parameterTypeName);
}

public virtual string GenerateSuggestionForParameterWildcard(SemanticModel model, IMethodSymbol methodSymbol, ArgumentListSyntax arguments)
{
var suggestionParameter = methodSymbol.Parameters.Aggregate(String.Empty, (acc, p) => $"{acc}, {this.FormatMockingWildcardMethod(p.Type.ToMinimalDisplayString(model, arguments.SpanStart))}").TrimStart(',').Trim();
return suggestionParameter;
}

public virtual string GenerateSuggestionForParameterWildcardOfType(SemanticModel model, ITypeSymbol typeSymbol, ArgumentListSyntax arguments)
{
var suggestionParameter = this.FormatMockingWildcardMethod(typeSymbol.ToMinimalDisplayString(model, arguments.SpanStart));
return suggestionParameter;
}
}
}
4 changes: 2 additions & 2 deletions Mocking.Helpers/Mocking.Helpers/Moq/MoqIsAnyCompletion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext context)
var syntaxRoot = await context.Document.GetSyntaxRootAsync();
var token = SyntaxHelpers.GetSelectedTokens(syntaxRoot, context.Position);

// ?ot in an opened method
// Not in an opened method
if (token.Parent == null) return;

var mockedMethodArgumentList = token.Parent as ArgumentListSyntax;
Expand All @@ -48,7 +48,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext context)
if (setupMethodInvocation == null) return;

var semanticModel = await context.Document.GetSemanticModelAsync();
var matchingMockedMethods = SyntaxHelpers.GetCandidatesMockedMethodSignatures(semanticModel, setupMethodInvocation);
var matchingMockedMethods = SyntaxHelpers.GetCandidatesMockedMethodSignaturesForLambda(semanticModel, setupMethodInvocation);

var completionService = new CompletionService(context, token, semanticModel, this._provider);

Expand Down
26 changes: 5 additions & 21 deletions Mocking.Helpers/Mocking.Helpers/Moq/MoqProvider.cs
Original file line number Diff line number Diff line change
@@ -1,33 +1,17 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Mocking.Helpers.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Mocking.Helpers.Moq
{
public class MoqProvider
public class MoqProvider : BaseMockingProvider
{
public string MockingMethodName { get; } = "Setup";
public string AssemblyName { get; } = "Moq";
public string MockingWildcardMethod { get; } = "It.IsAny<{0}>()";

string FormatMockingWildcardMethod(string parameterTypeName)
{
return string.Format(this.MockingWildcardMethod, parameterTypeName);
}

public string GenerateSuggestionForParameterWildcard(SemanticModel model, IMethodSymbol methodSymbol, ArgumentListSyntax arguments)
{
var suggestionParameter = methodSymbol.Parameters.Aggregate(String.Empty, (acc, p) => $"{acc}, {this.FormatMockingWildcardMethod(p.Type.ToMinimalDisplayString(model, arguments.SpanStart))}").TrimStart(',').Trim();
return suggestionParameter;
}

public string GenerateSuggestionForParameterWildcardOfType(SemanticModel model, ITypeSymbol typeSymbol, ArgumentListSyntax arguments)
{
var suggestionParameter = this.FormatMockingWildcardMethod(typeSymbol.ToMinimalDisplayString(model, arguments.SpanStart));
return suggestionParameter;
}
public override string MockingMethodName => "Setup";
public override string AssemblyName => "Moq";
public override string MockingWildcardMethod => "It.IsAny<{0}>()";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Mocking.Helpers.NSubstitute
{
[ExportCompletionProvider(nameof(NSubstituteArgAnyCompletion), LanguageNames.CSharp)]
public class NSubstituteArgAnyCompletion : CompletionProvider
{
private NSubstituteProvider _provider;

public NSubstituteArgAnyCompletion()
{
this._provider = new NSubstituteProvider();
}

internal bool IsSubstituteForMethod(InvocationExpressionSyntax invocation)
{
return SyntaxHelpers.IsMethodNamed(invocation, this._provider.MockingMethodName);
}

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

var hasNSubstituteReferenced = context.Document.Project.MetadataReferences.Any(r => r.Display.Contains(this._provider.AssemblyName));
if (!hasNSubstituteReferenced) return;

var syntaxRoot = await context.Document.GetSyntaxRootAsync();
var token = SyntaxHelpers.GetSelectedTokens(syntaxRoot, context.Position);

// Not in an opened method
if (token.Parent == null) return;

var mockedMethodArgumentList = token.Parent as ArgumentListSyntax;
var mockedMethodInvocation = mockedMethodArgumentList.Ancestors()
.OfType<InvocationExpressionSyntax>()
.FirstOrDefault();

if (mockedMethodInvocation == null) return;

var semanticModel = await context.Document.GetSemanticModelAsync();
var matchingMockedMethods = SyntaxHelpers.GetCandidatesMockedMethodSignatures(semanticModel, mockedMethodInvocation);

var completionService = new CompletionService(context, token, semanticModel, this._provider);

foreach (IMethodSymbol matchingMockedMethodSymbol in matchingMockedMethods)
{
completionService.AddSuggestionsForMethod(matchingMockedMethodSymbol, mockedMethodArgumentList);
}
}
catch
{
}
}
}
}

16 changes: 16 additions & 0 deletions Mocking.Helpers/Mocking.Helpers/NSubstitute/NSubstituteProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Mocking.Helpers.Interfaces;
using System;
using System.Collections.Generic;
using System.Text;

namespace Mocking.Helpers.NSubstitute
{
public class NSubstituteProvider : BaseMockingProvider
{
public override string AssemblyName => "NSubstitute";
public override string MockingMethodName => "For";
public override string MockingWildcardMethod => "Arg.Any<{0}>()";
}
}
15 changes: 13 additions & 2 deletions Mocking.Helpers/Mocking.Helpers/SyntaxHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,27 @@ static internal SyntaxToken GetSelectedTokens(SyntaxNode node, int currentPositi
}

/// <summary>
/// Get all signature candidates which have parameters
/// Get all signature candidates which have parameters in a lambda call
/// </summary>
/// <param name="semanticModel"></param>
/// <param name="setupMethodInvocation"></param>
/// <returns></returns>
static internal IEnumerable<IMethodSymbol> GetCandidatesMockedMethodSignatures(SemanticModel semanticModel, InvocationExpressionSyntax setupMethodInvocation)
static internal IEnumerable<IMethodSymbol> GetCandidatesMockedMethodSignaturesForLambda(SemanticModel semanticModel, InvocationExpressionSyntax setupMethodInvocation)
{
var setupLambda = setupMethodInvocation.ArgumentList.Arguments.FirstOrDefault()?.Expression as LambdaExpressionSyntax;

var methodToMockInLambda = setupLambda?.Body as InvocationExpressionSyntax;
return GetCandidatesMockedMethodSignatures(semanticModel, methodToMockInLambda);
}

/// <summary>
/// Get all signature candidates which have parameters in a lambda call
/// </summary>
/// <param name="semanticModel"></param>
/// <param name="methodToMockInLambda"></param>
/// <returns></returns>
static internal IEnumerable<IMethodSymbol> GetCandidatesMockedMethodSignatures(SemanticModel semanticModel, InvocationExpressionSyntax methodToMockInLambda)
{
if (methodToMockInLambda == null) return Enumerable.Empty<IMethodSymbol>();

var mockSignatures = new List<IMethodSymbol>();
Expand Down
6 changes: 3 additions & 3 deletions paket.dependencies
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
version 5.137.2
storage: none
framework: netstandard20
framework: auto-detect
source https://www.nuget.org/api/v2

nuget Microsoft.CodeAnalysis.CSharp 2.6.1
nuget Microsoft.CodeAnalysis.EditorFeatures 2.6.1
nuget Microsoft.CodeAnalysis.EditorFeatures.Text 2.6.1
nuget Microsoft.CodeAnalysis.Features 2.6.1
nuget Microsoft.CodeAnalysis.Features 2.6.1
nuget Microsoft.VSSDK.BuildTools 15.5.100
Loading

0 comments on commit 2bc4356

Please sign in to comment.