using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; namespace Nebula.SourceGenerators; [Generator] public class DependencyAutoGenerator : IIncrementalGenerator { private static readonly string ConstructGeneratorAttributeName = "Nebula.Shared.Attributes.ConstructGeneratorAttribute"; private static readonly string GeneratePropertyAttributeName = "Nebula.Shared.Attributes.GeneratePropertyAttribute"; public void Initialize(IncrementalGeneratorInitializationContext context) { var provider = context.SyntaxProvider .CreateSyntaxProvider( (s, _) => s is ClassDeclarationSyntax, (ctx, _) => SourceHelper.GetClassDeclarationForSourceGen(ctx,ConstructGeneratorAttributeName)) .Where(t => t.reportAttributeFound) .Select((t, _) => t.Item1); context.RegisterSourceOutput(context.CompilationProvider.Combine(provider.Collect()), (ctx, t) => GenerateCode(ctx, t.Left, t.Right)); } private void GenerateCode(SourceProductionContext context, Compilation compilation, ImmutableArray classDeclarations) { foreach (var classDeclarationSyntax in classDeclarations) { var semanticModel = compilation.GetSemanticModel(classDeclarationSyntax.SyntaxTree); if (semanticModel.GetDeclaredSymbol(classDeclarationSyntax) is not INamedTypeSymbol classSymbol) continue; var namespaceName = classSymbol.ContainingNamespace.ToDisplayString(); var className = classDeclarationSyntax.Identifier.Text; var propertiesGenerated = GetProperties(classSymbol).ToList(); var constr = propertiesGenerated.Select(a => $"{a.Type.ToDisplayString()} g{a.Name}"); var body = propertiesGenerated.Select(a => $"this.{a.Name} = g{a.Name};"); var propertiesGeneratedC = GetProperties(classSymbol) .Where(a=> SourceHelper.HasAttribute(a,"Nebula.Shared.Attributes.DesignConstructAttribute")) .ToList(); var bodyC = propertiesGeneratedC.Select(a => $"this.{a.Name} = new {a.Type.ToDisplayString()}();"); var code = $@"// namespace {namespaceName}; partial class {className} {{ public {className}(){{ {string.Join("\n\t\t", bodyC)} InitialiseInDesignMode(); }} public {className}( {string.Join(",\n\t\t", constr)} ) : base(){{ {string.Join("\n\t\t", body)} Initialise(); }} }} "; // Add the source code to the compilation. context.AddSource($"{className}.g.cs", SourceText.From(code, Encoding.UTF8)); } } private static IEnumerable GetProperties(INamedTypeSymbol classSymbol) { return classSymbol.GetMembers().OfType().Where(a => SourceHelper.HasAttribute(a, GeneratePropertyAttributeName)); } }