diff --git a/.gitignore b/.gitignore
index da6f619..5e831d2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,7 @@
/ExampleGenerator/obj
/TestConsole/obj/
+/GetComponentGenerator/obj/
/ExampleGenerator/bin
/TestConsole/bin
+/GetComponentGenerator/bin
diff --git a/ExampleGenerator/ExampleGenerator.csproj b/ExampleGenerator/ExampleGenerator.csproj
index 360c7d0..08914b9 100644
--- a/ExampleGenerator/ExampleGenerator.csproj
+++ b/ExampleGenerator/ExampleGenerator.csproj
@@ -15,4 +15,8 @@
+
+
+
+
diff --git a/ExampleGenerator/Unity/Ui/UIBackingClassGenerator.cs b/ExampleGenerator/Unity/Ui/UIBackingClassGenerator.cs
index ba383ed..2ceef29 100644
--- a/ExampleGenerator/Unity/Ui/UIBackingClassGenerator.cs
+++ b/ExampleGenerator/Unity/Ui/UIBackingClassGenerator.cs
@@ -15,6 +15,7 @@ namespace ExampleGenerator.Unity.Ui
public static class Helpers
{
public const string UiElementAttribute = "UiElementAttribute";
+ public const string AtUiComponentAttribute = "AtUiComponentAttribute";
internal static bool IsDerivedFrom( INamedTypeSymbol baseType , string targetType )
{
@@ -34,6 +35,16 @@ namespace ExampleGenerator.Unity.Ui
public class UiBackingClassGenerator : ISourceGenerator
{
+ private static readonly string AtUiComponentAttributeText = $@"//
+using System;
+
+[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
+internal class {Helpers.AtUiComponentAttribute} : Attribute
+{{
+ public {Helpers.AtUiComponentAttribute}(string uxmlPath) {{ }}
+}}
+";
+
private static readonly string UiElementAttributeText = $@"//
using System;
@@ -50,6 +61,9 @@ internal class {Helpers.UiElementAttribute} : Attribute
{
context.RegisterForPostInitialization( i =>
{
+ i.AddSource( $"{Helpers.AtUiComponentAttribute}_g.cs"
+ , SourceText.From( AtUiComponentAttributeText , Encoding.UTF8 ) );
+
i.AddSource( $"{Helpers.UiElementAttribute}_g.cs"
, SourceText.From( UiElementAttributeText , Encoding.UTF8 ) );
} );
@@ -62,57 +76,129 @@ internal class {Helpers.UiElementAttribute} : Attribute
if ( !(context.SyntaxContextReceiver is SyntaxReceiver receiver) )
return;
- INamedTypeSymbol uiElementAttributeSymbol = context.Compilation.GetTypeByMetadataName( Helpers.UiElementAttribute );
- foreach ( IGrouping group in receiver.Fields
- .GroupBy( f => f.ContainingType
- , SymbolEqualityComparer.Default ) )
+ var atUiComponentAttributeSymbol = context.Compilation.GetTypeByMetadataName( Helpers.AtUiComponentAttribute );
+ var uiElementAttributeSymbol = context.Compilation.GetTypeByMetadataName( Helpers.UiElementAttribute );
+ foreach ( var group in receiver.Fields
+ .GroupBy( f => f.ContainingType
+ , SymbolEqualityComparer.Default ) )
{
- var classSource = ProcessClass( group.Key , group , uiElementAttributeSymbol );
+ var classSymbol = group.Key;
+ // if (! Helpers.IsDerivedFrom( classSymbol , "AtVisualElement" ) )
+ // {
+ // continue;
+ // }
+ var classSource = ProcessClassUiElement( classSymbol , group , uiElementAttributeSymbol );
if ( classSource == null )
continue;
- context.AddSource( $"{group.Key.Name}_ui_g.cs" , SourceText.From( classSource , Encoding.UTF8 ) );
+ context.AddSource( $"{classSymbol.Name}_ui_query_g.cs" , SourceText.From( classSource , Encoding.UTF8 ) );
+ }
+ foreach ( var classSymbol in receiver.Classes )
+ {
+ if ( classSymbol is null )
+ {
+ continue;
+ }
+ // if (! Helpers.IsDerivedFrom( classSymbol , "AtVisualElement" ) )
+ // {
+ // continue;
+ // }
+ var classSource = ProcessClassUiComponent( classSymbol , atUiComponentAttributeSymbol );
+ if ( classSource == null )
+ continue;
+
+ context.AddSource( $"{classSymbol.Name}_at_ui_component_g.cs" , SourceText.From( classSource , Encoding.UTF8 ) );
}
}
- private string ProcessClass( INamedTypeSymbol classSymbol
+ private static string ProcessClassUiComponent( INamedTypeSymbol classSymbol
+ , INamedTypeSymbol atUiComponentAttributeSymbol )
+ {
+ var uiComponentAttributeData = GetUiElementAttributeData( classSymbol , atUiComponentAttributeSymbol );
+
+ var uxmlPath = uiComponentAttributeData?.UxmlPath;
+ var source = new StringBuilder();
+ AppendClassFrameStart( classSymbol,source );
+ if ( !string.IsNullOrWhiteSpace( uxmlPath ) )
+ {
+ // Example output:
+ // protected override string UxmlPath => "Ingame/Inventory/Inventory";
+ source.AppendLine( $" protected override string UxmlPath => \"{uxmlPath}\";" );
+ }
+ AppendClassFrameEnd( source );
+ return source.ToString();
+ }
+ private string ProcessClassUiElement( INamedTypeSymbol classSymbol
, IEnumerable fields
, INamedTypeSymbol uiElementAttributeSymbol )
{
- var fieldsList = fields.ToList();
- if ( !fieldsList.Any() )
+ var elementFields = fields.Where( f => GetUiElementAttributeData( f , uiElementAttributeSymbol ) != null ).ToList();
+ if ( !elementFields.Any() && uiElementAttributeSymbol is null )
return null;
+ var source = new StringBuilder();
+ AppendClassFrameStart( classSymbol , source );
- List elementFields = fieldsList.Where( f => GetUiElementAttributeData( f , uiElementAttributeSymbol ) != null ).ToList();
+ if ( elementFields.Any() )
+ {
- var source = new StringBuilder( $@"//
-
-using UnityEngine.UIElements;
-namespace {classSymbol.ContainingNamespace}
-{{
-public partial class {classSymbol.Name}
-{{" );
-
- source.Append( $@"
+ source.Append( @"
protected override void QueryElements()
- {{
+ {
" );
- foreach ( ISymbol fieldSymbol in elementFields )
- {
- source.AppendLine( $" {fieldSymbol.Name} = this.Q<{GetQualifyingTypeNameFromSymbol( fieldSymbol )}>(\"{GetUiElementAttributeData( fieldSymbol , uiElementAttributeSymbol )?.Name}\");" );
+ foreach ( var fieldSymbol in elementFields )
+ {
+ source.AppendLine( $" {fieldSymbol.Name} = this.Q<{GetQualifyingTypeNameFromSymbol( fieldSymbol )}>(\"{GetUiElementAttributeData( fieldSymbol , uiElementAttributeSymbol )?.Name}\");" );
+ }
+
+ source.AppendLine( " }" );
}
- source.Append( $@" }}
-}}
-}}
-" );
+ AppendClassFrameEnd( source );
return source.ToString();
}
- private static string GetQualifyingTypeName( ITypeSymbol type ) { return type.ToDisplayString( SymbolDisplayFormat.FullyQualifiedFormat ); }
- private string GetQualifyingTypeNameFromSymbol( ISymbol symbol ) => GetQualifyingTypeName( GetTypeFromSymbol( symbol ) );
+
+ private static void AppendClassFrameStart( INamedTypeSymbol classSymbol , StringBuilder source )
+ {
+ source.AppendLine($@"//
+
+using UnityEngine.UIElements;
+namespace {classSymbol.ContainingNamespace}
+{{
+public partial class {classSymbol.Name} : AtVisualElement
+{{");
+ }
+ private static void AppendClassFrameEnd( StringBuilder source )
+ {
+ source.Append( @"}
+}
+" );
+ }
+
+ private static string GetQualifyingTypeName( ITypeSymbol type ) { return type.ToDisplayString( SymbolDisplayFormat.FullyQualifiedFormat ); }
+ private static string GetQualifyingTypeNameFromSymbol( ISymbol symbol ) => GetQualifyingTypeName( GetTypeFromSymbol( symbol ) );
+
+ private static AtUiComponentAttributeData? GetUiElementAttributeData( INamedTypeSymbol classSymbol , INamedTypeSymbol uiElementAttributeSymbol )
+ {
+ if ( classSymbol is null || uiElementAttributeSymbol is null )
+ {
+ return null;
+ }
+ var attr = GetSingleAttributeData( classSymbol , uiElementAttributeSymbol );
+ if ( attr == null )
+ return null;
+ var args = attr.ConstructorArguments.ToList();
+ if ( args.Count != 1 )
+ {
+ throw new NotImplementedException( $"Attribute had a different parameter amount than expected: expected 1 got {args.Count} {attr}: args: {args}" );
+ }
+ return new AtUiComponentAttributeData
+ {
+ UxmlPath = args[0].Value as string ,
+ };
+ }
private static UiElementAttributeData? GetUiElementAttributeData( ISymbol fieldSymbol , INamedTypeSymbol uiElementAttributeSymbol )
{
@@ -123,7 +209,7 @@ public partial class {classSymbol.Name}
var args = attr.ConstructorArguments.ToList();
if ( args.Count > 1 )
{
- throw new NotImplementedException( $"Attribute did not have enough parameters: expected 1 got {args.Count} {attr}: args: {args}" );
+ throw new NotImplementedException( $"Attribute had more parameters than expected: expected 1 got {args.Count} {attr}: args: {args}" );
}
string name = null;
@@ -137,7 +223,7 @@ public partial class {classSymbol.Name}
name = fieldSymbol.Name;
}
- return new UiElementAttributeData()
+ return new UiElementAttributeData
{
Name = name ,
};
@@ -168,11 +254,16 @@ public partial class {classSymbol.Name}
}
}
- struct UiElementAttributeData
+ private struct UiElementAttributeData
{
public string Name;
}
+ private struct AtUiComponentAttributeData
+ {
+ public string UxmlPath;
+ }
+
#endregion
}
@@ -180,7 +271,8 @@ public partial class {classSymbol.Name}
public class SyntaxReceiver : ISyntaxContextReceiver
{
- public List Fields { get; } = new List();
+ public List Fields { get; } = new List();
+ public List Classes { get; } = new List();
#region Implementation of ISyntaxContextReceiver
@@ -188,9 +280,19 @@ public partial class {classSymbol.Name}
{
switch ( context.Node )
{
+ case ClassDeclarationSyntax classDeclarationSyntax when classDeclarationSyntax.AttributeLists.Count > 0:
+ if ( context.SemanticModel.GetDeclaredSymbol( classDeclarationSyntax ) is INamedTypeSymbol namedTypeSymbol
+ && Helpers.IsDerivedFrom( namedTypeSymbol.BaseType , "AtVisualElement" )
+ && namedTypeSymbol.GetAttributes()
+ .Any( ad => ad.AttributeClass?.ToDisplayString() == Helpers.AtUiComponentAttribute ) )
+ {
+ Classes.Add( namedTypeSymbol );
+ }
+
+ break;
case FieldDeclarationSyntax fieldDeclarationSyntax when fieldDeclarationSyntax.AttributeLists.Count > 0:
{
- foreach ( VariableDeclaratorSyntax variable in fieldDeclarationSyntax.Declaration.Variables )
+ foreach ( var variable in fieldDeclarationSyntax.Declaration.Variables )
{
if ( context.SemanticModel.GetDeclaredSymbol( variable ) is IFieldSymbol symbol
&& Helpers.IsDerivedFrom( symbol.ContainingType.BaseType , "AtVisualElement" )
diff --git a/ExampleGenerator/Unity/Components/GetComponentGenerator.cs b/GetComponentGenerator/GetComponentGenerator.cs
similarity index 99%
rename from ExampleGenerator/Unity/Components/GetComponentGenerator.cs
rename to GetComponentGenerator/GetComponentGenerator.cs
index 374d5bb..616b8cd 100644
--- a/ExampleGenerator/Unity/Components/GetComponentGenerator.cs
+++ b/GetComponentGenerator/GetComponentGenerator.cs
@@ -6,8 +6,7 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
-
-namespace ExampleGenerator.Unity.Components
+namespace GetComponentGenerator
{
[Generator]
public class GetComponentGenerator : ISourceGenerator
diff --git a/GetComponentGenerator/GetComponentGenerator.csproj b/GetComponentGenerator/GetComponentGenerator.csproj
new file mode 100644
index 0000000..66bc8d7
--- /dev/null
+++ b/GetComponentGenerator/GetComponentGenerator.csproj
@@ -0,0 +1,18 @@
+
+
+
+ netstandard2.0
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/TestConsole/Program.cs b/TestConsole/Program.cs
index 5637536..c8fddfb 100644
--- a/TestConsole/Program.cs
+++ b/TestConsole/Program.cs
@@ -2,30 +2,60 @@
using System.Diagnostics;
+using TestConsole;
-namespace ConsoleApp;
-
-partial class Program
+namespace TestConsole
{
- static void Main( string[] args ) { HelloFrom( "Generated Code" ); }
-
- static partial void HelloFrom( string name );
-}
-
-public partial class Test1 : AtVisualElement
-{
- // [UxmlTrait( "health" , 9 )]
- // public int MyProperty { get; set; }
-
- // [UxmlTrait( "health2" , 8)] public int MyField;
- // [UxmlTrait( "health1" , 8)] public int MyField2{get; set; }
- // [UxmlTrait( "health1" , false)] public bool MyBoolField2;
- // [UxmlTrait( "health1" , "hi")] public string MyStringField2;
- public void Test123()
+ static partial class Program
{
- Debug.Write( "test" );
- // Test987();
+ static void Main( string[] args )
+ {
+ var t = new Test1();
+ t.Test123();
+ HelloFrom( "Generated Code" );
+ }
+
+ static partial void HelloFrom( string name );
+ }
+
+ [AtUiComponent("Test123")]
+ public partial class Test1 : AtVisualElement
+ {
+ // protected override string UxmlPath =>"";
+ // [UiElement]
+ public AtVisualElement test;
+
+ public void Test123()
+ {
+ Console.WriteLine( "test" );
+ Console.WriteLine( $"UxmlPath: '{UxmlPath}'" );
+ QueryElements();
+ }
+ }
+
+ public abstract class AtVisualElement
+ {
+ protected abstract string UxmlPath { get; }
+ // protected virtual string UxmlPath { get; }
+
+ protected virtual void QueryElements()
+ {
+ Console.WriteLine( "Nothing overwriting this..." );
+ }
}
}
-public abstract class AtVisualElement { }
+namespace UnityEngine
+{
+ namespace UIElements
+ {
+ static class VisualElementExtensions
+ {
+ public static AtVisualElement Q( this AtVisualElement self, string name )
+ {
+ Console.WriteLine( $"Querying for '{name}' inside type {self.GetType().Name}" );
+ return new Test1();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/TestConsole/TestConsole.csproj b/TestConsole/TestConsole.csproj
index 44ff54c..6f9e940 100644
--- a/TestConsole/TestConsole.csproj
+++ b/TestConsole/TestConsole.csproj
@@ -2,13 +2,14 @@
Exe
- net6.0
+ net8.0
enable
enable
+
diff --git a/TestGenerators.sln b/TestGenerators.sln
index 403e2af..2ac1f41 100644
--- a/TestGenerators.sln
+++ b/TestGenerators.sln
@@ -4,6 +4,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExampleGenerator", "Example
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestConsole", "TestConsole\TestConsole.csproj", "{4B37526B-5EE6-453B-BF68-3D5B9E9BB417}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GetComponentGenerator", "GetComponentGenerator\GetComponentGenerator.csproj", "{F52CD30D-C7EB-42F2-B4C9-8F2AB202C1EA}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -18,5 +20,9 @@ Global
{4B37526B-5EE6-453B-BF68-3D5B9E9BB417}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4B37526B-5EE6-453B-BF68-3D5B9E9BB417}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4B37526B-5EE6-453B-BF68-3D5B9E9BB417}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F52CD30D-C7EB-42F2-B4C9-8F2AB202C1EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F52CD30D-C7EB-42F2-B4C9-8F2AB202C1EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F52CD30D-C7EB-42F2-B4C9-8F2AB202C1EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F52CD30D-C7EB-42F2-B4C9-8F2AB202C1EA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal