- shit: Cleanup this mess
This commit is contained in:
132
Nebula.SourceGenerators/DependencyAutoGenerator.cs
Normal file
132
Nebula.SourceGenerators/DependencyAutoGenerator.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
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 = "ConstructGeneratorAttribute";
|
||||
private static readonly string GeneratePropertyAttributeName = "GeneratePropertyAttribute";
|
||||
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
GenerateAttribute(ConstructGeneratorAttributeName, "Class", context);
|
||||
GenerateAttribute(GeneratePropertyAttributeName, "Property", context);
|
||||
|
||||
var provider = context.SyntaxProvider
|
||||
.CreateSyntaxProvider(
|
||||
(s, _) => s is ClassDeclarationSyntax,
|
||||
(ctx, _) => GetClassDeclarationForSourceGen(ctx))
|
||||
.Where(t => t.reportAttributeFound)
|
||||
.Select((t, _) => t.Item1);
|
||||
|
||||
context.RegisterSourceOutput(context.CompilationProvider.Combine(provider.Collect()),
|
||||
(ctx, t) => GenerateCode(ctx, t.Left, t.Right));
|
||||
}
|
||||
|
||||
private static void GenerateAttribute(string attributeName, string usage,
|
||||
IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
var attributeSourceCode = $@"// <auto-generated/>
|
||||
|
||||
namespace SourceGen
|
||||
{{
|
||||
[System.AttributeUsage(System.AttributeTargets.{usage})]
|
||||
public class {attributeName} : System.Attribute
|
||||
{{
|
||||
}}
|
||||
}}";
|
||||
|
||||
context.RegisterPostInitializationOutput(ctx => ctx.AddSource(
|
||||
$"{attributeName}.g.cs",
|
||||
SourceText.From(attributeSourceCode, Encoding.UTF8)));
|
||||
}
|
||||
|
||||
private static (ClassDeclarationSyntax, bool reportAttributeFound) GetClassDeclarationForSourceGen(
|
||||
GeneratorSyntaxContext context)
|
||||
{
|
||||
var classDeclarationSyntax = (ClassDeclarationSyntax)context.Node;
|
||||
|
||||
foreach (var attributeListSyntax in classDeclarationSyntax.AttributeLists)
|
||||
foreach (var attributeSyntax in attributeListSyntax.Attributes)
|
||||
{
|
||||
if (context.SemanticModel.GetSymbolInfo(attributeSyntax).Symbol is not IMethodSymbol attributeSymbol)
|
||||
continue;
|
||||
|
||||
var attributeName = attributeSymbol.ContainingType.ToDisplayString();
|
||||
|
||||
if (attributeName == $"SourceGen.{ConstructGeneratorAttributeName}")
|
||||
return (classDeclarationSyntax, true);
|
||||
}
|
||||
|
||||
return (classDeclarationSyntax, false);
|
||||
}
|
||||
|
||||
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}(){{}}";
|
||||
|
||||
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/>
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace {namespaceName};
|
||||
|
||||
partial class {className}
|
||||
{{
|
||||
|
||||
{defaultConstruct}
|
||||
public {className}(
|
||||
{string.Join(",\n\t\t", constr)}
|
||||
) : base(){{
|
||||
{string.Join("\n\t\t", body)}
|
||||
if (Avalonia.Controls.Design.IsDesignMode) InitialiseInDesignMode();
|
||||
else 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 =>
|
||||
HasAttribute(a, $"SourceGen.{GeneratePropertyAttributeName}"));
|
||||
}
|
||||
|
||||
private static bool HasAttribute(ISymbol type, string attributeName)
|
||||
{
|
||||
foreach (var attribute in type.GetAttributes())
|
||||
if (attribute.AttributeClass?.ToDisplayString() == attributeName)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user