Compare commits
22 Commits
dc07ffae51
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 400cc3a048 | |||
| f93f1eaf74 | |||
| 7f7ef09231 | |||
| f53c66a8ec | |||
| 9668c27e2d | |||
| ed9ae2113d | |||
| 03468ad89c | |||
| 302d11e2dd | |||
| 8bb977c735 | |||
| c7970c2274 | |||
| fc54380ce3 | |||
| 0a3a6faca5 | |||
| e97b2f098f | |||
| 6738080639 | |||
| 24a74a6f80 | |||
| a34fca7581 | |||
| 6683b6252c | |||
| cdf94b3e0c | |||
| 59529419e5 | |||
| a52ebfa2f2 | |||
| 2812d98d15 | |||
| 2f3e5ad146 |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -2,4 +2,8 @@ bin/
|
||||
obj/
|
||||
/packages/
|
||||
riderModule.iml
|
||||
/_ReSharper.Caches/
|
||||
/_ReSharper.Caches/
|
||||
.idea/
|
||||
VersionInfo.props
|
||||
/summerizer.py
|
||||
/summerizer
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Polonium.Generators;
|
||||
|
||||
public abstract class AssetProcessGenerator : ISourceGenerator
|
||||
{
|
||||
public void Initialize(GeneratorInitializationContext context)
|
||||
{
|
||||
context.RegisterForSyntaxNotifications(() => new ClassSyntaxReceiver());
|
||||
}
|
||||
|
||||
public abstract void Execute(GeneratorExecutionContext context);
|
||||
|
||||
protected IEnumerable<AssetInfo> GetAssetsOfType(GeneratorExecutionContext context, INamedTypeSymbol? t)
|
||||
{
|
||||
if (!(context.SyntaxReceiver is ClassSyntaxReceiver receiver))
|
||||
yield break;
|
||||
Compilation compilation = context.Compilation;
|
||||
foreach (var derivedClassDeclaration in receiver.Classes)
|
||||
{
|
||||
SemanticModel model = compilation.GetSemanticModel(derivedClassDeclaration.SyntaxTree);
|
||||
INamedTypeSymbol? derivedClassSymbol = model.GetDeclaredSymbol(derivedClassDeclaration) as INamedTypeSymbol;
|
||||
string filePath = derivedClassDeclaration.SyntaxTree.FilePath;
|
||||
string className = Path.GetFileNameWithoutExtension(filePath);
|
||||
if(derivedClassSymbol == null || !filePath.Contains("Assets"))
|
||||
continue;
|
||||
AssetInfo res = new AssetInfo
|
||||
{
|
||||
DeclarationSyntax = derivedClassDeclaration,
|
||||
ClassName = className,
|
||||
ClassFullName = derivedClassSymbol.ToDisplayString(),
|
||||
Path = filePath,
|
||||
Symbol = derivedClassSymbol
|
||||
};
|
||||
if (IsDerivedFrom(derivedClassSymbol, t))
|
||||
{
|
||||
yield return res;
|
||||
continue;
|
||||
}
|
||||
foreach (INamedTypeSymbol interfaceSymbol in derivedClassSymbol.AllInterfaces)
|
||||
{
|
||||
if (SymbolEqualityComparer.Default.Equals(interfaceSymbol, t))
|
||||
{
|
||||
yield return res;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected IEnumerable<AssetInfo> GetAssetsOfType(GeneratorExecutionContext context, string t)
|
||||
{
|
||||
|
||||
Compilation compilation = context.Compilation;
|
||||
INamedTypeSymbol? specificType = compilation.GetTypeByMetadataName(t);
|
||||
return GetAssetsOfType(context, specificType);
|
||||
}
|
||||
protected static bool IsDerivedFrom(INamedTypeSymbol? symbol, INamedTypeSymbol? baseType)
|
||||
{
|
||||
if (symbol == null)
|
||||
return false;
|
||||
if (baseType == null)
|
||||
return true;
|
||||
INamedTypeSymbol? current = symbol.BaseType;
|
||||
|
||||
while (current != null)
|
||||
{
|
||||
if (SymbolEqualityComparer.Default.Equals(current, baseType))
|
||||
return true;
|
||||
current = current.BaseType;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Polonium.Generators;
|
||||
[Generator]
|
||||
public class GlobalRegistryGenerator : ISourceGenerator
|
||||
{
|
||||
public void Initialize(GeneratorInitializationContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void Execute(GeneratorExecutionContext context)
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb
|
||||
.AppendLine("using Godot;")
|
||||
.AppendLine("using System;")
|
||||
.AppendLine("using System.Collections.Generic;")
|
||||
.AppendLine("using System.Linq;")
|
||||
.AppendLine("using System.Reflection;")
|
||||
.AppendLine("using Polonium.Attributes;")
|
||||
.AppendLine("using Polonium;")
|
||||
.AppendLine("public static partial class GlobalRegistry")
|
||||
.AppendLine("{")
|
||||
.AppendLine(" public static void Register()")
|
||||
.AppendLine(" {")
|
||||
.AppendLine(" Assembly assembly = Assembly.GetExecutingAssembly();")
|
||||
.AppendLine(" IEnumerable<Type> registerTypes = Utils.GetLoadableTypes(assembly);")
|
||||
.AppendLine(" registerTypes = registerTypes.Where(t => t.IsClass && t.GetCustomAttributes(typeof(AutoRegister), false).Any());")
|
||||
.AppendLine(" foreach(Type t in registerTypes)")
|
||||
.AppendLine(" {")
|
||||
.AppendLine(" MethodInfo registerMethod = t.GetMethod(\"Register\", BindingFlags.Static | BindingFlags.Public);")
|
||||
.AppendLine(" if (registerMethod != null)")
|
||||
.AppendLine(" registerMethod.Invoke(null, null);")
|
||||
.AppendLine(" }")
|
||||
.AppendLine(" }")
|
||||
.AppendLine(" public static class Asset<T> where T : Node")
|
||||
.AppendLine(" {")
|
||||
.AppendLine(" private static readonly Queue<T> Pool = new();")
|
||||
.AppendLine(" public static PackedScene Scene { get; set; }")
|
||||
.AppendLine(" public static T Instance => Scene.Instantiate<T>();")
|
||||
.AppendLine(" public static T Get() => Pool.Count > 0 ? Pool.Dequeue() : Instance;")
|
||||
.AppendLine(" public static void Return(T obj)")
|
||||
.AppendLine(" {")
|
||||
.AppendLine(" if(Pool.Count < 10)")
|
||||
.AppendLine(" Pool.Enqueue(obj);")
|
||||
.AppendLine(" else")
|
||||
.AppendLine(" obj.QueueFree();")
|
||||
.AppendLine(" }")
|
||||
.AppendLine(" }")
|
||||
.AppendLine("}");
|
||||
context.AddSource("GlobalRegistry.g.cs", sb.ToString());
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=Hangman_002ESDK_002EGenerators_002FResources/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/ResxEditorPersonal/Initialized/@EntryValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
8
NuGet.config
Normal file
8
NuGet.config
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="hangman-lab" value="https://git.hangman-lab.top/api/packages/hzhang/nuget/index.json"/>
|
||||
<add key="Local" value="/NuGetFeed"/>
|
||||
</packageSources>
|
||||
|
||||
</configuration>
|
||||
44
Polonium.Generators.csproj
Normal file
44
Polonium.Generators.csproj
Normal file
@@ -0,0 +1,44 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="VersionInfo.props" Condition="Exists('VersionInfo.props')" />
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<IsPackable>true</IsPackable>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||
<IsRoslynComponent>true</IsRoslynComponent>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Version>$(PoloniumGeneratorsVersion)</Version>
|
||||
<Authors>Hangman</Authors>
|
||||
<PackageId>Polonium.Generators</PackageId>
|
||||
<DisableImplicitRestore>true</DisableImplicitRestore>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers\dotnet\cs" Link="Package\analyzers\dotnet\cs\$(AssemblyName).dll" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.0"/>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.3.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CleanPreviousPackages" BeforeTargets="Build">
|
||||
<ItemGroup>
|
||||
<ExistingPackages Include="$(OutputPath)../*.nupkg"/>
|
||||
</ItemGroup>
|
||||
<Delete Files="@(ExistingPackages)" ContinueOnError="true"/>
|
||||
</Target>
|
||||
|
||||
<Target Name="CopyPackageToLocalFeed" AfterTargets="Pack">
|
||||
<ItemGroup>
|
||||
<NuGetPackages Include="$(OutputPath)../*.nupkg" />
|
||||
</ItemGroup>
|
||||
<Copy SourceFiles="@(NuGetPackages)" DestinationFolder="/NuGetFeed"/>
|
||||
</Target>
|
||||
|
||||
<Target Name="RestoreNoCache" BeforeTargets="CoreCompile">
|
||||
<Exec Command="dotnet restore --no-cache"/>
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hangman.SDK.Generators", "Hangman.SDK.Generators.csproj", "{884B5A6A-4063-4FC7-8FE6-EE1C45CF251E}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Polonium.Generators", "Polonium.Generators.csproj", "{884B5A6A-4063-4FC7-8FE6-EE1C45CF251E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
||||
3
Polonium.Generators.sln.DotSettings.user
Normal file
3
Polonium.Generators.sln.DotSettings.user
Normal file
@@ -0,0 +1,3 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=Hangman_002ESDK_002EGenerators_002FResources/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/ResxEditorPersonal/Initialized/@EntryValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
9
Properties/launchSettings.json
Normal file
9
Properties/launchSettings.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"DebugRoslynSourceGenerator": {
|
||||
"commandName": "DebugRoslynComponent",
|
||||
"targetProject": "../Polonium.Workspace/Polonium.Test/Polonium.Test.csproj"
|
||||
}
|
||||
}
|
||||
}
|
||||
36
README.md
Normal file
36
README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Polonium.Generators
|
||||
|
||||
**Project Description**
|
||||
Polonium.Generators is a C# source generator suite specifically built for the Polonium ecosystem. By analyzing attributes like `[ProxyNode]`, `[AutoPatch]`, `[RegistryEntity]`, and `[RegistryPassThrough]`, it generates boilerplate code that ties together key features in Polonium, reducing repetitive tasks and ensuring consistency.
|
||||
|
||||
---
|
||||
|
||||
## Code Generation Highlights
|
||||
|
||||
1. **`[ProxyNode]`**
|
||||
When a class is annotated with `[ProxyNode]` (and optionally `[ProxyProperty]` for properties or `[ProxyMethod]` for methods), Polonium.Generators automatically creates partial classes or methods, simplifying script bridging and property handling. This is primarily used in Polonium’s embedded classes, but it can also be extended.
|
||||
|
||||
2. **`[AutoPatch]` & `[PatchableProperty]`**
|
||||
If an interface is marked with `[AutoPatch]` and contains at most one `[PatchableProperty]` (with a getter and setter), Polonium.Generators will create a corresponding Patch class. This allows you to apply changes ("patches") to classes implementing that interface without direct references, greatly simplifying state synchronization.
|
||||
|
||||
3. **`[RegistryEntity]`**
|
||||
Classes annotated with `[RegistryEntity]` trigger a code generation step that adds a property for that class in `GlobalRegistry`, enabling you to easily access and manage instances across your entire project.
|
||||
|
||||
4. **`[RegistryPassThrough]`**
|
||||
Properties marked with `[RegistryPassThrough]` are automatically bridged so that getting or setting them references the central `PoloniumRegistry`. While primarily used by Polonium’s own embedded classes, it can be adapted for larger multi-module scenarios.
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
1. Ensure you’re targeting .NET 6 or higher (or the same version Polonium requires).
|
||||
2. Install via NuGet:
|
||||
```bash
|
||||
dotnet add package Polonium.Generators
|
||||
```
|
||||
3. Add `<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>` in your `.csproj` if you want to inspect the generated sources.
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
This project is licensed under the [MIT License](https://opensource.org/licenses/MIT).
|
||||
|
||||
12
publish
Normal file → Executable file
12
publish
Normal file → Executable file
@@ -1,2 +1,12 @@
|
||||
#!/bin/bash
|
||||
dotnet nuget push "$(ls -t ./bin/Debug/Hangman.SDK.Generators.*.nupkg | head -n 1)" --source hangman-lab
|
||||
|
||||
SCRIPT_DIR=$(dirname "$(realpath "${BASH_SOURCE[0]}")")/
|
||||
LATEST_PACKAGE=$(ls -t "${SCRIPT_DIR}"bin/Debug/Polonium.Generators.*.nupkg 2>/dev/null | head -n 1)
|
||||
|
||||
if [[ -z "$LATEST_PACKAGE" ]]; then
|
||||
echo "❌ Error: No .nupkg file found in ${SCRIPT_DIR}/bin/Debug/"
|
||||
exit 1
|
||||
fi
|
||||
echo "🚀 Pushing NuGet package: $LATEST_PACKAGE"
|
||||
dotnet nuget push "$LATEST_PACKAGE" --source hangman-lab --skip-duplicate
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
namespace Polonium.Generators;
|
||||
|
||||
using System;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace Polonium.Generators;
|
||||
|
||||
public struct AssetInfo
|
||||
{
|
||||
public INamedTypeSymbol Symbol;
|
||||
@@ -21,4 +21,5 @@ public struct AssetInfo
|
||||
}
|
||||
|
||||
public string ScenePath => GodotPath.Replace(".cs", ".tscn");
|
||||
|
||||
}
|
||||
222
src/AssetProcessGenerator.cs
Normal file
222
src/AssetProcessGenerator.cs
Normal file
@@ -0,0 +1,222 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace Polonium.Generators;
|
||||
|
||||
public abstract class AssetProcessGenerator : ISourceGenerator
|
||||
{
|
||||
public void Initialize(GeneratorInitializationContext context)
|
||||
{
|
||||
context.RegisterForSyntaxNotifications(() => new ClassSyntaxReceiver());
|
||||
}
|
||||
|
||||
public abstract void Execute(GeneratorExecutionContext context);
|
||||
|
||||
protected IEnumerable<AssetInfo> GetAssetsOfType(GeneratorExecutionContext context, INamedTypeSymbol? t)
|
||||
{
|
||||
if (!(context.SyntaxReceiver is ClassSyntaxReceiver receiver))
|
||||
yield break;
|
||||
Compilation compilation = context.Compilation;
|
||||
foreach (var derivedClassDeclaration in receiver.Classes)
|
||||
{
|
||||
SemanticModel model = compilation.GetSemanticModel(derivedClassDeclaration.SyntaxTree);
|
||||
INamedTypeSymbol? derivedClassSymbol = model.GetDeclaredSymbol(derivedClassDeclaration) as INamedTypeSymbol;
|
||||
string filePath = derivedClassDeclaration.SyntaxTree.FilePath;
|
||||
string className = Path.GetFileNameWithoutExtension(filePath);
|
||||
if(derivedClassSymbol == null || !filePath.Contains("Assets"))
|
||||
continue;
|
||||
AssetInfo res = new AssetInfo
|
||||
{
|
||||
DeclarationSyntax = derivedClassDeclaration,
|
||||
ClassName = className,
|
||||
ClassFullName = derivedClassSymbol.ToDisplayString(),
|
||||
Path = filePath,
|
||||
Symbol = derivedClassSymbol
|
||||
};
|
||||
if (IsDerivedFrom(derivedClassSymbol, t))
|
||||
{
|
||||
yield return res;
|
||||
continue;
|
||||
}
|
||||
foreach (INamedTypeSymbol interfaceSymbol in derivedClassSymbol.AllInterfaces)
|
||||
{
|
||||
if (SymbolEqualityComparer.Default.Equals(interfaceSymbol, t))
|
||||
{
|
||||
yield return res;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected IEnumerable<AssetInfo> GetAssetsOfType(GeneratorExecutionContext context, string t)
|
||||
{
|
||||
|
||||
Compilation compilation = context.Compilation;
|
||||
INamedTypeSymbol? specificType = compilation.GetTypeByMetadataName(t);
|
||||
return GetAssetsOfType(context, specificType);
|
||||
}
|
||||
protected static bool IsDerivedFrom(INamedTypeSymbol? symbol, INamedTypeSymbol? baseType)
|
||||
{
|
||||
if (symbol == null)
|
||||
return false;
|
||||
if (baseType == null)
|
||||
return true;
|
||||
INamedTypeSymbol? current = symbol.BaseType;
|
||||
|
||||
while (current != null)
|
||||
{
|
||||
if (SymbolEqualityComparer.Default.Equals(current, baseType))
|
||||
return true;
|
||||
current = current.BaseType;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
protected IEnumerable<InterfaceInfo> GetInterfacesWithAttribute(GeneratorExecutionContext context,
|
||||
INamedTypeSymbol? attribute)
|
||||
{
|
||||
if(context.SyntaxReceiver is not ClassSyntaxReceiver receiver)
|
||||
yield break;
|
||||
Compilation compilation = context.Compilation;
|
||||
foreach (InterfaceDeclarationSyntax ifc in receiver.Interfaces)
|
||||
{
|
||||
SemanticModel model = compilation.GetSemanticModel(ifc.SyntaxTree);
|
||||
INamedTypeSymbol? interfaceSymbol = model.GetDeclaredSymbol(ifc) as INamedTypeSymbol;
|
||||
string filePath = ifc.SyntaxTree.FilePath;
|
||||
string interfaceName = Path.GetFileNameWithoutExtension(filePath);
|
||||
if(interfaceSymbol is null)
|
||||
continue;
|
||||
if (HasAttribute(interfaceSymbol, attribute))
|
||||
yield return new InterfaceInfo
|
||||
{
|
||||
DeclarationSyntax = ifc,
|
||||
InterfaceName = interfaceName,
|
||||
InterfaceFillName = interfaceSymbol.ToDisplayString(),
|
||||
Path = filePath,
|
||||
Symbol = interfaceSymbol,
|
||||
Namespace = interfaceSymbol.ContainingNamespace.ToDisplayString()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
protected IEnumerable<ClassInfo> GetClassesWithAttribute(GeneratorExecutionContext context,
|
||||
INamedTypeSymbol? attribute)
|
||||
{
|
||||
if(context.SyntaxReceiver is not ClassSyntaxReceiver receiver)
|
||||
yield break;
|
||||
Compilation compilation = context.Compilation;
|
||||
foreach (ClassDeclarationSyntax? declaration in receiver.Classes)
|
||||
{
|
||||
SemanticModel model = compilation.GetSemanticModel(declaration.SyntaxTree);
|
||||
INamedTypeSymbol? sClassSymbol = model.GetDeclaredSymbol(declaration) as INamedTypeSymbol;
|
||||
string filePath = declaration.SyntaxTree.FilePath;
|
||||
string className = Path.GetFileNameWithoutExtension(filePath);
|
||||
if (sClassSymbol is null)
|
||||
continue;
|
||||
if (HasAttribute(sClassSymbol, attribute))
|
||||
yield return new ClassInfo
|
||||
{
|
||||
DeclarationSyntax = declaration,
|
||||
ClassName = className,
|
||||
ClassFullName = sClassSymbol.ToDisplayString(),
|
||||
Path = filePath,
|
||||
Symbol = sClassSymbol,
|
||||
Namespace = sClassSymbol.ContainingNamespace.ToDisplayString()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
protected IEnumerable<ClassInfo> GetClassesWithInterface(GeneratorExecutionContext context, INamedTypeSymbol? ifc, bool direct = true)
|
||||
{
|
||||
if(context.SyntaxReceiver is not ClassSyntaxReceiver receiver)
|
||||
yield break;
|
||||
Compilation compilation = context.Compilation;
|
||||
foreach (ClassDeclarationSyntax? declaration in receiver.Classes)
|
||||
{
|
||||
SemanticModel model = compilation.GetSemanticModel(declaration.SyntaxTree);
|
||||
INamedTypeSymbol? sClassSymbol = model.GetDeclaredSymbol(declaration) as INamedTypeSymbol;
|
||||
string filePath = declaration.SyntaxTree.FilePath;
|
||||
string className = Path.GetFileNameWithoutExtension(filePath);
|
||||
bool interfaceFound = false;
|
||||
if (sClassSymbol is null)
|
||||
yield break;
|
||||
if (direct)
|
||||
interfaceFound = sClassSymbol.Interfaces.Any(i => SymbolEqualityComparer.Default.Equals(i, ifc));
|
||||
else
|
||||
interfaceFound = sClassSymbol.AllInterfaces.Any(i => SymbolEqualityComparer.Default.Equals(i, ifc));
|
||||
|
||||
if (interfaceFound)
|
||||
{
|
||||
yield return new ClassInfo
|
||||
{
|
||||
DeclarationSyntax = declaration,
|
||||
ClassName = className,
|
||||
ClassFullName = sClassSymbol.ToDisplayString(),
|
||||
Path = filePath,
|
||||
Symbol = sClassSymbol,
|
||||
Namespace = sClassSymbol.ContainingNamespace.ToDisplayString()
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected static bool HasAttribute(INamedTypeSymbol? type, INamedTypeSymbol? attr)
|
||||
{
|
||||
if (type is null)
|
||||
return false;
|
||||
return type.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attr));
|
||||
}
|
||||
protected IEnumerable<ClassInfo> GetClassesOfType(GeneratorExecutionContext context, INamedTypeSymbol? type)
|
||||
{
|
||||
if(context.SyntaxReceiver is not ClassSyntaxReceiver receiver)
|
||||
yield break;
|
||||
Compilation compilation = context.Compilation;
|
||||
foreach (ClassDeclarationSyntax? declaration in receiver.Classes)
|
||||
{
|
||||
SemanticModel model = compilation.GetSemanticModel(declaration.SyntaxTree);
|
||||
INamedTypeSymbol? sClassSymbol = model.GetDeclaredSymbol(declaration) as INamedTypeSymbol;
|
||||
string filePath = declaration.SyntaxTree.FilePath;
|
||||
string className = Path.GetFileNameWithoutExtension(filePath);
|
||||
if (sClassSymbol is null)
|
||||
continue;
|
||||
ClassInfo res = new ClassInfo
|
||||
{
|
||||
DeclarationSyntax = declaration,
|
||||
ClassName = className,
|
||||
ClassFullName = sClassSymbol.ToDisplayString(),
|
||||
Path = filePath,
|
||||
Symbol = sClassSymbol,
|
||||
Namespace = sClassSymbol.ContainingNamespace.ToDisplayString()
|
||||
};
|
||||
if (IsDerivedFrom(sClassSymbol, type))
|
||||
{
|
||||
yield return res;
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (INamedTypeSymbol interfaceSymbol in sClassSymbol.AllInterfaces)
|
||||
{
|
||||
if (SymbolEqualityComparer.Default.Equals(interfaceSymbol, type))
|
||||
{
|
||||
yield return res;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/ClassInfo.cs
Normal file
15
src/ClassInfo.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace Polonium.Generators;
|
||||
|
||||
public class ClassInfo
|
||||
{
|
||||
public INamedTypeSymbol Symbol { get; set; }
|
||||
public ClassDeclarationSyntax DeclarationSyntax { get; set; }
|
||||
public string ClassName { get; set; }
|
||||
public string ClassFullName { get; set; }
|
||||
public string Namespace { get; set; }
|
||||
public string Path { get; set; }
|
||||
}
|
||||
|
||||
@@ -6,13 +6,14 @@ namespace Polonium.Generators;
|
||||
|
||||
public class ClassSyntaxReceiver : ISyntaxReceiver
|
||||
{
|
||||
public List<ClassDeclarationSyntax> Classes { get; set; } = new ();
|
||||
public List<ClassDeclarationSyntax> Classes { get; } = new ();
|
||||
public List<InterfaceDeclarationSyntax> Interfaces { get; } = new ();
|
||||
|
||||
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
|
||||
{
|
||||
if (syntaxNode is ClassDeclarationSyntax classDeclaration)
|
||||
{
|
||||
Classes.Add(classDeclaration);
|
||||
}
|
||||
if(syntaxNode is InterfaceDeclarationSyntax interfaceDeclaration)
|
||||
Interfaces.Add(interfaceDeclaration);
|
||||
}
|
||||
}
|
||||
34
src/ContextExtensions.cs
Normal file
34
src/ContextExtensions.cs
Normal 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));
|
||||
|
||||
}
|
||||
}
|
||||
42
src/Generators/AbstractSkinItemTemplateGenerator.cs
Normal file
42
src/Generators/AbstractSkinItemTemplateGenerator.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
|
||||
namespace Polonium.Generators.Generators;
|
||||
|
||||
public class AbstractSkinItemTemplateGenerator : AssetProcessGenerator
|
||||
{
|
||||
public override void Execute(GeneratorExecutionContext context)
|
||||
{
|
||||
if (context.SyntaxReceiver is not ClassSyntaxReceiver receiver)
|
||||
return;
|
||||
Compilation compilation = context.Compilation;
|
||||
INamedTypeSymbol? skinItem = compilation.GetTypeByMetadataName("Polonium.ItemManagers.SkinItem");
|
||||
foreach (ClassInfo c in GetClassesOfType(context, skinItem))
|
||||
{
|
||||
if (!c.DeclarationSyntax.Modifiers.Any(m => m.IsKind(SyntaxKind.AbstractKeyword)))
|
||||
continue;
|
||||
StringBuilder sb = new();
|
||||
sb
|
||||
.AppendLine("using System;")
|
||||
.AppendLine("using Skin=Polonium.SkinManagers.Skin;")
|
||||
.AppendLine($"namespace {c.Namespace}")
|
||||
.AppendLine($"public abstract partial class {c.ClassName}")
|
||||
.AppendLine("{")
|
||||
.AppendLine($" public new abstract partial class Generic<T{c.ClassName}, TSkin>")
|
||||
.AppendLine($" where T{c.ClassName} : Generic<T{c.ClassName}, TSkin>")
|
||||
.AppendLine($" where TSkin : Skin")
|
||||
.AppendLine(" {")
|
||||
.AppendLine($" public new abstract class Template : {c.ClassName}.Template")
|
||||
.AppendLine(" {")
|
||||
.AppendLine($" public override T{c.ClassName} Get => GenericGet<T{c.ClassName}, TSkin>();")
|
||||
.AppendLine($" public override void Return(T{c.ClassName} item) => GenericReturn<T{c.ClassName}, TSkin>(item);")
|
||||
.AppendLine(" }")
|
||||
.AppendLine(" }")
|
||||
.AppendLine(" }")
|
||||
.AppendLine("}");
|
||||
context.AddSource($"{c.ClassName}_AbstractSkinItemTemplate.g.cs", sb.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
32
src/Generators/ActionNameRegisterGenerator.cs
Normal file
32
src/Generators/ActionNameRegisterGenerator.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Polonium.Generators.Generators;
|
||||
[Generator]
|
||||
public class ActionNameRegisterGenerator : AssetProcessGenerator
|
||||
{
|
||||
public override void Execute(GeneratorExecutionContext context)
|
||||
{
|
||||
if (!(context.SyntaxReceiver is ClassSyntaxReceiver receiver))
|
||||
return;
|
||||
Compilation compilation = context.Compilation;
|
||||
INamedTypeSymbol? actionSyntax = compilation.GetTypeByMetadataName("Polonium.Agents.AgentAction");
|
||||
StringBuilder sb = new();
|
||||
sb
|
||||
.AppendLine("using System;")
|
||||
.AppendLine("using Polonium;")
|
||||
.AppendLine("using Polonium.Attributes;")
|
||||
.AppendLine("[AutoRegister]")
|
||||
.AppendLine("public static class ActionNameRegister")
|
||||
.AppendLine("{")
|
||||
.AppendLine(" public static void Register()")
|
||||
.AppendLine(" {");
|
||||
foreach (AssetInfo a in GetAssetsOfType(context, actionSyntax))
|
||||
sb.AppendLine($" PoloniumRegistry.Action<{a.ClassName}>.Name = \"{a.ClassName}\";");
|
||||
sb
|
||||
.AppendLine(" }")
|
||||
.AppendLine("}");
|
||||
context.AddSource("ActionNameRegister.g.cs", sb.ToString());
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Polonium.Generators;
|
||||
namespace Polonium.Generators.Generators;
|
||||
[Generator]
|
||||
public class AssetRegisterGenerator : AssetProcessGenerator
|
||||
{
|
||||
@@ -18,6 +18,7 @@ public class AssetRegisterGenerator : AssetProcessGenerator
|
||||
sb
|
||||
.AppendLine("using System;")
|
||||
.AppendLine("using Godot;")
|
||||
.AppendLine("using Polonium;")
|
||||
.AppendLine("using Polonium.Attributes;")
|
||||
.AppendLine("[AutoRegister]")
|
||||
.AppendLine("public static class AssetRegister")
|
||||
@@ -40,7 +41,7 @@ public class AssetRegisterGenerator : AssetProcessGenerator
|
||||
string className = Path.GetFileNameWithoutExtension(filePath);
|
||||
sb
|
||||
.AppendLine(
|
||||
$" GlobalRegistry.Asset<{className}>.Scene = ResourceLoader.Load<PackedScene>(\"{gdPath}\");"
|
||||
$" PoloniumRegistry.Asset<{className}>.Scene = ResourceLoader.Load<PackedScene>(\"{gdPath}\");"
|
||||
);
|
||||
}
|
||||
}
|
||||
131
src/Generators/FramePatchGenerator.cs
Normal file
131
src/Generators/FramePatchGenerator.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace Polonium.Generators.Generators;
|
||||
[Generator]
|
||||
public class FramePatchGenerator : AssetProcessGenerator
|
||||
{
|
||||
public override void Execute(GeneratorExecutionContext context)
|
||||
{
|
||||
if(context.SyntaxReceiver is not ClassSyntaxReceiver receiver)
|
||||
return;
|
||||
Compilation compilation = context.Compilation;
|
||||
INamedTypeSymbol? autoPatch = compilation.GetTypeByMetadataName("Polonium.Attributes.AutoPatch");
|
||||
INamedTypeSymbol? patchableProperty = compilation.GetTypeByMetadataName("Polonium.Attributes.PatchableProperty");
|
||||
foreach (InterfaceInfo inf in GetInterfacesWithAttribute(context, autoPatch))
|
||||
{
|
||||
|
||||
PropertyDeclarationSyntax? underlying = inf.DeclarationSyntax.Members
|
||||
.OfType<PropertyDeclarationSyntax>()
|
||||
.FirstOrDefault(m => m.AttributeLists
|
||||
.SelectMany(a => a.Attributes)
|
||||
.Any(a =>
|
||||
{
|
||||
SemanticModel model = compilation.GetSemanticModel(a.SyntaxTree);
|
||||
INamedTypeSymbol? attributeSymbol = model.GetSymbolInfo(a).Symbol?.ContainingType;
|
||||
return SymbolEqualityComparer.Default.Equals(attributeSymbol, patchableProperty);
|
||||
}));
|
||||
if (underlying is null)
|
||||
continue;
|
||||
SemanticModel model = compilation.GetSemanticModel(underlying.SyntaxTree);
|
||||
ITypeSymbol? underlyingType = model.GetTypeInfo(underlying.Type).Type;
|
||||
ITypeSymbol? hashSet = compilation
|
||||
.GetTypeByMetadataName("Polonium.DataStructures.PatchableItems.PatchableHashSet`1");
|
||||
ITypeSymbol? dictionary = compilation
|
||||
.GetTypeByMetadataName("Polonium.DataStructures.PatchableItems.PatchableDictionary`2");
|
||||
string typeString = "";
|
||||
StringBuilder sb = new();
|
||||
sb
|
||||
.AppendLine("using System;")
|
||||
.AppendLine("using System.Collections.Generic;")
|
||||
.AppendLine("using System.Linq;")
|
||||
.AppendLine("using Polonium.Interfaces;")
|
||||
.AppendLine("using Polonium.DataStructures.PatchableItems;")
|
||||
.AppendLine("using Polonium.Resources.FramePatches;")
|
||||
.AppendLine("using Polonium.Resources.FramePatches.Generic;")
|
||||
.AppendLine($"namespace {inf.Namespace};");
|
||||
|
||||
string lName = inf.InterfaceName[0] == 'I' ? inf.InterfaceName.Substring(1) : inf.InterfaceName;
|
||||
if (SymbolEqualityComparer.Default.Equals(underlyingType?.OriginalDefinition, hashSet))
|
||||
{
|
||||
ITypeSymbol? x1 = (underlyingType as INamedTypeSymbol)?.TypeArguments[0];
|
||||
sb
|
||||
.AppendLine($"public partial class {lName}Patch : HashSetFramePatch<{x1?.ToDisplayString()}>")
|
||||
.AppendLine("{")
|
||||
.AppendLine($" public override void Patch(IPatchableFrame frame)")
|
||||
.AppendLine(" {")
|
||||
.AppendLine($" if(frame is not {inf.InterfaceFillName} sFrame)")
|
||||
.AppendLine(" return;")
|
||||
.AppendLine($" switch (sFrame.{underlying.Identifier.ToString()}.UpdateMethod )")
|
||||
.AppendLine(" {")
|
||||
.AppendLine(" case UpdateMethods.None:")
|
||||
.AppendLine(" return;")
|
||||
.AppendLine(" case UpdateMethods.Update:")
|
||||
.AppendLine($" foreach({x1?.ToDisplayString()} x in Updates)")
|
||||
.AppendLine($" sFrame.{underlying.Identifier.ToString()}.Add(x);")
|
||||
.AppendLine(" return;")
|
||||
.AppendLine(" case UpdateMethods.Replace:")
|
||||
.AppendLine($" sFrame.{underlying.Identifier.ToString()}.Data = Updates;")
|
||||
.AppendLine(" return;")
|
||||
.AppendLine(" case UpdateMethods.Custom:")
|
||||
.AppendLine($" sFrame.{underlying.Identifier.ToString()}.CustomUpdate(Updates);")
|
||||
.AppendLine(" return;")
|
||||
.AppendLine(" default:")
|
||||
.AppendLine(" return;")
|
||||
.AppendLine(" }")
|
||||
.AppendLine(" }")
|
||||
.AppendLine("}");
|
||||
}
|
||||
else if (SymbolEqualityComparer.Default.Equals(underlyingType?.OriginalDefinition, dictionary))
|
||||
{
|
||||
ITypeSymbol? x1 = (underlyingType as INamedTypeSymbol)?.TypeArguments[0];
|
||||
ITypeSymbol? x2 = (underlyingType as INamedTypeSymbol)?.TypeArguments[1];
|
||||
sb
|
||||
.AppendLine($"public partial class {lName}Patch : DictionaryFramePatch<{x1?.ToDisplayString()}, {x2?.ToDisplayString()}>")
|
||||
.AppendLine("{")
|
||||
.AppendLine(" public override void Patch(IPatchableFrame frame)")
|
||||
.AppendLine(" {")
|
||||
.AppendLine($" if(frame is not {inf.InterfaceFillName} sFrame)")
|
||||
.AppendLine(" return;")
|
||||
.AppendLine($" switch (sFrame.{underlying.Identifier.ToString()}.UpdateMethod )")
|
||||
.AppendLine(" {")
|
||||
.AppendLine(" case UpdateMethods.None:")
|
||||
.AppendLine(" return;")
|
||||
.AppendLine(" case UpdateMethods.Update:")
|
||||
.AppendLine($" foreach({x1?.ToDisplayString()} x in Updates.Keys)")
|
||||
.AppendLine($" sFrame.{underlying.Identifier.ToString()}[x] = Updates[x];")
|
||||
.AppendLine(" return;")
|
||||
.AppendLine(" case UpdateMethods.Replace:")
|
||||
.AppendLine($" sFrame.{underlying.Identifier.ToString()}.Data = Updates;")
|
||||
.AppendLine(" return;")
|
||||
.AppendLine(" case UpdateMethods.Custom:")
|
||||
.AppendLine($" sFrame.{underlying.Identifier.ToString()}.CustomUpdate(Updates);")
|
||||
.AppendLine(" return;")
|
||||
.AppendLine(" default:")
|
||||
.AppendLine(" return;")
|
||||
.AppendLine(" }")
|
||||
.AppendLine(" }")
|
||||
.AppendLine("}");
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
sb
|
||||
|
||||
.AppendLine($"public partial class {lName}Patch : FramePatch<{underlyingType?.ToDisplayString()}>")
|
||||
.AppendLine("{")
|
||||
.AppendLine(" public override void Patch(IPatchableFrame frame)")
|
||||
.AppendLine(" {")
|
||||
.AppendLine($" if(frame is not {inf.InterfaceFillName} sFrame)")
|
||||
.AppendLine(" return;")
|
||||
.AppendLine($" sFrame.{underlying.Identifier.ToString()} = Updates;")
|
||||
.AppendLine(" }")
|
||||
.AppendLine("}");
|
||||
}
|
||||
context.AddSource($"{lName}Patch.g.cs", sb.ToString());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
58
src/Generators/PoloniumFactoryGenerator.cs
Normal file
58
src/Generators/PoloniumFactoryGenerator.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace Polonium.Generators.Generators;
|
||||
[Generator]
|
||||
public class PoloniumFactoryGenerator : AssetProcessGenerator
|
||||
{
|
||||
public override void Execute(GeneratorExecutionContext context)
|
||||
{
|
||||
if (!(context.SyntaxReceiver is ClassSyntaxReceiver receiver))
|
||||
return;
|
||||
StringBuilder sb = new();
|
||||
sb
|
||||
.AppendLine("using System;")
|
||||
.AppendLine("using Polonium.Attributes;")
|
||||
.AppendLine("using Polonium.Interfaces;")
|
||||
.AppendLine("using Polonium.Resources;")
|
||||
.AppendLine("using Polonium.MessageManager;")
|
||||
.AppendLine("[AutoRegister]")
|
||||
.AppendLine("public class PoloniumFactoryRegister")
|
||||
.AppendLine("{")
|
||||
.AppendLine(" public static void Register()")
|
||||
.AppendLine(" {")
|
||||
.AppendLine(" GlobalRegistry.PoloniumFactory = new PoloniumFactory();")
|
||||
.AppendLine(" }")
|
||||
.AppendLine(" public class PoloniumFactory : IPoloniumFactory")
|
||||
.AppendLine(" {")
|
||||
.AppendLine(" public MessageHeader CreateMessageHeader()")
|
||||
.AppendLine(" {");
|
||||
Compilation compilation = context.Compilation;
|
||||
INamedTypeSymbol? signalHeader = compilation.GetTypeByMetadataName("Polonium.MessageManager.MessageHeader");
|
||||
|
||||
bool founded = false;
|
||||
foreach (ClassDeclarationSyntax cls in receiver.Classes)
|
||||
{
|
||||
SemanticModel semanticModel = compilation.GetSemanticModel(cls.SyntaxTree);
|
||||
INamedTypeSymbol? nCls = semanticModel.GetDeclaredSymbol(cls) as INamedTypeSymbol;
|
||||
if(nCls is null)
|
||||
continue;
|
||||
if (IsDerivedFrom(nCls, signalHeader) && !nCls.IsAbstract)
|
||||
{
|
||||
sb.AppendLine($" return new {nCls.ToDisplayString()}();");
|
||||
founded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!founded)
|
||||
sb.AppendLine($" throw new NotImplementedException();");
|
||||
|
||||
sb
|
||||
.AppendLine(" }")
|
||||
.AppendLine(" }")
|
||||
.AppendLine("}");
|
||||
|
||||
context.AddSource("PoloniumFactoryRegistry.g.cs", sb.ToString());
|
||||
}
|
||||
}
|
||||
34
src/Generators/RegistryEntityGenerator.cs
Normal file
34
src/Generators/RegistryEntityGenerator.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace Polonium.Generators.Generators;
|
||||
[Generator]
|
||||
public class RegistryEntityGenerator : AssetProcessGenerator
|
||||
{
|
||||
public override void Execute(GeneratorExecutionContext context)
|
||||
{
|
||||
if (!(context.SyntaxReceiver is ClassSyntaxReceiver receiver))
|
||||
return;
|
||||
Compilation compilation = context.Compilation;
|
||||
INamedTypeSymbol? regEntity = compilation.GetTypeByMetadataName("Polonium.Attributes.RegistryEntity");
|
||||
foreach (ClassDeclarationSyntax? derivedClassDeclaration in receiver.Classes)
|
||||
{
|
||||
SemanticModel model = compilation.GetSemanticModel(derivedClassDeclaration.SyntaxTree);
|
||||
if (
|
||||
model.GetDeclaredSymbol(derivedClassDeclaration) is INamedTypeSymbol symbol &&
|
||||
symbol.GetAttributes().Any(x => SymbolEqualityComparer.Default.Equals(x.AttributeClass, regEntity))
|
||||
)
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb
|
||||
.AppendLine("public static partial class GlobalRegistry")
|
||||
.AppendLine("{")
|
||||
.AppendLine($" public static {symbol.ToDisplayString()} {symbol.Name} {{ get; set; }} ")
|
||||
.AppendLine("}");
|
||||
context.AddSource($"RegistryEntity_{symbol.Name}.g.cs", sb.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/Generators/SendMessageMethodGenerator.cs
Normal file
31
src/Generators/SendMessageMethodGenerator.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Polonium.Generators.Generators;
|
||||
[Generator]
|
||||
public class SendMessageMethodGenerator : AssetProcessGenerator
|
||||
{
|
||||
public override void Execute(GeneratorExecutionContext context)
|
||||
{
|
||||
if (context.SyntaxReceiver is not ClassSyntaxReceiver receiver)
|
||||
return;
|
||||
Compilation compilation = context.Compilation;
|
||||
INamedTypeSymbol? ifc = compilation.GetTypeByMetadataName("Polonium.Interfaces.IMessageClient");
|
||||
foreach (ClassInfo cls in GetClassesWithInterface(context, ifc))
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb
|
||||
.AppendLine("using Polonium.Interfaces;")
|
||||
.AppendLine("using System;")
|
||||
.AppendLine("using Polonium.Resources;")
|
||||
.AppendLine("using Polonium.MessageManager;")
|
||||
.AppendLine($"namespace {cls.Namespace};")
|
||||
.AppendLine($"public partial class {cls.ClassName}")
|
||||
.AppendLine("{")
|
||||
.AppendLine(" public event IMessageClient.MessageSentEventHandler MessageSent;")
|
||||
.AppendLine(" public void SendMessage(PoloniumMessage msg) => MessageSent?.Invoke(msg); ")
|
||||
.AppendLine("}");
|
||||
context.AddSource($"{cls.ClassName}_MessageClient.g.cs", sb.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
67
src/Generators/TemplateBlockGenerator.cs
Normal file
67
src/Generators/TemplateBlockGenerator.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
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());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
63
src/Generators/TemplateInlineGenerator.cs
Normal file
63
src/Generators/TemplateInlineGenerator.cs
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/InterfaceInfo.cs
Normal file
14
src/InterfaceInfo.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace Polonium.Generators;
|
||||
|
||||
public class InterfaceInfo
|
||||
{
|
||||
public INamedTypeSymbol Symbol { get; set; }
|
||||
public InterfaceDeclarationSyntax DeclarationSyntax { get; set; }
|
||||
public string InterfaceName { get; set; }
|
||||
public string InterfaceFillName { get; set; }
|
||||
public string Namespace { get; set; }
|
||||
public string Path { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user