Files
NebulaLauncher/Nebula.SourceGenerators/DependencyAutoGenerator.cs
2025-01-16 21:07:50 +03:00

78 lines
2.9 KiB
C#

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<ClassDeclarationSyntax> 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 defaultConstruct = $@"public {className}(){{
InitialiseInDesignMode();
}}";
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};");
//if (!constr.Any()) defaultConstruct = "";
var code = $@"// <auto-generated/>
namespace {namespaceName};
partial class {className}
{{
{defaultConstruct}
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<IPropertySymbol> GetProperties(INamedTypeSymbol classSymbol)
{
return classSymbol.GetMembers().OfType<IPropertySymbol>().Where(a =>
SourceHelper.HasAttribute(a, GeneratePropertyAttributeName));
}
}