rough outline
Signed-off-by: Timothy Schenk <admin@rainote.dev>
This commit is contained in:
parent
b6cf60e909
commit
da32bd5957
6 changed files with 110 additions and 93 deletions
|
@ -0,0 +1,5 @@
|
||||||
|
namespace RaiNote.PacketMediator;
|
||||||
|
|
||||||
|
internal record IntermediateHandlerAndStructTuple(
|
||||||
|
IntermediatePacketHandlerData HandlerData,
|
||||||
|
IntermediatePacketStructData StructData);
|
|
@ -1,7 +1,5 @@
|
||||||
// Licensed to Timothy Schenk under the Apache 2.0 License.
|
// Licensed to Timothy Schenk under the Apache 2.0 License.
|
||||||
|
|
||||||
using Microsoft.CodeAnalysis;
|
|
||||||
|
|
||||||
namespace RaiNote.PacketMediator;
|
namespace RaiNote.PacketMediator;
|
||||||
|
|
||||||
internal class IntermediatePacketHandlerData {
|
internal class IntermediatePacketHandlerData {
|
||||||
|
@ -14,16 +12,3 @@ internal class IntermediatePacketHandlerData {
|
||||||
public string PacketHandlerIdentifier { get; set; }
|
public string PacketHandlerIdentifier { get; set; }
|
||||||
public IntermediatePacketStructHandlerData? PacketStructHandlerData { get; set; }
|
public IntermediatePacketStructHandlerData? PacketStructHandlerData { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
internal record IntermediatePacketStructData(
|
|
||||||
Location SymbolLocation,
|
|
||||||
string PacketStructFullIdentifier,
|
|
||||||
string EnumValue,
|
|
||||||
string EnumTypeFullIdentifier,
|
|
||||||
string EnumMemberIdentifier,
|
|
||||||
string EnumMaxValue,
|
|
||||||
bool ImplementsInterface);
|
|
||||||
|
|
||||||
internal record IntermediateHandlerAndStructTuple(
|
|
||||||
IntermediatePacketHandlerData HandlerData,
|
|
||||||
IntermediatePacketStructData StructData);
|
|
||||||
|
|
12
RaiNote.PacketMediator/IntermediatePacketStructData.cs
Normal file
12
RaiNote.PacketMediator/IntermediatePacketStructData.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace RaiNote.PacketMediator;
|
||||||
|
|
||||||
|
internal record IntermediatePacketStructData(
|
||||||
|
Location SymbolLocation,
|
||||||
|
string PacketStructFullIdentifier,
|
||||||
|
string? EnumValue,
|
||||||
|
string EnumTypeFullIdentifier,
|
||||||
|
string EnumMemberIdentifier,
|
||||||
|
string? EnumMaxValue,
|
||||||
|
bool ImplementsInterface);
|
|
@ -88,69 +88,7 @@ public class PacketMediatorGenerator : IIncrementalGenerator {
|
||||||
var structsWithAttributes = context.SyntaxProvider
|
var structsWithAttributes = context.SyntaxProvider
|
||||||
.CreateSyntaxProvider(
|
.CreateSyntaxProvider(
|
||||||
predicate: (node, _) => node is StructDeclarationSyntax { AttributeLists.Count: > 0 },
|
predicate: (node, _) => node is StructDeclarationSyntax { AttributeLists.Count: > 0 },
|
||||||
transform: (syntaxContext, cancellationToken) => {
|
transform: TransformPacketStructs)
|
||||||
var structDeclaration = (StructDeclarationSyntax)syntaxContext.Node;
|
|
||||||
var model = syntaxContext.SemanticModel;
|
|
||||||
var symbol =
|
|
||||||
ModelExtensions.GetDeclaredSymbol(model, structDeclaration,
|
|
||||||
cancellationToken: cancellationToken) as
|
|
||||||
INamedTypeSymbol;
|
|
||||||
var requiredInterfaces = new[] {
|
|
||||||
"IPacket", "IIncomingPacket", "IOutgoingPacket", "IBidirectionalPacket"
|
|
||||||
};
|
|
||||||
var implementsInterface = symbol != null && symbol.AllInterfaces
|
|
||||||
.Any(i => requiredInterfaces.Contains(i.Name, StringComparer.Ordinal));
|
|
||||||
if (!implementsInterface) {
|
|
||||||
// TODO: https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.cookbook.md#issue-diagnostics
|
|
||||||
// or Analyzer
|
|
||||||
var diagnostic = Diagnostic.Create(_rpmGen001Diagnostic, symbol?.Locations.First(),
|
|
||||||
symbol?.ToDisplayString());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for the marker attribute
|
|
||||||
var attribute = symbol?.GetAttributes()
|
|
||||||
.FirstOrDefault(attr => {
|
|
||||||
var attrClass = attr.AttributeClass;
|
|
||||||
while (attrClass != null) {
|
|
||||||
if (string.Equals(attrClass.Name, "PacketIdAttribute", StringComparison.Ordinal) &&
|
|
||||||
string.Equals(attrClass.ContainingNamespace.ToDisplayString(),
|
|
||||||
"RaiNote.PacketMediator", StringComparison.Ordinal)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
attrClass = attrClass.BaseType;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
if (attribute == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var attributeConstructorArgument = attribute.ConstructorArguments[0];
|
|
||||||
var enumType = attributeConstructorArgument.Type;
|
|
||||||
var enumValue = attributeConstructorArgument.Value;
|
|
||||||
|
|
||||||
var enumMember = enumType?.GetMembers()
|
|
||||||
.OfType<IFieldSymbol>()
|
|
||||||
.FirstOrDefault(f => f.ConstantValue?.Equals(enumValue) == true);
|
|
||||||
|
|
||||||
var enumMaxValue = enumType?.GetMembers()
|
|
||||||
.OfType<IFieldSymbol>().Max(x => x.ConstantValue);
|
|
||||||
|
|
||||||
if (symbol == null || enumMember == null || enumMaxValue == null || enumType == null ||
|
|
||||||
enumValue == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var intermediatePacketStructData = new IntermediatePacketStructData(symbol.Locations.First(),
|
|
||||||
symbol.ToDisplayString(),
|
|
||||||
enumValue.ToString(),
|
|
||||||
enumType.ToDisplayString(),
|
|
||||||
enumMember.ToDisplayString(),
|
|
||||||
enumMaxValue.ToString(), implementsInterface);
|
|
||||||
|
|
||||||
return intermediatePacketStructData;
|
|
||||||
})
|
|
||||||
.Where(result => result != null);
|
.Where(result => result != null);
|
||||||
|
|
||||||
var packetHandlerValues = context.SyntaxProvider.CreateSyntaxProvider(
|
var packetHandlerValues = context.SyntaxProvider.CreateSyntaxProvider(
|
||||||
|
@ -170,9 +108,11 @@ public class PacketMediatorGenerator : IIncrementalGenerator {
|
||||||
if (handlerData == null)
|
if (handlerData == null)
|
||||||
return null;
|
return null;
|
||||||
var structs = structDatas.Where(sData => {
|
var structs = structDatas.Where(sData => {
|
||||||
|
if (handlerData.PacketStructHandlerData == null)
|
||||||
|
return false;
|
||||||
var equals =
|
var equals =
|
||||||
sData != null && sData.PacketStructFullIdentifier.Equals(handlerData.PacketStructHandlerData
|
sData != null && sData.PacketStructFullIdentifier.Equals(handlerData.PacketStructHandlerData
|
||||||
?.PacketStructFullIdentifier);
|
.PacketStructFullIdentifier, StringComparison.Ordinal);
|
||||||
|
|
||||||
return equals;
|
return equals;
|
||||||
}).FirstOrDefault();
|
}).FirstOrDefault();
|
||||||
|
@ -188,7 +128,8 @@ public class PacketMediatorGenerator : IIncrementalGenerator {
|
||||||
|
|
||||||
// Collect and generate the dictionary
|
// Collect and generate the dictionary
|
||||||
context.RegisterSourceOutput(combinedResults, (ctx, result) => {
|
context.RegisterSourceOutput(combinedResults, (ctx, result) => {
|
||||||
var combinedInfo = result.ToList();
|
var combinedInfo = result.Where(x => x != null).Select(IntermediateHandlerAndStructTuple (x) => x!)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
if (combinedInfo.Count <= 0)
|
if (combinedInfo.Count <= 0)
|
||||||
return;
|
return;
|
||||||
|
@ -198,7 +139,8 @@ public class PacketMediatorGenerator : IIncrementalGenerator {
|
||||||
var intermediatePacketStructData = combinedInfo.First()?.StructData;
|
var intermediatePacketStructData = combinedInfo.First()?.StructData;
|
||||||
if (intermediatePacketStructData?.EnumMaxValue == null)
|
if (intermediatePacketStructData?.EnumMaxValue == null)
|
||||||
return;
|
return;
|
||||||
var highestValue = long.Parse(intermediatePacketStructData.EnumMaxValue);
|
var highestValue = long.Parse(intermediatePacketStructData.EnumMaxValue, NumberStyles.Integer,
|
||||||
|
new NumberFormatInfo());
|
||||||
var ms = new MemoryStream();
|
var ms = new MemoryStream();
|
||||||
var sw = new StreamWriter(ms, Encoding.UTF8);
|
var sw = new StreamWriter(ms, Encoding.UTF8);
|
||||||
sw.AutoFlush = true;
|
sw.AutoFlush = true;
|
||||||
|
@ -216,11 +158,13 @@ public class PacketMediatorGenerator : IIncrementalGenerator {
|
||||||
{
|
{
|
||||||
""");
|
""");
|
||||||
|
|
||||||
var valueTuples = combinedInfo.Select((value, i) => (value, i));
|
foreach (var (handlerData, packetStructData) in combinedInfo) {
|
||||||
foreach (var ((handlerData, packetStructData), i) in valueTuples) {
|
if (packetStructData.EnumValue != null) {
|
||||||
var tempVal = long.Parse(packetStructData.EnumValue,
|
var tempVal = long.Parse(packetStructData.EnumValue,
|
||||||
new NumberFormatInfo());
|
new NumberFormatInfo());
|
||||||
usedValues.Add(tempVal);
|
usedValues.Add(tempVal);
|
||||||
|
}
|
||||||
|
|
||||||
sw.WriteLine($"""
|
sw.WriteLine($"""
|
||||||
case {packetStructData.EnumMemberIdentifier}:
|
case {packetStructData.EnumMemberIdentifier}:
|
||||||
var packet = new {handlerData.PacketHandlerIdentifier}();
|
var packet = new {handlerData.PacketHandlerIdentifier}();
|
||||||
|
@ -279,6 +223,67 @@ public class PacketMediatorGenerator : IIncrementalGenerator {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IntermediatePacketStructData? TransformPacketStructs(GeneratorSyntaxContext syntaxContext,
|
||||||
|
CancellationToken cancellationToken) {
|
||||||
|
var structDeclaration = (StructDeclarationSyntax)syntaxContext.Node;
|
||||||
|
var model = syntaxContext.SemanticModel;
|
||||||
|
var symbol =
|
||||||
|
ModelExtensions.GetDeclaredSymbol(model, structDeclaration, cancellationToken: cancellationToken) as
|
||||||
|
INamedTypeSymbol;
|
||||||
|
var requiredInterfaces = new[] { "IPacket", "IIncomingPacket", "IOutgoingPacket", "IBidirectionalPacket" };
|
||||||
|
var implementsInterface = symbol != null &&
|
||||||
|
symbol.AllInterfaces.Any(i =>
|
||||||
|
requiredInterfaces.Contains(i.Name, StringComparer.Ordinal));
|
||||||
|
if (!implementsInterface) {
|
||||||
|
// TODO: https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.cookbook.md#issue-diagnostics
|
||||||
|
// or Analyzer
|
||||||
|
/*var diagnostic = Diagnostic.Create(_rpmGen001Diagnostic, symbol?.Locations.First(),
|
||||||
|
symbol?.ToDisplayString());*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for the marker attribute
|
||||||
|
var attribute = symbol?.GetAttributes()
|
||||||
|
.FirstOrDefault(attr => {
|
||||||
|
var attrClass = attr.AttributeClass;
|
||||||
|
while (attrClass != null) {
|
||||||
|
if (string.Equals(attrClass.Name, "PacketIdAttribute", StringComparison.Ordinal) &&
|
||||||
|
attrClass.ContainingNamespace != null &&
|
||||||
|
string.Equals(attrClass.ContainingNamespace.ToDisplayString(), "RaiNote.PacketMediator",
|
||||||
|
StringComparison.Ordinal)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
attrClass = attrClass.BaseType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
if (attribute == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var attributeConstructorArgument = attribute.ConstructorArguments[0];
|
||||||
|
var enumType = attributeConstructorArgument.Type;
|
||||||
|
var enumValue = attributeConstructorArgument.Value;
|
||||||
|
|
||||||
|
var enumMember = enumType?.GetMembers()
|
||||||
|
.OfType<IFieldSymbol>()
|
||||||
|
.FirstOrDefault(f => f.ConstantValue?.Equals(enumValue) == true);
|
||||||
|
|
||||||
|
var enumMaxValue = enumType?.GetMembers()
|
||||||
|
.OfType<IFieldSymbol>()
|
||||||
|
.Max(x => x.ConstantValue);
|
||||||
|
|
||||||
|
if (symbol == null || enumMember == null || enumMaxValue == null || enumType == null ||
|
||||||
|
enumValue == null) return null;
|
||||||
|
|
||||||
|
var intermediatePacketStructData = new IntermediatePacketStructData(symbol.Locations.First(),
|
||||||
|
symbol.ToDisplayString(), enumValue.ToString(), enumType.ToDisplayString(), enumMember.ToDisplayString(),
|
||||||
|
enumMaxValue.ToString(), implementsInterface);
|
||||||
|
|
||||||
|
return intermediatePacketStructData;
|
||||||
|
}
|
||||||
|
|
||||||
private static IntermediatePacketHandlerData? TransformPacketHandlers(GeneratorSyntaxContext syntaxContext,
|
private static IntermediatePacketHandlerData? TransformPacketHandlers(GeneratorSyntaxContext syntaxContext,
|
||||||
CancellationToken cancellationToken) {
|
CancellationToken cancellationToken) {
|
||||||
var classDeclaration = (ClassDeclarationSyntax)syntaxContext.Node;
|
var classDeclaration = (ClassDeclarationSyntax)syntaxContext.Node;
|
||||||
|
@ -286,8 +291,8 @@ public class PacketMediatorGenerator : IIncrementalGenerator {
|
||||||
var symbol =
|
var symbol =
|
||||||
ModelExtensions.GetDeclaredSymbol(model, classDeclaration, cancellationToken: cancellationToken) as
|
ModelExtensions.GetDeclaredSymbol(model, classDeclaration, cancellationToken: cancellationToken) as
|
||||||
INamedTypeSymbol;
|
INamedTypeSymbol;
|
||||||
var packetStruct = (symbol.Interfaces.Select(interfaceSyntax => {
|
var packetStruct = (symbol?.Interfaces.Select(interfaceSyntax => {
|
||||||
if (!interfaceSyntax.Name.Equals("IPacketHandler")) {
|
if (!interfaceSyntax.Name.Equals("IPacketHandler", StringComparison.Ordinal)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,10 +318,10 @@ public class PacketMediatorGenerator : IIncrementalGenerator {
|
||||||
Expression: MemberAccessExpressionSyntax { Name: { } nameSyntax }
|
Expression: MemberAccessExpressionSyntax { Name: { } nameSyntax }
|
||||||
} invocation &&
|
} invocation &&
|
||||||
context.SemanticModel.GetOperation(context.Node,
|
context.SemanticModel.GetOperation(context.Node,
|
||||||
cancellationToken) is IInvocationOperation targetOperation &&
|
cancellationToken) is IInvocationOperation {
|
||||||
targetOperation.TargetMethod is
|
TargetMethod:
|
||||||
{ Name: "AddPacketHandlerServices", ContainingNamespace: { Name: "RaiNote.PacketMediator" } }
|
{ Name: "AddPacketHandlerServices", ContainingNamespace.Name: "RaiNote.PacketMediator" }
|
||||||
) {
|
}) {
|
||||||
#pragma warning disable RSEXPERIMENTAL002 // / Experimental interceptable location API
|
#pragma warning disable RSEXPERIMENTAL002 // / Experimental interceptable location API
|
||||||
if (context.SemanticModel.GetInterceptableLocation(invocation, cancellationToken: cancellationToken) is
|
if (context.SemanticModel.GetInterceptableLocation(invocation, cancellationToken: cancellationToken) is
|
||||||
{ } location) {
|
{ } location) {
|
||||||
|
@ -338,6 +343,6 @@ public class PacketMediatorGenerator : IIncrementalGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning disable RSEXPERIMENTAL002 // / Experimental interceptable location API
|
#pragma warning disable RSEXPERIMENTAL002 // / Experimental interceptable location API
|
||||||
public record CandidateInvocation(InterceptableLocation Location);
|
private record CandidateInvocation(InterceptableLocation Location);
|
||||||
#pragma warning restore RSEXPERIMENTAL002
|
#pragma warning restore RSEXPERIMENTAL002
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,14 @@
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
<PackageReference Include="Roslynator.CodeAnalysis.Analyzers" Version="4.12.10">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Roslynator.Formatting.Analyzers" Version="4.12.10">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:String x:Key="/Default/CodeInspection/Daemon/ConfigureAwaitAnalysisMode/@EntryValue">Library</s:String></wpf:ResourceDictionary>
|
Loading…
Add table
Reference in a new issue