XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEExtension.NetCore.AutoImplement

【DLL】自动生成实现类

公开
关注 0 Fork 0 Star 0
UTF-8
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Generic;
using System.Linq;

namespace XFEExtension.NetCore.AutoImplement.Analyzer.Generator;

[Generator]
public class ImplementAutoGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        //var implementationProvider = context.SyntaxProvider.ForAttributeWithMetadataName("CreateImpl",
        //static (x, _) => x is ClassDeclarationSyntax classDeclaration && classDeclaration.AttributeLists.Any(IsCreateImplAttribute),
        //GetCreateImpl);
        //context.RegisterSourceOutput(implementationProvider.Combine(context.CompilationProvider), Generate);
        context.RegisterSourceOutput(context.CompilationProvider, Generate);
    }

    //public void Generate(SourceProductionContext context, (ImplementationInfo? ImplementationInfo, Compilation Compilation) args)
    //{
    //    if (args.ImplementationInfo is null)
    //        return;
    //    var root = args.ImplementationInfo.ClassDeclaration.SyntaxTree.GetRoot();
    //    var usingDirectives = root.DescendantNodes().OfType<UsingDirectiveSyntax>().ToArray();
    //    FileScopedNamespaceDeclarationSyntax? fileScopedNamespaceDeclarationSyntax = null;
    //    var namespaceResults = root.DescendantNodes().OfType<FileScopedNamespaceDeclarationSyntax>();
    //    if (namespaceResults != null && namespaceResults.Count() > 0)
    //        fileScopedNamespaceDeclarationSyntax = namespaceResults.First();
    //    var implementationSyntaxTree = GenerateImplementationSyntaxTree(args.ImplementationInfo.ClassDeclaration, usingDirectives, fileScopedNamespaceDeclarationSyntax, args.ImplementationInfo.TargetClassName, args.ImplementationInfo.Modifiers, args.ImplementationInfo.NameSpace);
    //    context.AddSource($"{args.ImplementationInfo.TargetClassName}.g.cs", implementationSyntaxTree.ToString());
    //}

    public void Generate(SourceProductionContext context, Compilation compilation)
    {
        var syntaxTrees = compilation.SyntaxTrees;
        foreach (var syntaxTree in syntaxTrees)
        {
            var root = syntaxTree.GetRoot();
            var classDeclarations = root.DescendantNodes().OfType<ClassDeclarationSyntax>()
                .Where(classDeclaration => classDeclaration.AttributeLists.Any(IsCreateImplAttribute));
            var usingDirectives = root.DescendantNodes().OfType<UsingDirectiveSyntax>().ToArray();
            FileScopedNamespaceDeclarationSyntax? fileScopedNamespaceDeclarationSyntax = null;
            var namespaceResults = root.DescendantNodes().OfType<FileScopedNamespaceDeclarationSyntax>();
            if (namespaceResults != null && namespaceResults.Count() > 0)
                fileScopedNamespaceDeclarationSyntax = namespaceResults.First();
            foreach (var classDeclaration in classDeclarations)
            {
                foreach (var attribute in GetCreateImplAttributeList(classDeclaration))
                {
                    var className = $"{classDeclaration.Identifier.ValueText}Impl";
                    var modifiers = new string[] { "internal", "partial" };
                    var nameSpace = string.Empty;
                    if (attribute.ArgumentList is not null && attribute.ArgumentList.Arguments.Count > 0)
                    {
                        foreach (var argument in attribute.ArgumentList.Arguments)
                        {
                            if (argument.Expression is LiteralExpressionSyntax literalExpressionSyntax && literalExpressionSyntax.IsKind(SyntaxKind.StringLiteralExpression))
                            {
                                className = literalExpressionSyntax.Token.ValueText;
                            }
                            else if (argument.NameEquals is NameEqualsSyntax nameEqualsSyntax)
                            {
                                var argumentName = nameEqualsSyntax.Name.Identifier.ValueText;
                                if (argumentName == "ClassName" && argument.Expression is LiteralExpressionSyntax classNameLiteral && classNameLiteral.IsKind(SyntaxKind.StringLiteralExpression))
                                {
                                    className = classNameLiteral.Token.ValueText;
                                }
                                else if (argumentName == "Modifiers")
                                {
                                    if (argument.Expression is ArrayCreationExpressionSyntax arrayCreationExpressionSyntax)
                                    {
                                        if (arrayCreationExpressionSyntax.Initializer is InitializerExpressionSyntax initializerExpressionSyntax)
                                        {
                                            modifiers = [.. initializerExpressionSyntax.Expressions.OfType<LiteralExpressionSyntax>()
                                                                                             .Where(literal => literal.IsKind(SyntaxKind.StringLiteralExpression))
                                                                                             .Select(literal => literal.Token.ValueText)
                                                                                             .Where(s => !string.IsNullOrEmpty(s))];
                                        }
                                    }
                                    else if (argument.Expression is CollectionExpressionSyntax collectionExpressionSyntax)
                                    {
                                        modifiers = [.. collectionExpressionSyntax.ChildNodes().OfType<ExpressionElementSyntax>()
                                                                                           .Select(element => element.Expression.ToString().Replace("\"",""))
                                                                                           .Where(s => !string.IsNullOrEmpty(s))];
                                    }
                                }
                                else if (argumentName == "NameSpace" && argument.Expression is LiteralExpressionSyntax nameSpaceLiteral && nameSpaceLiteral.IsKind(SyntaxKind.StringLiteralExpression))
                                {
                                    nameSpace = nameSpaceLiteral.Token.ValueText;
                                }
                            }
                        }
                    }
                    var implementationSyntaxTree = GenerateImplementationSyntaxTree(classDeclaration, usingDirectives, fileScopedNamespaceDeclarationSyntax, className, modifiers, nameSpace);
                    context.AddSource($"{className}.g.cs", implementationSyntaxTree.ToString());
                }
            }
        }
    }

    private static bool IsCreateImplAttribute(AttributeListSyntax attributeList) => attributeList.Attributes.Any(attribute => attribute.Name.ToString() == "CreateImpl");

    public static List<AttributeSyntax> GetCreateImplAttributeList(ClassDeclarationSyntax classDeclaration) => [.. classDeclaration.AttributeLists.Where(IsCreateImplAttribute).SelectMany(attributeList => attributeList.Attributes)];

    //public static ImplementationInfo? GetCreateImpl(GeneratorAttributeSyntaxContext context, CancellationToken token)
    //{
    //    if (context is { TargetSymbol: INamedTypeSymbol })
    //        Debugger.Launch();
    //    token.ThrowIfCancellationRequested();
    //    var classDeclaration = (ClassDeclarationSyntax)context.TargetNode;
    //    var attributeData = context.Attributes.FirstOrDefault(ad => ad.AttributeClass?.ToDisplayString() == "CreateImpl");
    //    if (attributeData is null)
    //        return null;
    //    var className = classDeclaration.Identifier.ValueText;
    //    var targetClassName = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "ClassName").Value.Value as string ?? $"{className}Impl";
    //    //var targetClassName = $"{className}Impl";
    //    var nameSpace = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "NameSpace").Value.Value as string ?? string.Empty;
    //    //var nameSpace = string.Empty;
    //    //var value = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "Modifiers").Value;
    //    //var modifiers = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "Modifiers").Value.Values.Select(v => v.Value?.ToString() ?? string.Empty).Where(s => !string.IsNullOrEmpty(s)).ToArray() ?? [];
    //    var modifiers = Array.Empty<string>();
    //    //foreach (var attributeList in classDeclaration.AttributeLists)
    //    //{
    //    //    foreach (var attribute in attributeList.Attributes)
    //    //    {
    //    //        if (attribute is null || attribute.Name.ToString() != "CreateImpl")
    //    //            continue;
    //    //        foreach (var argument in attribute.ArgumentList.Arguments)
    //    //        {

    //    //        }
    //    //    }
    //    //}
    //    if (modifiers.Length == 0)
    //        modifiers = ["internal", "partial"];
    //    return new ImplementationInfo
    //    {
    //        ClassDeclaration = classDeclaration,
    //        TargetClassName = targetClassName,
    //        Modifiers = modifiers,
    //        NameSpace = nameSpace
    //    };
    //}

    private static SyntaxTree GenerateImplementationSyntaxTree(ClassDeclarationSyntax classDeclaration, UsingDirectiveSyntax[] usingDirectiveSyntaxes, FileScopedNamespaceDeclarationSyntax? fileScopedNamespaceDeclarationSyntax, string className, string[] modifierParameters, string nameSpace)
    {
        var fatherClassName = classDeclaration.Identifier.ValueText;
        var modifiers = modifierParameters.Select(modifier => SyntaxFactory.ParseToken(modifier)).ToArray();
        ClassDeclarationSyntax implementationClass;
        if (classDeclaration.ParameterList is null)
        {
            implementationClass = SyntaxFactory.ClassDeclaration(className)
                .AddModifiers(modifiers)
                .AddMembers([.. classDeclaration.Members.OfType<ConstructorDeclarationSyntax>().Select(constructor =>
                {
                    return SyntaxFactory.ConstructorDeclaration(className)
                                        .AddModifiers(SyntaxFactory.Token(SyntaxKind.InternalKeyword))
                                        .WithBody(SyntaxFactory.Block())
                                        .WithParameterList(constructor.ParameterList)
                                        .WithInitializer(SyntaxFactory.ConstructorInitializer(SyntaxKind.BaseConstructorInitializer, SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(constructor.ParameterList.Parameters.Select(parameter => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(parameter.Identifier)))))));
                })])
                .WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia($@"/// <summary>
/// <seealso cref=""{className}""/> 是根据 <seealso cref=""{fatherClassName}""/> 自动生成的实现类
/// </summary>
"))
                .NormalizeWhitespace();
        }
        else
        {
            implementationClass = SyntaxFactory.ClassDeclaration(className)
                .AddModifiers(modifiers)
                .AddMembers(SyntaxFactory.ConstructorDeclaration(className)
                                         .AddModifiers(SyntaxFactory.Token(SyntaxKind.InternalKeyword))
                                         .WithBody(SyntaxFactory.Block())
                                         .WithParameterList(classDeclaration.ParameterList)
                                         .WithInitializer(SyntaxFactory.ConstructorInitializer(SyntaxKind.BaseConstructorInitializer, SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(classDeclaration.ParameterList.Parameters.Select(parameter => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(parameter.Identifier))))))))
                .WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia($@"/// <summary>
/// <seealso cref=""{className}""/> 是根据 <seealso cref=""{fatherClassName}""/> 自动生成的实现类
/// </summary>
"))
                .NormalizeWhitespace();
        }
        if (classDeclaration.TypeParameterList is TypeParameterListSyntax typeParameterListSyntax && typeParameterListSyntax.Parameters.Count > 0)
        {
            implementationClass = implementationClass.AddTypeParameterListParameters([.. typeParameterListSyntax.Parameters])
                                                     .AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName($"{fatherClassName}{typeParameterListSyntax}")))
                                                     .AddConstraintClauses([.. classDeclaration.ConstraintClauses]);
        }
        else
        {
            implementationClass = implementationClass.AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(fatherClassName)));
        }
        MemberDeclarationSyntax memberDeclaration;
        if (nameSpace != string.Empty)
        {
            memberDeclaration = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName(nameSpace))
                .AddMembers(implementationClass);
        }
        else if (fileScopedNamespaceDeclarationSyntax is null)
        {
            var namespaceDeclaration = classDeclaration.FirstAncestorOrSelf<NamespaceDeclarationSyntax>();
            if (namespaceDeclaration is null)
                memberDeclaration = implementationClass;
            else
                memberDeclaration = SyntaxFactory.NamespaceDeclaration(namespaceDeclaration.Name)
                    .AddMembers(implementationClass);
        }
        else
        {
            memberDeclaration = SyntaxFactory.FileScopedNamespaceDeclaration(fileScopedNamespaceDeclarationSyntax.Name)
                .AddMembers(implementationClass);
        }
        var implementationCompilationUnit = SyntaxFactory.CompilationUnit()
            .AddUsings(usingDirectiveSyntaxes)
            .AddMembers(memberDeclaration)
            .NormalizeWhitespace();
        return SyntaxFactory.SyntaxTree(implementationCompilationUnit);
    }
}