diff --git a/Directory.Build.targets b/Directory.Build.targets
index 7694e4abe..6eda0850d 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -1,52 +1,49 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LSP.sln b/LSP.sln
index 110faead4..14e66c9f4 100644
--- a/LSP.sln
+++ b/LSP.sln
@@ -14,7 +14,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{2F323ED5-E
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".config", ".config", "{AE4D7807-6F78-428C-A0D9-914BA583A104}"
- ProjectSection(SolutionItems) = prePrlaoject
+ ProjectSection(SolutionItems) = preProject
.appveyor.yml = .appveyor.yml
.editorconfig = .editorconfig
.gitattributes = .gitattributes
diff --git a/README.md b/README.md
index 33100a9de..26af571e1 100644
--- a/README.md
+++ b/README.md
@@ -58,6 +58,7 @@ For more information about using the `DebugAdapterClient` / `DebugAdapterServer`
| OmniSharp.Extensions.DebugAdapter.Shared | [![nuget-version-2fkn0yzdbhmg-badge]![nuget-downloads-2fkn0yzdbhmg-badge]][nuget-2fkn0yzdbhmg] |
| OmniSharp.Extensions.DebugAdapter.Testing | [![nuget-version-jppuysmkpfcw-badge]![nuget-downloads-jppuysmkpfcw-badge]][nuget-jppuysmkpfcw] |
| OmniSharp.Extensions.JsonRpc | [![nuget-version-a1bmkwyotvkg-badge]![nuget-downloads-a1bmkwyotvkg-badge]][nuget-a1bmkwyotvkg] |
+| OmniSharp.Extensions.JsonRpc.Generators | [![nuget-version-m6majhsjiw1q-badge]![nuget-downloads-m6majhsjiw1q-badge]][nuget-m6majhsjiw1q] |
| OmniSharp.Extensions.JsonRpc.Testing | [![nuget-version-punkj7/efvjq-badge]![nuget-downloads-punkj7/efvjq-badge]][nuget-punkj7/efvjq] |
| OmniSharp.Extensions.LanguageClient | [![nuget-version-fclou9t/p2ba-badge]![nuget-downloads-fclou9t/p2ba-badge]][nuget-fclou9t/p2ba] |
| OmniSharp.Extensions.LanguageProtocol | [![nuget-version-vddj9t6jnirq-badge]![nuget-downloads-vddj9t6jnirq-badge]][nuget-vddj9t6jnirq] |
@@ -118,6 +119,9 @@ This project is supported by the [.NET Foundation](http://www.dotnetfoundation.o
[nuget-a1bmkwyotvkg]: https://www.nuget.org/packages/OmniSharp.Extensions.JsonRpc/
[nuget-version-a1bmkwyotvkg-badge]: https://img.shields.io/nuget/v/OmniSharp.Extensions.JsonRpc.svg?color=004880&logo=nuget&style=flat-square "NuGet Version"
[nuget-downloads-a1bmkwyotvkg-badge]: https://img.shields.io/nuget/dt/OmniSharp.Extensions.JsonRpc.svg?color=004880&logo=nuget&style=flat-square "NuGet Downloads"
+[nuget-m6majhsjiw1q]: https://www.nuget.org/packages/OmniSharp.Extensions.JsonRpc.Generators/
+[nuget-version-m6majhsjiw1q-badge]: https://img.shields.io/nuget/v/OmniSharp.Extensions.JsonRpc.Generators.svg?color=004880&logo=nuget&style=flat-square "NuGet Version"
+[nuget-downloads-m6majhsjiw1q-badge]: https://img.shields.io/nuget/dt/OmniSharp.Extensions.JsonRpc.Generators.svg?color=004880&logo=nuget&style=flat-square "NuGet Downloads"
[nuget-punkj7/efvjq]: https://www.nuget.org/packages/OmniSharp.Extensions.JsonRpc.Testing/
[nuget-version-punkj7/efvjq-badge]: https://img.shields.io/nuget/v/OmniSharp.Extensions.JsonRpc.Testing.svg?color=004880&logo=nuget&style=flat-square "NuGet Version"
[nuget-downloads-punkj7/efvjq-badge]: https://img.shields.io/nuget/dt/OmniSharp.Extensions.JsonRpc.Testing.svg?color=004880&logo=nuget&style=flat-square "NuGet Downloads"
diff --git a/src/Dap.Protocol/Dap.Protocol.csproj b/src/Dap.Protocol/Dap.Protocol.csproj
index f922cb5cd..40417c6b1 100644
--- a/src/Dap.Protocol/Dap.Protocol.csproj
+++ b/src/Dap.Protocol/Dap.Protocol.csproj
@@ -14,8 +14,7 @@
-
-
+
<_Parameter1>OmniSharp.Extensions.DebugAdapter.Server, PublicKey=0024000004800000940000000602000000240000525341310004000001000100391db875e68eb4bfef49ce14313b9e13f2cd3cc89eb273bbe6c11a55044c7d4f566cf092e1c77ef9e7c75b1496ae7f95d925938f5a01793dd8d9f99ae0a7595779b71b971287d7d7b5960d052078d14f5ce1a85ea5c9fb2f59ac735ff7bc215cab469b7c3486006860bad6f4c3b5204ea2f28dd4e1d05e2cca462cfd593b9f9f
diff --git a/src/JsonRpc.Generators/GenerateHandlerMethodsGenerator.cs b/src/JsonRpc.Generators/GenerateHandlerMethodsGenerator.cs
index a207514ae..99739c072 100644
--- a/src/JsonRpc.Generators/GenerateHandlerMethodsGenerator.cs
+++ b/src/JsonRpc.Generators/GenerateHandlerMethodsGenerator.cs
@@ -1,130 +1,254 @@
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Linq;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using CodeGeneration.Roslyn;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using static OmniSharp.Extensions.JsonRpc.Generators.Helpers;
namespace OmniSharp.Extensions.JsonRpc.Generators
{
- public class GenerateHandlerMethodsGenerator : IRichCodeGenerator
+ [Generator]
+ public class GenerateHandlerMethodsGenerator : ISourceGenerator
{
- private readonly AttributeData _attributeData;
-
- public GenerateHandlerMethodsGenerator(AttributeData attributeData) => _attributeData = attributeData;
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
+ }
- public Task GenerateRichAsync(TransformationContext context, IProgress progress, CancellationToken cancellationToken)
+ public void Execute(GeneratorExecutionContext context)
{
- if (!( context.ProcessingNode is InterfaceDeclarationSyntax handlerInterface ))
+ if (!( context.SyntaxReceiver is SyntaxReceiver syntaxReceiver ))
{
- return Task.FromResult(new RichGenerationResult());
+ return;
}
- var methods = new List();
- var additionalUsings = new HashSet {
- "System",
- "System.Collections.Generic",
- "System.Threading",
- "System.Threading.Tasks",
- "MediatR",
- "Microsoft.Extensions.DependencyInjection",
- };
- var symbol = context.SemanticModel.GetDeclaredSymbol(handlerInterface);
+ var options = ( context.Compilation as CSharpCompilation )?.SyntaxTrees[0].Options as CSharpParseOptions;
+ var compilation = context.Compilation;
+
+ var generateHandlerMethodsAttributeSymbol = compilation.GetTypeByMetadataName("OmniSharp.Extensions.JsonRpc.Generation.GenerateHandlerMethodsAttribute");
+ var generateRequestMethodsAttributeSymbol = compilation.GetTypeByMetadataName("OmniSharp.Extensions.JsonRpc.Generation.GenerateRequestMethodsAttribute");
- var className = GetExtensionClassName(symbol);
+// context.ReportDiagnostic(Diagnostic.Create(GeneratorDiagnostics.Message, null, $"generateHandlerMethodsAttributeSymbol: {generateHandlerMethodsAttributeSymbol.ToDisplayString()}"));
+// context.ReportDiagnostic(Diagnostic.Create(GeneratorDiagnostics.Message, null, $"generateRequestMethodsAttributeSymbol: {generateRequestMethodsAttributeSymbol.ToDisplayString()}"));
- foreach (var registry in GetRegistries(_attributeData, handlerInterface, symbol, context, progress, additionalUsings))
+ foreach (var candidateClass in syntaxReceiver.Candidates)
+ {
+// context.ReportDiagnostic(Diagnostic.Create(GeneratorDiagnostics.Message, null, $"candidate: {candidateClass.Identifier.ToFullString()}"));
+ // can this be async???
+ context.CancellationToken.ThrowIfCancellationRequested();
+
+ var model = compilation.GetSemanticModel(candidateClass.SyntaxTree);
+ var symbol = model.GetDeclaredSymbol(candidateClass);
+
+ var methods = new List();
+ var additionalUsings = new HashSet {
+ "System",
+ "System.Collections.Generic",
+ "System.Threading",
+ "System.Threading.Tasks",
+ "MediatR",
+ "Microsoft.Extensions.DependencyInjection",
+ };
+
+// context.ReportDiagnostic(Diagnostic.Create(GeneratorDiagnostics.Message, null, $"candidate: {candidateClass.Identifier.ToFullString()}"));
+
+ var attribute = symbol?.GetAttributes().FirstOrDefault(z => SymbolEqualityComparer.Default.Equals(z.AttributeClass, generateHandlerMethodsAttributeSymbol));
+ if (attribute != null)
+ {
+ GetExtensionHandlers(
+ context,
+ candidateClass,
+ symbol,
+ attribute,
+ methods,
+ additionalUsings
+ );
+ }
+
+ attribute = symbol?.GetAttributes().FirstOrDefault(z => SymbolEqualityComparer.Default.Equals(z.AttributeClass, generateRequestMethodsAttributeSymbol));
+ if (attribute != null)
+ {
+ GetExtensionRequestHandlers(
+ context,
+ candidateClass,
+ symbol,
+ attribute,
+ methods,
+ additionalUsings
+ );
+ }
+
+ if (!methods.Any()) continue;
+
+ var className = GetExtensionClassName(symbol);
+
+ var existingUsings = candidateClass.SyntaxTree.GetCompilationUnitRoot()
+ .Usings
+ .Select(x => x.WithoutTrivia())
+ .Union(
+ additionalUsings
+ .Except(
+ candidateClass.SyntaxTree.GetCompilationUnitRoot()
+ .Usings.Select(z => z.Name.ToFullString())
+ )
+ .Distinct()
+ .Select(z => UsingDirective(IdentifierName(z)))
+ )
+ .OrderBy(x => x.Name.ToFullString())
+ .ToImmutableArray();
+
+ var obsoleteAttribute = candidateClass.AttributeLists
+ .SelectMany(z => z.Attributes)
+ .Where(z => z.Name.ToFullString() == nameof(ObsoleteAttribute) || z.Name.ToFullString() == "Obsolete")
+ .ToArray();
+ var attributes = List(
+ new[] {
+ AttributeList(
+ SeparatedList(
+ new[] {
+ Attribute(ParseName("System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute")),
+ Attribute(ParseName("System.Runtime.CompilerServices.CompilerGeneratedAttribute")),
+ }.Union(obsoleteAttribute)
+ )
+ )
+ }
+ );
+
+ var isInternal = candidateClass.Modifiers.Any(z => z.IsKind(SyntaxKind.InternalKeyword));
+
+ var cu = CompilationUnit(
+ List(),
+ List(existingUsings),
+ List(),
+ List(
+ new[] {
+ NamespaceDeclaration(ParseName(symbol.ContainingNamespace.ToDisplayString()))
+ .WithMembers(
+ SingletonList(
+ ClassDeclaration(className)
+ .WithAttributeLists(attributes)
+ .WithModifiers(
+ TokenList(
+ new[] { isInternal ? Token(SyntaxKind.InternalKeyword) : Token(SyntaxKind.PublicKeyword) }.Concat(
+ new[] {
+ Token(SyntaxKind.StaticKeyword),
+ Token(SyntaxKind.PartialKeyword)
+ }
+ )
+ )
+ )
+ .WithMembers(List(methods))
+ .WithLeadingTrivia(TriviaList(Trivia(NullableDirectiveTrivia(Token(SyntaxKind.EnableKeyword), true))))
+ .WithTrailingTrivia(TriviaList(Trivia(NullableDirectiveTrivia(Token(SyntaxKind.RestoreKeyword), true))))
+ .NormalizeWhitespace()
+ )
+ )
+ }
+ )
+ )
+ .WithLeadingTrivia(Comment(Preamble.GeneratedByATool))
+ .WithTrailingTrivia(CarriageReturnLineFeed)
+ .NormalizeWhitespace();
+
+ context.AddSource(
+ $"JsonRpc_Handlers_{candidateClass.Identifier.ToFullString().Replace(".", "_")}.cs",
+ cu.SyntaxTree.GetRoot().GetText(Encoding.UTF8)
+ );
+ }
+ }
+
+
+ private void GetExtensionHandlers(
+ GeneratorExecutionContext context,
+ TypeDeclarationSyntax handlerClassOrInterface,
+ INamedTypeSymbol symbol,
+ AttributeData attributeData,
+ List methods,
+ HashSet additionalUsings
+ )
+ {
+ foreach (var registry in GetRegistries(attributeData, handlerClassOrInterface, symbol, context, additionalUsings))
{
if (IsNotification(symbol))
{
var requestType = GetRequestType(symbol);
- methods.AddRange(HandleNotifications(handlerInterface, symbol, requestType, registry, additionalUsings));
+ methods.AddRange(HandleNotifications(handlerClassOrInterface, symbol, requestType, registry, additionalUsings, attributeData));
}
else if (IsRequest(symbol))
{
var requestType = GetRequestType(symbol);
- var responseType = GetResponseType(handlerInterface);
- methods.AddRange(HandleRequest(handlerInterface, symbol, requestType, responseType!, registry, additionalUsings));
+ var responseType = GetResponseType(handlerClassOrInterface);
+ methods.AddRange(HandleRequest(handlerClassOrInterface, symbol, requestType, responseType!, registry, additionalUsings, attributeData));
}
}
+ }
- var existingUsings = context.CompilationUnitUsings
- .Join(additionalUsings, z => z.Name.ToFullString(), z => z, (a, b) => b)
- ;
-
- var newUsings = additionalUsings
- .Except(existingUsings)
- .Select(z => UsingDirective(IdentifierName(z)))
- ;
+ private void GetExtensionRequestHandlers(
+ GeneratorExecutionContext context,
+ TypeDeclarationSyntax handlerClassOrInterface,
+ INamedTypeSymbol symbol,
+ AttributeData attributeData,
+ List methods,
+ HashSet additionalUsings
+ )
+ {
+ var registries = GetProxies(
+ context,
+ attributeData,
+ handlerClassOrInterface,
+ symbol,
+ methods,
+ additionalUsings
+ );
- var obsoleteAttribute = handlerInterface.AttributeLists
- .SelectMany(z => z.Attributes)
- .Where(z => z.Name.ToFullString() == nameof(ObsoleteAttribute) || z.Name.ToFullString() == "Obsolete")
- .ToArray();
- var attributes = List(
- new[] {
- AttributeList(
- SeparatedList(
- new[] {
- Attribute(ParseName("System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute")),
- Attribute(ParseName("System.Runtime.CompilerServices.CompilerGeneratedAttribute")),
- }.Union(obsoleteAttribute)
- )
+ if (( attributeData.ConstructorArguments.Length == 0 ||
+ ( attributeData.ConstructorArguments[0].Kind != TypedConstantKind.Array && attributeData.ConstructorArguments[0].Value == null )
+ || ( attributeData.ConstructorArguments[0].Kind == TypedConstantKind.Array && attributeData.ConstructorArguments[0].Values.Length == 0 ) )
+ && !symbol.ContainingNamespace.ToDisplayString().StartsWith("OmniSharp.Extensions.DebugAdapter.Protocol"))
+ {
+ context.ReportDiagnostic(
+ Diagnostic.Create(
+ GeneratorDiagnostics.NoResponseRouterProvided, handlerClassOrInterface.Identifier.GetLocation(), symbol.Name,
+ string.Join(", ", registries.Select(z => z.ToFullString()))
)
+ );
+ }
+
+ foreach (var registry in registries)
+ {
+ if (IsNotification(symbol))
+ {
+ var requestType = GetRequestType(symbol);
+ methods.AddRange(HandleRequestNotifications(handlerClassOrInterface, symbol, requestType, registry, additionalUsings, attributeData));
}
- );
- var isInternal = handlerInterface.Modifiers.Any(z => z.IsKind(SyntaxKind.InternalKeyword));
-
- return Task.FromResult(
- new RichGenerationResult {
- Usings = List(newUsings),
- Members = List(
- new[] {
- NamespaceDeclaration(ParseName(symbol.ContainingNamespace.ToDisplayString()))
- .WithMembers(
- List(
- new MemberDeclarationSyntax[] {
- ClassDeclaration(className)
- .WithAttributeLists(attributes)
- .WithModifiers(
- TokenList(
- new[] { isInternal ? Token(SyntaxKind.InternalKeyword) : Token(SyntaxKind.PublicKeyword) }.Concat(
- new[] {
- Token(SyntaxKind.StaticKeyword),
- Token(SyntaxKind.PartialKeyword)
- }
- )
- )
- )
- .WithMembers(List(methods))
- .WithLeadingTrivia(TriviaList(Trivia(NullableDirectiveTrivia(Token(SyntaxKind.EnableKeyword), true))))
- .WithTrailingTrivia(TriviaList(Trivia(NullableDirectiveTrivia(Token(SyntaxKind.RestoreKeyword), true))))
- .NormalizeWhitespace()
- }
- )
- )
- }
- )
+ if (IsRequest(symbol))
+ {
+ var requestType = GetRequestType(symbol);
+ var responseType = GetResponseType(handlerClassOrInterface);
+ methods.AddRange(HandleRequestRequests(handlerClassOrInterface, symbol, requestType, responseType!, registry, additionalUsings, attributeData));
}
- );
+ }
}
private IEnumerable HandleNotifications(
- InterfaceDeclarationSyntax handlerInterface,
+ TypeDeclarationSyntax handlerInterface,
INamedTypeSymbol interfaceType,
INamedTypeSymbol requestType,
NameSyntax registryType,
- HashSet additionalUsings
+ HashSet additionalUsings,
+ AttributeData attributeData
)
{
- var methodName = GetOnMethodName(interfaceType, _attributeData);
+ var methodName = GetOnMethodName(interfaceType, attributeData);
var parameters = ParameterList(
SeparatedList(
@@ -223,15 +347,16 @@ MemberDeclarationSyntax MakeAction(TypeSyntax syntax)
}
private IEnumerable HandleRequest(
- InterfaceDeclarationSyntax handlerInterface,
+ TypeDeclarationSyntax handlerInterface,
INamedTypeSymbol interfaceType,
INamedTypeSymbol requestType,
TypeSyntax responseType,
NameSyntax registryType,
- HashSet additionalUsings
+ HashSet additionalUsings,
+ AttributeData attributeData
)
{
- var methodName = GetOnMethodName(interfaceType, _attributeData);
+ var methodName = GetOnMethodName(interfaceType, attributeData);
var capability = GetCapability(interfaceType);
var registrationOptions = GetRegistrationOptions(interfaceType);
@@ -252,10 +377,10 @@ HashSet additionalUsings
)
);
- var allowDerivedRequests = _attributeData.NamedArguments
- .Where(z => z.Key == "AllowDerivedRequests")
- .Select(z => z.Value.Value)
- .FirstOrDefault() is bool b && b;
+ var allowDerivedRequests = attributeData.NamedArguments
+ .Where(z => z.Key == "AllowDerivedRequests")
+ .Select(z => z.Value.Value)
+ .FirstOrDefault() is bool b && b;
if (registrationOptions == null)
@@ -344,7 +469,9 @@ MemberDeclarationSyntax MakeDerivedAction(TypeSyntax syntax)
yield return MakeAction(CreatePartialAction(requestType, partialTypeSyntax, false));
if (capability != null)
{
- method = method.WithExpressionBody(GetPartialResultCapabilityHandlerExpression(GetMethodName(handlerInterface), requestType, partialTypeSyntax, responseType, capability));
+ method = method.WithExpressionBody(
+ GetPartialResultCapabilityHandlerExpression(GetMethodName(handlerInterface), requestType, partialTypeSyntax, responseType, capability)
+ );
yield return MakeAction(CreatePartialAction(requestType, partialTypeSyntax, capability));
}
}
@@ -358,6 +485,7 @@ MemberDeclarationSyntax MakeDerivedAction(TypeSyntax syntax)
{
method = method.WithExpressionBody(GetVoidRequestCapabilityHandlerExpression(GetMethodName(handlerInterface), requestType, capability));
}
+
yield return MakeAction(CreateAsyncFunc(responseType, requestType, capability));
}
}
@@ -456,7 +584,9 @@ MemberDeclarationSyntax MakeDerivedAction(TypeSyntax syntax)
{
var partialTypeSyntax = ResolveTypeName(partialItem);
- method = method.WithBody(GetPartialResultRegistrationHandlerExpression(GetMethodName(handlerInterface), requestType, partialTypeSyntax, responseType, registrationOptions));
+ method = method.WithBody(
+ GetPartialResultRegistrationHandlerExpression(GetMethodName(handlerInterface), requestType, partialTypeSyntax, responseType, registrationOptions)
+ );
yield return MakeAction(CreatePartialAction(requestType, partialTypeSyntax, true));
yield return MakeAction(CreatePartialAction(requestType, partialTypeSyntax, false));
@@ -489,14 +619,18 @@ MemberDeclarationSyntax MakeDerivedAction(TypeSyntax syntax)
private static IEnumerable GetRegistries(
AttributeData attributeData,
- InterfaceDeclarationSyntax interfaceSyntax,
+ TypeDeclarationSyntax interfaceSyntax,
INamedTypeSymbol interfaceType,
- TransformationContext context,
- IProgress progress,
+ GeneratorExecutionContext context,
HashSet additionalUsings
)
{
- if (attributeData.ConstructorArguments[0].Values.Length > 0)
+ if (attributeData.ConstructorArguments[0].Kind != TypedConstantKind.Array)
+ {
+ if (attributeData.ConstructorArguments[0].Value is INamedTypeSymbol namedTypeSymbol)
+ return new[] { ResolveTypeName(namedTypeSymbol) };
+ }
+ else if (attributeData.ConstructorArguments[0].Kind == TypedConstantKind.Array && attributeData.ConstructorArguments[0].Values.Length > 0)
{
return attributeData.ConstructorArguments[0].Values.Select(z => z.Value).OfType()
.Select(ResolveTypeName);
@@ -507,7 +641,7 @@ HashSet additionalUsings
var attribute = interfaceType.GetAttributes().First(z => z.AttributeClass?.Name == "MethodAttribute");
if (attribute.ConstructorArguments.Length < 2)
{
- progress.Report(Diagnostic.Create(GeneratorDiagnostics.MissingDirection, interfaceSyntax.Identifier.GetLocation()));
+ context.ReportDiagnostic(Diagnostic.Create(GeneratorDiagnostics.MissingDirection, interfaceSyntax.Identifier.GetLocation()));
return Enumerable.Empty();
}
@@ -526,13 +660,13 @@ HashSet additionalUsings
{
additionalUsings.Add("OmniSharp.Extensions.LanguageServer.Protocol.Client");
additionalUsings.Add("OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities");
- return new[] { LanguageProtocolServerToClient };
+ return new[] { LanguageProtocolServerToClientRegistry };
}
if (maskedDirection == 2)
{
additionalUsings.Add("OmniSharp.Extensions.LanguageServer.Protocol.Server");
- return new[] { LanguageProtocolClientToServer };
+ return new[] { LanguageProtocolClientToServerRegistry };
}
if (maskedDirection == 3)
@@ -540,7 +674,7 @@ HashSet additionalUsings
additionalUsings.Add("OmniSharp.Extensions.LanguageServer.Protocol.Client");
additionalUsings.Add("OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities");
additionalUsings.Add("OmniSharp.Extensions.LanguageServer.Protocol.Server");
- return new[] { LanguageProtocolClientToServer, LanguageProtocolServerToClient };
+ return new[] { LanguageProtocolClientToServerRegistry, LanguageProtocolServerToClientRegistry };
}
}
@@ -549,7 +683,7 @@ HashSet additionalUsings
var attribute = interfaceType.GetAttributes().First(z => z.AttributeClass?.Name == "MethodAttribute");
if (attribute.ConstructorArguments.Length < 2)
{
- progress.Report(Diagnostic.Create(GeneratorDiagnostics.MissingDirection, interfaceSyntax.Identifier.GetLocation()));
+ context.ReportDiagnostic(Diagnostic.Create(GeneratorDiagnostics.MissingDirection, interfaceSyntax.Identifier.GetLocation()));
return Enumerable.Empty();
}
@@ -566,6 +700,117 @@ HashSet additionalUsings
additionalUsings.Add("OmniSharp.Extensions.DebugAdapter.Protocol.Client");
additionalUsings.Add("OmniSharp.Extensions.DebugAdapter.Protocol.Server");
+ if (maskedDirection == 1)
+ {
+ return new[] { DebugProtocolServerToClientRegistry };
+ }
+
+ if (maskedDirection == 2)
+ {
+ return new[] { DebugProtocolClientToServerRegistry };
+ }
+
+ if (maskedDirection == 3)
+ {
+ return new[] { DebugProtocolClientToServerRegistry, DebugProtocolServerToClientRegistry };
+ }
+ }
+
+ throw new NotImplementedException("Add inference logic here " + interfaceSyntax.Identifier.ToFullString());
+ }
+
+ private static NameSyntax LanguageProtocolServerToClientRegistry { get; } =
+ IdentifierName("ILanguageClientRegistry");
+
+ private static NameSyntax LanguageProtocolClientToServerRegistry { get; } =
+ IdentifierName("ILanguageServerRegistry");
+
+ private static NameSyntax DebugProtocolServerToClientRegistry { get; } =
+ IdentifierName("IDebugAdapterClientRegistry");
+
+ private static NameSyntax DebugProtocolClientToServerRegistry { get; } =
+ IdentifierName("IDebugAdapterServerRegistry");
+
+
+ public static IEnumerable GetProxies(
+ GeneratorExecutionContext context,
+ AttributeData attributeData,
+ TypeDeclarationSyntax interfaceSyntax,
+ INamedTypeSymbol interfaceType,
+ List methods,
+ HashSet additionalUsings
+ )
+ {
+ if (attributeData.ConstructorArguments[0].Kind != TypedConstantKind.Array)
+ {
+ if (attributeData.ConstructorArguments[0].Value is INamedTypeSymbol namedTypeSymbol)
+ return new[] { ResolveTypeName(namedTypeSymbol) };
+ }
+ else if (attributeData.ConstructorArguments[0].Kind == TypedConstantKind.Array && attributeData.ConstructorArguments[0].Values.Length > 0)
+ {
+ return attributeData.ConstructorArguments[0].Values.Select(z => z.Value).OfType()
+ .Select(ResolveTypeName);
+ }
+
+ if (interfaceType.ContainingNamespace.ToDisplayString().StartsWith("OmniSharp.Extensions.LanguageServer.Protocol"))
+ {
+ var attribute = interfaceType.GetAttributes().First(z => z.AttributeClass?.Name == "MethodAttribute");
+ if (attribute.ConstructorArguments.Length < 2)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(GeneratorDiagnostics.MissingDirection, interfaceSyntax.Identifier.GetLocation()));
+ return Enumerable.Empty();
+ }
+
+ var direction = (int) interfaceType.GetAttributes().First(z => z.AttributeClass?.Name == "MethodAttribute").ConstructorArguments[1].Value;
+
+ /*
+ Unspecified = 0b0000,
+ ServerToClient = 0b0001,
+ ClientToServer = 0b0010,
+ Bidirectional = 0b0011
+ */
+ var maskedDirection = 0b0011 & direction;
+
+ if (maskedDirection == 1)
+ {
+ additionalUsings.Add("OmniSharp.Extensions.LanguageServer.Protocol.Server");
+ return new[] { LanguageProtocolServerToClient };
+ }
+
+ if (maskedDirection == 2)
+ {
+ additionalUsings.Add("OmniSharp.Extensions.LanguageServer.Protocol.Client");
+ return new[] { LanguageProtocolClientToServer };
+ }
+
+ if (maskedDirection == 3)
+ {
+ additionalUsings.Add("OmniSharp.Extensions.LanguageServer.Protocol.Server");
+ additionalUsings.Add("OmniSharp.Extensions.LanguageServer.Protocol.Client");
+ return new[] { LanguageProtocolClientToServer, LanguageProtocolServerToClient };
+ }
+ }
+
+ if (interfaceType.ContainingNamespace.ToDisplayString().StartsWith("OmniSharp.Extensions.DebugAdapter.Protocol"))
+ {
+ var attribute = interfaceType.GetAttributes().First(z => z.AttributeClass?.Name == "MethodAttribute");
+ if (attribute.ConstructorArguments.Length < 2)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(GeneratorDiagnostics.MissingDirection, interfaceSyntax.Identifier.GetLocation()));
+ return Enumerable.Empty();
+ }
+
+ var direction = (int) interfaceType.GetAttributes().First(z => z.AttributeClass?.Name == "MethodAttribute").ConstructorArguments[1].Value;
+
+ /*
+ Unspecified = 0b0000,
+ ServerToClient = 0b0001,
+ ClientToServer = 0b0010,
+ Bidirectional = 0b0011
+ */
+ var maskedDirection = 0b0011 & direction;
+ additionalUsings.Add("OmniSharp.Extensions.DebugAdapter.Protocol");
+
if (maskedDirection == 1)
{
return new[] { DebugProtocolServerToClient };
@@ -586,24 +831,199 @@ HashSet additionalUsings
}
private static NameSyntax LanguageProtocolServerToClient { get; } =
- IdentifierName("ILanguageClientRegistry");
+ ParseName("ILanguageServer");
private static NameSyntax LanguageProtocolClientToServer { get; } =
- IdentifierName("ILanguageServerRegistry");
+ ParseName("ILanguageClient");
private static NameSyntax DebugProtocolServerToClient { get; } =
- IdentifierName("IDebugAdapterClientRegistry");
+ ParseName("IDebugAdapterServer");
private static NameSyntax DebugProtocolClientToServer { get; } =
- IdentifierName("IDebugAdapterServerRegistry");
+ ParseName("IDebugAdapterClient");
- public Task> GenerateAsync(TransformationContext context, IProgress progress, CancellationToken cancellationToken) =>
- throw new NotImplementedException();
- }
+ private IEnumerable HandleRequestNotifications(
+ TypeDeclarationSyntax handlerInterface,
+ INamedTypeSymbol interfaceType,
+ INamedTypeSymbol requestType,
+ NameSyntax registryType,
+ HashSet additionalUsings,
+ AttributeData attributeData
+ )
+ {
+ var methodName = GetSendMethodName(interfaceType, attributeData);
+ var method = MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), methodName)
+ .WithModifiers(
+ TokenList(
+ Token(SyntaxKind.PublicKeyword),
+ Token(SyntaxKind.StaticKeyword)
+ )
+ )
+ .WithExpressionBody(GetNotificationInvokeExpression())
+ .WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
+
+ yield return method
+ .WithParameterList(
+ ParameterList(
+ SeparatedList(
+ new[] {
+ Parameter(Identifier("mediator"))
+ .WithType(registryType)
+ .WithModifiers(TokenList(Token(SyntaxKind.ThisKeyword))),
+ Parameter(Identifier("@params"))
+ .WithType(IdentifierName(requestType.Name))
+ }
+ )
+ )
+ )
+ .NormalizeWhitespace();
+ }
+
+ private IEnumerable HandleRequestRequests(
+ TypeDeclarationSyntax handlerInterface,
+ INamedTypeSymbol interfaceType,
+ INamedTypeSymbol requestType,
+ TypeSyntax responseType,
+ NameSyntax registryType,
+ HashSet additionalUsings,
+ AttributeData attributeData
+ )
+ {
+ var methodName = GetSendMethodName(interfaceType, attributeData);
+ var parameterList = ParameterList(
+ SeparatedList(
+ new[] {
+ Parameter(Identifier("mediator"))
+ .WithType(registryType)
+ .WithModifiers(TokenList(Token(SyntaxKind.ThisKeyword))),
+ Parameter(Identifier("@params"))
+ .WithType(IdentifierName(requestType.Name)),
+ Parameter(Identifier("cancellationToken"))
+ .WithType(IdentifierName("CancellationToken"))
+ .WithDefault(
+ EqualsValueClause(
+ LiteralExpression(SyntaxKind.DefaultLiteralExpression, Token(SyntaxKind.DefaultKeyword))
+ )
+ )
+ }
+ )
+ );
+ var partialItem = GetPartialItem(requestType);
+ if (partialItem != null)
+ {
+ additionalUsings.Add("OmniSharp.Extensions.LanguageServer.Protocol.Progress");
+ var partialTypeSyntax = ResolveTypeName(partialItem);
+ yield return MethodDeclaration(
+ GenericName(
+ Identifier("IRequestProgressObservable")
+ )
+ .WithTypeArgumentList(
+ TypeArgumentList(
+ SeparatedList(
+ new TypeSyntax[] {
+ partialTypeSyntax,
+ responseType
+ }
+ )
+ )
+ ),
+ Identifier(methodName)
+ )
+ .WithModifiers(
+ TokenList(
+ Token(SyntaxKind.PublicKeyword),
+ Token(SyntaxKind.StaticKeyword)
+ )
+ )
+ .WithParameterList(parameterList)
+ .WithExpressionBody(GetPartialInvokeExpression(responseType))
+ .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
+ .NormalizeWhitespace();
+ yield break;
+ }
+
+ var partialItems = GetPartialItems(requestType);
+ if (partialItems != null)
+ {
+ additionalUsings.Add("OmniSharp.Extensions.LanguageServer.Protocol.Progress");
+ var partialTypeSyntax = ResolveTypeName(partialItems);
+ var partialItemsSyntax = GenericName("IEnumerable").WithTypeArgumentList(TypeArgumentList(SeparatedList(new[] { partialTypeSyntax })));
+ yield return MethodDeclaration(
+ GenericName(
+ Identifier("IRequestProgressObservable")
+ )
+ .WithTypeArgumentList(
+ TypeArgumentList(
+ SeparatedList(
+ new TypeSyntax[] {
+ partialItemsSyntax,
+ responseType
+ }
+ )
+ )
+ ),
+ Identifier(methodName)
+ )
+ .WithModifiers(
+ TokenList(
+ Token(SyntaxKind.PublicKeyword),
+ Token(SyntaxKind.StaticKeyword)
+ )
+ )
+ .WithParameterList(parameterList)
+ .WithExpressionBody(GetPartialInvokeExpression(responseType))
+ .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
+ .NormalizeWhitespace();
+ ;
+ yield break;
+ }
+
+
+ var responseSyntax = responseType.ToFullString().EndsWith("Unit")
+ ? IdentifierName("Task") as NameSyntax
+ : GenericName("Task").WithTypeArgumentList(TypeArgumentList(SeparatedList(new[] { responseType })));
+ yield return MethodDeclaration(responseSyntax, methodName)
+ .WithModifiers(
+ TokenList(
+ Token(SyntaxKind.PublicKeyword),
+ Token(SyntaxKind.StaticKeyword)
+ )
+ )
+ .WithParameterList(parameterList)
+ .WithExpressionBody(GetRequestInvokeExpression())
+ .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
+ .NormalizeWhitespace();
+ }
- /*
- IJsonRpcNotificationHandler
- IJsonRpcRequestHandler
- IJsonRpcRequestHandler
- */
+
+ ///
+ /// Created on demand before each generation pass
+ ///
+ internal class SyntaxReceiver : ISyntaxReceiver
+ {
+ public List Candidates { get; } = new List();
+
+ ///
+ /// Called for every syntax node in the compilation, we can inspect the nodes and save any information useful for generation
+ ///
+ public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
+ {
+ // any field with at least one attribute is a candidate for property generation
+ if (syntaxNode is ClassDeclarationSyntax classDeclarationSyntax &&
+ classDeclarationSyntax.AttributeLists.Count > 0
+ )
+ {
+ Candidates.Add(classDeclarationSyntax);
+ }
+
+ // any field with at least one attribute is a candidate for property generation
+ if (syntaxNode is InterfaceDeclarationSyntax interfaceDeclarationSyntax &&
+ interfaceDeclarationSyntax.AttributeLists.Count > 0
+ )
+ {
+ Candidates.Add(interfaceDeclarationSyntax);
+ }
+ }
+ }
+ }
}
diff --git a/src/JsonRpc.Generators/GenerateRequestMethodsGenerator.cs b/src/JsonRpc.Generators/GenerateRequestMethodsGenerator.cs
deleted file mode 100644
index 44d90907d..000000000
--- a/src/JsonRpc.Generators/GenerateRequestMethodsGenerator.cs
+++ /dev/null
@@ -1,393 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using CodeGeneration.Roslyn;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
-using static OmniSharp.Extensions.JsonRpc.Generators.Helpers;
-
-namespace OmniSharp.Extensions.JsonRpc.Generators
-{
- public class GenerateRequestMethodsGenerator : IRichCodeGenerator
- {
- private readonly AttributeData _attributeData;
-
- public GenerateRequestMethodsGenerator(AttributeData attributeData) => _attributeData = attributeData;
-
- public Task> GenerateAsync(TransformationContext context, IProgress progress, CancellationToken cancellationToken) =>
- throw new NotImplementedException();
-
- public Task GenerateRichAsync(TransformationContext context, IProgress progress, CancellationToken cancellationToken)
- {
- if (!( context.ProcessingNode is InterfaceDeclarationSyntax handlerInterface ))
- {
- return Task.FromResult(new RichGenerationResult());
- }
-
- var methods = new List();
- var additionalUsings = new HashSet {
- "System",
- "System.Collections.Generic",
- "System.Threading",
- "System.Threading.Tasks",
- "MediatR",
- "Microsoft.Extensions.DependencyInjection",
- };
- var symbol = context.SemanticModel.GetDeclaredSymbol(handlerInterface);
-
- var className = GetExtensionClassName(symbol);
-
- var registries = GetProxies(_attributeData, handlerInterface, symbol, context, progress, additionalUsings);
-
- if (_attributeData.ConstructorArguments[0].Values.Length == 0 && !symbol.ContainingNamespace.ToDisplayString().StartsWith("OmniSharp.Extensions.DebugAdapter.Protocol"))
- {
- progress.Report(
- Diagnostic.Create(
- GeneratorDiagnostics.NoResponseRouterProvided, handlerInterface.Identifier.GetLocation(), symbol.Name,
- string.Join(", ", registries.Select(z => z.ToFullString()))
- )
- );
- }
-
- foreach (var registry in registries)
- {
- if (IsNotification(symbol))
- {
- var requestType = GetRequestType(handlerInterface);
- methods.AddRange(HandleNotifications(handlerInterface, symbol, requestType, registry, additionalUsings));
- }
-
- if (IsRequest(symbol))
- {
- var requestType = GetRequestType(handlerInterface);
- var responseType = GetResponseType(handlerInterface);
- methods.AddRange(HandleRequests(handlerInterface, symbol, requestType, responseType!, registry, additionalUsings));
- }
- }
-
-
- var obsoleteAttribute = handlerInterface.AttributeLists
- .SelectMany(z => z.Attributes)
- .Where(z => z.Name.ToFullString() == nameof(ObsoleteAttribute) || z.Name.ToFullString() == "Obsolete")
- .ToArray();
- var attributes = List(
- new[] {
- AttributeList(
- SeparatedList(
- new[] {
- Attribute(ParseName("System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute")),
- Attribute(ParseName("System.Runtime.CompilerServices.CompilerGeneratedAttribute")),
- }.Union(obsoleteAttribute)
- )
- )
- }
- );
- if (symbol.GetAttributes().Any(z => z.AttributeClass.Name == "GenerateRequestMethodsAttribute"))
- {
- attributes = List();
- }
-
- var existingUsings = context.CompilationUnitUsings
- .Join(additionalUsings, z => z.Name.ToFullString(), z => z, (a, b) => b)
- ;
-
- var newUsings = additionalUsings
- .Except(existingUsings)
- .Select(z => UsingDirective(IdentifierName(z)))
- ;
- var isInternal = handlerInterface.Modifiers.Any(z => z.IsKind(SyntaxKind.InternalKeyword));
- return Task.FromResult(
- new RichGenerationResult {
- Usings = List(newUsings),
- Members = List(
- new[] {
- NamespaceDeclaration(ParseName(symbol.ContainingNamespace.ToDisplayString()))
- .WithMembers(
- List(
- new MemberDeclarationSyntax[] {
- ClassDeclaration(className)
- .WithAttributeLists(attributes)
- .WithModifiers(
- TokenList(
- new[] { isInternal ? Token(SyntaxKind.InternalKeyword) : Token(SyntaxKind.PublicKeyword) }.Concat(
- new[] {
- Token(SyntaxKind.StaticKeyword),
- Token(SyntaxKind.PartialKeyword)
- }
- )
- )
- )
- .WithMembers(List(methods))
- .WithLeadingTrivia(TriviaList(Trivia(NullableDirectiveTrivia(Token(SyntaxKind.EnableKeyword), true))))
- .WithTrailingTrivia(TriviaList(Trivia(NullableDirectiveTrivia(Token(SyntaxKind.RestoreKeyword), true))))
- .NormalizeWhitespace()
- }
- )
- )
- }
- )
- }
- );
- }
-
- public static IEnumerable GetProxies(
- AttributeData attributeData,
- InterfaceDeclarationSyntax interfaceSyntax,
- INamedTypeSymbol interfaceType,
- TransformationContext context,
- IProgress progress,
- HashSet additionalUsings
- )
- {
- if (attributeData.ConstructorArguments[0].Values.Length > 0)
- {
- return attributeData.ConstructorArguments[0].Values.Select(z => z.Value).OfType()
- .Select(ResolveTypeName);
- }
-
- if (interfaceType.ContainingNamespace.ToDisplayString().StartsWith("OmniSharp.Extensions.LanguageServer.Protocol"))
- {
- var attribute = interfaceType.GetAttributes().First(z => z.AttributeClass?.Name == "MethodAttribute");
- if (attribute.ConstructorArguments.Length < 2)
- {
- progress.Report(Diagnostic.Create(GeneratorDiagnostics.MissingDirection, interfaceSyntax.Identifier.GetLocation()));
- return Enumerable.Empty();
- }
-
- var direction = (int) interfaceType.GetAttributes().First(z => z.AttributeClass?.Name == "MethodAttribute").ConstructorArguments[1].Value!;
-
- /*
- Unspecified = 0b0000,
- ServerToClient = 0b0001,
- ClientToServer = 0b0010,
- Bidirectional = 0b0011
- */
- var maskedDirection = 0b0011 & direction;
-
- if (maskedDirection == 1)
- {
- additionalUsings.Add("OmniSharp.Extensions.LanguageServer.Protocol.Server");
- return new[] { LanguageProtocolServerToClient };
- }
-
- if (maskedDirection == 2)
- {
- additionalUsings.Add("OmniSharp.Extensions.LanguageServer.Protocol.Client");
- return new[] { LanguageProtocolClientToServer };
- }
-
- if (maskedDirection == 3)
- {
- additionalUsings.Add("OmniSharp.Extensions.LanguageServer.Protocol.Server");
- additionalUsings.Add("OmniSharp.Extensions.LanguageServer.Protocol.Client");
- return new[] { LanguageProtocolClientToServer, LanguageProtocolServerToClient };
- }
- }
-
- if (interfaceType.ContainingNamespace.ToDisplayString().StartsWith("OmniSharp.Extensions.DebugAdapter.Protocol"))
- {
- var attribute = interfaceType.GetAttributes().First(z => z.AttributeClass?.Name == "MethodAttribute");
- if (attribute.ConstructorArguments.Length < 2)
- {
- progress.Report(Diagnostic.Create(GeneratorDiagnostics.MissingDirection, interfaceSyntax.Identifier.GetLocation()));
- return Enumerable.Empty();
- }
-
- var direction = (int) interfaceType.GetAttributes().First(z => z.AttributeClass?.Name == "MethodAttribute").ConstructorArguments[1].Value!;
-
- /*
- Unspecified = 0b0000,
- ServerToClient = 0b0001,
- ClientToServer = 0b0010,
- Bidirectional = 0b0011
- */
- var maskedDirection = 0b0011 & direction;
- additionalUsings.Add("OmniSharp.Extensions.DebugAdapter.Protocol");
-
- if (maskedDirection == 1)
- {
- return new[] { DebugProtocolServerToClient };
- }
-
- if (maskedDirection == 2)
- {
- return new[] { DebugProtocolClientToServer };
- }
-
- if (maskedDirection == 3)
- {
- return new[] { DebugProtocolClientToServer, DebugProtocolServerToClient };
- }
- }
-
- throw new NotImplementedException("Add inference logic here " + interfaceSyntax.Identifier.ToFullString());
- }
-
- private static NameSyntax LanguageProtocolServerToClient { get; } =
- ParseName("ILanguageServer");
-
- private static NameSyntax LanguageProtocolClientToServer { get; } =
- ParseName("ILanguageClient");
-
- private static NameSyntax DebugProtocolServerToClient { get; } =
- ParseName("IDebugAdapterServer");
-
- private static NameSyntax DebugProtocolClientToServer { get; } =
- ParseName("IDebugAdapterClient");
-
- private IEnumerable HandleNotifications(
- InterfaceDeclarationSyntax handlerInterface,
- INamedTypeSymbol interfaceType,
- TypeSyntax requestType,
- NameSyntax registryType,
- HashSet additionalUsings
- )
- {
- var methodName = GetSendMethodName(interfaceType, _attributeData);
- var method = MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), methodName)
- .WithModifiers(
- TokenList(
- Token(SyntaxKind.PublicKeyword),
- Token(SyntaxKind.StaticKeyword)
- )
- )
- .WithExpressionBody(GetNotificationInvokeExpression())
- .WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
-
- yield return method
- .WithParameterList(
- ParameterList(
- SeparatedList(
- new[] {
- Parameter(Identifier("mediator"))
- .WithType(registryType)
- .WithModifiers(TokenList(Token(SyntaxKind.ThisKeyword))),
- Parameter(Identifier("@params"))
- .WithType(requestType)
- }
- )
- )
- )
- .NormalizeWhitespace();
- }
-
- private IEnumerable HandleRequests(
- InterfaceDeclarationSyntax handlerInterface,
- INamedTypeSymbol interfaceType,
- TypeSyntax requestType,
- TypeSyntax responseType,
- NameSyntax registryType,
- HashSet additionalUsings
- )
- {
- var methodName = GetSendMethodName(interfaceType, _attributeData);
- var parameterList = ParameterList(
- SeparatedList(
- new[] {
- Parameter(Identifier("mediator"))
- .WithType(registryType)
- .WithModifiers(TokenList(Token(SyntaxKind.ThisKeyword))),
- Parameter(Identifier("@params"))
- .WithType(requestType),
- Parameter(Identifier("cancellationToken"))
- .WithType(IdentifierName("CancellationToken"))
- .WithDefault(
- EqualsValueClause(
- LiteralExpression(SyntaxKind.DefaultLiteralExpression, Token(SyntaxKind.DefaultKeyword))
- )
- )
- }
- )
- );
- var partialItem = GetPartialItem(GetRequestType(interfaceType));
- if (partialItem != null)
- {
- additionalUsings.Add("OmniSharp.Extensions.LanguageServer.Protocol.Progress");
- var partialTypeSyntax = ResolveTypeName(partialItem);
- yield return MethodDeclaration(
- GenericName(
- Identifier("IRequestProgressObservable")
- )
- .WithTypeArgumentList(
- TypeArgumentList(
- SeparatedList(
- new TypeSyntax[] {
- partialTypeSyntax,
- responseType
- }
- )
- )
- ),
- Identifier(methodName)
- )
- .WithModifiers(
- TokenList(
- Token(SyntaxKind.PublicKeyword),
- Token(SyntaxKind.StaticKeyword)
- )
- )
- .WithParameterList(parameterList)
- .WithExpressionBody(GetPartialInvokeExpression(responseType))
- .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
- .NormalizeWhitespace();
- yield break;
- }
-
- var partialItems = GetPartialItems(GetRequestType(interfaceType));
- if (partialItems != null)
- {
- additionalUsings.Add("OmniSharp.Extensions.LanguageServer.Protocol.Progress");
- var partialTypeSyntax = ResolveTypeName(partialItems);
- var partialItemsSyntax = GenericName("IEnumerable").WithTypeArgumentList(TypeArgumentList(SeparatedList(new[] { partialTypeSyntax })));
- yield return MethodDeclaration(
- GenericName(
- Identifier("IRequestProgressObservable")
- )
- .WithTypeArgumentList(
- TypeArgumentList(
- SeparatedList(
- new TypeSyntax[] {
- partialItemsSyntax,
- responseType
- }
- )
- )
- ),
- Identifier(methodName)
- )
- .WithModifiers(
- TokenList(
- Token(SyntaxKind.PublicKeyword),
- Token(SyntaxKind.StaticKeyword)
- )
- )
- .WithParameterList(parameterList)
- .WithExpressionBody(GetPartialInvokeExpression(responseType))
- .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
- .NormalizeWhitespace();
-
- yield break;
- }
-
-
- var responseSyntax = responseType.ToFullString().EndsWith("Unit")
- ? IdentifierName("Task") as NameSyntax
- : GenericName("Task").WithTypeArgumentList(TypeArgumentList(SeparatedList(new[] { responseType })));
- yield return MethodDeclaration(responseSyntax, methodName)
- .WithModifiers(
- TokenList(
- Token(SyntaxKind.PublicKeyword),
- Token(SyntaxKind.StaticKeyword)
- )
- )
- .WithParameterList(parameterList)
- .WithExpressionBody(GetRequestInvokeExpression())
- .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
- .NormalizeWhitespace();
- }
- }
-}
diff --git a/src/JsonRpc.Generators/GeneratorDiagnostics.cs b/src/JsonRpc.Generators/GeneratorDiagnostics.cs
index 76b4f835a..3642d47d0 100644
--- a/src/JsonRpc.Generators/GeneratorDiagnostics.cs
+++ b/src/JsonRpc.Generators/GeneratorDiagnostics.cs
@@ -6,17 +6,36 @@ internal static class GeneratorDiagnostics
{
public static DiagnosticDescriptor MissingDirection { get; } = new DiagnosticDescriptor(
"LSP1000", "Missing Direction",
- "No direction defined for Language Server Protocol Handler", "JsonRPC", DiagnosticSeverity.Warning, true
+ "No direction defined for Language Server Protocol Handler", "LSP", DiagnosticSeverity.Warning, true
+ );
+ public static DiagnosticDescriptor Exception { get; } = new DiagnosticDescriptor(
+ "JRPC0001", "Exception",
+ "{0}", "JRPC", DiagnosticSeverity.Error, true
);
public static DiagnosticDescriptor NoHandlerRegistryProvided { get; } = new DiagnosticDescriptor(
"JRPC1000", "No Handler Registry Provided",
- "No Handler Registry Provided for handler {0}.", "JsonRPC", DiagnosticSeverity.Warning, true
+ "No Handler Registry Provided for handler {0}.", "JsonRPC", DiagnosticSeverity.Info, true
);
public static DiagnosticDescriptor NoResponseRouterProvided { get; } = new DiagnosticDescriptor(
"JRPC1001", "No Response Router Provided",
- "No Response Router Provided for handler {0}, defaulting to {1}.", "JsonRPC", DiagnosticSeverity.Warning, true
+ "No Response Router Provided for handler {0}, defaulting to {1}.", "JsonRPC", DiagnosticSeverity.Info, true
+ );
+
+ public static DiagnosticDescriptor ClassMustBePartial { get; } = new DiagnosticDescriptor(
+ "JRPC1002", "Class must be made partial",
+ "Class {0} must be made partial.", "JsonRPC", DiagnosticSeverity.Warning, true
+ );
+
+ public static DiagnosticDescriptor MustInheritFromCanBeResolved { get; } = new DiagnosticDescriptor(
+ "LSP1001", "The target class must implement ICanBeResolved",
+ "The target class must implement ICanBeResolved", "LSP", DiagnosticSeverity.Error, true
+ );
+
+ public static DiagnosticDescriptor MustInheritFromCanHaveData { get; } = new DiagnosticDescriptor(
+ "LSP1002", "The target class must implement ICanHaveData",
+ "The target class must implement ICanHaveData", "LSP", DiagnosticSeverity.Error, true
);
}
}
diff --git a/src/JsonRpc.Generators/Helpers.cs b/src/JsonRpc.Generators/Helpers.cs
index a74c98e65..7aab668bd 100644
--- a/src/JsonRpc.Generators/Helpers.cs
+++ b/src/JsonRpc.Generators/Helpers.cs
@@ -16,7 +16,7 @@ internal static class Helpers
public static bool IsRequest(INamedTypeSymbol symbol) => symbol.AllInterfaces.Any(z => z.Name == "IJsonRpcRequestHandler");
- public static ExpressionSyntax GetMethodName(InterfaceDeclarationSyntax interfaceSyntax)
+ public static ExpressionSyntax GetMethodName(TypeDeclarationSyntax interfaceSyntax)
{
var methodAttribute = interfaceSyntax.AttributeLists
.SelectMany(z => z.Attributes)
diff --git a/src/JsonRpc.Generators/JsonRpc.Generators.csproj b/src/JsonRpc.Generators/JsonRpc.Generators.csproj
index 3a1bf25f0..e9cdf72c9 100644
--- a/src/JsonRpc.Generators/JsonRpc.Generators.csproj
+++ b/src/JsonRpc.Generators/JsonRpc.Generators.csproj
@@ -1,17 +1,25 @@
-
-
- netstandard2.0
+ netstandard2.0
OmniSharp.Extensions.JsonRpc.Generators
OmniSharp.Extensions.JsonRpc.Generators
- false
+ true
+
+
+
-
-
+
+
+
+ <_Parameter1>Generation.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100391db875e68eb4bfef49ce14313b9e13f2cd3cc89eb273bbe6c11a55044c7d4f566cf092e1c77ef9e7c75b1496ae7f95d925938f5a01793dd8d9f99ae0a7595779b71b971287d7d7b5960d052078d14f5ce1a85ea5c9fb2f59ac735ff7bc215cab469b7c3486006860bad6f4c3b5204ea2f28dd4e1d05e2cca462cfd593b9f9f
+
+
+
+
+
diff --git a/src/JsonRpc.Generators/Preamble.cs b/src/JsonRpc.Generators/Preamble.cs
new file mode 100644
index 000000000..cf24c3b87
--- /dev/null
+++ b/src/JsonRpc.Generators/Preamble.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace OmniSharp.Extensions.JsonRpc.Generators
+{
+ public static class Preamble {
+ ///
+ /// A "generated by tool" comment string with environment/os-normalized newlines.
+ ///
+ public static readonly string GeneratedByATool = @"// ------------------------------------------------------------------------------
+//
+// This code was generated a code generator.
+//
+// ------------------------------------------------------------------------------
+".Replace("\r\n", "\n").Replace("\n", Environment.NewLine); // normalize regardless of git checkout policy
+ }
+}
diff --git a/src/JsonRpc.Generators/RegistrationOptionsGenerator.cs b/src/JsonRpc.Generators/RegistrationOptionsGenerator.cs
new file mode 100644
index 000000000..90bb604e8
--- /dev/null
+++ b/src/JsonRpc.Generators/RegistrationOptionsGenerator.cs
@@ -0,0 +1,295 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
+using SyntaxTrivia = Microsoft.CodeAnalysis.SyntaxTrivia;
+
+namespace OmniSharp.Extensions.JsonRpc.Generators
+{
+ [Generator]
+ public class RegistrationOptionsGenerator : ISourceGenerator
+ {
+ private static string[] RequiredUsings = new[] {
+ "OmniSharp.Extensions.LanguageServer.Protocol.Server.Capabilities",
+ "OmniSharp.Extensions.LanguageServer.Protocol",
+ };
+
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ if (!( context.SyntaxReceiver is SyntaxReceiver syntaxReceiver ))
+ {
+ return;
+ }
+
+ var options = ( context.Compilation as CSharpCompilation )?.SyntaxTrees[0].Options as CSharpParseOptions;
+ var compilation = context.Compilation;
+
+ var registrationOptionsAttribute = compilation.GetTypeByMetadataName("OmniSharp.Extensions.LanguageServer.Protocol.RegistrationOptionsAttribute")!;
+ var textDocumentAttributeSymbol = compilation.GetTypeByMetadataName("OmniSharp.Extensions.LanguageServer.Protocol.TextDocumentAttribute")!;
+ var workDoneProgressAttributeSymbol = compilation.GetTypeByMetadataName("OmniSharp.Extensions.LanguageServer.Protocol.WorkDoneProgressAttribute")!;
+ var registrationOptionsConverterAttributeSymbol =
+ compilation.GetTypeByMetadataName("OmniSharp.Extensions.LanguageServer.Protocol.RegistrationOptionsConverterAttribute")!;
+ var registrationOptionsInterfaceSymbol = compilation.GetTypeByMetadataName("OmniSharp.Extensions.LanguageServer.Protocol.IRegistrationOptions")!;
+ var textDocumentRegistrationOptionsInterfaceSymbol =
+ compilation.GetTypeByMetadataName("OmniSharp.Extensions.LanguageServer.Protocol.Models.ITextDocumentRegistrationOptions")!;
+ var workDoneProgressOptionsInterfaceSymbol = compilation.GetTypeByMetadataName("OmniSharp.Extensions.LanguageServer.Protocol.Models.IWorkDoneProgressOptions")!;
+
+ foreach (var registrationOptions in syntaxReceiver.RegistrationOptions)
+ {
+ var semanticModel = context.Compilation.GetSemanticModel(registrationOptions.SyntaxTree);
+ var typeSymbol = semanticModel.GetDeclaredSymbol(registrationOptions);
+ var hasAttribute = typeSymbol?.GetAttributes().Any(z => SymbolEqualityComparer.Default.Equals(z.AttributeClass, registrationOptionsAttribute));
+ if (typeSymbol == null || typeSymbol.IsAbstract || hasAttribute != true) continue;
+
+ var converterAttribute = typeSymbol.GetAttributes()
+ .FirstOrDefault(z => SymbolEqualityComparer.Default.Equals(z.AttributeClass, registrationOptionsConverterAttributeSymbol));
+
+ var extendedRegistrationOptions = registrationOptions
+ .WithAttributeLists(List())
+ .WithBaseList(
+ BaseList(
+ SingletonSeparatedList(
+ SimpleBaseType(ParseName(registrationOptionsInterfaceSymbol.ToDisplayString()))
+ )
+ )
+ )
+ .WithMembers(List());
+
+ var staticRegistrationOptions = registrationOptions
+ .WithIdentifier(Identifier($"Static{registrationOptions.Identifier.Text}"))
+ .WithMembers(List(registrationOptions.Members.OfType()))
+ .WithAttributeLists(List());
+
+ if (typeSymbol.GetAttributes().Any(z => SymbolEqualityComparer.Default.Equals(z.AttributeClass, textDocumentAttributeSymbol)))
+ {
+ extendedRegistrationOptions = ExtendAndImplementInterface(extendedRegistrationOptions, textDocumentRegistrationOptionsInterfaceSymbol)
+ .AddMembers(
+ PropertyDeclaration(NullableType(IdentifierName("DocumentSelector")), Identifier("DocumentSelector"))
+ .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword)))
+ .WithAccessorList(
+ AccessorList(
+ List(
+ new[] {
+ AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
+ .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
+ AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
+ .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
+ }
+ )
+ )
+ )
+ );
+ }
+
+ if (typeSymbol.GetAttributes().Any(z => SymbolEqualityComparer.Default.Equals(z.AttributeClass, workDoneProgressAttributeSymbol)))
+ {
+ extendedRegistrationOptions = ExtendAndImplementInterface(extendedRegistrationOptions, workDoneProgressOptionsInterfaceSymbol)
+ .AddMembers(GetWorkDoneProperty());
+ staticRegistrationOptions = ExtendAndImplementInterface(staticRegistrationOptions, workDoneProgressOptionsInterfaceSymbol)
+ .AddMembers(GetWorkDoneProperty());
+ }
+
+ if (converterAttribute == null)
+ {
+ var converter = CreateConverter(registrationOptions);
+ extendedRegistrationOptions = extendedRegistrationOptions
+ .AddAttributeLists(
+ AttributeList(
+ SingletonSeparatedList(
+ Attribute(
+ IdentifierName("RegistrationOptionsConverterAttribute"),
+ AttributeArgumentList(
+ SingletonSeparatedList(
+ AttributeArgument(
+ TypeOfExpression(IdentifierName(converter.Identifier.Text))
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ .AddMembers(converter);
+ }
+
+ var cu = CompilationUnit()
+ .WithUsings(registrationOptions.SyntaxTree.GetCompilationUnitRoot().Usings)
+ .AddMembers(
+ NamespaceDeclaration(ParseName(typeSymbol.ContainingNamespace.ToDisplayString()))
+ .AddMembers(extendedRegistrationOptions, staticRegistrationOptions)
+ .WithLeadingTrivia(TriviaList(Trivia(NullableDirectiveTrivia(Token(SyntaxKind.EnableKeyword), true))))
+ .WithTrailingTrivia(TriviaList(Trivia(NullableDirectiveTrivia(Token(SyntaxKind.RestoreKeyword), true))))
+ )
+ .WithLeadingTrivia(Comment(Preamble.GeneratedByATool))
+ .WithTrailingTrivia(CarriageReturnLineFeed);
+
+ foreach (var ns in RequiredUsings)
+ {
+ if (cu.Usings.All(z => z.Name.ToFullString() != ns))
+ {
+ cu = cu.AddUsings(UsingDirective(ParseName(ns)));
+ }
+ }
+
+ context.AddSource(
+ $"{registrationOptions.Identifier.Text}Container.cs",
+ cu.NormalizeWhitespace().SyntaxTree.GetRoot().GetText(Encoding.UTF8)
+ );
+ }
+
+ static ClassDeclarationSyntax ExtendAndImplementInterface(ClassDeclarationSyntax syntax, ITypeSymbol symbolToExtendFrom)
+ {
+ return syntax
+ .AddBaseListTypes(SimpleBaseType(ParseName(symbolToExtendFrom.Name)));
+ }
+
+ static PropertyDeclarationSyntax GetWorkDoneProperty()
+ {
+ return PropertyDeclaration(PredefinedType(Token(SyntaxKind.BoolKeyword)), Identifier("WorkDoneProgress"))
+ .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword)))
+ .WithAttributeLists(
+ SingletonList(
+ AttributeList(
+ SingletonSeparatedList(Attribute(IdentifierName("Optional")))
+ )
+ )
+ )
+ .WithAccessorList(
+ AccessorList(
+ List(
+ new[] {
+ AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
+ AccessorDeclaration(SyntaxKind.SetAccessorDeclaration).WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
+ }
+ )
+ )
+ );
+ }
+ }
+
+ private static IEnumerable GetMapping(ClassDeclarationSyntax syntax, IdentifierNameSyntax paramName)
+ {
+ return syntax.Members.OfType()
+ .Where(z => z.AccessorList?.Accessors.Any(a => a.Keyword.Kind() == SyntaxKind.SetKeyword || a.Keyword.Kind() == SyntaxKind.InitKeyword) == true)
+ .Select(
+ property => AssignmentExpression(
+ SyntaxKind.SimpleAssignmentExpression,
+ IdentifierName(property.Identifier.Text),
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ paramName,
+ IdentifierName(property.Identifier.Text)
+ )
+ )
+ );
+ }
+
+ private ClassDeclarationSyntax CreateConverter(ClassDeclarationSyntax syntax)
+ {
+ var attribute = syntax.AttributeLists
+ .SelectMany(z => z.Attributes)
+ .FirstOrDefault(z => z.Name.ToFullString().Contains("RegistrationOptions") && !z.Name.ToFullString().Contains("RegistrationOptionsConverter"));
+
+ return ClassDeclaration($"{syntax.Identifier.Text}Converter")
+ .WithBaseList(
+ BaseList(
+ SingletonSeparatedList(
+ SimpleBaseType(
+ GenericName(Identifier("RegistrationOptionsConverterBase"))
+ .WithTypeArgumentList(
+ TypeArgumentList(
+ SeparatedList(
+ new SyntaxNodeOrToken[] {
+ IdentifierName(syntax.Identifier.Text),
+ Token(SyntaxKind.CommaToken),
+ IdentifierName($"Static{syntax.Identifier.Text}")
+ }
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ .WithMembers(
+ List(
+ new MemberDeclarationSyntax[] {
+ ConstructorDeclaration(Identifier("Converter"))
+ .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword)))
+ .WithInitializer(
+ ConstructorInitializer(
+ SyntaxKind.BaseConstructorInitializer,
+ ArgumentList(SingletonSeparatedList(Argument(attribute!.ArgumentList!.Arguments[0].Expression)))
+ )
+ )
+ .WithBody(Block()),
+ MethodDeclaration(IdentifierName($"Static{syntax.Identifier.Text}"), Identifier("Convert"))
+ .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.OverrideKeyword)))
+ .WithParameterList(
+ ParameterList(
+ SingletonSeparatedList(
+ Parameter(Identifier("source"))
+ .WithType(IdentifierName(syntax.Identifier.Text))
+ )
+ )
+ )
+ .WithBody(
+ Block(
+ SingletonList(
+ ReturnStatement(
+ ObjectCreationExpression(
+ IdentifierName($"Static{syntax.Identifier.Text}")
+ )
+ .WithInitializer(
+ InitializerExpression(
+ SyntaxKind.ObjectInitializerExpression,
+ SeparatedList(
+ GetMapping(syntax, IdentifierName("source")).ToArray()
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ }
+ )
+ );
+ }
+
+ ///
+ /// Created on demand before each generation pass
+ ///
+ internal class SyntaxReceiver : ISyntaxReceiver
+ {
+ public List RegistrationOptions { get; } = new();
+
+ ///
+ /// Called for every syntax node in the compilation, we can inspect the nodes and save any information useful for generation
+ ///
+ public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
+ {
+ // any field with at least one attribute is a candidate for property generation
+ if (syntaxNode is ClassDeclarationSyntax classDeclarationSyntax)
+ {
+ if (classDeclarationSyntax.AttributeLists
+ .SelectMany(z => z.Attributes)
+ .Any(z => z.Name.ToFullString().Contains("RegistrationOptions"))
+ )
+ {
+ RegistrationOptions.Add(classDeclarationSyntax);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/JsonRpc.Generators/StronglyTypedGenerator.cs b/src/JsonRpc.Generators/StronglyTypedGenerator.cs
new file mode 100644
index 000000000..5b53c71af
--- /dev/null
+++ b/src/JsonRpc.Generators/StronglyTypedGenerator.cs
@@ -0,0 +1,872 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
+using SyntaxTrivia = Microsoft.CodeAnalysis.SyntaxTrivia;
+
+namespace OmniSharp.Extensions.JsonRpc.Generators
+{
+ [Generator]
+ public class StronglyTypedGenerator : ISourceGenerator
+ {
+ private static string[] RequiredUsings = new[] {
+ "System.Collections.Generic",
+ "System.Collections.ObjectModel",
+ "System.Collections.Immutable",
+ "System.Linq",
+ };
+
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ if (!( context.SyntaxReceiver is SyntaxReceiver syntaxReceiver ))
+ {
+ return;
+ }
+
+ var options = ( context.Compilation as CSharpCompilation )?.SyntaxTrees[0].Options as CSharpParseOptions;
+ var compilation = context.Compilation;
+
+ var canBeResolvedSymbol = compilation.GetTypeByMetadataName("OmniSharp.Extensions.LanguageServer.Protocol.Models.ICanBeResolved");
+ var canHaveDataSymbol = compilation.GetTypeByMetadataName("OmniSharp.Extensions.LanguageServer.Protocol.Models.ICanHaveData");
+ var generateTypedDataAttributeSymbol = compilation.GetTypeByMetadataName("OmniSharp.Extensions.LanguageServer.Protocol.GenerateTypedDataAttribute");
+ var generateContainerAttributeSymbol = compilation.GetTypeByMetadataName("OmniSharp.Extensions.LanguageServer.Protocol.GenerateContainerAttribute");
+ var requestSymbol = compilation.GetTypeByMetadataName("MediatR.IRequest");
+
+ foreach (var classToContain in syntaxReceiver.CreateContainers)
+ {
+ var semanticModel = context.Compilation.GetSemanticModel(classToContain.SyntaxTree);
+ var typeSymbol = semanticModel.GetDeclaredSymbol(classToContain);
+ var hasAttribute = typeSymbol?.GetAttributes().Any(z => SymbolEqualityComparer.Default.Equals(z.AttributeClass, generateContainerAttributeSymbol));
+ if (typeSymbol == null || hasAttribute != true) continue;
+
+ var container = CreateContainerClass(classToContain)
+ .AddAttributeLists(
+ AttributeList(
+ SeparatedList(
+ new[] {
+ Attribute(ParseName("System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute")),
+ Attribute(ParseName("System.Runtime.CompilerServices.CompilerGeneratedAttribute"))
+ }
+ )
+ )
+ );
+
+ var cu = CompilationUnit()
+ .WithUsings(classToContain.SyntaxTree.GetCompilationUnitRoot().Usings)
+ .AddMembers(
+ NamespaceDeclaration(ParseName(typeSymbol.ContainingNamespace.ToDisplayString()))
+ .AddMembers(container)
+ .WithLeadingTrivia(TriviaList(Trivia(NullableDirectiveTrivia(Token(SyntaxKind.EnableKeyword), true))))
+ .WithTrailingTrivia(TriviaList(Trivia(NullableDirectiveTrivia(Token(SyntaxKind.RestoreKeyword), true))))
+ )
+ .WithLeadingTrivia(Comment(Preamble.GeneratedByATool))
+ .WithTrailingTrivia(CarriageReturnLineFeed);
+
+ foreach (var ns in RequiredUsings)
+ {
+ if (cu.Usings.All(z => z.Name.ToFullString() != ns))
+ {
+ cu = cu.AddUsings(UsingDirective(ParseName(ns)));
+ }
+ }
+
+ context.AddSource(
+ $"{classToContain.Identifier.Text}Container.cs",
+ cu.NormalizeWhitespace().SyntaxTree.GetRoot().GetText(Encoding.UTF8)
+ );
+ }
+
+ foreach (var canBeResolved in syntaxReceiver.CanBeResolved)
+ {
+ var semanticModel = context.Compilation.GetSemanticModel(canBeResolved.SyntaxTree);
+ var typeSymbol = semanticModel.GetDeclaredSymbol(canBeResolved);
+ if (typeSymbol == null) continue;
+ var attribute = typeSymbol?.GetAttributes().FirstOrDefault(z => SymbolEqualityComparer.Default.Equals(z.AttributeClass, generateTypedDataAttributeSymbol));
+ var isContainer = typeSymbol?.GetAttributes().Any(z => SymbolEqualityComparer.Default.Equals(z.AttributeClass, generateContainerAttributeSymbol)) == true;
+ if (attribute == null) continue;
+
+ if (!canBeResolved.Modifiers.Any(SyntaxKind.PartialKeyword))
+ {
+ context.ReportDiagnostic(Diagnostic.Create(GeneratorDiagnostics.ClassMustBePartial, canBeResolved.Identifier.GetLocation()));
+ }
+
+ var property = canBeResolved.Members.OfType().Single(z => z.Identifier.Text == "Data");
+ var dataInterfaceName = IdentifierName("ICanBeResolved");
+ var partialClass = canBeResolved
+ .WithAttributeLists(List())
+ .WithBaseList(null)
+ .WithMembers(List())
+ .AddMembers(GetWithDataMethod(canBeResolved, TypeConstraint(NullableType(IdentifierName("HandlerIdentity")))));
+
+ var compilationMembers = new List() {
+ };
+
+ // remove the data property
+ var typedClass = canBeResolved
+ .WithTypeParameterList(TypeParameterList(SingletonSeparatedList(TypeParameter("T"))))
+ .WithMembers(canBeResolved.Members.Replace(property, GetPropertyImpl(property, dataInterfaceName).WithType(IdentifierName("T"))))
+ .AddMembers(
+ GetWithDataMethod(canBeResolved, TypeConstraint(NullableType(IdentifierName("HandlerIdentity")))),
+ GetExplicitProperty(property, dataInterfaceName),
+ GetJDataProperty(),
+ GetConvertFromOperator(canBeResolved, dataInterfaceName),
+ GetConvertToOperator(canBeResolved, dataInterfaceName)
+ )
+ .WithAttributeLists(
+ List(
+ canBeResolved.AttributeLists
+ .Where(z => !z.ToFullString().Contains("Method") && !z.ToFullString().Contains("GenerateTypedData"))
+ )
+ )
+ .WithBaseList(BaseList(SingletonSeparatedList(SimpleBaseType(dataInterfaceName))))
+ .WithConstraintClauses(
+ SingletonList(
+ TypeParameterConstraintClause(IdentifierName("T"))
+ .WithConstraints(
+ SeparatedList(
+ new SyntaxNodeOrToken[] {
+ TypeConstraint(NullableType(IdentifierName("HandlerIdentity"))),
+ Token(SyntaxKind.CommaToken),
+ ConstructorConstraint()
+ }
+ )
+ )
+ )
+ )
+ .AddAttributeLists(
+ AttributeList(
+ SeparatedList(
+ new[] {
+ Attribute(ParseName("System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute")),
+ Attribute(ParseName("System.Runtime.CompilerServices.CompilerGeneratedAttribute"))
+ }
+ )
+ )
+ )
+ .WithLeadingTrivia(canBeResolved.GetLeadingTrivia().Where(z => !z.ToString().Contains("#nullable")))
+ .WithTrailingTrivia(canBeResolved.GetTrailingTrivia().Where(z => !z.ToString().Contains("#nullable")))
+ ;
+
+ if (isContainer)
+ {
+ var typedContainer = CreateContainerClass(typedClass)
+ .WithTypeParameterList(TypeParameterList(SingletonSeparatedList(TypeParameter("T"))))
+ .WithConstraintClauses(
+ SingletonList(
+ TypeParameterConstraintClause(IdentifierName("T"))
+ .WithConstraints(
+ SeparatedList(
+ new TypeParameterConstraintSyntax[] {
+ TypeConstraint(NullableType(IdentifierName("HandlerIdentity"))),
+ ConstructorConstraint()
+ }
+ )
+ )
+ )
+ );
+
+ var typedArgumentList = TypeArgumentList(SingletonSeparatedList(IdentifierName("T")));
+ typedContainer = typedContainer
+ .AddMembers(
+ ConversionOperatorDeclaration(Token(SyntaxKind.ImplicitKeyword), IdentifierName(typedContainer.Identifier))
+ .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)))
+ .WithParameterList(
+ ParameterList(
+ SingletonSeparatedList(
+ Parameter(Identifier("container"))
+ .WithType(GenericName(typedContainer.Identifier).WithTypeArgumentList(typedArgumentList))
+ )
+ )
+ )
+ .WithExpressionBody(
+ ArrowExpressionClause(
+ ObjectCreationExpression(IdentifierName(typedContainer.Identifier))
+ .WithArgumentList(
+ ArgumentList(
+ SingletonSeparatedList(
+ Argument(
+ InvocationExpression(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName("container"),
+ IdentifierName("Select")
+ )
+ )
+ .WithArgumentList(
+ ArgumentList(
+ SingletonSeparatedList(
+ Argument(
+ SimpleLambdaExpression(Parameter(Identifier("z")))
+ .WithExpressionBody(
+ CastExpression(
+ IdentifierName(canBeResolved.Identifier),
+ IdentifierName("z")
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ .WithSemicolonToken(
+ Token(SyntaxKind.SemicolonToken)
+ )
+ );
+
+ compilationMembers.Add(typedContainer);
+ }
+
+ var cu = CompilationUnit()
+ .WithUsings(canBeResolved.SyntaxTree.GetCompilationUnitRoot().Usings)
+ .AddMembers(
+ NamespaceDeclaration(ParseName(typeSymbol.ContainingNamespace.ToDisplayString()))
+ .AddMembers(partialClass, typedClass)
+ .AddMembers(compilationMembers.ToArray())
+ .WithLeadingTrivia(TriviaList(Trivia(NullableDirectiveTrivia(Token(SyntaxKind.EnableKeyword), true))))
+ .WithTrailingTrivia(TriviaList(Trivia(NullableDirectiveTrivia(Token(SyntaxKind.RestoreKeyword), true))))
+ )
+ .WithLeadingTrivia(Comment(Preamble.GeneratedByATool))
+ .WithTrailingTrivia(CarriageReturnLineFeed);
+ foreach (var ns in RequiredUsings)
+ {
+ if (cu.Usings.All(z => z.Name.ToFullString() != ns))
+ {
+ cu = cu.AddUsings(UsingDirective(ParseName(ns)));
+ }
+ }
+
+ context.AddSource(
+ $"{canBeResolved.Identifier.Text}Typed.cs",
+ cu.NormalizeWhitespace().SyntaxTree.GetRoot().GetText(Encoding.UTF8)
+ );
+ }
+ }
+
+ private static MethodDeclarationSyntax GetWithDataMethod(ClassDeclarationSyntax syntax, TypeParameterConstraintSyntax constraintSyntax)
+ {
+ return MethodDeclaration(
+ GenericName(Identifier(syntax.Identifier.Text))
+ .WithTypeArgumentList(TypeArgumentList(SingletonSeparatedList(IdentifierName("TData")))),
+ Identifier("WithData")
+ )
+ .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword)))
+ .WithTypeParameterList(TypeParameterList(SingletonSeparatedList(TypeParameter(Identifier("TData")))))
+ .WithParameterList(ParameterList(SingletonSeparatedList(Parameter(Identifier("data")).WithType(IdentifierName("TData")))))
+ .WithConstraintClauses(
+ SingletonList(
+ TypeParameterConstraintClause(IdentifierName("TData"))
+ .WithConstraints(SeparatedList(new[] { constraintSyntax, ConstructorConstraint() }))
+ )
+ )
+ .WithBody(
+ Block(
+ SingletonList(
+ ReturnStatement(
+ ObjectCreationExpression(GenericName(Identifier(syntax.Identifier.Text))
+ .WithTypeArgumentList(TypeArgumentList(SingletonSeparatedList(IdentifierName("TData"))))
+ )
+ .WithInitializer(
+ InitializerExpression(
+ SyntaxKind.ObjectInitializerExpression,
+ SeparatedList(
+ GetMapping(syntax, null).Concat(
+ new ExpressionSyntax[] {
+ AssignmentExpression(
+ SyntaxKind.SimpleAssignmentExpression,
+ IdentifierName("Data"),
+ IdentifierName("data")
+ )
+ }
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ );
+ }
+
+ private static IEnumerable GetMapping(ClassDeclarationSyntax syntax, IdentifierNameSyntax? paramName)
+ {
+ return syntax.Members.OfType()
+ .Where(z => z.AccessorList?.Accessors.Any(a => a.Keyword.Kind() == SyntaxKind.SetKeyword || a.Keyword.Kind() == SyntaxKind.InitKeyword) == true)
+ .Where(z => z.Identifier.Text != "Data")
+ .Select(
+ property => AssignmentExpression(
+ SyntaxKind.SimpleAssignmentExpression,
+ IdentifierName(property.Identifier.Text),
+ paramName == null
+ ? IdentifierName(property.Identifier.Text)
+ : MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ paramName,
+ IdentifierName(property.Identifier.Text)
+ )
+ )
+ );
+ }
+
+ private static ConversionOperatorDeclarationSyntax GetConvertToOperator(ClassDeclarationSyntax syntax, IdentifierNameSyntax dataInterfaceName)
+ {
+ var name = IdentifierName(syntax.Identifier.Text);
+ var identifier = Identifier(syntax.Identifier.Text);
+ var paramName = IdentifierName("value");
+ return ConversionOperatorDeclaration(Token(SyntaxKind.ImplicitKeyword), name)
+ .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)))
+ .WithParameterList(
+ ParameterList(
+ SingletonSeparatedList(
+ Parameter(
+ Identifier("value")
+ )
+ .WithType(
+ GenericName(identifier)
+ .WithTypeArgumentList(TypeArgumentList(SingletonSeparatedList(IdentifierName("T"))))
+ )
+ )
+ )
+ )
+ .WithExpressionBody(
+ ArrowExpressionClause(
+ ObjectCreationExpression(name)
+ .WithInitializer(
+ InitializerExpression(
+ SyntaxKind.ObjectInitializerExpression,
+ SeparatedList(
+ GetMapping(syntax, paramName)
+ .Concat(
+ new[] {
+ AssignmentExpression(
+ SyntaxKind.SimpleAssignmentExpression,
+ IdentifierName("Data"),
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ ParenthesizedExpression(CastExpression(dataInterfaceName, paramName)),
+ IdentifierName("Data")
+ )
+ )
+ }
+ )
+ )
+ )
+ )
+ )
+ )
+ .WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
+ }
+
+ private static ConversionOperatorDeclarationSyntax GetConvertFromOperator(ClassDeclarationSyntax syntax, IdentifierNameSyntax dataInterfaceName)
+ {
+ var name = IdentifierName(syntax.Identifier.Text);
+ var identifier = Identifier(syntax.Identifier.Text);
+ var paramName = IdentifierName("value");
+ var paramIdentifier = Identifier("value");
+ return ConversionOperatorDeclaration(
+ Token(SyntaxKind.ImplicitKeyword),
+ GenericName(identifier).WithTypeArgumentList(TypeArgumentList(SingletonSeparatedList(IdentifierName("T"))))
+ )
+ .WithModifiers(TokenList(new[] { Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword) }))
+ .WithParameterList(
+ ParameterList(
+ SingletonSeparatedList(Parameter(paramIdentifier).WithType(name))
+ )
+ )
+ .WithExpressionBody(
+ ArrowExpressionClause(
+ ObjectCreationExpression(
+ GenericName(identifier)
+ .WithTypeArgumentList(
+ TypeArgumentList(
+ SingletonSeparatedList(
+ IdentifierName("T")
+ )
+ )
+ )
+ )
+ .WithInitializer(
+ InitializerExpression(
+ SyntaxKind.ObjectInitializerExpression,
+ SeparatedList(
+ GetMapping(syntax, paramName)
+ .Concat(
+ new[] {
+ AssignmentExpression(
+ SyntaxKind.SimpleAssignmentExpression,
+ IdentifierName("JData"),
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ ParenthesizedExpression(CastExpression(dataInterfaceName, paramName)),
+ IdentifierName("Data")
+ )
+ )
+ }
+ )
+ )
+ )
+ )
+ )
+ )
+ .WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
+ }
+
+ private static PropertyDeclarationSyntax GetExplicitProperty(PropertyDeclarationSyntax syntax, IdentifierNameSyntax dataInterfaceName)
+ {
+ return syntax
+ .WithExplicitInterfaceSpecifier(ExplicitInterfaceSpecifier(dataInterfaceName))
+ .WithModifiers(TokenList())
+ .WithAttributeLists(List(SeparatedList()))
+ .WithAccessorList(
+ AccessorList(
+ List(
+ new[] {
+ AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
+ .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
+ AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
+ .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
+ }
+ )
+ )
+ );
+ }
+
+ private static PropertyDeclarationSyntax GetJDataProperty()
+ {
+ return PropertyDeclaration(NullableType(IdentifierName("JToken")), Identifier("JData"))
+ .WithModifiers(TokenList(Token(SyntaxKind.PrivateKeyword)))
+ .WithAccessorList(
+ AccessorList(
+ List(
+ new[] {
+ AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
+ .WithExpressionBody(
+ ArrowExpressionClause(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ ParenthesizedExpression(
+ CastExpression(
+ IdentifierName("ICanBeResolved"),
+ ThisExpression()
+ )
+ ),
+ IdentifierName("Data")
+ )
+ )
+ )
+ .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
+ AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
+ .WithExpressionBody(
+ ArrowExpressionClause(
+ AssignmentExpression(
+ SyntaxKind.SimpleAssignmentExpression,
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ ParenthesizedExpression(
+ CastExpression(
+ IdentifierName("ICanBeResolved"),
+ ThisExpression()
+ )
+ ),
+ IdentifierName("Data")
+ ),
+ IdentifierName("value")
+ )
+ )
+ )
+ .WithSemicolonToken(
+ Token(SyntaxKind.SemicolonToken)
+ )
+ }
+ )
+ )
+ );
+ }
+
+ static PropertyDeclarationSyntax GetPropertyImpl(PropertyDeclarationSyntax syntax, IdentifierNameSyntax dataInterfaceName)
+ {
+ var canBeResolvedAccess = MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ ParenthesizedExpression(CastExpression(dataInterfaceName, ThisExpression())),
+ IdentifierName("Data")
+ );
+ return syntax.WithAccessorList(
+ AccessorList(
+ List(
+ new[] {
+ AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
+ .WithExpressionBody(
+ ArrowExpressionClause(
+ PostfixUnaryExpression(
+ SyntaxKind.SuppressNullableWarningExpression,
+ ConditionalAccessExpression(
+ canBeResolvedAccess,
+ InvocationExpression(
+ MemberBindingExpression(
+ GenericName(Identifier("ToObject"))
+ .WithTypeArgumentList(
+ TypeArgumentList(
+ SingletonSeparatedList(IdentifierName("T"))
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ ).WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
+ AccessorDeclaration(
+ SyntaxKind.SetAccessorDeclaration
+ )
+ .WithExpressionBody(
+ ArrowExpressionClause(
+ AssignmentExpression(
+ SyntaxKind.SimpleAssignmentExpression,
+ canBeResolvedAccess,
+ InvocationExpression(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName("JToken"),
+ IdentifierName("FromObject")
+ )
+ )
+ .WithArgumentList(
+ ArgumentList(
+ SingletonSeparatedList(
+ Argument(
+ IdentifierName("value")
+ )
+ )
+ )
+ )
+ )
+ )
+ ).WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
+ }
+ )
+ )
+ );
+ }
+
+ private static ClassDeclarationSyntax CreateContainerClass(ClassDeclarationSyntax syntax)
+ {
+ TypeSyntax typeName = IdentifierName(syntax.Identifier.Text);
+ var classIdentifier = Identifier($"{syntax.Identifier.Text}Container");
+ TypeSyntax className = IdentifierName($"{syntax.Identifier.Text}Container");
+ if (syntax.Arity > 0)
+ {
+ typeName = GenericName(syntax.Identifier.Text)
+ .WithTypeArgumentList(TypeArgumentList(SingletonSeparatedList(IdentifierName("T"))));
+ className = GenericName($"{syntax.Identifier.Text}Container")
+ .WithTypeArgumentList(TypeArgumentList(SingletonSeparatedList(IdentifierName("T"))));
+ }
+
+ var typeArgumentList = TypeArgumentList(SingletonSeparatedList(typeName));
+
+ return ClassDeclaration(classIdentifier)
+ .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.PartialKeyword)))
+ .WithBaseList(
+ BaseList(
+ SingletonSeparatedList(
+ SimpleBaseType(
+ GenericName(Identifier("ContainerBase"))
+ .WithTypeArgumentList(typeArgumentList)
+ )
+ )
+ )
+ )
+ .WithMembers(
+ List(
+ new MemberDeclarationSyntax[] {
+ ConstructorDeclaration(classIdentifier)
+ .WithModifiers(
+ TokenList(
+ Token(SyntaxKind.PublicKeyword)
+ )
+ )
+ .WithInitializer(
+ ConstructorInitializer(
+ SyntaxKind.ThisConstructorInitializer,
+ ArgumentList(
+ SingletonSeparatedList(
+ Argument(
+ InvocationExpression(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName("Enumerable"),
+ GenericName(Identifier("Empty"))
+ .WithTypeArgumentList(typeArgumentList)
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ .WithBody(
+ Block()
+ ),
+ ConstructorDeclaration(classIdentifier)
+ .WithModifiers(
+ TokenList(
+ Token(SyntaxKind.PublicKeyword)
+ )
+ )
+ .WithParameterList(
+ ParameterList(
+ SingletonSeparatedList(
+ Parameter(Identifier("items")).WithType(GenericName(Identifier("IEnumerable")).WithTypeArgumentList(typeArgumentList))
+ )
+ )
+ )
+ .WithInitializer(
+ ConstructorInitializer(SyntaxKind.BaseConstructorInitializer, ArgumentList(SingletonSeparatedList(Argument(IdentifierName("items")))))
+ )
+ .WithBody(Block()),
+ ConstructorDeclaration(classIdentifier)
+ .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword)))
+ .WithParameterList(ParameterList(SingletonSeparatedList(ArrayParameter(typeName).WithModifiers(TokenList(Token(SyntaxKind.ParamsKeyword))))))
+ .WithInitializer(
+ ConstructorInitializer(SyntaxKind.BaseConstructorInitializer, ArgumentList(SingletonSeparatedList(Argument(IdentifierName("items")))))
+ )
+ .WithBody(Block()),
+ AddConversionBody(
+ typeName,
+ Identifier("List"),
+ ConversionOperatorDeclaration(
+ Token(SyntaxKind.ImplicitKeyword),
+ className
+ )
+ )
+ // array init is different param list
+ .WithParameterList(ArrayParameters(typeName)),
+ AddConversionBody(
+ typeName,
+ Identifier("List"),
+ MethodDeclaration(className, Identifier("Create"))
+ )
+ .WithParameterList(ParameterList(SingletonSeparatedList(ArrayParameter(typeName).WithModifiers(TokenList(Token(SyntaxKind.ParamsKeyword)))))),
+ AddConversionBody(
+ typeName,
+ Identifier("Collection"),
+ ConversionOperatorDeclaration(
+ Token(SyntaxKind.ImplicitKeyword),
+ className
+ )
+ ),
+ AddConversionBody(
+ typeName,
+ Identifier("Collection"),
+ MethodDeclaration(
+ className,
+ Identifier("Create")
+ )
+ ),
+ AddConversionBody(
+ typeName,
+ Identifier("List"),
+ ConversionOperatorDeclaration(
+ Token(SyntaxKind.ImplicitKeyword),
+ className
+ )
+ ),
+
+ AddConversionBody(
+ typeName,
+ Identifier("List"), MethodDeclaration(
+ className,
+ Identifier("Create")
+ )
+ ),
+ AddConversionBody(
+ typeName,
+ Identifier("List"),
+ ConversionOperatorDeclaration(
+ Token(SyntaxKind.ImplicitKeyword),
+ className
+ )
+ )
+ .WithParameterList(ImmutableArrayParameters(typeName)),
+
+ AddConversionBody(
+ typeName,
+ Identifier("List"),
+ MethodDeclaration(
+ className,
+ Identifier("Create")
+ )
+ )
+ .WithParameterList(ImmutableArrayParameters(typeName)),
+
+ AddConversionBody(
+ typeName,
+ Identifier("ImmutableList"),
+ ConversionOperatorDeclaration(
+ Token(SyntaxKind.ImplicitKeyword),
+ className
+ )
+ ),
+
+ AddConversionBody(
+ typeName,
+ Identifier("ImmutableList"), MethodDeclaration(
+ className,
+ Identifier("Create")
+ )
+ )
+ }
+ )
+ );
+
+ static ParameterListSyntax ImmutableArrayParameters(TypeSyntax typeName)
+ {
+ return ParameterList(
+ SingletonSeparatedList(
+ Parameter(
+ Identifier("items")
+ )
+ .WithModifiers(
+ TokenList(
+ Token(SyntaxKind.InKeyword)
+ )
+ )
+ .WithType(
+ GenericName(
+ Identifier("ImmutableArray")
+ )
+ .WithTypeArgumentList(
+ TypeArgumentList(
+ SingletonSeparatedList(
+ typeName
+ )
+ )
+ )
+ )
+ )
+ );
+ }
+
+ static ParameterListSyntax ArrayParameters(TypeSyntax typeName)
+ {
+ return ParameterList(SingletonSeparatedList(ArrayParameter(typeName)));
+ }
+
+ static ParameterSyntax ArrayParameter(TypeSyntax typeName)
+ {
+ return Parameter(Identifier("items"))
+ .WithType(
+ ArrayType(typeName)
+ .WithRankSpecifiers(
+ SingletonList(
+ ArrayRankSpecifier(
+ SingletonSeparatedList(
+ OmittedArraySizeExpression()
+ )
+ )
+ )
+ )
+ );
+ }
+
+ static BaseMethodDeclarationSyntax AddConversionBody(TypeSyntax typeName, SyntaxToken collectionName, BaseMethodDeclarationSyntax syntax)
+ {
+ return syntax
+ .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)))
+ .WithParameterList(
+ ParameterList(
+ SingletonSeparatedList(
+ Parameter(
+ Identifier("items")
+ )
+ .WithType(
+ GenericName(collectionName)
+ .WithTypeArgumentList(
+ TypeArgumentList(
+ SingletonSeparatedList(
+ typeName
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ .WithExpressionBody(
+ ArrowExpressionClause(
+ ObjectCreationExpression(syntax is ConversionOperatorDeclarationSyntax d ? d.Type : syntax is MethodDeclarationSyntax m ? m.ReturnType : null!)
+ .WithArgumentList(
+ ArgumentList(
+ SingletonSeparatedList(
+ Argument(
+ IdentifierName("items")
+ )
+ )
+ )
+ )
+ )
+ )
+ .WithSemicolonToken(
+ Token(SyntaxKind.SemicolonToken)
+ );
+ }
+ }
+
+ ///
+ /// Created on demand before each generation pass
+ ///
+ internal class SyntaxReceiver : ISyntaxReceiver
+ {
+ public List CanBeResolved { get; } = new();
+ public List CanHaveData { get; } = new();
+ public List CreateContainers { get; } = new();
+
+ ///
+ /// Called for every syntax node in the compilation, we can inspect the nodes and save any information useful for generation
+ ///
+ public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
+ {
+ // any field with at least one attribute is a candidate for property generation
+ if (syntaxNode is ClassDeclarationSyntax classDeclarationSyntax)
+ {
+ if (classDeclarationSyntax.AttributeLists
+ .SelectMany(z => z.Attributes)
+ .Any(z => z.Name.ToFullString().Contains("GenerateContainer"))
+ )
+ {
+ CreateContainers.Add(classDeclarationSyntax);
+ }
+
+ if (
+ classDeclarationSyntax.BaseList != null &&
+ classDeclarationSyntax.SyntaxTree.HasCompilationUnitRoot &&
+ classDeclarationSyntax.Members.OfType().Any(z => z.Identifier.Text == "Data")
+ )
+ {
+ if (classDeclarationSyntax.BaseList.Types.Any(z => z.ToString().EndsWith("ICanBeResolved")))
+ {
+ CanBeResolved.Add(classDeclarationSyntax);
+ }
+
+ if (classDeclarationSyntax.BaseList.Types.Any(z => z.ToString().EndsWith("ICanHaveData")))
+ {
+ CanHaveData.Add(classDeclarationSyntax);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/JsonRpc/Generation/GenerateHandlerMethodsAttribute.cs b/src/JsonRpc/Generation/GenerateHandlerMethodsAttribute.cs
index fead28ca1..dee2fde23 100644
--- a/src/JsonRpc/Generation/GenerateHandlerMethodsAttribute.cs
+++ b/src/JsonRpc/Generation/GenerateHandlerMethodsAttribute.cs
@@ -1,6 +1,5 @@
using System;
using System.Diagnostics;
-using CodeGeneration.Roslyn;
namespace OmniSharp.Extensions.JsonRpc.Generation
{
@@ -10,8 +9,7 @@ namespace OmniSharp.Extensions.JsonRpc.Generation
///
/// Efforts will be made to make this available for consumers once source generators land
///
- [AttributeUsage(AttributeTargets.Interface)]
- [CodeGenerationAttribute("OmniSharp.Extensions.JsonRpc.Generators.GenerateHandlerMethodsGenerator, OmniSharp.Extensions.JsonRpc.Generators")]
+ [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class)]
[Conditional("CodeGeneration")]
public class GenerateHandlerMethodsAttribute : Attribute
{
diff --git a/src/JsonRpc/Generation/GenerateRequestMethodsAttribute.cs b/src/JsonRpc/Generation/GenerateRequestMethodsAttribute.cs
index 37bb2766f..bf04425a3 100644
--- a/src/JsonRpc/Generation/GenerateRequestMethodsAttribute.cs
+++ b/src/JsonRpc/Generation/GenerateRequestMethodsAttribute.cs
@@ -1,6 +1,5 @@
using System;
using System.Diagnostics;
-using CodeGeneration.Roslyn;
namespace OmniSharp.Extensions.JsonRpc.Generation
{
@@ -10,8 +9,7 @@ namespace OmniSharp.Extensions.JsonRpc.Generation
///
/// Efforts will be made to make this available for consumers once source generators land
///
- [AttributeUsage(AttributeTargets.Interface)]
- [CodeGenerationAttribute("OmniSharp.Extensions.JsonRpc.Generators.GenerateRequestMethodsGenerator, OmniSharp.Extensions.JsonRpc.Generators")]
+ [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class)]
[Conditional("CodeGeneration")]
public class GenerateRequestMethodsAttribute : Attribute
{
diff --git a/src/JsonRpc/JsonRpc.csproj b/src/JsonRpc/JsonRpc.csproj
index d349f23b6..712b5cb96 100644
--- a/src/JsonRpc/JsonRpc.csproj
+++ b/src/JsonRpc/JsonRpc.csproj
@@ -15,8 +15,8 @@
-
+
diff --git a/src/JsonRpc/Nullable/Records.cs b/src/JsonRpc/Nullable/Records.cs
new file mode 100644
index 000000000..66cef4915
--- /dev/null
+++ b/src/JsonRpc/Nullable/Records.cs
@@ -0,0 +1,23 @@
+#pragma warning disable MA0048 // File name must match type name
+#define INTERNAL_RECORD_ATTRIBUTES
+#if NETSTANDARD2_0 || NETSTANDARD2_1 || NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NET45 || NET451 || NET452 || NET6 || NET461 || NET462 || NET47 || NET471 || NET472 || NET48
+using System.ComponentModel;
+
+// ReSharper disable once CheckNamespace
+namespace System.Runtime.CompilerServices
+{
+ ///
+ /// Reserved to be used by the compiler for tracking metadata.
+ /// This class should not be used by developers in source code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+#if INTERNAL_RECORD_ATTRIBUTES
+ internal
+#else
+ public
+#endif
+ static class IsExternalInit
+ {
+ }
+}
+#endif
diff --git a/src/Protocol/Attributes.cs b/src/Protocol/Attributes.cs
new file mode 100644
index 000000000..e324f76d0
--- /dev/null
+++ b/src/Protocol/Attributes.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Diagnostics;
+using OmniSharp.Extensions.LanguageServer.Protocol.Models;
+using OmniSharp.Extensions.LanguageServer.Protocol.Models.Proposals;
+
+namespace OmniSharp.Extensions.LanguageServer.Protocol
+{
+ ///
+ /// Allows generating a typed counterpart to any model that implements
+ ///
+ ///
+ /// Efforts will be made to make this available for consumers once source generators land
+ ///
+ [AttributeUsage(AttributeTargets.Class)]
+ [Conditional("CodeGeneration")]
+ public class GenerateTypedDataAttribute : Attribute { }
+
+ ///
+ /// Allows generating a typed container counterpart to any model that implements
+ ///
+ ///
+ /// Efforts will be made to make this available for consumers once source generators land
+ ///
+ [AttributeUsage(AttributeTargets.Class)]
+ [Conditional("CodeGeneration")]
+ public class GenerateContainerAttribute : Attribute { }
+
+ ///
+ /// Generates work done on a registration options object
+ ///
+ [AttributeUsage(AttributeTargets.Class)]
+ [Conditional("CodeGeneration")]
+ public class WorkDoneProgressAttribute : Attribute { }
+
+ ///
+ /// Generates text document on a registration options object
+ ///
+ [AttributeUsage(AttributeTargets.Class)]
+ [Conditional("CodeGeneration")]
+ public class TextDocumentAttribute : Attribute { }
+
+ ///
+ /// Defines a converter that is used for converting from dynamic to static
+ ///
+ [AttributeUsage(AttributeTargets.Class)]
+// [Conditional("CodeGeneration")]
+ public class RegistrationOptionsAttribute : Attribute
+ {
+ public string ServerCapabilitiesKey { get; }
+
+ public RegistrationOptionsAttribute(string serverCapabilitiesKey)
+ {
+ ServerCapabilitiesKey = serverCapabilitiesKey;
+ }
+ }
+
+ ///
+ /// Defines a converter that is used for converting from dynamic to static
+ ///
+ [AttributeUsage(AttributeTargets.Class)]
+// [Conditional("CodeGeneration")]
+ public class RegistrationOptionsConverterAttribute : Attribute
+ {
+ public Type ConverterType { get; }
+
+ public RegistrationOptionsConverterAttribute(Type converterType)
+ {
+ ConverterType = converterType;
+ }
+ }
+}
diff --git a/src/Protocol/Protocol.csproj b/src/Protocol/Protocol.csproj
index 980b11d8a..095658316 100644
--- a/src/Protocol/Protocol.csproj
+++ b/src/Protocol/Protocol.csproj
@@ -7,6 +7,11 @@
Language Server Protocol models, classes, interfaces and helper methods
+
+ true
+ $(BaseIntermediateOutputPath)\GeneratedFiles
+
+
diff --git a/test/Generation.Tests/GenerationHelpers.cs b/test/Generation.Tests/GenerationHelpers.cs
index de4a19f8e..834a3072c 100644
--- a/test/Generation.Tests/GenerationHelpers.cs
+++ b/test/Generation.Tests/GenerationHelpers.cs
@@ -1,19 +1,25 @@
using System;
+using System.Collections;
+using System.Collections.Generic;
using System.Collections.Immutable;
+using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
-using CodeGeneration.Roslyn;
-using CodeGeneration.Roslyn.Engine;
using FluentAssertions;
using MediatR;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Text;
+using Newtonsoft.Json.Linq;
+using NSubstitute;
using OmniSharp.Extensions.DebugAdapter.Protocol.Client;
using OmniSharp.Extensions.JsonRpc.Generation;
+using OmniSharp.Extensions.JsonRpc.Generators;
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
using Xunit;
@@ -39,10 +45,10 @@ static GenerationHelpers()
coreAssemblyNames.Select(x => MetadataReference.CreateFromFile(Path.Combine(coreAssemblyPath, x)));
var otherAssemblies = new[] {
typeof(CSharpCompilation).Assembly,
- typeof(CodeGenerationAttributeAttribute).Assembly,
typeof(GenerateHandlerMethodsAttribute).Assembly,
typeof(IDebugAdapterClientRegistry).Assembly,
typeof(Unit).Assembly,
+ typeof(JToken).Assembly,
typeof(ILanguageServerRegistry).Assembly,
};
MetadataReferences = coreMetaReferences
@@ -56,23 +62,24 @@ static GenerationHelpers()
internal const string CSharpDefaultFileExt = "cs";
internal const string TestProjectName = "TestProject";
- internal static readonly string NormalizedPreamble = NormalizeToLf(DocumentTransform.GeneratedByAToolPreamble + Lf);
+ internal static readonly string NormalizedPreamble = NormalizeToLf(Preamble.GeneratedByATool + Lf);
internal static readonly ImmutableArray MetadataReferences;
- public static async Task AssertGeneratedAsExpected(string source, string expected)
+ public static async Task AssertGeneratedAsExpected(string source, string expected) where T : ISourceGenerator, new()
{
- var generatedTree = await GenerateAsync(source);
+ var generatedTree = await GenerateAsync(source);
// normalize line endings to just LF
- var generatedText = NormalizeToLf(generatedTree.GetText().ToString());
+ var generatedText = NormalizeToLf(generatedTree.GetText().ToString()).Trim();
// and append preamble to the expected
var expectedText = NormalizedPreamble + NormalizeToLf(expected).Trim();
+// Assert.Equal(expectedText, generatedText);
generatedText.Should().Be(expectedText);
}
- public static async Task Generate(string source)
+ public static async Task Generate(string source) where T : ISourceGenerator, new()
{
- var generatedTree = await GenerateAsync(source);
+ var generatedTree = await GenerateAsync(source);
// normalize line endings to just LF
var generatedText = NormalizeToLf(generatedTree.GetText().ToString());
// and append preamble to the expected
@@ -81,7 +88,7 @@ public static async Task Generate(string source)
public static string NormalizeToLf(string input) => input.Replace(CrLf, Lf);
- public static async Task GenerateAsync(string source)
+ public static async Task GenerateAsync(string source) where T : ISourceGenerator, new()
{
var document = CreateProject(source).Documents.Single();
var tree = await document.GetSyntaxTreeAsync();
@@ -97,10 +104,21 @@ public static async Task GenerateAsync(string source)
}
var diagnostics = compilation.GetDiagnostics();
+// Assert.Empty(diagnostics.Where(x => x.Severity >= DiagnosticSeverity.Warning));
+
+ ISourceGenerator generator = new T();
+
+ var driver = CSharpGeneratorDriver.Create(
+ ImmutableArray.Create(generator),
+ ImmutableArray.Empty,
+ compilation.SyntaxTrees[0].Options as CSharpParseOptions
+ );
+
+ driver.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out diagnostics);
Assert.Empty(diagnostics.Where(x => x.Severity >= DiagnosticSeverity.Warning));
- var progress = new Progress();
- var result = await DocumentTransform.TransformAsync(compilation, tree, null, Assembly.Load, progress, CancellationToken.None);
- return result;
+
+ // the syntax tree added by the generator will be the last one in the compilation
+ return outputCompilation.SyntaxTrees.Last();
}
public static Project CreateProject(params string[] sources)
@@ -137,4 +155,21 @@ public static Project CreateProject(params string[] sources)
return project;
}
}
+
+ class NotSureWhatToCallYou : CSharpSyntaxWalker
+ {
+ private readonly ISyntaxReceiver _syntaxReceiver;
+
+ public NotSureWhatToCallYou(ISyntaxReceiver syntaxReceiver)
+ {
+ _syntaxReceiver = syntaxReceiver;
+ }
+
+ public override void Visit(SyntaxNode? node)
+ {
+ if (node == null) return;
+ _syntaxReceiver.OnVisitSyntaxNode(node);
+ base.Visit(node);
+ }
+ }
}
diff --git a/test/Generation.Tests/JsonRpcGenerationTests.cs b/test/Generation.Tests/JsonRpcGenerationTests.cs
index a57ee19d9..44d036d16 100644
--- a/test/Generation.Tests/JsonRpcGenerationTests.cs
+++ b/test/Generation.Tests/JsonRpcGenerationTests.cs
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
+using OmniSharp.Extensions.JsonRpc.Generators;
using TestingUtils;
using Xunit;
using static Generation.Tests.GenerationHelpers;
@@ -30,18 +31,19 @@ public interface IExitHandler : IJsonRpcNotificationHandler
}
}";
- var expected = @"using System;
-using System.Threading;
-using System.Threading.Tasks;
+ var expected = @"
using MediatR;
+using Microsoft.Extensions.DependencyInjection;
using OmniSharp.Extensions.JsonRpc;
using OmniSharp.Extensions.JsonRpc.Generation;
using OmniSharp.Extensions.LanguageServer.Protocol;
using OmniSharp.Extensions.LanguageServer.Protocol.Client;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
+using System;
using System.Collections.Generic;
-using Microsoft.Extensions.DependencyInjection;
+using System.Threading;
+using System.Threading.Tasks;
namespace OmniSharp.Extensions.LanguageServer.Protocol.Test
{
@@ -53,20 +55,11 @@ public static partial class ExitExtensions
public static ILanguageServerRegistry OnExit(this ILanguageServerRegistry registry, Func handler) => registry.AddHandler(GeneralNames.Exit, NotificationHandler.For(handler));
public static ILanguageServerRegistry OnExit(this ILanguageServerRegistry registry, Action handler) => registry.AddHandler(GeneralNames.Exit, NotificationHandler.For(handler));
public static ILanguageServerRegistry OnExit(this ILanguageServerRegistry registry, Func handler) => registry.AddHandler(GeneralNames.Exit, NotificationHandler.For(handler));
- }
-#nullable restore
-}
-
-namespace OmniSharp.Extensions.LanguageServer.Protocol.Test
-{
-#nullable enable
- public static partial class ExitExtensions
- {
public static void SendExit(this ILanguageClient mediator, ExitParams @params) => mediator.SendNotification(@params);
}
#nullable restore
}";
- await AssertGeneratedAsExpected(source, expected);
+ await AssertGeneratedAsExpected(source, expected);
}
[FactWithSkipOn(SkipOnPlatform.Windows)]
@@ -87,17 +80,17 @@ public interface ICapabilitiesHandler : IJsonRpcNotificationHandler handler) => registry.AddHandler(EventNames.Capabilities, NotificationHandler.For(handler));
public static IDebugAdapterClientRegistry OnCapabilities(this IDebugAdapterClientRegistry registry, Action handler) => registry.AddHandler(EventNames.Capabilities, NotificationHandler.For(handler));
public static IDebugAdapterClientRegistry OnCapabilities(this IDebugAdapterClientRegistry registry, Func handler) => registry.AddHandler(EventNames.Capabilities, NotificationHandler.For(handler));
- }
-#nullable restore
-}
-
-namespace OmniSharp.Extensions.DebugAdapter.Protocol.Events.Test
-{
-#nullable enable
- public static partial class CapabilitiesExtensions
- {
public static void SendCapabilities(this IDebugAdapterServer mediator, CapabilitiesEvent @params) => mediator.SendNotification(@params);
}
#nullable restore
}";
- await AssertGeneratedAsExpected(source, expected);
+ await AssertGeneratedAsExpected(source, expected);
}
@@ -149,18 +133,19 @@ public interface IExitHandler : IJsonRpcNotificationHandler
}
}";
- var expected = @"using System;
-using System.Threading;
-using System.Threading.Tasks;
+ var expected = @"
using MediatR;
+using Microsoft.Extensions.DependencyInjection;
using OmniSharp.Extensions.JsonRpc;
using OmniSharp.Extensions.JsonRpc.Generation;
using OmniSharp.Extensions.LanguageServer.Protocol;
using OmniSharp.Extensions.LanguageServer.Protocol.Client;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
+using System;
using System.Collections.Generic;
-using Microsoft.Extensions.DependencyInjection;
+using System.Threading;
+using System.Threading.Tasks;
namespace Test
{
@@ -172,20 +157,11 @@ public static partial class ExitExtensions
public static ILanguageServerRegistry OnExit(this ILanguageServerRegistry registry, Func handler) => registry.AddHandler(GeneralNames.Exit, NotificationHandler.For(handler));
public static ILanguageServerRegistry OnExit(this ILanguageServerRegistry registry, Action handler) => registry.AddHandler(GeneralNames.Exit, NotificationHandler.For(handler));
public static ILanguageServerRegistry OnExit(this ILanguageServerRegistry registry, Func handler) => registry.AddHandler(GeneralNames.Exit, NotificationHandler.For(handler));
- }
-#nullable restore
-}
-
-namespace Test
-{
-#nullable enable
- public static partial class ExitExtensions
- {
public static void SendExit(this ILanguageClient mediator, ExitParams @params) => mediator.SendNotification(@params);
}
#nullable restore
}";
- await AssertGeneratedAsExpected(source, expected);
+ await AssertGeneratedAsExpected(source, expected);
}
[FactWithSkipOn(SkipOnPlatform.Windows)]
@@ -213,10 +189,8 @@ public interface IDidChangeTextDocumentHandler : IJsonRpcNotificationHandler(handler, registrationOptions));
}
- }
-#nullable restore
-}
-namespace OmniSharp.Extensions.LanguageServer.Protocol.Test
-{
-#nullable enable
- public static partial class DidChangeTextDocumentExtensions
- {
public static void DidChangeTextDocument(this ILanguageClient mediator, DidChangeTextDocumentParams @params) => mediator.SendNotification(@params);
}
#nullable restore
-}";
- await AssertGeneratedAsExpected(source, expected);
+}
+";
+ await AssertGeneratedAsExpected(source, expected);
}
[FactWithSkipOn(SkipOnPlatform.Windows)]
@@ -311,20 +280,20 @@ public interface IFoldingRangeHandler : IJsonRpcRequestHandler, FoldingRangeCapability, FoldingRangeRegistrationOptions>(handler, registrationOptions));
}
- }
-#nullable restore
-}
-namespace OmniSharp.Extensions.LanguageServer.Protocol.Test
-{
-#nullable enable
- public static partial class FoldingRangeExtensions
- {
public static IRequestProgressObservable, Container> RequestFoldingRange(this ITextDocumentLanguageClient mediator, FoldingRangeRequestParam @params, CancellationToken cancellationToken = default) => mediator.ProgressManager.MonitorUntil(@params, value => new Container(value), cancellationToken);
public static IRequestProgressObservable, Container> RequestFoldingRange(this ILanguageClient mediator, FoldingRangeRequestParam @params, CancellationToken cancellationToken = default) => mediator.ProgressManager.MonitorUntil(@params, value => new Container(value), cancellationToken);
}
#nullable restore
-}";
- await AssertGeneratedAsExpected(source, expected);
+}
+";
+ await AssertGeneratedAsExpected(source, expected);
}
[FactWithSkipOn(SkipOnPlatform.Windows)]
@@ -407,20 +369,20 @@ public interface IDefinitionHandler : IJsonRpcRequestHandler(handler, registrationOptions));
}
- }
-#nullable restore
-}
-namespace OmniSharp.Extensions.LanguageServer.Protocol.Test
-{
-#nullable enable
- public static partial class DefinitionExtensions
- {
public static IRequestProgressObservable, LocationOrLocationLinks> RequestDefinition(this ILanguageClient mediator, DefinitionParams @params, CancellationToken cancellationToken = default) => mediator.ProgressManager.MonitorUntil(@params, value => new LocationOrLocationLinks(value), cancellationToken);
}
#nullable restore
}";
- await AssertGeneratedAsExpected(source, expected);
+ await AssertGeneratedAsExpected(source, expected);
}
@@ -503,20 +457,20 @@ public interface IDefinitionHandler : IJsonRpcRequestHandler(handler, registrationOptions));
}
- }
-#nullable restore
-}
-namespace Test
-{
-#nullable enable
- public static partial class DefinitionExtensions
- {
public static IRequestProgressObservable, LocationOrLocationLinks> RequestDefinition(this ITextDocumentLanguageClient mediator, DefinitionParams @params, CancellationToken cancellationToken = default) => mediator.ProgressManager.MonitorUntil(@params, value => new LocationOrLocationLinks(value), cancellationToken);
}
#nullable restore
}";
- await AssertGeneratedAsExpected(source, expected);
+ await AssertGeneratedAsExpected(source, expected);
}
[FactWithSkipOn(SkipOnPlatform.Windows)]
@@ -595,18 +541,18 @@ namespace Test
public interface ILanguageProtocolInitializeHandler : IJsonRpcRequestHandler {}
}";
var expected = @"
-using System;
-using System.Threading;
-using System.Threading.Tasks;
+using MediatR;
+using Microsoft.Extensions.DependencyInjection;
using OmniSharp.Extensions.JsonRpc;
using OmniSharp.Extensions.JsonRpc.Generation;
using OmniSharp.Extensions.LanguageServer.Protocol;
using OmniSharp.Extensions.LanguageServer.Protocol.Client;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
+using System;
using System.Collections.Generic;
-using MediatR;
-using Microsoft.Extensions.DependencyInjection;
+using System.Threading;
+using System.Threading.Tasks;
namespace Test
{
@@ -616,20 +562,11 @@ public static partial class LanguageProtocolInitializeExtensions
{
public static ILanguageServerRegistry OnLanguageProtocolInitialize(this ILanguageServerRegistry registry, Func> handler) => registry.AddHandler(GeneralNames.Initialize, RequestHandler.For(handler));
public static ILanguageServerRegistry OnLanguageProtocolInitialize(this ILanguageServerRegistry registry, Func> handler) => registry.AddHandler(GeneralNames.Initialize, RequestHandler.For(handler));
- }
-#nullable restore
-}
-
-namespace Test
-{
-#nullable enable
- public static partial class LanguageProtocolInitializeExtensions
- {
public static Task RequestLanguageProtocolInitialize(this ITextDocumentLanguageClient mediator, InitializeParams @params, CancellationToken cancellationToken = default) => mediator.SendRequest(@params, cancellationToken);
}
#nullable restore
}";
- await AssertGeneratedAsExpected(source, expected);
+ await AssertGeneratedAsExpected(source, expected);
}
[FactWithSkipOn(SkipOnPlatform.Windows)]
@@ -651,17 +588,17 @@ namespace OmniSharp.Extensions.DebugAdapter.Protocol.Requests
public interface IAttachHandler : IJsonRpcRequestHandler { }
}";
var expected = @"
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-using OmniSharp.Extensions.JsonRpc;
-using OmniSharp.Extensions.JsonRpc.Generation;
-using System.Collections.Generic;
using MediatR;
using Microsoft.Extensions.DependencyInjection;
using OmniSharp.Extensions.DebugAdapter.Protocol;
using OmniSharp.Extensions.DebugAdapter.Protocol.Client;
using OmniSharp.Extensions.DebugAdapter.Protocol.Server;
+using OmniSharp.Extensions.JsonRpc;
+using OmniSharp.Extensions.JsonRpc.Generation;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
namespace OmniSharp.Extensions.DebugAdapter.Protocol.Requests
{
@@ -675,20 +612,11 @@ public static IDebugAdapterServerRegistry OnAttach(this IDebugAdapterServerRe
where T : AttachRequestArguments => registry.AddHandler(RequestNames.Attach, RequestHandler.For(handler));
public static IDebugAdapterServerRegistry OnAttach(this IDebugAdapterServerRegistry registry, Func> handler)
where T : AttachRequestArguments => registry.AddHandler(RequestNames.Attach, RequestHandler.For(handler));
- }
-#nullable restore
-}
-
-namespace OmniSharp.Extensions.DebugAdapter.Protocol.Requests
-{
-#nullable enable
- public static partial class AttachExtensions
- {
public static Task RequestAttach(this IDebugAdapterClient mediator, AttachRequestArguments @params, CancellationToken cancellationToken = default) => mediator.SendRequest(@params, cancellationToken);
}
#nullable restore
}";
- await AssertGeneratedAsExpected(source, expected);
+ await AssertGeneratedAsExpected(source, expected);
}
[Fact]
@@ -712,17 +640,17 @@ public interface IAttachHandler : IJsonRpcRequestHandler(this IDebugAdapterServerRe
where T : AttachRequestArguments => registry.AddHandler(RequestNames.Attach, RequestHandler.For(handler));
public static IDebugAdapterServerRegistry OnAttach(this IDebugAdapterServerRegistry registry, Func> handler)
where T : AttachRequestArguments => registry.AddHandler(RequestNames.Attach, RequestHandler.For(handler));
- }
-#nullable restore
-}
-
-namespace OmniSharp.Extensions.DebugAdapter.Protocol.Requests
-{
-#nullable enable
- public static partial class AttachExtensions
- {
public static Task RequestAttach(this IDebugAdapterClient mediator, AttachRequestArguments @params, CancellationToken cancellationToken = default) => mediator.SendRequest(@params, cancellationToken);
}
#nullable restore
}";
- await AssertGeneratedAsExpected(source, expected);
+ await AssertGeneratedAsExpected(source, expected);
}
[FactWithSkipOn(SkipOnPlatform.Windows)]
@@ -776,17 +695,17 @@ public interface IAttachHandler : IJsonRpcRequestHandler { }
}";
var expected = @"
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-using OmniSharp.Extensions.JsonRpc;
-using OmniSharp.Extensions.JsonRpc.Generation;
-using System.Collections.Generic;
using MediatR;
using Microsoft.Extensions.DependencyInjection;
using OmniSharp.Extensions.DebugAdapter.Protocol;
using OmniSharp.Extensions.DebugAdapter.Protocol.Client;
using OmniSharp.Extensions.DebugAdapter.Protocol.Server;
+using OmniSharp.Extensions.JsonRpc;
+using OmniSharp.Extensions.JsonRpc.Generation;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
namespace OmniSharp.Extensions.DebugAdapter.Protocol.Bogus
{
@@ -800,20 +719,11 @@ public static IDebugAdapterServerRegistry OnAttach(this IDebugAdapterServerRe
where T : AttachRequestArguments => registry.AddHandler(""attach"", RequestHandler.For(handler));
public static IDebugAdapterServerRegistry OnAttach(this IDebugAdapterServerRegistry registry, Func> handler)
where T : AttachRequestArguments => registry.AddHandler(""attach"", RequestHandler.For(handler));
- }
-#nullable restore
-}
-
-namespace OmniSharp.Extensions.DebugAdapter.Protocol.Bogus
-{
-#nullable enable
- public static partial class AttachExtensions
- {
public static Task RequestAttach(this IDebugAdapterClient mediator, AttachRequestArguments @params, CancellationToken cancellationToken = default) => mediator.SendRequest(@params, cancellationToken);
}
#nullable restore
}";
- await AssertGeneratedAsExpected(source, expected);
+ await AssertGeneratedAsExpected(source, expected);
}
}
}
diff --git a/test/Generation.Tests/TypedCanBeResolvedTests.cs b/test/Generation.Tests/TypedCanBeResolvedTests.cs
new file mode 100644
index 000000000..603b4768b
--- /dev/null
+++ b/test/Generation.Tests/TypedCanBeResolvedTests.cs
@@ -0,0 +1,301 @@
+using System.Threading.Tasks;
+using OmniSharp.Extensions.JsonRpc.Generators;
+using TestingUtils;
+
+namespace Generation.Tests
+{
+ public class TypedCanBeResolvedTests
+ {
+ [FactWithSkipOn(SkipOnPlatform.Windows)]
+ public async Task Supports_Generating_Strongly_Typed_ICanBeResolved_Data()
+ {
+ var source = @"
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+using MediatR;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using OmniSharp.Extensions.JsonRpc;
+using OmniSharp.Extensions.JsonRpc.Generation;
+using OmniSharp.Extensions.LanguageServer.Protocol;
+using OmniSharp.Extensions.LanguageServer.Protocol.Client;
+using OmniSharp.Extensions.LanguageServer.Protocol.Models;
+using OmniSharp.Extensions.LanguageServer.Protocol.Serialization;
+using OmniSharp.Extensions.LanguageServer.Protocol.Server;
+using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range;
+
+#nullable enable
+namespace OmniSharp.Extensions.LanguageServer.Protocol.Test
+{
+ ///
+ /// A code lens represents a command that should be shown along with
+ /// source text, like the number of references, a way to run tests, etc.
+ ///
+ /// A code lens is _unresolved_ when no command is associated to it. For performance
+ /// reasons the creation of a code lens and resolving should be done in two stages.
+ ///
+ [DebuggerDisplay(""{"" + nameof(DebuggerDisplay) + "",nq}"")]
+ [Method(TextDocumentNames.CodeLensResolve, Direction.ClientToServer)]
+ [GenerateTypedData, GenerateContainer]
+ public partial class CodeLens : IRequest, ICanBeResolved
+ {
+ ///
+ /// The range in which this code lens is valid. Should only span a single line.
+ ///
+ public Range Range { get; set; } = null!;
+ [Optional]
+ public Command? Command { get; set; }
+ ///
+ /// A data entry field that is preserved on a code lens item between
+ /// a code lens and a code lens resolve request.
+ ///
+ [Optional]
+ public JToken? Data { get; set; }
+ private string DebuggerDisplay => $""{Range}{( Command != null ? $"" {Command}"" : """" )}"";
+ public override string ToString() => DebuggerDisplay;
+ }
+}
+#nullable restore";
+
+ var expected = @"
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+using MediatR;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using OmniSharp.Extensions.JsonRpc;
+using OmniSharp.Extensions.JsonRpc.Generation;
+using OmniSharp.Extensions.LanguageServer.Protocol;
+using OmniSharp.Extensions.LanguageServer.Protocol.Client;
+using OmniSharp.Extensions.LanguageServer.Protocol.Models;
+using OmniSharp.Extensions.LanguageServer.Protocol.Serialization;
+using OmniSharp.Extensions.LanguageServer.Protocol.Server;
+using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Immutable;
+using System.Linq;
+
+#nullable enable
+namespace OmniSharp.Extensions.LanguageServer.Protocol.Test
+{
+ public partial class CodeLens
+ {
+ public CodeLens WithData(TData data)
+ where TData : HandlerIdentity? , new()
+ {
+ return new CodeLens{Range = Range, Command = Command, Data = data};
+ }
+ }
+
+ ///
+ /// A code lens represents a command that should be shown along with
+ /// source text, like the number of references, a way to run tests, etc.
+ ///
+ /// A code lens is _unresolved_ when no command is associated to it. For performance
+ /// reasons the creation of a code lens and resolving should be done in two stages.
+ ///
+ [DebuggerDisplay(""{"" + nameof(DebuggerDisplay) + "",nq}"")]
+ [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute, System.Runtime.CompilerServices.CompilerGeneratedAttribute]
+ public partial class CodeLens : ICanBeResolved where T : HandlerIdentity? , new()
+ {
+ ///
+ /// The range in which this code lens is valid. Should only span a single line.
+ ///
+ public Range Range
+ {
+ get;
+ set;
+ }
+
+ = null !;
+ [Optional]
+ public Command? Command
+ {
+ get;
+ set;
+ }
+
+ ///
+ /// A data entry field that is preserved on a code lens item between
+ /// a code lens and a code lens resolve request.
+ ///
+ [Optional]
+ public T Data
+ {
+ get => ((ICanBeResolved)this).Data?.ToObject()!;
+ set => ((ICanBeResolved)this).Data = JToken.FromObject(value);
+ }
+
+ private string DebuggerDisplay => $""{Range}{(Command != null ? $"" {Command}"" : """")}"";
+ public override string ToString() => DebuggerDisplay;
+ public CodeLens WithData(TData data)
+ where TData : HandlerIdentity? , new()
+ {
+ return new CodeLens{Range = Range, Command = Command, Data = data};
+ }
+
+ JToken? ICanBeResolved.Data
+ {
+ get;
+ set;
+ }
+
+ private JToken? JData
+ {
+ get => ((ICanBeResolved)this).Data;
+ set => ((ICanBeResolved)this).Data = value;
+ }
+
+ public static implicit operator CodeLens(CodeLens value) => new CodeLens{Range = value.Range, Command = value.Command, JData = ((ICanBeResolved)value).Data};
+ public static implicit operator CodeLens(CodeLens value) => new CodeLens{Range = value.Range, Command = value.Command, Data = ((ICanBeResolved)value).Data};
+ }
+
+ public partial class CodeLensContainer : ContainerBase> where T : HandlerIdentity? , new()
+ {
+ public CodeLensContainer(): this(Enumerable.Empty>())
+ {
+ }
+
+ public CodeLensContainer(IEnumerable> items): base(items)
+ {
+ }
+
+ public CodeLensContainer(params CodeLens[] items): base(items)
+ {
+ }
+
+ public static implicit operator CodeLensContainer(CodeLens[] items) => new CodeLensContainer(items);
+ public static CodeLensContainer Create(params CodeLens[] items) => new CodeLensContainer(items);
+ public static implicit operator CodeLensContainer(Collection> items) => new CodeLensContainer(items);
+ public static CodeLensContainer Create(Collection> items) => new CodeLensContainer(items);
+ public static implicit operator CodeLensContainer(List> items) => new CodeLensContainer(items);
+ public static CodeLensContainer Create(List> items) => new CodeLensContainer(items);
+ public static implicit operator CodeLensContainer(in ImmutableArray> items) => new CodeLensContainer(items);
+ public static CodeLensContainer Create(in ImmutableArray> items) => new CodeLensContainer(items);
+ public static implicit operator CodeLensContainer(ImmutableList> items) => new CodeLensContainer(items);
+ public static CodeLensContainer Create(ImmutableList> items) => new CodeLensContainer(items);
+ public static implicit operator CodeLensContainer(CodeLensContainer container) => new CodeLensContainer(container.Select(z => (CodeLens)z));
+ }
+}
+#nullable restore
+
+";
+ await GenerationHelpers.AssertGeneratedAsExpected(source, expected);
+ }
+
+ [FactWithSkipOn(SkipOnPlatform.Windows)]
+ public async Task Supports_Generating_Strongly_Typed_Container()
+ {
+ var source = @"
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+using MediatR;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using OmniSharp.Extensions.JsonRpc;
+using OmniSharp.Extensions.JsonRpc.Generation;
+using OmniSharp.Extensions.LanguageServer.Protocol;
+using OmniSharp.Extensions.LanguageServer.Protocol.Client;
+using OmniSharp.Extensions.LanguageServer.Protocol.Models;
+using OmniSharp.Extensions.LanguageServer.Protocol.Serialization;
+using OmniSharp.Extensions.LanguageServer.Protocol.Server;
+using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range;
+
+#nullable enable
+namespace OmniSharp.Extensions.LanguageServer.Protocol.Test
+{
+ ///
+ /// A code lens represents a command that should be shown along with
+ /// source text, like the number of references, a way to run tests, etc.
+ ///
+ /// A code lens is _unresolved_ when no command is associated to it. For performance
+ /// reasons the creation of a code lens and resolving should be done in two stages.
+ ///
+ [DebuggerDisplay(""{"" + nameof(DebuggerDisplay) + "",nq}"")]
+ [Method(TextDocumentNames.CodeLensResolve, Direction.ClientToServer)]
+ [GenerateContainer]
+ public partial class CodeLens : IRequest, ICanBeResolved
+ {
+ ///
+ /// The range in which this code lens is valid. Should only span a single line.
+ ///
+ public Range Range { get; set; } = null!;
+ [Optional]
+ public Command? Command { get; set; }
+ ///
+ /// A data entry field that is preserved on a code lens item between
+ /// a code lens and a code lens resolve request.
+ ///
+ [Optional]
+ public JToken? Data { get; set; }
+ private string DebuggerDisplay => $""{Range}{( Command != null ? $"" {Command}"" : """" )}"";
+ public override string ToString() => DebuggerDisplay;
+ }
+}
+#nullable restore";
+
+ var expected = @"
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+using MediatR;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using OmniSharp.Extensions.JsonRpc;
+using OmniSharp.Extensions.JsonRpc.Generation;
+using OmniSharp.Extensions.LanguageServer.Protocol;
+using OmniSharp.Extensions.LanguageServer.Protocol.Client;
+using OmniSharp.Extensions.LanguageServer.Protocol.Models;
+using OmniSharp.Extensions.LanguageServer.Protocol.Serialization;
+using OmniSharp.Extensions.LanguageServer.Protocol.Server;
+using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Immutable;
+using System.Linq;
+
+#nullable enable
+namespace OmniSharp.Extensions.LanguageServer.Protocol.Test
+{
+ [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute, System.Runtime.CompilerServices.CompilerGeneratedAttribute]
+ public partial class CodeLensContainer : ContainerBase
+ {
+ public CodeLensContainer(): this(Enumerable.Empty())
+ {
+ }
+
+ public CodeLensContainer(IEnumerable items): base(items)
+ {
+ }
+
+ public CodeLensContainer(params CodeLens[] items): base(items)
+ {
+ }
+
+ public static implicit operator CodeLensContainer(CodeLens[] items) => new CodeLensContainer(items);
+ public static CodeLensContainer Create(params CodeLens[] items) => new CodeLensContainer(items);
+ public static implicit operator CodeLensContainer(Collection items) => new CodeLensContainer(items);
+ public static CodeLensContainer Create(Collection items) => new CodeLensContainer(items);
+ public static implicit operator CodeLensContainer(List items) => new CodeLensContainer(items);
+ public static CodeLensContainer Create(List items) => new CodeLensContainer(items);
+ public static implicit operator CodeLensContainer(in ImmutableArray items) => new CodeLensContainer(items);
+ public static CodeLensContainer Create(in ImmutableArray items) => new CodeLensContainer(items);
+ public static implicit operator CodeLensContainer(ImmutableList items) => new CodeLensContainer(items);
+ public static CodeLensContainer Create(ImmutableList items) => new CodeLensContainer(items);
+ }
+}
+#nullable restore
+
+";
+ await GenerationHelpers.AssertGeneratedAsExpected(source, expected);
+ }
+ }
+}
diff --git a/test/JsonRpc.Tests/JsonRpc.Tests.csproj b/test/JsonRpc.Tests/JsonRpc.Tests.csproj
index 568d9e53b..a85c0bd09 100644
--- a/test/JsonRpc.Tests/JsonRpc.Tests.csproj
+++ b/test/JsonRpc.Tests/JsonRpc.Tests.csproj
@@ -1,11 +1,11 @@
- netcoreapp3.1;netcoreapp2.1
+ netcoreapp3.1;net5.0;netcoreapp2.1
true
AnyCPU
-
+
diff --git a/test/Lsp.Tests/Integration/DisableDefaultsTests.cs b/test/Lsp.Tests/Integration/DisableDefaultsTests.cs
index d40fdef46..4804420d7 100644
--- a/test/Lsp.Tests/Integration/DisableDefaultsTests.cs
+++ b/test/Lsp.Tests/Integration/DisableDefaultsTests.cs
@@ -1,10 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Composition;
-using System.Linq;
-using System.Reactive.Linq;
-using System.Reactive.Threading.Tasks;
-using System.Threading;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
@@ -18,10 +13,7 @@
using OmniSharp.Extensions.LanguageServer.Protocol;
using OmniSharp.Extensions.LanguageServer.Protocol.Client;
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
-using OmniSharp.Extensions.LanguageServer.Protocol.Document;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
-using OmniSharp.Extensions.LanguageServer.Protocol.Server;
-using OmniSharp.Extensions.LanguageServer.Protocol.Window;
using OmniSharp.Extensions.LanguageServer.Protocol.Workspace;
using OmniSharp.Extensions.LanguageServer.Server;
using OmniSharp.Extensions.LanguageServer.Shared;
diff --git a/test/Lsp.Tests/Integration/ExecuteCommandTests.cs b/test/Lsp.Tests/Integration/ExecuteCommandTests.cs
index 3871443a3..17599ca21 100644
--- a/test/Lsp.Tests/Integration/ExecuteCommandTests.cs
+++ b/test/Lsp.Tests/Integration/ExecuteCommandTests.cs
@@ -356,7 +356,7 @@ public async Task Should_Execute_4_Args()
return Task.FromResult(
new CompletionList(
new CompletionItem {
- Command = Command.Create("execute-a", 1, "2", true, new Range(( 0, 1 ), ( 1, 1 )))
+ Command = Command.Create("execute-a", 1, "2", true, new Range((0, 1), (1, 1)))
}
)
);
@@ -368,7 +368,7 @@ public async Task Should_Execute_4_Args()
i.Should().Be(1);
s.Should().Be("2");
arg3.Should().BeTrue();
- arg4.Should().Be(new Range(( 0, 1 ), ( 1, 1 )));
+ arg4.Should().Be(new Range((0, 1), (1, 1)));
return Task.CompletedTask;
}
@@ -397,7 +397,7 @@ public async Task Should_Execute_5_Args()
new CompletionList(
new CompletionItem {
Command = Command.Create(
- "execute-a", 1, "2", true, new Range(( 0, 1 ), ( 1, 1 )), new Dictionary { ["a"] = "123", ["b"] = "456" }
+ "execute-a", 1, "2", true, new Range((0, 1), (1, 1)), new Dictionary { ["a"] = "123", ["b"] = "456" }
)
}
)
@@ -410,7 +410,7 @@ public async Task Should_Execute_5_Args()
i.Should().Be(1);
s.Should().Be("2");
arg3.Should().BeTrue();
- arg4.Should().Be(new Range(( 0, 1 ), ( 1, 1 )));
+ arg4.Should().Be(new Range((0, 1), (1, 1)));
arg5.Should().ContainKeys("a", "b");
return Task.CompletedTask;
@@ -440,7 +440,7 @@ public async Task Should_Execute_6_Args()
new CompletionList(
new CompletionItem {
Command = Command.Create(
- "execute-a", 1, "2", true, new Range(( 0, 1 ), ( 1, 1 )), new Dictionary { ["a"] = "123", ["b"] = "456" },
+ "execute-a", 1, "2", true, new Range((0, 1), (1, 1)), new Dictionary { ["a"] = "123", ["b"] = "456" },
Guid.NewGuid()
)
}
@@ -454,7 +454,7 @@ public async Task Should_Execute_6_Args()
i.Should().Be(1);
s.Should().Be("2");
arg3.Should().BeTrue();
- arg4.Should().Be(new Range(( 0, 1 ), ( 1, 1 )));
+ arg4.Should().Be(new Range((0, 1), (1, 1)));
arg5.Should().ContainKeys("a", "b");
arg6.Should().NotBeEmpty();
diff --git a/test/Lsp.Tests/Lsp.Tests.csproj b/test/Lsp.Tests/Lsp.Tests.csproj
index a903ade0f..0d749a9cf 100644
--- a/test/Lsp.Tests/Lsp.Tests.csproj
+++ b/test/Lsp.Tests/Lsp.Tests.csproj
@@ -1,6 +1,6 @@
- netcoreapp3.1;netcoreapp2.1
+ netcoreapp3.1;net5.0;netcoreapp2.1
true
AnyCPU
@@ -9,12 +9,11 @@
-
+
-
diff --git a/test/Lsp.Tests/Models/FormattingOptionsTests.cs b/test/Lsp.Tests/Models/FormattingOptionsTests.cs
index 27dec2e08..762e7ee50 100644
--- a/test/Lsp.Tests/Models/FormattingOptionsTests.cs
+++ b/test/Lsp.Tests/Models/FormattingOptionsTests.cs
@@ -1,5 +1,4 @@
using FluentAssertions;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OmniSharp.Extensions.LanguageServer.Protocol.Serialization;