251 lines
8.6 KiB
C#
251 lines
8.6 KiB
C#
using InnovEnergy.Lib.SrcGen.Trees;
|
|
using InnovEnergy.Lib.Utils;
|
|
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CSharp;
|
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
using Sawmill;
|
|
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
|
using static Microsoft.CodeAnalysis.CSharp.SyntaxKind;
|
|
|
|
namespace InnovEnergy.Lib.SrcGen;
|
|
|
|
|
|
|
|
public static class NestProperties
|
|
{
|
|
private static readonly SyntaxToken PublicKeyword = Token(SyntaxKind.PublicKeyword);
|
|
|
|
public static async Task Generate(Project project, Compilation compilation)
|
|
{
|
|
var syntaxTrees = await project
|
|
.Documents
|
|
.Where(d => d.Project == project)
|
|
.Where(d => d.SupportsSyntaxTree)
|
|
.Select(async d => await d.GetSyntaxRootAsync())
|
|
.WhenAll();
|
|
|
|
syntaxTrees.NotNull()
|
|
.SelectMany(c => c.GetNestPropertiesAttributes())
|
|
.ForEach(GenerateFile);
|
|
|
|
String GenerateFile(AttributeSyntax attribute)
|
|
{
|
|
var typeName = attribute.ArgumentList!.Arguments.Single().ToString().Replace("\"", "");
|
|
var type = attribute.GetAncestor<TypeDeclarationSyntax>();
|
|
var nameSpace = attribute.GetAncestor<NamespaceDeclarationSyntax>();
|
|
var filePath = type.SyntaxTree.FilePath;
|
|
var generatedPath = filePath.GetGeneratedPath(nameof(NestProperties));
|
|
|
|
var recordProperties = type
|
|
.Members
|
|
.OfType<PropertyDeclarationSyntax>()
|
|
.Where(m => m.Modifiers.Any(mod => mod.IsKind(SyntaxKind.PublicKeyword)))
|
|
.ToList();
|
|
|
|
var propNames = recordProperties.Select(m => m.Identifier.ValueText);
|
|
|
|
var prefixTree = PrefixTree.Create(propNames);
|
|
|
|
prefixTree.WriteLine();
|
|
Console.WriteLine("$$$$$$$$$$$$$");
|
|
|
|
|
|
var wrappedTypeName = type.Identifier.ValueText;
|
|
|
|
var records = new List<RecordDeclarationSyntax>();
|
|
|
|
prefixTree.Fold<Tree<String>, MemberDeclarationSyntax>((memory, tree) =>
|
|
{
|
|
var path = tree
|
|
.Ancestors
|
|
.Prepend(tree)
|
|
.Aggregate("", (a, b) => b.Node + a);
|
|
|
|
if (tree.IsLeaf)
|
|
{
|
|
var prop = recordProperties.First(p => p.Identifier.ValueText == path);
|
|
|
|
var writable = prop
|
|
.DescendantNodes()
|
|
.OfType<AccessorDeclarationSyntax>()
|
|
.Any(d => d.IsKind(SetAccessorDeclaration)
|
|
&& !d.DescendantTokens().Any(t => t.IsKind(PrivateKeyword)));
|
|
|
|
return CreateAccessorProperty
|
|
(
|
|
prop.Type,
|
|
tree.Node,
|
|
path,
|
|
writable
|
|
);
|
|
}
|
|
else
|
|
{
|
|
var children = memory.ToArray();
|
|
|
|
var rec = CreateWrapperRecord(typeName + path, wrappedTypeName, children);
|
|
records.Add(rec);
|
|
|
|
return CreateWrapperProperty(IdentifierName(typeName + path), Identifier(tree.Node));
|
|
}
|
|
});
|
|
|
|
//x.NormalizeWhitespace().GetText().WriteLine();
|
|
|
|
records.Reverse();
|
|
records.ForEach(r => r.NormalizeWhitespace().WriteLine("\n"));
|
|
|
|
|
|
|
|
Console.WriteLine($"Generating {generatedPath}");
|
|
|
|
|
|
|
|
|
|
|
|
return "";
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
private static CompilationUnitSyntax CreateCompilationUnit(NameSyntax nameSpaceName, params MemberDeclarationSyntax[] members)
|
|
{
|
|
var nameSpace = NamespaceDeclaration(nameSpaceName)
|
|
.WithMembers(members.Apply(List));
|
|
|
|
return CompilationUnit().WithMembers(nameSpace.Apply(SingletonList<MemberDeclarationSyntax>));
|
|
}
|
|
|
|
private static RecordDeclarationSyntax CreateWrapperRecord(String structName,
|
|
String wrappedTypeName,
|
|
IEnumerable<MemberDeclarationSyntax> properties)
|
|
{
|
|
const String self = "Self";
|
|
|
|
var wrappedType = IdentifierName(wrappedTypeName);
|
|
var recId = Identifier(structName);
|
|
var modifiers = TokenList(Token(SyntaxKind.PublicKeyword), Token(ReadOnlyKeyword));
|
|
|
|
|
|
|
|
|
|
//properties.Prepend<MemberDeclarationSyntax>(selfProperty).Prepend(constructor).Apply(List);
|
|
|
|
// var structMembers = new MemberDeclarationSyntax[] { selfProperty, constructor }
|
|
// .Apply(List);
|
|
|
|
var recMembers = properties.Apply(List);
|
|
|
|
|
|
var recParams = self
|
|
.Apply(Identifier)
|
|
.Apply(Parameter)
|
|
.WithType(wrappedType)
|
|
.Apply(SingletonSeparatedList)
|
|
.Apply(ParameterList);
|
|
|
|
return RecordDeclaration(Token(RecordKeyword), recId)
|
|
.WithModifiers(modifiers)
|
|
.WithClassOrStructKeyword(Token(StructKeyword))
|
|
.WithParameterList(recParams)
|
|
.WithModifiers(modifiers)
|
|
.WithOpenBraceToken(Token(OpenBraceToken))
|
|
.WithMembers(recMembers)
|
|
.WithCloseBraceToken(Token(CloseBraceToken));
|
|
|
|
//return /*StructDeclaration(structId)*/
|
|
|
|
|
|
}
|
|
|
|
|
|
private static PropertyDeclarationSyntax CreateAccessorProperty(TypeSyntax propertyTypeName,
|
|
String propertyName,
|
|
String parentPropertyName,
|
|
Boolean writeable)
|
|
{
|
|
const String self = "Self";
|
|
const String value = "value";
|
|
|
|
var propertyModifier = SyntaxKind
|
|
.PublicKeyword
|
|
.Apply(Token)
|
|
.Apply(TokenList);
|
|
|
|
var semicolonToken = Token(SemicolonToken);
|
|
|
|
var accessExpression = MemberAccessExpression
|
|
(
|
|
SimpleMemberAccessExpression,
|
|
IdentifierName(self),
|
|
IdentifierName(parentPropertyName)
|
|
);
|
|
|
|
var getterBody = ArrowExpressionClause(accessExpression);
|
|
|
|
var getter = GetAccessorDeclaration
|
|
.Apply(AccessorDeclaration)
|
|
.WithExpressionBody(getterBody)
|
|
.WithSemicolonToken(semicolonToken);
|
|
|
|
var setterBody = AssignmentExpression(SimpleAssignmentExpression, accessExpression, IdentifierName(value))
|
|
.Apply(ArrowExpressionClause);
|
|
|
|
var setter = SetAccessorDeclaration
|
|
.Apply(AccessorDeclaration)
|
|
.WithExpressionBody(setterBody)
|
|
.WithSemicolonToken(semicolonToken);
|
|
|
|
var accessors = writeable
|
|
? new[] { getter, setter }
|
|
: new[] { getter };
|
|
|
|
var accessorsList = accessors
|
|
.Apply(List)
|
|
.Apply(AccessorList);
|
|
|
|
var property = PropertyDeclaration
|
|
(
|
|
type : propertyTypeName,
|
|
identifier: Identifier(propertyName)
|
|
);
|
|
|
|
return property
|
|
.WithModifiers(propertyModifier)
|
|
.WithAccessorList(accessorsList);
|
|
}
|
|
|
|
|
|
|
|
private static PropertyDeclarationSyntax CreateWrapperProperty(TypeSyntax type, SyntaxToken identifier)
|
|
{
|
|
var modifiers = SyntaxKind
|
|
.PublicKeyword
|
|
.Apply(Token)
|
|
.Apply(TokenList);
|
|
|
|
var self = IdentifierName("Self")
|
|
.Apply(Argument)
|
|
.Apply(SingletonSeparatedList)
|
|
.Apply(ArgumentList);
|
|
|
|
var body = ObjectCreationExpression(type)
|
|
.WithArgumentList(self)
|
|
.Apply(ArrowExpressionClause);
|
|
|
|
return PropertyDeclaration(type, identifier)
|
|
.WithModifiers(modifiers)
|
|
.WithExpressionBody(body)
|
|
.WithSemicolonToken(Token(SemicolonToken));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|