diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json index 8c639d2..99e4065 100644 --- a/Properties/launchSettings.json +++ b/Properties/launchSettings.json @@ -3,7 +3,7 @@ "profiles": { "DebugRoslynSourceGenerator": { "commandName": "DebugRoslynComponent", - "targetProject": "../Polonium.Generators.Test/Polonium.Generators.Test.csproj" + "targetProject": "../Polonium.Workspace/Polonium.Test/Polonium.Test.csproj" } } } \ No newline at end of file diff --git a/src/AssetProcessGenerator.cs b/src/AssetProcessGenerator.cs index 7dbe304..16afd2e 100644 --- a/src/AssetProcessGenerator.cs +++ b/src/AssetProcessGenerator.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using System.IO; +using System.Linq; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Polonium.Generators; @@ -10,7 +12,7 @@ public abstract class AssetProcessGenerator : ISourceGenerator { context.RegisterForSyntaxNotifications(() => new ClassSyntaxReceiver()); } - + public abstract void Execute(GeneratorExecutionContext context); protected IEnumerable GetAssetsOfType(GeneratorExecutionContext context, INamedTypeSymbol? t) @@ -75,6 +77,14 @@ public abstract class AssetProcessGenerator : ISourceGenerator 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; + } + } diff --git a/src/ContextExtensions.cs b/src/ContextExtensions.cs new file mode 100644 index 0000000..8248bd7 --- /dev/null +++ b/src/ContextExtensions.cs @@ -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 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 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)); + + } +} \ No newline at end of file diff --git a/src/Generators/RegistryEntityGenerator.cs b/src/Generators/RegistryEntityGenerator.cs index e32b3c6..3e8b71a 100644 --- a/src/Generators/RegistryEntityGenerator.cs +++ b/src/Generators/RegistryEntityGenerator.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Text; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Polonium.Generators.Generators; [Generator] @@ -12,7 +13,7 @@ public class RegistryEntityGenerator : AssetProcessGenerator return; Compilation compilation = context.Compilation; 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); if ( diff --git a/src/Generators/TemplateBlockGenerator.cs b/src/Generators/TemplateBlockGenerator.cs new file mode 100644 index 0000000..66ac58f --- /dev/null +++ b/src/Generators/TemplateBlockGenerator.cs @@ -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 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()); + } + + } + + +} \ No newline at end of file diff --git a/src/Generators/TemplateInlineGenerator.cs b/src/Generators/TemplateInlineGenerator.cs new file mode 100644 index 0000000..82e8a8c --- /dev/null +++ b/src/Generators/TemplateInlineGenerator.cs @@ -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 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()); + } + } +}