add: Template Define

This commit is contained in:
h z
2025-02-22 03:56:27 +00:00
parent c7970c2274
commit 8bb977c735
6 changed files with 180 additions and 3 deletions

View File

@@ -3,7 +3,7 @@
"profiles": { "profiles": {
"DebugRoslynSourceGenerator": { "DebugRoslynSourceGenerator": {
"commandName": "DebugRoslynComponent", "commandName": "DebugRoslynComponent",
"targetProject": "../Polonium.Generators.Test/Polonium.Generators.Test.csproj" "targetProject": "../Polonium.Workspace/Polonium.Test/Polonium.Test.csproj"
} }
} }
} }

View File

@@ -1,6 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Polonium.Generators; namespace Polonium.Generators;
@@ -75,6 +77,14 @@ public abstract class AssetProcessGenerator : ISourceGenerator
return false; return false;
} }
protected static string Format(string template, string data)
{
string[] dataParts = data.Split(':');
for(int i = 0; i < dataParts.Length; i++)
template = template.Replace($"/*{i}*/", dataParts[i]);
return template;
}
} }

34
src/ContextExtensions.cs Normal file
View File

@@ -0,0 +1,34 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Polonium.Generators;
public static class ContextExtensions
{
public static IEnumerable<ClassDeclarationSyntax> GetClassesWithAttribute(this GeneratorExecutionContext context, ClassSyntaxReceiver receiver, INamedTypeSymbol? t)
{
Compilation compilation = context.Compilation;
foreach (ClassDeclarationSyntax? cls in receiver.Classes)
{
SemanticModel model = compilation.GetSemanticModel(cls.SyntaxTree);
if (
model.GetDeclaredSymbol(cls) is INamedTypeSymbol symbol &&
symbol.GetAttributes().Any(x => SymbolEqualityComparer.Default.Equals(x.AttributeClass, t))
)
yield return cls;
}
}
public static IEnumerable<AttributeData> GetAttributes(this GeneratorExecutionContext context, ClassDeclarationSyntax cls, INamedTypeSymbol? t)
{
Compilation compilation = context.Compilation;
SemanticModel model = compilation.GetSemanticModel(cls.SyntaxTree);
return model
.GetDeclaredSymbol(cls)!
.GetAttributes()
.Where(x => SymbolEqualityComparer.Default.Equals(x.AttributeClass, t));
}
}

View File

@@ -1,6 +1,7 @@
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Polonium.Generators.Generators; namespace Polonium.Generators.Generators;
[Generator] [Generator]
@@ -12,7 +13,7 @@ public class RegistryEntityGenerator : AssetProcessGenerator
return; return;
Compilation compilation = context.Compilation; Compilation compilation = context.Compilation;
INamedTypeSymbol? regEntity = compilation.GetTypeByMetadataName("Polonium.Attributes.RegistryEntity"); INamedTypeSymbol? regEntity = compilation.GetTypeByMetadataName("Polonium.Attributes.RegistryEntity");
foreach (var derivedClassDeclaration in receiver.Classes) foreach (ClassDeclarationSyntax? derivedClassDeclaration in receiver.Classes)
{ {
SemanticModel model = compilation.GetSemanticModel(derivedClassDeclaration.SyntaxTree); SemanticModel model = compilation.GetSemanticModel(derivedClassDeclaration.SyntaxTree);
if ( if (

View File

@@ -0,0 +1,69 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Polonium.Generators.Generators;
[Generator]
public class TemplateBlockGenerator : AssetProcessGenerator
{
public override void Execute(GeneratorExecutionContext context)
{
if (!(context.SyntaxReceiver is ClassSyntaxReceiver receiver))
return;
Compilation compilation = context.Compilation;
INamedTypeSymbol? templateDefine = compilation.GetTypeByMetadataName("Polonium.Attributes.TemplateDefines.TemplateBlock");
foreach (ClassDeclarationSyntax? cls in context.GetClassesWithAttribute(receiver, templateDefine))
{
if(!cls.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword)))
continue;
string isStatic = "";
if(cls.Modifiers.Any(m => m.IsKind(SyntaxKind.StaticKeyword)))
isStatic = "static";
StringBuilder sb = new StringBuilder();
sb
.AppendLine($"public {isStatic} partial class {cls.Identifier.ToString()}")
.AppendLine("{");
HashSet<string> headers = new();
foreach (AttributeData attr in context.GetAttributes(cls, templateDefine))
{
string signature = attr.ConstructorArguments[0].Value?.ToString()??string.Empty;
string template = attr.ConstructorArguments[1].Value?.ToString()??string.Empty;
string dataFile = attr.ConstructorArguments[2].Value?.ToString()??string.Empty;
string depsStr = attr.ConstructorArguments[3].Value?.ToString()??string.Empty;
string[] deps = depsStr.Split(';');
foreach (string dep in deps)
headers.Add(dep);
sb
.AppendLine($" {signature}")
.AppendLine(" {");
AdditionalText? df = context.AdditionalFiles.FirstOrDefault(f => f.Path.EndsWith(dataFile));
string[] data =(df?.GetText()?.ToString() ?? "").Split('\n');
foreach (string d in data)
{
if(d.Trim().Equals(""))
continue;
sb.AppendLine($" {Format(template, d)}");
}
sb.AppendLine(" }");
}
sb.AppendLine("}");
StringBuilder hb = new StringBuilder();
foreach (string header in headers)
{
if(header.Trim().Equals(""))
continue;
hb.AppendLine($"using {header};");
}
hb.Append(sb);
context.AddSource($"{cls.Identifier.ToString()}_TemplateDefine.g.cs", hb.ToString());
}
}
}

View File

@@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Polonium.Generators.Generators;
[Generator]
public class TemplateInlineGenerator : AssetProcessGenerator
{
public override void Execute(GeneratorExecutionContext context)
{
if (!(context.SyntaxReceiver is ClassSyntaxReceiver receiver))
return;
Compilation compilation = context.Compilation;
INamedTypeSymbol? templateDefine = compilation.GetTypeByMetadataName("Polonium.Attributes.TemplateDefines.TemplateInline");
foreach (ClassDeclarationSyntax? cls in context.GetClassesWithAttribute(receiver, templateDefine))
{
if(!cls.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword)))
continue;
string isStatic = "";
if(cls.Modifiers.Any(m => m.IsKind(SyntaxKind.StaticKeyword)))
isStatic = "static";
StringBuilder sb = new StringBuilder();
sb
.AppendLine($"public {isStatic} partial class {cls.Identifier.ToString()}")
.AppendLine("{");
HashSet<string> headers = new();
foreach (AttributeData attr in context.GetAttributes(cls, templateDefine))
{
string template = attr.ConstructorArguments[0].Value?.ToString()??string.Empty;
string dataFile = attr.ConstructorArguments[1].Value?.ToString()??string.Empty;
string depsString = attr.ConstructorArguments[2].Value?.ToString()??string.Empty;
string[] deps = depsString.Split(';');
foreach (string dep in deps)
headers.Add(dep);
AdditionalText? df = context.AdditionalFiles.FirstOrDefault(f => f.Path.EndsWith(dataFile));
string[] data =(df?.GetText()?.ToString() ?? "").Split('\n');
foreach (string d in data)
{
if(d.Trim().Equals(""))
continue;
sb.AppendLine($" {Format(template, d)}");
}
}
sb.AppendLine("}");
StringBuilder hb = new();
foreach(string header in headers)
{
if(header.Trim().Equals(""))
continue;
hb.AppendLine($"using {header};");
}
hb.Append(sb);
Console.WriteLine(hb.ToString());
context.AddSource($"{cls.Identifier.ToString()}_TemplateInline.g.cs", hb.ToString());
}
}
}