From 450fb63109b4a5a986fa7e696a1dffe355bc94ed Mon Sep 17 00:00:00 2001 From: kandreyc Date: Wed, 31 May 2023 15:17:31 +0300 Subject: [PATCH 1/2] filter system generator --- AspectGenerator/AspectGenerator.cs | 21 +- AspectGenerator/AspectGenerator.csproj | 6 +- AspectGenerator/GlobalUsings.cs | 12 + AspectGenerator/Model/Argument.cs | 3 +- AspectGenerator/Model/AspectData.cs | 2 +- .../AspectFilterExtensionsTemplate.cs | 15 +- .../Templates/AspectInterfacesTemplate.cs | 7 +- AspectGenerator/Templates/AspectTemplate.cs | 8 +- Core/Core.projitems | 21 ++ Core/Core.shproj | 13 + .../Extensions/AttributeDataExtensions.cs | 4 +- .../Extensions/INamespaceSymbolExtensions.cs | 4 +- .../Extensions/ITypeSymbolExtensions.cs | 4 +- .../Extensions/StringExtensions.cs | 2 +- .../Core => Core}/GeneratedFile.cs | 2 +- .../Core => Core}/ISourceFileGenerator.cs | 2 +- .../Core => Core}/IncrementalGenerator.cs | 12 +- {AspectGenerator => Core}/Logger/Logger.cs | 4 +- .../FilterSystemGenerator.cs | 95 ++++++ .../FilterSystemGenerator.csproj | 24 ++ FilterSystemGenerator/GlobalUsings.cs | 11 + FilterSystemGenerator/Model/FilterInfo.cs | 10 + FilterSystemGenerator/Model/SystemInfo.cs | 8 + .../Templates/FilterSystemTemplate.cs | 57 ++++ .../Templates/PartialImplTemplate.cs | 156 +++++++++ MorpehCodeGenerator.sln | 8 + README.md | 295 ++++++++++++++++-- Tests/AspectGenerator/Aspects.cs | 19 +- Tests/AspectGenerator/Components.cs | 26 +- Tests/AspectGenerator/Mocks/FilterBuilder.cs | 16 - .../FilterSystemGenerator/AspectOneExtend.cs | 18 ++ .../AspectOneExtendOneWithAspect.cs | 19 ++ Tests/FilterSystemGenerator/AspectWithOne.cs | 19 ++ .../AspectWithOneWithoutOne.cs | 20 ++ .../AspectWithOneWithoutTwo.cs | 21 ++ Tests/FilterSystemGenerator/Aspects.cs | 15 + .../FilterSystemGenerator/ComponentWithOne.cs | 18 ++ .../ComponentWithOneWithoutOne.cs | 19 ++ .../ComponentWithOneWithoutTwo.cs | 19 ++ .../ComponentWithRepeated.cs | 18 ++ .../FilterSystemGenerator/ComponentWithTwo.cs | 19 ++ .../ComponentWithoutOne.cs | 18 ++ .../ComponentWithoutTwo.cs | 19 ++ Tests/FilterSystemGenerator/Components.cs | 23 ++ Tests/FilterSystemGenerator/EmptyFilter.cs | 17 + Tests/Mocks/FilterBuilder.cs | 21 ++ .../Mocks/IFilterExtension.cs | 0 Tests/Tests.csproj | 3 + 48 files changed, 1059 insertions(+), 114 deletions(-) create mode 100644 AspectGenerator/GlobalUsings.cs create mode 100644 Core/Core.projitems create mode 100644 Core/Core.shproj rename {AspectGenerator/Core => Core}/Extensions/AttributeDataExtensions.cs (86%) rename {AspectGenerator/Core => Core}/Extensions/INamespaceSymbolExtensions.cs (74%) rename {AspectGenerator/Core => Core}/Extensions/ITypeSymbolExtensions.cs (70%) rename {AspectGenerator/Core => Core}/Extensions/StringExtensions.cs (77%) rename {AspectGenerator/Core => Core}/GeneratedFile.cs (67%) rename {AspectGenerator/Core => Core}/ISourceFileGenerator.cs (71%) rename {AspectGenerator/Core => Core}/IncrementalGenerator.cs (85%) rename {AspectGenerator => Core}/Logger/Logger.cs (84%) create mode 100644 FilterSystemGenerator/FilterSystemGenerator.cs create mode 100644 FilterSystemGenerator/FilterSystemGenerator.csproj create mode 100644 FilterSystemGenerator/GlobalUsings.cs create mode 100644 FilterSystemGenerator/Model/FilterInfo.cs create mode 100644 FilterSystemGenerator/Model/SystemInfo.cs create mode 100644 FilterSystemGenerator/Templates/FilterSystemTemplate.cs create mode 100644 FilterSystemGenerator/Templates/PartialImplTemplate.cs delete mode 100644 Tests/AspectGenerator/Mocks/FilterBuilder.cs create mode 100644 Tests/FilterSystemGenerator/AspectOneExtend.cs create mode 100644 Tests/FilterSystemGenerator/AspectOneExtendOneWithAspect.cs create mode 100644 Tests/FilterSystemGenerator/AspectWithOne.cs create mode 100644 Tests/FilterSystemGenerator/AspectWithOneWithoutOne.cs create mode 100644 Tests/FilterSystemGenerator/AspectWithOneWithoutTwo.cs create mode 100644 Tests/FilterSystemGenerator/Aspects.cs create mode 100644 Tests/FilterSystemGenerator/ComponentWithOne.cs create mode 100644 Tests/FilterSystemGenerator/ComponentWithOneWithoutOne.cs create mode 100644 Tests/FilterSystemGenerator/ComponentWithOneWithoutTwo.cs create mode 100644 Tests/FilterSystemGenerator/ComponentWithRepeated.cs create mode 100644 Tests/FilterSystemGenerator/ComponentWithTwo.cs create mode 100644 Tests/FilterSystemGenerator/ComponentWithoutOne.cs create mode 100644 Tests/FilterSystemGenerator/ComponentWithoutTwo.cs create mode 100644 Tests/FilterSystemGenerator/Components.cs create mode 100644 Tests/FilterSystemGenerator/EmptyFilter.cs create mode 100644 Tests/Mocks/FilterBuilder.cs rename Tests/{AspectGenerator => }/Mocks/IFilterExtension.cs (100%) diff --git a/AspectGenerator/AspectGenerator.cs b/AspectGenerator/AspectGenerator.cs index 813d29b..cc18648 100644 --- a/AspectGenerator/AspectGenerator.cs +++ b/AspectGenerator/AspectGenerator.cs @@ -1,18 +1,10 @@ -using System.Linq; -using System.Threading; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Scellecs.Morpeh.SourceGenerator.Aspect.Core; -using Scellecs.Morpeh.SourceGenerator.Aspect.Core.Extensions; -using Scellecs.Morpeh.SourceGenerator.Aspect.Model; -using Scellecs.Morpeh.SourceGenerator.Aspect.Templates; - -namespace Scellecs.Morpeh.SourceGenerator.Aspect; +namespace Scellecs.Morpeh.SourceGenerator.AspectGenerator; [Generator] public class AspectGenerator : IncrementalGenerator { + private const string DisposableInterface = "System.IDisposable"; + protected override void OnPostInitialize() { AddPostInitializeSource(AspectInterfacesTemplate.GenerateFile()); @@ -42,7 +34,7 @@ protected override bool Filter(ISymbol? symbol) return symbol is INamedTypeSymbol typeSymbol && typeSymbol.Interfaces.Any(IsAspectInterface); } - protected override AspectData Select(ISymbol symbol) + protected override AspectData Select(SyntaxNode _, ISymbol symbol, SemanticModel __) { var typeSymbol = (INamedTypeSymbol)symbol; var aspectInterface = typeSymbol.Interfaces.First(IsAspectInterface); @@ -52,7 +44,8 @@ protected override AspectData Select(ISymbol symbol) NamePascalCase = p.Name, Type = p.ToDisplayString(), Namespace = p.GetNamespaceName(), - NameCamelCase = p.Name.FirstLetterLowerCase() + NameCamelCase = p.Name.FirstLetterLowerCase(), + IsDisposable = p.AllInterfaces.Any(i => i.ToDisplayString() == DisposableInterface) }) .Distinct() .ToArray(); @@ -68,7 +61,7 @@ protected override AspectData Select(ISymbol symbol) private static bool IsAspectInterface(INamedTypeSymbol symbol) { const string @name = "IAspect"; - const string @namespace = "Scellecs.Morpeh.SourceGenerator.Aspect"; + const string @namespace = "Scellecs.Morpeh.SourceGenerator.AspectGenerator"; return symbol is { IsGenericType: true, Name: @name, ContainingNamespace: not null } && symbol.GetNamespaceName() is @namespace; diff --git a/AspectGenerator/AspectGenerator.csproj b/AspectGenerator/AspectGenerator.csproj index 2409af9..205b0d5 100644 --- a/AspectGenerator/AspectGenerator.csproj +++ b/AspectGenerator/AspectGenerator.csproj @@ -5,10 +5,10 @@ false true true - Scellecs.Morpeh.SourceGenerator.Aspect + Scellecs.Morpeh.SourceGenerator.AspectGenerator enable netstandard2.1 - Scellecs.Morpeh.SourceGenerator.Aspect + Scellecs.Morpeh.SourceGenerator.AspectGenerator @@ -19,4 +19,6 @@ + + \ No newline at end of file diff --git a/AspectGenerator/GlobalUsings.cs b/AspectGenerator/GlobalUsings.cs new file mode 100644 index 0000000..8a12141 --- /dev/null +++ b/AspectGenerator/GlobalUsings.cs @@ -0,0 +1,12 @@ +global using System.Linq; +global using System.Threading; + +global using Microsoft.CodeAnalysis; +global using Microsoft.CodeAnalysis.CSharp; +global using Microsoft.CodeAnalysis.CSharp.Syntax; + +global using Scellecs.Morpeh.SourceGenerator.Core; +global using Scellecs.Morpeh.SourceGenerator.Core.Extensions; + +global using Scellecs.Morpeh.SourceGenerator.AspectGenerator.Model; +global using Scellecs.Morpeh.SourceGenerator.AspectGenerator.Templates; \ No newline at end of file diff --git a/AspectGenerator/Model/Argument.cs b/AspectGenerator/Model/Argument.cs index 70aa079..a9fa258 100644 --- a/AspectGenerator/Model/Argument.cs +++ b/AspectGenerator/Model/Argument.cs @@ -1,8 +1,9 @@ -namespace Scellecs.Morpeh.SourceGenerator.Aspect.Model; +namespace Scellecs.Morpeh.SourceGenerator.AspectGenerator.Model; public struct Argument { public string Type { get; set; } + public bool IsDisposable { get; set; } public string? Namespace { get; set; } public string NameCamelCase { get; set; } public string NamePascalCase { get; set; } diff --git a/AspectGenerator/Model/AspectData.cs b/AspectGenerator/Model/AspectData.cs index 5999f06..7e32ab0 100644 --- a/AspectGenerator/Model/AspectData.cs +++ b/AspectGenerator/Model/AspectData.cs @@ -1,4 +1,4 @@ -namespace Scellecs.Morpeh.SourceGenerator.Aspect.Model; +namespace Scellecs.Morpeh.SourceGenerator.AspectGenerator.Model; public struct AspectData { diff --git a/AspectGenerator/Templates/AspectFilterExtensionsTemplate.cs b/AspectGenerator/Templates/AspectFilterExtensionsTemplate.cs index 124d064..f1c4d17 100644 --- a/AspectGenerator/Templates/AspectFilterExtensionsTemplate.cs +++ b/AspectGenerator/Templates/AspectFilterExtensionsTemplate.cs @@ -1,23 +1,22 @@ -using Scellecs.Morpeh.SourceGenerator.Aspect.Core; -using Scellecs.Morpeh.SourceGenerator.Aspect.Model; - -namespace Scellecs.Morpeh.SourceGenerator.Aspect.Templates; +namespace Scellecs.Morpeh.SourceGenerator.AspectGenerator.Templates; public static class AspectFilterExtensionsTemplate { public static GeneratedFile GenerateFile() { + const string @namespace = "Scellecs.Morpeh.SourceGenerator.AspectGenerator"; + return new GeneratedFile { - FileName = "AspectFilterExtensions.g.cs", - Content = """ + FileName = $"{@namespace}.AspectFilterExtensions.g.cs", + Content = $$""" using Scellecs.Morpeh; -namespace Scellecs.Morpeh.SourceGenerator.Aspect +namespace {{@namespace}} { public static class AspectFilterExtensions { - public static FilterBuilder With(this FilterBuilder filter) where T : struct, IAspect, IFilterExtension + public static FilterBuilder WithAspect(this FilterBuilder filter) where T : struct, IAspect, IFilterExtension { return filter.Extend(); } diff --git a/AspectGenerator/Templates/AspectInterfacesTemplate.cs b/AspectGenerator/Templates/AspectInterfacesTemplate.cs index d5806f8..47c4671 100644 --- a/AspectGenerator/Templates/AspectInterfacesTemplate.cs +++ b/AspectGenerator/Templates/AspectInterfacesTemplate.cs @@ -1,7 +1,4 @@ -using Scellecs.Morpeh.SourceGenerator.Aspect.Core; -using Scellecs.Morpeh.SourceGenerator.Aspect.Model; - -namespace Scellecs.Morpeh.SourceGenerator.Aspect.Templates; +namespace Scellecs.Morpeh.SourceGenerator.AspectGenerator.Templates; public static class AspectInterfacesTemplate { @@ -13,7 +10,7 @@ public static GeneratedFile GenerateFile() Content = """ using Scellecs.Morpeh; -namespace Scellecs.Morpeh.SourceGenerator.Aspect +namespace Scellecs.Morpeh.SourceGenerator.AspectGenerator { public interface IAspect : IAspect, IFilterExtension where T1 : struct, IComponent diff --git a/AspectGenerator/Templates/AspectTemplate.cs b/AspectGenerator/Templates/AspectTemplate.cs index eb13688..ba41af2 100644 --- a/AspectGenerator/Templates/AspectTemplate.cs +++ b/AspectGenerator/Templates/AspectTemplate.cs @@ -1,8 +1,4 @@ -using System.Linq; -using Scellecs.Morpeh.SourceGenerator.Aspect.Core; -using Scellecs.Morpeh.SourceGenerator.Aspect.Model; - -namespace Scellecs.Morpeh.SourceGenerator.Aspect.Templates; +namespace Scellecs.Morpeh.SourceGenerator.AspectGenerator.Templates; public static class AspectTemplate { @@ -93,7 +89,7 @@ string GetProperties(int indent) string GetCtors(int indent) { var str = data.Arguments.Select( - static a => $"{Stash(a.NameCamelCase)} = world.GetStash<{a.NamePascalCase}>();" + static a => $"{Stash(a.NameCamelCase)} = world.GetStash<{a.NamePascalCase}>(){(a.IsDisposable ? ".AsDisposable()" : string.Empty)};" ); return string.Join($"\n{Tab(indent)}", str); diff --git a/Core/Core.projitems b/Core/Core.projitems new file mode 100644 index 0000000..2614221 --- /dev/null +++ b/Core/Core.projitems @@ -0,0 +1,21 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + E85D9420-616C-4636-A5C8-1181428E4C57 + + + Core + + + + + + + + + + + + \ No newline at end of file diff --git a/Core/Core.shproj b/Core/Core.shproj new file mode 100644 index 0000000..e1edefe --- /dev/null +++ b/Core/Core.shproj @@ -0,0 +1,13 @@ + + + + {B2A951FB-4BAD-4AA7-8986-A291B103D443} + Scellecs.Morpeh.SourceGenerator.Core + Scellecs.Morpeh.SourceGenerator.Core + + + + + + + diff --git a/AspectGenerator/Core/Extensions/AttributeDataExtensions.cs b/Core/Extensions/AttributeDataExtensions.cs similarity index 86% rename from AspectGenerator/Core/Extensions/AttributeDataExtensions.cs rename to Core/Extensions/AttributeDataExtensions.cs index df3a0d8..f34f080 100644 --- a/AspectGenerator/Core/Extensions/AttributeDataExtensions.cs +++ b/Core/Extensions/AttributeDataExtensions.cs @@ -1,8 +1,6 @@ using System.Collections.Immutable; -using System.Linq; -using Microsoft.CodeAnalysis; -namespace Scellecs.Morpeh.SourceGenerator.Aspect.Core.Extensions; +namespace Scellecs.Morpeh.SourceGenerator.Core.Extensions; internal static class AttributeDataExtensions { diff --git a/AspectGenerator/Core/Extensions/INamespaceSymbolExtensions.cs b/Core/Extensions/INamespaceSymbolExtensions.cs similarity index 74% rename from AspectGenerator/Core/Extensions/INamespaceSymbolExtensions.cs rename to Core/Extensions/INamespaceSymbolExtensions.cs index b27468a..4d064be 100644 --- a/AspectGenerator/Core/Extensions/INamespaceSymbolExtensions.cs +++ b/Core/Extensions/INamespaceSymbolExtensions.cs @@ -1,6 +1,4 @@ -using Microsoft.CodeAnalysis; - -namespace Scellecs.Morpeh.SourceGenerator.Aspect.Core.Extensions; +namespace Scellecs.Morpeh.SourceGenerator.Core.Extensions; // ReSharper disable once InconsistentNaming public static class INamespaceSymbolExtensions diff --git a/AspectGenerator/Core/Extensions/ITypeSymbolExtensions.cs b/Core/Extensions/ITypeSymbolExtensions.cs similarity index 70% rename from AspectGenerator/Core/Extensions/ITypeSymbolExtensions.cs rename to Core/Extensions/ITypeSymbolExtensions.cs index 1f68388..34e5aec 100644 --- a/AspectGenerator/Core/Extensions/ITypeSymbolExtensions.cs +++ b/Core/Extensions/ITypeSymbolExtensions.cs @@ -1,6 +1,4 @@ -using Microsoft.CodeAnalysis; - -namespace Scellecs.Morpeh.SourceGenerator.Aspect.Core.Extensions; +namespace Scellecs.Morpeh.SourceGenerator.Core.Extensions; // ReSharper disable once InconsistentNaming public static class ITypeSymbolExtensions diff --git a/AspectGenerator/Core/Extensions/StringExtensions.cs b/Core/Extensions/StringExtensions.cs similarity index 77% rename from AspectGenerator/Core/Extensions/StringExtensions.cs rename to Core/Extensions/StringExtensions.cs index a134ebe..59c49b3 100644 --- a/AspectGenerator/Core/Extensions/StringExtensions.cs +++ b/Core/Extensions/StringExtensions.cs @@ -1,4 +1,4 @@ -namespace Scellecs.Morpeh.SourceGenerator.Aspect.Core.Extensions; +namespace Scellecs.Morpeh.SourceGenerator.Core.Extensions; public static class StringExtensions { diff --git a/AspectGenerator/Core/GeneratedFile.cs b/Core/GeneratedFile.cs similarity index 67% rename from AspectGenerator/Core/GeneratedFile.cs rename to Core/GeneratedFile.cs index 261007f..9dbe151 100644 --- a/AspectGenerator/Core/GeneratedFile.cs +++ b/Core/GeneratedFile.cs @@ -1,4 +1,4 @@ -namespace Scellecs.Morpeh.SourceGenerator.Aspect.Core; +namespace Scellecs.Morpeh.SourceGenerator.Core; public struct GeneratedFile { diff --git a/AspectGenerator/Core/ISourceFileGenerator.cs b/Core/ISourceFileGenerator.cs similarity index 71% rename from AspectGenerator/Core/ISourceFileGenerator.cs rename to Core/ISourceFileGenerator.cs index 406b266..d577ac5 100644 --- a/AspectGenerator/Core/ISourceFileGenerator.cs +++ b/Core/ISourceFileGenerator.cs @@ -1,4 +1,4 @@ -namespace Scellecs.Morpeh.SourceGenerator.Aspect.Core; +namespace Scellecs.Morpeh.SourceGenerator.Core; public interface ISourceFileGenerator { diff --git a/AspectGenerator/Core/IncrementalGenerator.cs b/Core/IncrementalGenerator.cs similarity index 85% rename from AspectGenerator/Core/IncrementalGenerator.cs rename to Core/IncrementalGenerator.cs index 5138418..943c4cf 100644 --- a/AspectGenerator/Core/IncrementalGenerator.cs +++ b/Core/IncrementalGenerator.cs @@ -1,11 +1,9 @@ using System; using System.Text; -using System.Threading; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; using System.Collections.Immutable; -namespace Scellecs.Morpeh.SourceGenerator.Aspect.Core; +namespace Scellecs.Morpeh.SourceGenerator.Core; public abstract class IncrementalGenerator : IIncrementalGenerator, ISourceFileGenerator where TData: struct @@ -31,15 +29,15 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .CreateSyntaxProvider(SyntaxFilter, (c, _) => { var symbol = c.SemanticModel.GetDeclaredSymbol(c.Node); - return Filter(symbol) ? symbol : null; + return Filter(symbol) ? (node: c.Node, symbol, model: c.SemanticModel) : default; }) - .Where(symbol => symbol is not null) - .Select((symbol, _) => Select(symbol!)); + .Where(t => t.symbol is not null) + .Select((t, _) => Select(t.node, t.symbol!, t.model)); context.RegisterSourceOutput(provider.Collect(), GenerateInternal); } - protected abstract TData Select(ISymbol symbol); + protected abstract TData Select(SyntaxNode syntax, ISymbol symbol, SemanticModel model); private void GenerateInternal(SourceProductionContext context, ImmutableArray data) { diff --git a/AspectGenerator/Logger/Logger.cs b/Core/Logger/Logger.cs similarity index 84% rename from AspectGenerator/Logger/Logger.cs rename to Core/Logger/Logger.cs index 6766c3a..ca4e7c6 100644 --- a/AspectGenerator/Logger/Logger.cs +++ b/Core/Logger/Logger.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; -using System.Linq; -using Scellecs.Morpeh.SourceGenerator.Aspect.Core; -namespace Scellecs.Morpeh.SourceGenerator.Aspect.Logger; +namespace Scellecs.Morpeh.SourceGenerator.Core.Logger; public class Logger { diff --git a/FilterSystemGenerator/FilterSystemGenerator.cs b/FilterSystemGenerator/FilterSystemGenerator.cs new file mode 100644 index 0000000..023818a --- /dev/null +++ b/FilterSystemGenerator/FilterSystemGenerator.cs @@ -0,0 +1,95 @@ +namespace Scellecs.Morpeh.SourceGenerator.FilterSystemGenerator; + +[Generator] +public class FilterSystemGenerator : IncrementalGenerator +{ + private const string MethodName = "GetFilter"; + private const string DisposableInterface = "System.IDisposable"; + private const string AspectInterface = "Scellecs.Morpeh.IAspect"; + private const string FilterBuilderType = "Scellecs.Morpeh.FilterBuilder"; + private const string FilterSystemType = "Scellecs.Morpeh.SourceGenerator.FilterSystemGenerator.FilterSystem"; + + private static readonly HashSet WithMethodNames = new() { "With", "Extend", "WithAspect" }; + + protected override void OnPostInitialize() + { + AddPostInitializeSource(FilterSystemTemplate.GenerateFile()); + } + + protected override bool SyntaxFilter(SyntaxNode node, CancellationToken _) + { + return node is MethodDeclarationSyntax { Parent: ClassDeclarationSyntax classDeclarationSyntax } + && classDeclarationSyntax.Modifiers.Any(SyntaxKind.PartialKeyword) + && !classDeclarationSyntax.Modifiers.Any(SyntaxKind.AbstractKeyword); + } + + protected override bool Filter(ISymbol? symbol) + { + + return symbol is IMethodSymbol methodSymbol + && methodSymbol.ContainingType?.BaseType?.ToDisplayString() is FilterSystemType + && IsGetFilterMethod(methodSymbol); + } + + private static bool IsGetFilterMethod(IMethodSymbol method) + { + + return method is { Name: MethodName, Parameters.Length: 1 } + && method.ReturnType.ToDisplayString() is FilterBuilderType + && method.Parameters[0].Type.ToDisplayString() is FilterBuilderType; + } + + protected override SystemInfo Select(SyntaxNode methodSyntax, ISymbol symbol, SemanticModel model) + { + var methodSymbol = (IMethodSymbol)symbol; + var @class = methodSymbol.ContainingType; + + return new SystemInfo + { + Name = @class.Name, + Namespace = @class.GetNamespaceName(), + Filters = GetFilters(methodSyntax, model).ToArray() + }; + } + + private static IEnumerable GetFilters(SyntaxNode methodSyntax, SemanticModel model) + { + foreach (var componentType in GetAllTypeArgumentsWithIdentifier(WithMethodNames, methodSyntax, model)) + { + yield return new FilterInfo + { + NamePascalCase = componentType.Name, + NameCamelCase = componentType.Name.FirstLetterLowerCase(), + Namespace = componentType.GetNamespaceName(), + IsAspect = componentType.AllInterfaces.Any(i => i.ToDisplayString() is AspectInterface), + IsDisposable = componentType.AllInterfaces.Any(i => i.ToDisplayString() is DisposableInterface) + }; + } + } + + private static IEnumerable GetAllTypeArgumentsWithIdentifier(HashSet identifiers, SyntaxNode methodSyntax, SemanticModel model) + { + foreach (var syntax in methodSyntax.DescendantNodes()) + { + if (syntax is not GenericNameSyntax genericNameSyntax) continue; + if (!identifiers.Contains(genericNameSyntax.Identifier.Text)) continue; + if (genericNameSyntax.TypeArgumentList.Arguments.Count is not 1) continue; + + var typeArgument = genericNameSyntax.TypeArgumentList.Arguments[0]; + var type = model.GetTypeInfo(typeArgument).Type; + + if (type is not null) + { + yield return type; + } + } + } + + protected override void Generate(SystemInfo systemInfo) + { + // if (systemInfo.Filters.Length is not 0) + { + AddSource(PartialImplTemplate.GenerateFile(systemInfo)); + } + } +} \ No newline at end of file diff --git a/FilterSystemGenerator/FilterSystemGenerator.csproj b/FilterSystemGenerator/FilterSystemGenerator.csproj new file mode 100644 index 0000000..3808e2a --- /dev/null +++ b/FilterSystemGenerator/FilterSystemGenerator.csproj @@ -0,0 +1,24 @@ + + + + latest + false + true + true + Scellecs.Morpeh.SourceGenerator.FilterSystemGenerator + Scellecs.Morpeh.SourceGenerator.FilterSystemGenerator + enable + netstandard2.1 + + + + + + + + + + + + + \ No newline at end of file diff --git a/FilterSystemGenerator/GlobalUsings.cs b/FilterSystemGenerator/GlobalUsings.cs new file mode 100644 index 0000000..99bca4d --- /dev/null +++ b/FilterSystemGenerator/GlobalUsings.cs @@ -0,0 +1,11 @@ +global using System.Linq; +global using System.Threading; +global using System.Collections.Generic; + +global using Microsoft.CodeAnalysis; +global using Microsoft.CodeAnalysis.CSharp; +global using Microsoft.CodeAnalysis.CSharp.Syntax; +global using Scellecs.Morpeh.SourceGenerator.Core; +global using Scellecs.Morpeh.SourceGenerator.Core.Extensions; +global using Scellecs.Morpeh.SourceGenerator.FilterSystemGenerator.Model; +global using Scellecs.Morpeh.SourceGenerator.FilterSystemGenerator.Templates; \ No newline at end of file diff --git a/FilterSystemGenerator/Model/FilterInfo.cs b/FilterSystemGenerator/Model/FilterInfo.cs new file mode 100644 index 0000000..efa1382 --- /dev/null +++ b/FilterSystemGenerator/Model/FilterInfo.cs @@ -0,0 +1,10 @@ +namespace Scellecs.Morpeh.SourceGenerator.FilterSystemGenerator.Model; + +public struct FilterInfo +{ + public string NamePascalCase { get; set; } + public string NameCamelCase { get; set; } + public string? Namespace { get; set; } + public bool IsAspect { get; set; } + public bool IsDisposable { get; set; } +} \ No newline at end of file diff --git a/FilterSystemGenerator/Model/SystemInfo.cs b/FilterSystemGenerator/Model/SystemInfo.cs new file mode 100644 index 0000000..c473513 --- /dev/null +++ b/FilterSystemGenerator/Model/SystemInfo.cs @@ -0,0 +1,8 @@ +namespace Scellecs.Morpeh.SourceGenerator.FilterSystemGenerator.Model; + +public struct SystemInfo +{ + public string Name { get; set; } + public string? Namespace { get; set; } + public FilterInfo[] Filters { get; set; } +} \ No newline at end of file diff --git a/FilterSystemGenerator/Templates/FilterSystemTemplate.cs b/FilterSystemGenerator/Templates/FilterSystemTemplate.cs new file mode 100644 index 0000000..523b9ee --- /dev/null +++ b/FilterSystemGenerator/Templates/FilterSystemTemplate.cs @@ -0,0 +1,57 @@ +namespace Scellecs.Morpeh.SourceGenerator.FilterSystemGenerator.Templates; + +public static class FilterSystemTemplate +{ + public static GeneratedFile GenerateFile() + { + const string @namespace = "Scellecs.Morpeh.SourceGenerator.FilterSystemGenerator"; + + return new GeneratedFile + { + FileName = $"{@namespace}.FilterSystem.g.cs", + Content = $$""" +using Scellecs.Morpeh; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace {{@namespace}} +{ + public abstract class FilterSystem : ISystem + { + public World World { get; set; } + private Filter Filter { get; set; } + + public virtual void OnAwake() + { + Initialize(); + Filter = GetFilter(World.Filter).Build(); + } + + protected virtual void Initialize() { } + + protected abstract FilterBuilder GetFilter(FilterBuilder builder); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void OnUpdate(float _) + { + Execute(Filter); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Execute(IEnumerable entities) + { + foreach (var entity in entities) + { + Execute(entity); + } + } + + protected virtual void Execute(Entity entity) { } + + public virtual void Dispose() { } + } +} +""" + }; + } +} \ No newline at end of file diff --git a/FilterSystemGenerator/Templates/PartialImplTemplate.cs b/FilterSystemGenerator/Templates/PartialImplTemplate.cs new file mode 100644 index 0000000..8a02502 --- /dev/null +++ b/FilterSystemGenerator/Templates/PartialImplTemplate.cs @@ -0,0 +1,156 @@ +namespace Scellecs.Morpeh.SourceGenerator.FilterSystemGenerator.Templates; + +public static class PartialImplTemplate +{ + public static GeneratedFile GenerateFile(SystemInfo systemInfo) + { + var data = new Data(); + + foreach (var filter in systemInfo.Filters.Distinct().OrderByDescending(f => f.IsAspect)) + { + if (!string.IsNullOrEmpty(filter.Namespace)) + { + data.Usings.Add($"using {filter.Namespace};"); + } + + data.Properties.Add(filter.IsAspect + ? $$"""private AspectFactory<{{filter.NamePascalCase}}> _{{filter.NameCamelCase}}Aspect;""" + : $$"""private Stash<{{filter.NamePascalCase}}> _{{filter.NameCamelCase}}Stash { get; set; }""" + ); + + data.Initializers.Add(filter.IsAspect + ? $$"""_{{filter.NameCamelCase}}Aspect = World.GetAspectFactory<{{filter.NamePascalCase}}>();""" + : $$"""_{{filter.NameCamelCase}}Stash = World.GetStash<{{filter.NamePascalCase}}>(){{(filter.IsDisposable ? ".AsDisposable()" : string.Empty)}};""" + ); + + data.Getters.Add(filter.IsAspect + ? $$"""_{{filter.NameCamelCase}}Aspect.Get(entity)""" + : $$"""ref _{{filter.NameCamelCase}}Stash.Get(entity)""" + ); + + data.Arguments.Add(filter.IsAspect + ? $$"""{{filter.NamePascalCase}} {{filter.NameCamelCase}}""" + : $$"""ref {{filter.NamePascalCase}} {{filter.NameCamelCase}}"""); + } + + return new GeneratedFile + { + FileName = string.IsNullOrEmpty(systemInfo.Namespace) + ? $"{systemInfo.Name}.g.cs" + : $"{systemInfo.Namespace}.{systemInfo.Name}.g.cs", + + Content = string.IsNullOrEmpty(systemInfo.Namespace) + ? GenerateWithoutNameSpace(systemInfo, data) + : GenerateWithNameSpace(systemInfo, data) + }; + } + + private static string GenerateWithNameSpace(SystemInfo info, Data data) + { + return $$""" +using Scellecs.Morpeh; +using System.Runtime.CompilerServices; +{{GetUsings(data.Usings)}} + +namespace {{info.Namespace}} +{ + public partial class {{info.Name}} + { + {{GetProperties(data.Properties, indent: 2)}} + + protected override void Initialize() + { + {{GetInitializers(data.Initializers, indent: 3)}} + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override void Execute(Entity entity) + { + OnUpdate( + entity{{GetGetters(data.Getters, 4)}} + ); + } + + partial void OnUpdate(Entity entity{{GetArguments(data.Arguments)}}); + } +} +"""; + } + + private static string GenerateWithoutNameSpace(SystemInfo info, Data data) + { + return $$""" +using Scellecs.Morpeh; +using System.Runtime.CompilerServices; +{{GetUsings(data.Usings)}} + +public partial class {{info.Name}} +{ + {{GetProperties(data.Properties, indent: 1)}} + + protected override void Initialize() + { + {{GetInitializers(data.Initializers, indent: 2)}} + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override void Execute(Entity entity) + { + OnUpdate( + entity{{GetGetters(data.Getters, indent: 3)}} + ); + } + + partial void OnUpdate(Entity entity{{GetArguments(data.Arguments)}}); +} +"""; + } + + private static string GetIndent(int indent) + { + const string tab = " "; + return string.Concat(Enumerable.Repeat(tab, indent)); + } + + private static string GetUsings(IReadOnlyCollection usings) + { + return string.Join("\n", usings.Distinct()); + } + + private static string GetProperties(IReadOnlyCollection properties, int indent) + { + return string.Join($"\n{GetIndent(indent)}", properties); + } + + private static string GetInitializers(IReadOnlyCollection initializers, int indent) + { + return string.Join($"\n{GetIndent(indent)}", initializers); + } + + private static string GetGetters(IReadOnlyCollection getters, int indent) + { + var separator = $",\n{GetIndent(indent)}"; + + return getters.Count > 0 + ? string.Join(separator, getters).Insert(0, separator) + : string.Empty; + } + + private static string GetArguments(IReadOnlyCollection arguments) + { + return arguments.Count > 0 + ? string.Join(", ", arguments).Insert(0, ", ") + : string.Empty; + } + + private struct Data + { + public List Usings { get; } = new(); + public List Getters { get; } = new(); + public List Arguments { get; } = new(); + public List Properties { get; } = new(); + public List Initializers { get; } = new(); + + public Data() { } + } +} \ No newline at end of file diff --git a/MorpehCodeGenerator.sln b/MorpehCodeGenerator.sln index d8a788f..af02e39 100644 --- a/MorpehCodeGenerator.sln +++ b/MorpehCodeGenerator.sln @@ -1,7 +1,11 @@  Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Core", "Core\Core.shproj", "{B2A951FB-4BAD-4AA7-8986-A291B103D443}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspectGenerator", "AspectGenerator\AspectGenerator.csproj", "{61DDE34C-9933-440D-8ED6-ACE21CC0CDC8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FilterSystemGenerator", "FilterSystemGenerator\FilterSystemGenerator.csproj", "{F5740CF7-1DAC-4E68-9FBA-894B812545FD}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{C8D46555-2AB8-4755-B804-781DB113E0D0}" EndProject Global @@ -18,5 +22,9 @@ Global {C8D46555-2AB8-4755-B804-781DB113E0D0}.Debug|Any CPU.Build.0 = Debug|Any CPU {C8D46555-2AB8-4755-B804-781DB113E0D0}.Release|Any CPU.ActiveCfg = Release|Any CPU {C8D46555-2AB8-4755-B804-781DB113E0D0}.Release|Any CPU.Build.0 = Release|Any CPU + {F5740CF7-1DAC-4E68-9FBA-894B812545FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F5740CF7-1DAC-4E68-9FBA-894B812545FD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F5740CF7-1DAC-4E68-9FBA-894B812545FD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F5740CF7-1DAC-4E68-9FBA-894B812545FD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/README.md b/README.md index 1c84ad1..3fb42b4 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,304 @@ -## Aspects +### IAspect<> -Just use a ```IAspect<>``` with specified components and aspect boilerplate code will be generated for you. +Generic ```IAspect``` version with an ability to specify components and all required aspect implementation will be generated for you. ```cs -public struct Scale : IComponent -{ - public Vector3 Value; +public struct Position : IComponent +{ + public Vector3 Value; +} + +public struct Rotation : IComponent +{ + public Vector3 Value; +} + +public struct Scale : IComponent +{ + public Vector3 Value; } +public partial struct Transform : IAspect { } + +// Generated +public partial struct Transform +{ + private Stash _positionStash; + private Stash _rotationStash; + private Stash _scaleStash; + + public Entity Entity { get; set; } + + public ref Position Position => ref _positionStash.Get(Entity); + public ref Rotation Rotation => ref _rotationStash.Get(Entity); + public ref Scale Scale => ref _scaleStash.Get(Entity); + + void IAspect.OnGetAspectFactory(World world) + { + _positionStash = world.GetStash(); + _rotationStash = world.GetStash(); + _scaleStash = world.GetStash(); + } + + FilterBuilder IFilterExtension.Extend(FilterBuilder rootFilter) + { + return rootFilter.With().With().With(); + } +} +``` + +--- + +### FilterSystem + +FilterSystem Generator is inspired by Unity's JobSystem generation that allows dynamic arguments in `Execute()` method. Filter system allows to specify which entities you want to iterate on during the update, with dynamic arguments in `OnUpdate()` method. + +Lets see it in action: + +We have a `Position` and `Velocity` components + +```cs public struct Position : IComponent { public Vector3 Value; } -public struct Rotation : IComponent +public struct Velocity : IComponent { public Vector3 Value; } +``` -public partial struct Transform : IAspect { } +And we want to have a system that will operate with those entities. + +What we need is to nest `FilterSystem` and specify `partial` modifier. + +`FilterSystem` is a generated base class, that asks you to implement `GetFilter()` method, that should contain your filter build query. + +```cs +public partial class MoveSystem : FilterSystem +{ + protected override FilterBuilder GetFilter(FilterBuilder builder) + { + return builder.With().With(); + } +} ``` -Generator will provide the following: +Based on it, partial `OnUpdate()` method declaration will be generated with a direct ref access to each component. ```cs -public partial struct Transform +// Generated +public partial class MoveSystem { + // ... + partial void OnUpdate(Entity entity, ref Position position, ref Velocity velocity); +} +``` + +And then you can just implement that `OnUpdate()` method on your side. + +```cs +partial void OnUpdate(Entity entity, ref Position position, ref Velocity velocity) +{ + position.Value += velocity.Value * Time.deltaTime; +} +``` + +#### Lets Compare! + +Example of generated version: + +```cs +public partial class MoveSystem : FilterSystem +{ + protected override FilterBuilder GetFilter(FilterBuilder builder) + { + return builder.With().With(); + } + + partial void Execute(Entity entity, ref Position position, ref Velocity velocity) + { + position.Value += velocity.Value * Time.deltaTime; + } +} +``` + +Manually written system: + +1. Default version + +```cs +public class MoveSystem : ISystem +{ + private Filter _filter; + + public World World { get; set; } + + public void OnAwake() + { + _filter = World.Filter.With().With().Build(); + } + + public void OnUpdate(float deltaTime) + { + foreach (var entity in _filter) + { + ref var position = ref entity.GetComponent(); + ref var velocity = ref entity.GetComponent(); + + position.Value += velocity.Value * deltaTime; + } + } +} +``` + +2. More efficient version + +```cs +public class MoveSystem : ISystem +{ + private Filter _filter; private Stash _positionStash; - private Stash _rotationStash; - private Stash _scaleStash; + private Stash _velocityStash; - public Entity Entity { get; set; } + public World World { get; set; } - public ref Position Position => ref _positionStash.Get(Entity); - public ref Rotation Rotation => ref _rotationStash.Get(Entity); - public ref Scale Scale => ref _scaleStash.Get(Entity); + public void OnAwake() + { + _positionStash = World.GetStash(); + _velocityStash = World.GetStash(); + _filter = World.Filter.With().With().Build(); + } - void IAspect.OnGetAspectFactory(World world) + public void OnUpdate(float deltaTime) + { + foreach (var entity in _filter) + { + ref var position = ref _positionStash.Get(entity); + ref var velocity = ref _velocityStash.Get(entity); + + position.Value += velocity.Value * deltaTime; + } + } +} +``` + +#### Important notes + +Generated part of your `FilterSystem` gets the components in a more efficient way, directly from stash, not entity. + +```cs +// Generated +public partial class MoveSystem +{ + private Stash _positionStash; + private Stash _velocityStash; + + protected override void Initialize() { - _positionStash = world.GetStash(); - _rotationStash = world.GetStash(); - _scaleStash = world.GetStash(); + _positionStash = World.GetStash(); + _velocityStash = World.GetStash(); } - FilterBuilder IFilterExtension.Extend(FilterBuilder rootFilter) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override void Execute(Entity entity) { - return rootFilter.With().With().With(); + OnUpdate( + entity, + ref _positionStash.Get(entity), + ref _velocityStash.Get(entity) + ); } + // ... } ``` + +Generator supports `IDisposable` components and marks the `Stash` if it is required. + +```cs +public struct View : IComponent, IDisposable +{ + public GameObject Value; + // ... +} + +// Generated +public partial class DestroySystem +{ + private Stash _viewStash; + + protected override void Initialize() + { + _viewStash = World.GetStash().AsDisposable(); + } + // ... +} +``` + +Generator supports core `IAspect` and. generic `IAspect<>` + +```cs +// generic IAspect from AspectGenerator or manually written IAspect +public partial struct Transform : IAspect { } + +public struct Position : IComponent +{ + public Vector3 Value; +} + +public struct Rotation : IComponent +{ + public Vector3 Value; +} + +public struct Scale : IComponent +{ + public Vector3 Value; +} + +public struct Velocity : IComponent +{ + public Vector3 Value; +} + +public partial class MoveSystem : FilterSystem +{ + protected override FilterBuilder GetFilter(FilterBuilder builder) + { + // There are also extension 'WithAspect<>' instead of 'Extend' + return builder.With().Extend(); + } + + partial void OnUpdate(Entity entity, Transform transform, ref Velocity velocity) + { + transform.Position.Value += velocity.Value * Time.deltaTime; + } +} + + +// Generated +public partial class TransformSystem +{ + private AspectFactory _transformAspect; + private Stash _velocityStash; + + protected override void Initialize() + { + _transformAspect = World.GetAspectFactory(); + _velocityStash = World.GetStash(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override void Execute(Entity entity) + { + OnUpdate( + entity, + _transformAspect.Get(entity), + ref _velocityStash.Get(entity) + ); + } + + partial void OnUpdate(Entity entity, Transform transform, ref Velocity velocity); +} +``` \ No newline at end of file diff --git a/Tests/AspectGenerator/Aspects.cs b/Tests/AspectGenerator/Aspects.cs index 6410c6d..336d62a 100644 --- a/Tests/AspectGenerator/Aspects.cs +++ b/Tests/AspectGenerator/Aspects.cs @@ -1,10 +1,15 @@ -using Scellecs.Morpeh.SourceGenerator.Aspect; - -namespace Tests.AspectGenerator; - -public partial struct Aspect2 : IAspect { } - -public partial struct Aspect3 : IAspect { } +using Scellecs.Morpeh.SourceGenerator.AspectGenerator; +using Tests.AspectGenerator; + +namespace Tests.AspectGenerator.Aspect2Namespace +{ + public partial struct Aspect2 : IAspect { } +} + +namespace Tests.AspectGenerator.Aspect3Namespace +{ + public partial struct Aspect3 : IAspect { } +} public partial struct Aspect4 : IAspect { } diff --git a/Tests/AspectGenerator/Components.cs b/Tests/AspectGenerator/Components.cs index 0bd2bc3..64f0b5d 100644 --- a/Tests/AspectGenerator/Components.cs +++ b/Tests/AspectGenerator/Components.cs @@ -1,23 +1,27 @@ using Scellecs.Morpeh; -namespace Tests.AspectGenerator; - -public struct Test1 : IComponent { } +public struct Test1 : IComponent, IDisposable +{ + public void Dispose() { } +} public struct Test2 : IComponent { } -public struct Test3 : IComponent { } +namespace Tests.AspectGenerator +{ + public struct Test3 : IComponent { } -public struct Test4 : IComponent { } + public struct Test4 : IComponent { } -public struct Test5 : IComponent { } + public struct Test5 : IComponent { } -public struct Test6 : IComponent { } + public struct Test6 : IComponent { } -public struct Test7 : IComponent { } + public struct Test7 : IComponent { } -public struct Test8 : IComponent { } + public struct Test8 : IComponent { } -public struct Test9 : IComponent { } + public struct Test9 : IComponent { } -public struct Test10 : IComponent { } \ No newline at end of file + public struct Test10 : IComponent { } +} \ No newline at end of file diff --git a/Tests/AspectGenerator/Mocks/FilterBuilder.cs b/Tests/AspectGenerator/Mocks/FilterBuilder.cs deleted file mode 100644 index 5d4df0b..0000000 --- a/Tests/AspectGenerator/Mocks/FilterBuilder.cs +++ /dev/null @@ -1,16 +0,0 @@ -// ReSharper disable once CheckNamespace - -namespace Scellecs.Morpeh; - -public struct FilterBuilder -{ - public FilterBuilder With() - { - throw new NotImplementedException(); - } - - public FilterBuilder Extend() - { - throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/Tests/FilterSystemGenerator/AspectOneExtend.cs b/Tests/FilterSystemGenerator/AspectOneExtend.cs new file mode 100644 index 0000000..be8bcd6 --- /dev/null +++ b/Tests/FilterSystemGenerator/AspectOneExtend.cs @@ -0,0 +1,18 @@ +using Scellecs.Morpeh; +using Scellecs.Morpeh.SourceGenerator.FilterSystemGenerator; +using Tests.FilterSystemGenerator.DNamespace; + +namespace Tests.FilterSystemGenerator; + +public partial class AspectOneExtend : FilterSystem +{ + protected override FilterBuilder GetFilter(FilterBuilder filterBuilder) + { + return filterBuilder.Extend(); + } + + partial void OnUpdate(Entity entity, D d) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Tests/FilterSystemGenerator/AspectOneExtendOneWithAspect.cs b/Tests/FilterSystemGenerator/AspectOneExtendOneWithAspect.cs new file mode 100644 index 0000000..040b495 --- /dev/null +++ b/Tests/FilterSystemGenerator/AspectOneExtendOneWithAspect.cs @@ -0,0 +1,19 @@ +using Scellecs.Morpeh; +using Tests.FilterSystemGenerator.DNamespace; +using Scellecs.Morpeh.SourceGenerator.AspectGenerator; +using Scellecs.Morpeh.SourceGenerator.FilterSystemGenerator; + +namespace Tests.FilterSystemGenerator; + +public partial class AspectOneExtendOneWithAspect : FilterSystem +{ + protected override FilterBuilder GetFilter(FilterBuilder filterBuilder) + { + return filterBuilder.Extend().WithAspect(); + } + + partial void OnUpdate(Entity entity, D d, F f) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Tests/FilterSystemGenerator/AspectWithOne.cs b/Tests/FilterSystemGenerator/AspectWithOne.cs new file mode 100644 index 0000000..edffa5c --- /dev/null +++ b/Tests/FilterSystemGenerator/AspectWithOne.cs @@ -0,0 +1,19 @@ +using Scellecs.Morpeh; +using Scellecs.Morpeh.SourceGenerator.AspectGenerator; +using Scellecs.Morpeh.SourceGenerator.FilterSystemGenerator; +using Tests.FilterSystemGenerator.ENamespace; + +namespace Tests.FilterSystemGenerator; + +public partial class AspectWithOne : FilterSystem +{ + protected override FilterBuilder GetFilter(FilterBuilder filterBuilder) + { + return filterBuilder.WithAspect(); + } + + partial void OnUpdate(Entity entity, E e) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Tests/FilterSystemGenerator/AspectWithOneWithoutOne.cs b/Tests/FilterSystemGenerator/AspectWithOneWithoutOne.cs new file mode 100644 index 0000000..63f98dd --- /dev/null +++ b/Tests/FilterSystemGenerator/AspectWithOneWithoutOne.cs @@ -0,0 +1,20 @@ +using Scellecs.Morpeh; +using Scellecs.Morpeh.SourceGenerator.AspectGenerator; +using Scellecs.Morpeh.SourceGenerator.FilterSystemGenerator; +using Tests.FilterSystemGenerator.ANamespace; +using Tests.FilterSystemGenerator.ENamespace; + +namespace Tests.FilterSystemGenerator; + +public partial class AspectWithOneWithoutOne : FilterSystem +{ + protected override FilterBuilder GetFilter(FilterBuilder filterBuilder) + { + return filterBuilder.WithAspect().Without(); + } + + partial void OnUpdate(Entity entity, E e) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Tests/FilterSystemGenerator/AspectWithOneWithoutTwo.cs b/Tests/FilterSystemGenerator/AspectWithOneWithoutTwo.cs new file mode 100644 index 0000000..3448d80 --- /dev/null +++ b/Tests/FilterSystemGenerator/AspectWithOneWithoutTwo.cs @@ -0,0 +1,21 @@ +using Scellecs.Morpeh; +using Scellecs.Morpeh.SourceGenerator.AspectGenerator; +using Scellecs.Morpeh.SourceGenerator.FilterSystemGenerator; +using Tests.FilterSystemGenerator.ANamespace; +using Tests.FilterSystemGenerator.BNamespace; +using Tests.FilterSystemGenerator.ENamespace; + +namespace Tests.FilterSystemGenerator; + +public partial class AspectWithOneWithoutTwo : FilterSystem +{ + protected override FilterBuilder GetFilter(FilterBuilder filterBuilder) + { + return filterBuilder.WithAspect().Without().Without(); + } + + partial void OnUpdate(Entity entity, E e) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Tests/FilterSystemGenerator/Aspects.cs b/Tests/FilterSystemGenerator/Aspects.cs new file mode 100644 index 0000000..f996b98 --- /dev/null +++ b/Tests/FilterSystemGenerator/Aspects.cs @@ -0,0 +1,15 @@ +using Tests.FilterSystemGenerator.ANamespace; +using Tests.FilterSystemGenerator.BNamespace; +using Scellecs.Morpeh.SourceGenerator.AspectGenerator; + +namespace Tests.FilterSystemGenerator.DNamespace +{ + public partial struct D : IAspect { } +} + +namespace Tests.FilterSystemGenerator.ENamespace +{ + public partial struct E : IAspect { } +} + +public partial struct F : IAspect { } \ No newline at end of file diff --git a/Tests/FilterSystemGenerator/ComponentWithOne.cs b/Tests/FilterSystemGenerator/ComponentWithOne.cs new file mode 100644 index 0000000..a9478b3 --- /dev/null +++ b/Tests/FilterSystemGenerator/ComponentWithOne.cs @@ -0,0 +1,18 @@ +using Scellecs.Morpeh; +using Tests.FilterSystemGenerator.ANamespace; +using Scellecs.Morpeh.SourceGenerator.FilterSystemGenerator; + +namespace Tests.FilterSystemGenerator; + +public partial class ComponentWithOne : FilterSystem +{ + protected override FilterBuilder GetFilter(FilterBuilder filterBuilder) + { + return filterBuilder.With(); + } + + partial void OnUpdate(Entity entity, ref A a) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Tests/FilterSystemGenerator/ComponentWithOneWithoutOne.cs b/Tests/FilterSystemGenerator/ComponentWithOneWithoutOne.cs new file mode 100644 index 0000000..05a2913 --- /dev/null +++ b/Tests/FilterSystemGenerator/ComponentWithOneWithoutOne.cs @@ -0,0 +1,19 @@ +using Scellecs.Morpeh; +using Tests.FilterSystemGenerator.ANamespace; +using Tests.FilterSystemGenerator.BNamespace; +using Scellecs.Morpeh.SourceGenerator.FilterSystemGenerator; + +namespace Tests.FilterSystemGenerator; + +public partial class ComponentWithOneWithoutOne : FilterSystem +{ + protected override FilterBuilder GetFilter(FilterBuilder filterBuilder) + { + return filterBuilder.With().Without(); + } + + partial void OnUpdate(Entity entity, ref A a) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Tests/FilterSystemGenerator/ComponentWithOneWithoutTwo.cs b/Tests/FilterSystemGenerator/ComponentWithOneWithoutTwo.cs new file mode 100644 index 0000000..131c75b --- /dev/null +++ b/Tests/FilterSystemGenerator/ComponentWithOneWithoutTwo.cs @@ -0,0 +1,19 @@ +using Scellecs.Morpeh; +using Tests.FilterSystemGenerator.ANamespace; +using Tests.FilterSystemGenerator.BNamespace; +using Scellecs.Morpeh.SourceGenerator.FilterSystemGenerator; + +namespace Tests.FilterSystemGenerator; + +public partial class ComponentWithOneWithoutTwo : FilterSystem +{ + protected override FilterBuilder GetFilter(FilterBuilder filterBuilder) + { + return filterBuilder.With().Without().Without(); + } + + partial void OnUpdate(Entity entity, ref A a) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Tests/FilterSystemGenerator/ComponentWithRepeated.cs b/Tests/FilterSystemGenerator/ComponentWithRepeated.cs new file mode 100644 index 0000000..9ad5b42 --- /dev/null +++ b/Tests/FilterSystemGenerator/ComponentWithRepeated.cs @@ -0,0 +1,18 @@ +using Scellecs.Morpeh; +using Tests.FilterSystemGenerator.ANamespace; +using Scellecs.Morpeh.SourceGenerator.FilterSystemGenerator; + +namespace Tests.FilterSystemGenerator; + +public partial class ComponentWithRepeated : FilterSystem +{ + protected override FilterBuilder GetFilter(FilterBuilder filterBuilder) + { + return filterBuilder.With().With(); + } + + partial void OnUpdate(Entity entity, ref A a) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Tests/FilterSystemGenerator/ComponentWithTwo.cs b/Tests/FilterSystemGenerator/ComponentWithTwo.cs new file mode 100644 index 0000000..3472fe3 --- /dev/null +++ b/Tests/FilterSystemGenerator/ComponentWithTwo.cs @@ -0,0 +1,19 @@ +using Scellecs.Morpeh; +using Scellecs.Morpeh.SourceGenerator.FilterSystemGenerator; +using Tests.FilterSystemGenerator.ANamespace; +using Tests.FilterSystemGenerator.BNamespace; + +namespace Tests.FilterSystemGenerator; + +public partial class ComponentWithTwo : FilterSystem +{ + protected override FilterBuilder GetFilter(FilterBuilder filterBuilder) + { + return filterBuilder.With().With(); + } + + partial void OnUpdate(Entity entity, ref A a, ref B b) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Tests/FilterSystemGenerator/ComponentWithoutOne.cs b/Tests/FilterSystemGenerator/ComponentWithoutOne.cs new file mode 100644 index 0000000..6baab94 --- /dev/null +++ b/Tests/FilterSystemGenerator/ComponentWithoutOne.cs @@ -0,0 +1,18 @@ +using Scellecs.Morpeh; +using Scellecs.Morpeh.SourceGenerator.FilterSystemGenerator; +using Tests.FilterSystemGenerator.ANamespace; + +namespace Tests.FilterSystemGenerator; + +public partial class ComponentWithoutOne : FilterSystem +{ + protected override FilterBuilder GetFilter(FilterBuilder filterBuilder) + { + return filterBuilder.Without(); + } + + partial void OnUpdate(Entity entity) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Tests/FilterSystemGenerator/ComponentWithoutTwo.cs b/Tests/FilterSystemGenerator/ComponentWithoutTwo.cs new file mode 100644 index 0000000..111ee26 --- /dev/null +++ b/Tests/FilterSystemGenerator/ComponentWithoutTwo.cs @@ -0,0 +1,19 @@ +using Scellecs.Morpeh; +using Tests.FilterSystemGenerator.ANamespace; +using Tests.FilterSystemGenerator.BNamespace; +using Scellecs.Morpeh.SourceGenerator.FilterSystemGenerator; + +namespace Tests.FilterSystemGenerator; + +public partial class ComponentWithoutTwo : FilterSystem +{ + protected override FilterBuilder GetFilter(FilterBuilder filterBuilder) + { + return filterBuilder.Without().Without(); + } + + partial void OnUpdate(Entity entity) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Tests/FilterSystemGenerator/Components.cs b/Tests/FilterSystemGenerator/Components.cs new file mode 100644 index 0000000..a183d96 --- /dev/null +++ b/Tests/FilterSystemGenerator/Components.cs @@ -0,0 +1,23 @@ +using Scellecs.Morpeh; + +namespace Tests.FilterSystemGenerator.ANamespace +{ + public struct A : IComponent, IDisposable + { + public int Value; + public void Dispose() { } + } +} + +namespace Tests.FilterSystemGenerator.BNamespace +{ + public struct B : IComponent + { + public int Value; + } +} + +public struct C : IComponent +{ + public int Value; +} \ No newline at end of file diff --git a/Tests/FilterSystemGenerator/EmptyFilter.cs b/Tests/FilterSystemGenerator/EmptyFilter.cs new file mode 100644 index 0000000..1e93de6 --- /dev/null +++ b/Tests/FilterSystemGenerator/EmptyFilter.cs @@ -0,0 +1,17 @@ +using Scellecs.Morpeh; +using Scellecs.Morpeh.SourceGenerator.FilterSystemGenerator; + +namespace Tests.FilterSystemGenerator; + +public partial class EmptyFilter : FilterSystem +{ + protected override FilterBuilder GetFilter(FilterBuilder filterBuilder) + { + return filterBuilder; + } + + partial void OnUpdate(Entity entity) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Tests/Mocks/FilterBuilder.cs b/Tests/Mocks/FilterBuilder.cs new file mode 100644 index 0000000..023c631 --- /dev/null +++ b/Tests/Mocks/FilterBuilder.cs @@ -0,0 +1,21 @@ +// ReSharper disable once CheckNamespace + +namespace Scellecs.Morpeh; + +public struct FilterBuilder +{ + public FilterBuilder With() where T: IComponent + { + throw new NotImplementedException(); + } + + public FilterBuilder Without() where T: IComponent + { + throw new NotImplementedException(); + } + + public FilterBuilder Extend() where T: IFilterExtension + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Tests/AspectGenerator/Mocks/IFilterExtension.cs b/Tests/Mocks/IFilterExtension.cs similarity index 100% rename from Tests/AspectGenerator/Mocks/IFilterExtension.cs rename to Tests/Mocks/IFilterExtension.cs diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 1135a0a..6fa6473 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -13,6 +13,9 @@ + + + From fe8327fff69bb95a9795de2ce99f2b280c47b07a Mon Sep 17 00:00:00 2001 From: kandreyc Date: Sat, 3 Jun 2023 01:15:24 +0300 Subject: [PATCH 2/2] remove redundant code --- FilterSystemGenerator/FilterSystemGenerator.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/FilterSystemGenerator/FilterSystemGenerator.cs b/FilterSystemGenerator/FilterSystemGenerator.cs index 023818a..be73a86 100644 --- a/FilterSystemGenerator/FilterSystemGenerator.cs +++ b/FilterSystemGenerator/FilterSystemGenerator.cs @@ -87,9 +87,6 @@ private static IEnumerable GetAllTypeArgumentsWithIdentifier(HashSe protected override void Generate(SystemInfo systemInfo) { - // if (systemInfo.Filters.Length is not 0) - { - AddSource(PartialImplTemplate.GenerateFile(systemInfo)); - } + AddSource(PartialImplTemplate.GenerateFile(systemInfo)); } } \ No newline at end of file