From 573297136b000a07fb70c4deb0d3ef22d6f12e8c Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Mon, 27 Oct 2025 09:56:49 +0300 Subject: [PATCH 01/29] Add Aspid.Generators.Helper --- .../Aspid.MVVM.Generators.csproj | 3 + .../Binders/BinderGenerator.Find.cs | 30 +- .../Binders/BinderGenerator.Generate.cs | 19 +- .../Generators/Binders/BinderGenerator.cs | 7 +- .../Generators/Binders/Body/BinderLogBody.cs | 29 +- .../Generators/Binders/Data/BinderData.cs | 2 +- .../Generators/Binders/Data/BinderDataSpan.cs | 2 +- .../CreateFrom/Body/CreateFromBody.cs | 313 ------------------ .../CreateFrom/CreateFromGenerator.cs | 91 ----- .../CreateFrom/Data/CreateFromData.cs | 14 - .../CreateFrom/Data/CreateFromDataSpan.cs | 20 -- .../Generators/Descriptions/Classes.Aspid.cs | 64 ++-- .../Generators/Descriptions/Classes.System.cs | 28 -- .../Descriptions/Classes.UnityEngine.cs | 21 -- .../Generators/Descriptions/Defines.Aspid.cs | 4 +- .../Descriptions/Defines.UnityEngine.cs | 10 - .../Generators/Descriptions/General.cs | 4 +- .../Descriptions/Namespaces.Aspid.cs | 6 +- .../Descriptions/Namespaces.System.cs | 14 - .../Descriptions/Namespaces.UnityEngine.cs | 15 - .../Generators/Ids/Data/IdData.cs | 6 +- .../Ids/Extensions/IdGeneratorExtensions.cs | 9 +- .../Generators/Ids/IdGenerator.Find.cs | 20 +- .../Generators/Ids/IdGenerator.Generate.cs | 6 +- .../Generators/Ids/IdGenerator.cs | 7 +- .../ViewModels/Body/BindableMembersBody.cs | 18 +- .../Body/FindBindableMembersBody.cs | 22 +- .../ViewModels/Body/PropertiesBody.cs | 16 +- .../ViewModels/Body/RelayCommandBody.cs | 14 +- .../Generators/ViewModels/Data/BindMode.cs | 2 +- .../Generators/ViewModels/Data/Inheritor.cs | 2 +- .../Data/Members/BindableBindAlso.cs | 4 +- .../Data/Members/BindableCommand.cs | 10 +- .../ViewModels/Data/Members/BindableField.cs | 14 +- .../ViewModels/Data/Members/BindableMember.cs | 11 +- .../Collections/IdLengthMemberGroup.cs | 2 +- .../Data/Members/CustomViewModelInterface.cs | 2 +- .../ViewModels/Data/ViewModelData.cs | 6 +- .../ViewModels/Extensions/SymbolExtensions.cs | 10 +- .../Factories/BindableBindAlsoFactory.cs | 12 +- .../Factories/BindableCommandFactory.cs | 10 +- .../Factories/BindableFieldFactory.cs | 14 +- .../Factories/BindableMembersFactory.cs | 6 +- .../CustomViewModelInterfacesFactory.cs | 14 +- .../ViewModels/ViewModelGenerator.cs | 31 +- .../Generators/Views/Body/BinderCachedBody.cs | 22 +- .../Body/Extensions/BindSafelyExtensions.cs | 8 +- .../Views/Body/GenericInitializeView.cs | 27 +- .../Generators/Views/Body/InitializeBody.cs | 56 ++-- .../Generators/Views/Data/GenericViewData.cs | 2 +- .../Generators/Views/Data/Inheritor.cs | 2 +- .../Views/Data/Members/AsBinderMember.cs | 2 +- .../Views/Data/Members/BinderMember.cs | 6 +- .../Views/Data/Members/CachedBinderMember.cs | 4 +- .../BinderMembersCollectionSpanByType.cs | 4 +- .../Generators/Views/Data/ViewData.cs | 4 +- .../Generators/Views/Data/ViewDataSpan.cs | 6 +- .../Views/Factories/BinderMembersFactory.cs | 22 +- .../Generators/Views/ViewGenerator.Find.cs | 19 +- .../Views/ViewGenerator.Generate.cs | 10 +- .../Generators/Views/ViewGenerator.cs | 8 +- .../Helpers/AttributeText.cs | 17 - .../Helpers/{Data => }/CastedSpan.cs | 2 +- .../Helpers/CodeWriter.cs | 120 ------- .../Helpers/DeclarationText.cs | 37 --- .../CSharpSyntaxNodeExtensions.cs | 18 - .../MemberDeclarationSyntaxExtensions.cs | 19 -- .../PropertyDeclarationSyntaxExtensions.cs | 23 -- .../TypeDeclarationSyntaxExtensions.cs | 44 --- .../Symbols/MethodSymbolExtensions.cs | 27 -- .../Symbols/SymbolExtensions.Attribute.cs | 41 --- .../Symbols/TypeSymbolExtensions.Attribute.cs | 64 ---- .../Symbols/TypeSymbolExtensions.BaseType.cs | 47 --- .../Symbols/TypeSymbolExtensions.Interface.cs | 82 ----- .../Writer/CodeWriteClassExtension.cs | 81 ----- .../Writer/CodeWriteLoopExtensions.cs | 91 ----- .../Writer/CodeWriterIfExtensions.cs | 42 --- .../Helpers/FoundForGenerator.cs | 13 - .../Helpers/{Data => }/MembersByGroup.cs | 2 +- .../Helpers/NamespaceText.cs | 11 - .../Symbols => }/SymbolExtensions.cs | 23 +- .../Aspid.MVVM.Generators/Helpers/TypeText.cs | 18 - 82 files changed, 330 insertions(+), 1658 deletions(-) delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/CreateFrom/Body/CreateFromBody.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/CreateFrom/CreateFromGenerator.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/CreateFrom/Data/CreateFromData.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/CreateFrom/Data/CreateFromDataSpan.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Classes.System.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Classes.UnityEngine.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Defines.UnityEngine.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Namespaces.System.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Namespaces.UnityEngine.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/AttributeText.cs rename Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/{Data => }/CastedSpan.cs (95%) delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/CodeWriter.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/DeclarationText.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Declarations/CSharpSyntaxNodeExtensions.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Declarations/MemberDeclarationSyntaxExtensions.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Declarations/PropertyDeclarationSyntaxExtensions.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Declarations/TypeDeclarationSyntaxExtensions.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Symbols/MethodSymbolExtensions.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Symbols/SymbolExtensions.Attribute.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Symbols/TypeSymbolExtensions.Attribute.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Symbols/TypeSymbolExtensions.BaseType.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Symbols/TypeSymbolExtensions.Interface.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Writer/CodeWriteClassExtension.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Writer/CodeWriteLoopExtensions.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Writer/CodeWriterIfExtensions.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/FoundForGenerator.cs rename Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/{Data => }/MembersByGroup.cs (97%) delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/NamespaceText.cs rename Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/{Extensions/Symbols => }/SymbolExtensions.cs (65%) delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/TypeText.cs diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Aspid.MVVM.Generators.csproj b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Aspid.MVVM.Generators.csproj index 19aae24..a8001cc 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Aspid.MVVM.Generators.csproj +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Aspid.MVVM.Generators.csproj @@ -12,12 +12,15 @@ + + all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/BinderGenerator.Find.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/BinderGenerator.Find.cs index 255c714..34eeefa 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/BinderGenerator.Find.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/BinderGenerator.Find.cs @@ -2,27 +2,27 @@ using System.Threading; using System.Diagnostics; using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; +using Aspid.Generators.Helper; using System.Collections.Generic; using Microsoft.CodeAnalysis.CSharp; using System.Runtime.CompilerServices; -using Aspid.MVVM.Generators.Binders.Data; -using Aspid.MVVM.Generators.Descriptions; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Aspid.MVVM.Generators.Generators.Binders.Data; +using Classes = Aspid.MVVM.Generators.Generators.Descriptions.Classes; -namespace Aspid.MVVM.Generators.Binders; +namespace Aspid.MVVM.Generators.Generators.Binders; public partial class BinderGenerator { - private static FoundForGenerator FindBinders(GeneratorSyntaxContext context, + private static BinderData? FindBinders(GeneratorSyntaxContext context, CancellationToken cancellationToken) { Debug.Assert(context.Node is TypeDeclarationSyntax); var candidate = Unsafe.As(context.Node); var symbol = context.SemanticModel.GetDeclaredSymbol(candidate, cancellationToken); - if (symbol is null) return default; - if (!symbol.HasInterfaceInSelfOrBases(Classes.IBinder, out var binderInterface)) return default; + if (symbol is null) return null; + if (!symbol.TryGetAnyInterfaceInSelfAndBases(out var binderInterface, Classes.IBinder)) return null; var hasBinderLogInBaseType = false; const string setValueName = "SetValue"; @@ -40,11 +40,11 @@ private static FoundForGenerator FindBinders(GeneratorSyntaxContext method.ExplicitInterfaceImplementations.Length != 0; }); - if (methodsExplicitImplemented) return default; + if (methodsExplicitImplemented) return null; if (!hasBinderLogInBaseType && !SymbolEqualityComparer.Default.Equals(type, symbol) - && method.HasAnyAttribute(Classes.BinderLogAttribute)) + && method.HasAnyAttributeInSelf(Classes.BinderLogAttribute)) { hasBinderLogInBaseType = true; } @@ -57,15 +57,15 @@ private static FoundForGenerator FindBinders(GeneratorSyntaxContext { if (method.Parameters.Length != 1) continue; if (method.NameFromExplicitImplementation() != setValueName) continue; - if (!symbol.HasInterfaceInSelfOrBases($"{Classes.IBinder.FullName}<{method.Parameters[0].Type.ToDisplayString()}>")) continue; + if (!symbol.HasAnyInterfaceInSelfAndBases($"{Classes.IBinder.FullName}<{method.Parameters[0].Type.ToDisplayString()}>")) continue; - if (method.HasAnyAttribute(Classes.BinderLogAttribute) && + if (method.HasAnyAttributeInSelf(Classes.BinderLogAttribute) && !method.ExplicitInterfaceImplementations.Any()) binderLogMethods.Add(method); } - - if (binderLogMethods.Count == 0) return default; - - return new FoundForGenerator(new BinderData(symbol, candidate, hasBinderLogInBaseType, binderLogMethods)); + + return binderLogMethods.Count is 0 + ? null + : new BinderData(symbol, candidate, hasBinderLogInBaseType, binderLogMethods); } } \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/BinderGenerator.Generate.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/BinderGenerator.Generate.cs index c99b203..258009a 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/BinderGenerator.Generate.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/BinderGenerator.Generate.cs @@ -1,10 +1,11 @@ using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; -using Aspid.MVVM.Generators.Binders.Body; -using Aspid.MVVM.Generators.Binders.Data; -using Aspid.MVVM.Generators.Descriptions; +using Aspid.Generators.Helper; +using Aspid.MVVM.Generators.Generators.Binders.Body; +using Aspid.MVVM.Generators.Generators.Binders.Data; +using Aspid.MVVM.Generators.Generators.Descriptions; +using static Aspid.Generators.Helper.Unity.Defines; -namespace Aspid.MVVM.Generators.Binders; +namespace Aspid.MVVM.Generators.Generators.Binders; public partial class BinderGenerator { @@ -14,7 +15,7 @@ private static void GenerateCode(SourceProductionContext context, BinderData bin { var declaration = binderData.Declaration; var @namespace = declaration.GetNamespaceName(); - var declarationText = declaration.GetDeclarationText(); + var declarationText = new DeclarationText(declaration); GenerateBinderLog(@namespace, new BinderDataSpan(binderData), context, declarationText); } @@ -36,10 +37,10 @@ private static void GenerateBinderLog( .AppendClassEnd(@namespace) .AppendLine("#endif"); #else - code.AppendLine($"#if {Defines.UNITY_EDITOR} && !{Defines.ASPID_MVVM_BINDER_LOG_DISABLED}") - .AppendClassBegin(@namespace, declarationText) + code.AppendLine($"#if {UNITY_EDITOR} && !{Defines.ASPID_MVVM_BINDER_LOG_DISABLED}") + .BeginClass(@namespace, declarationText) .AppendBinderLogBody(data) - .AppendClassEnd(@namespace) + .EndClass(@namespace) .Append("#endif"); #endif diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/BinderGenerator.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/BinderGenerator.cs index 196ad03..f0b64e8 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/BinderGenerator.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/BinderGenerator.cs @@ -3,16 +3,17 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace Aspid.MVVM.Generators.Binders; +namespace Aspid.MVVM.Generators.Generators.Binders; [Generator] public partial class BinderGenerator : IIncrementalGenerator { public void Initialize(IncrementalGeneratorInitializationContext context) { + // ReSharper disable once NullableWarningSuppressionIsUsed var provider = context.SyntaxProvider.CreateSyntaxProvider(SyntacticPredicate, FindBinders) - .Where(foundForSourceGenerator => foundForSourceGenerator.IsNeed) - .Select((foundForSourceGenerator, _) => foundForSourceGenerator.Container); + .Where(data => data.HasValue) + .Select((data, _) => data!.Value); context.RegisterSourceOutput( source: provider, diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/Body/BinderLogBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/Body/BinderLogBody.cs index 51fec3b..def4d21 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/Body/BinderLogBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/Body/BinderLogBody.cs @@ -1,20 +1,21 @@ using System; using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; -using Aspid.MVVM.Generators.Binders.Data; -using Aspid.MVVM.Generators.Descriptions; +using Aspid.Generators.Helper; +using Aspid.MVVM.Generators.Generators.Binders.Data; +using Aspid.MVVM.Generators.Generators.Descriptions; +using static Aspid.Generators.Helper.Classes; +using static Aspid.Generators.Helper.Unity.UnityClasses; +using Classes = Aspid.MVVM.Generators.Generators.Descriptions.Classes; -namespace Aspid.MVVM.Generators.Binders.Body; +namespace Aspid.MVVM.Generators.Generators.Binders.Body; // ReSharper disable InconsistentNaming public static class BinderLogBody { private const string GeneratedAttribute = General.GeneratedCodeLogBinderAttribute; - private static readonly string List = Classes.List.Global; - private static readonly string IBinder = Classes.IBinder.Global; - private static readonly string Exception = Classes.Exception.Global; - private static readonly string SerializeFieldAttribute = Classes.SerializeField.Global; + private static readonly string IBinder = Classes.IBinder; + private static readonly string Exception = Aspid.Generators.Helper.Classes.Exception; public static CodeWriter AppendBinderLogBody(this CodeWriter code, in BinderDataSpan data) { @@ -39,7 +40,7 @@ private static CodeWriter AppendProfilerMarkers(this CodeWriter code, in BinderD var modifier = data.Symbol.IsSealed ? "private" : "protected"; var className = data.Declaration.Identifier.Text; - code.AppendLine($"{modifier} static readonly {Classes.ProfilerMarker.Global} SetValueMarker = new(\"{className}.SetValue\");"); + code.AppendLine($"{modifier} static readonly {ProfilerMarker} SetValueMarker = new(\"{className}.SetValue\");"); return code.AppendLine(); } @@ -50,11 +51,11 @@ private static CodeWriter AppendProperties(this CodeWriter code, in BinderDataSp code.AppendMultiline( $""" {GeneratedAttribute} - [{SerializeFieldAttribute}] private bool _isDebug; + [{SerializeField}] private bool _isDebug; // TODO Add Custom Property {GeneratedAttribute} - [{SerializeFieldAttribute}] private {Classes.List.Global} _log; + [{SerializeField}] private {List_1} _log; {GeneratedAttribute} {modifier} bool IsDebug => _isDebug; @@ -66,7 +67,7 @@ private static CodeWriter AppendProperties(this CodeWriter code, in BinderDataSp private static CodeWriter AppendSetValueMethods(this CodeWriter code, in ReadOnlySpan methods) { - code.AppendLoop(methods, method => + foreach (var method in methods) { var parameterName = method.Parameters[0].Name; var parameterType = method.Parameters[0].Type.ToDisplayStringGlobal(); @@ -103,7 +104,7 @@ private static CodeWriter AppendSetValueMethods(this CodeWriter code, in ReadOnl } """) .AppendLine(); - }); + } return code; } @@ -117,7 +118,7 @@ private static CodeWriter AppendAddLogMethod(this CodeWriter code, in BinderData {{GeneratedAttribute}} {{modifier}} void AddLog(string log) { - _log ??= new {{List}}(); + _log ??= new {{List_1}}(); _log.Add(log); } """); diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/Data/BinderData.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/Data/BinderData.cs index a870d34..f2aebe0 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/Data/BinderData.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/Data/BinderData.cs @@ -3,7 +3,7 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace Aspid.MVVM.Generators.Binders.Data; +namespace Aspid.MVVM.Generators.Generators.Binders.Data; public readonly struct BinderData( INamedTypeSymbol symbol, diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/Data/BinderDataSpan.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/Data/BinderDataSpan.cs index e3a607b..f2a5ddd 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/Data/BinderDataSpan.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/Data/BinderDataSpan.cs @@ -2,7 +2,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace Aspid.MVVM.Generators.Binders.Data; +namespace Aspid.MVVM.Generators.Generators.Binders.Data; public readonly ref struct BinderDataSpan(BinderData data) { diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/CreateFrom/Body/CreateFromBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/CreateFrom/Body/CreateFromBody.cs deleted file mode 100644 index 4bead8e..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/CreateFrom/Body/CreateFromBody.cs +++ /dev/null @@ -1,313 +0,0 @@ -using System; -using System.Text; -using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; -using Aspid.MVVM.Generators.Descriptions; -using Aspid.MVVM.Generators.CreateFrom.Data; - -namespace Aspid.MVVM.Generators.CreateFrom.Body; - -// ReSharper disable InconsistentNaming -public static class CreateFromBody -{ - private const string GeneratedAttribute = General.GeneratedCodeCreateFromAttribute; - - private static readonly string List = Classes.List.Global; - private static readonly string Func = Classes.Func.Global; - private static readonly string Span = Classes.Span.Global; - private static readonly string IList = Classes.IList.Global; - private static readonly string IEnumerable = Classes.IEnumerable.Global; - private static readonly string ReadOnlySpan = Classes.ReadOnlySpan.Global; - private static readonly SymbolEqualityComparer Comparer = SymbolEqualityComparer.Default; - - public static CodeWriter AppendCreateFromBody(this CodeWriter code, in CreateFromDataSpan data) - { - if (data.Constructor.Parameters.Length == 0) return code; - var parameters = GetParameters(data.Constructor, data.FromType); - - code.AppendLine($"[{Classes.MethodImplAttribute.Global}({Classes.MethodImplOptions.Global}.AggressiveInlining)]") - .AppendMethodDeclaration(data, parameters, ParameterType.None, ParameterType.None) - .AppendMultiline( - $$""" - { - return new({{parameters.Constructor(parameters.From)}}); - } - """) - .AppendLine(); - - code.AppendArrayMethodBody(data, ParameterType.Array, parameters) - .AppendArrayMethodBody(data, ParameterType.Span, parameters) - .AppendArrayMethodBody(data, ParameterType.ReadOnlySpan, parameters) - .AppendArrayMethodBody(data, ParameterType.List, parameters); - - code.AppendListMethodBody(data, ParameterType.Array, parameters) - .AppendListMethodBody(data, ParameterType.List, parameters) - .AppendListMethodBody(data, ParameterType.Span, parameters) - .AppendListMethodBody(data, ParameterType.ReadOnlySpan, parameters) - .AppendListMethodBody(data ,ParameterType.Enumerable, parameters); - - code.AppendIListMethodBody(data, ParameterType.Array, parameters) - .AppendIListMethodBody(data, ParameterType.List, parameters) - .AppendIListMethodBody(data, ParameterType.Span, parameters) - .AppendIListMethodBody(data, ParameterType.ReadOnlySpan, parameters) - .AppendIListMethodBody(data, ParameterType.Enumerable, parameters); - - code.AppendEnumerableMethodBody(data, ParameterType.Array, ParameterType.Enumerable, parameters) - .AppendEnumerableMethodBody(data, ParameterType.List, ParameterType.Enumerable, parameters) - .AppendEnumerableMethodBody(data, ParameterType.Enumerable, ParameterType.Enumerable, parameters); - - return code; - } - - private static CodeWriter AppendArrayMethodBody( - this CodeWriter code, - in CreateFromDataSpan data, - ParameterType fromParameterType, - in Parameters parameters) - { - var fromName = parameters.From; - - var capacity = GetLengthType(fromParameterType) switch - { - LengthType.None => "0", - LengthType.Count => $"{fromName}.Count", - LengthType.Length => $"{fromName}.Length", - _ => throw new ArgumentOutOfRangeException() - }; - - return code - .AppendMethodDeclaration(data, parameters, fromParameterType, ParameterType.Array) - .AppendMultiline( - $$""" - { - var __to = new {{data.ToTypeName}}[{{capacity}}]; - - for (var __i = 0; __i < __to.Length; __i++) - __to[__i] = {{fromName}}[__i].{{data.MethodName}}({{parameters.EnumParameters}}); - - return __to; - } - """) - .AppendLine(); - } - - private static CodeWriter AppendListMethodBody( - this CodeWriter code, - in CreateFromDataSpan data, - ParameterType fromParameterType, - in Parameters parameters) - { - var capacity = GetLengthType(fromParameterType) switch - { - LengthType.None => string.Empty, - LengthType.Count => $"{parameters.From}.Count", - LengthType.Length => $"{parameters.From}.Length", - _ => throw new ArgumentOutOfRangeException() - }; - - return code - .AppendMethodDeclaration(data, parameters, fromParameterType, ParameterType.List) - .AppendMultiline( - $$""" - { - var __to = new {{List}}<{{data.ToTypeName}}>({{capacity}}); - - foreach(var __from in {{parameters.From}}) - __to.Add(__from.{{data.MethodName}}({{parameters.EnumParameters}})); - - return __to; - } - """) - .AppendLine(); - } - - private static CodeWriter AppendIListMethodBody( - this CodeWriter code, - in CreateFromDataSpan data, - ParameterType fromParameterType, - in Parameters parameters) - { - return code - .AppendMethodDeclaration(data, parameters, fromParameterType, ParameterType.IList) - .AppendMultiline( - $$""" - { - var __to = __createList(); - - foreach(var __from in {{parameters.From}}) - __to.Add(__from.{{data.MethodName}}({{parameters.EnumParameters}})); - - return __to; - } - """) - .AppendLine(); - } - - private static CodeWriter AppendEnumerableMethodBody( - this CodeWriter code, - in CreateFromDataSpan data, - ParameterType fromParameterType, - ParameterType toParameterType, - in Parameters parameters) - { - return code - .AppendMethodDeclaration(data, parameters, fromParameterType, toParameterType) - .AppendMultiline( - $$""" - { - foreach (var __from in {{parameters.From}}) - yield return __from.{{data.MethodName}}({{parameters.EnumParameters}}); - } - """) - .AppendLine(); - } - - private static CodeWriter AppendMethodDeclaration( - this CodeWriter code, - in CreateFromDataSpan data, - in Parameters parameters, - ParameterType fromParameterType, - ParameterType toParameterType) - { - var methodName = data.MethodName; - var returnType = data.ToTypeName; - var additionalParameter = string.Empty; - - switch (toParameterType) - { - case ParameterType.None: break; - case ParameterType.List: - methodName += "AsList"; - returnType = $"{List}<{returnType}>"; - break; - - case ParameterType.Span: - case ParameterType.Array: - case ParameterType.ReadOnlySpan: - methodName += "AsArray"; - returnType += "[]"; - break; - - case ParameterType.Enumerable: - methodName += "AsEnumerable"; - returnType = $"{IEnumerable}<{returnType}>"; - break; - - case ParameterType.IList: - methodName += "AsList"; - returnType = $"{IList}<{returnType}>"; - additionalParameter = $", {Func}<{IList}<{data.ToTypeName}>> __createList"; - break; - - default: throw new ArgumentOutOfRangeException(nameof(toParameterType), toParameterType, null); - } - - if (!data.CanBeInherited) - { - return code.AppendMultiline( - $""" - {GeneratedAttribute} - public static {returnType} {methodName}({parameters.Method(fromParameterType, data.FromTypeName)}{additionalParameter}) - """); - } - - return code.AppendMultiline( - $""" - {GeneratedAttribute} - public static {returnType} {methodName}({parameters.Method(fromParameterType, "T")}{additionalParameter}) - where T : {data.FromTypeName} - """); - } - - private static LengthType GetLengthType(ParameterType type) => type switch - { - ParameterType.None => LengthType.None, - ParameterType.Array => LengthType.Length, - ParameterType.List => LengthType.Count, - ParameterType.IList => LengthType.Count, - ParameterType.Span => LengthType.Length, - ParameterType.ReadOnlySpan => LengthType.Length, - ParameterType.Enumerable => LengthType.None, - _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) - }; - - private static Parameters GetParameters(IMethodSymbol constructor, ITypeSymbol fromType) - { - string? fromName = null; - StringBuilder parameters = new(); - StringBuilder methodParameters = new(); - StringBuilder constructorParameters = new(); - - foreach (var parameter in constructor.Parameters) - { - if (constructorParameters.Length is not 0) - constructorParameters.Append(", "); - - if (fromName is null && Comparer.Equals(parameter.Type, fromType)) - { - fromName = parameter.Name; - constructorParameters.Append("{0}"); - } - else - { - if (parameters.Length is not 0) - parameters.Append(", "); - - parameters.Append($"{parameter.Name}"); - methodParameters.Append($", {parameter.Type.ToDisplayStringGlobal()} {parameter.Name}"); - constructorParameters.Append($"{parameter.Name}"); - } - } - - return new Parameters(fromName!, methodParameters, parameters, constructorParameters); - } - - private readonly ref struct Parameters( - string from, - StringBuilder method, - StringBuilder parameters, - StringBuilder constructor) - { - public readonly string From = from; - public readonly string EnumParameters = parameters.ToString(); - - private readonly string _methods = from + method; - private readonly string _constructor = constructor.ToString(); - - - public string Method(ParameterType type, string fromType) - { - return type switch - { - ParameterType.None => $"this {fromType} {_methods}", - ParameterType.Array => $"this {fromType}[] {_methods}", - ParameterType.Span => $"this {Span}<{fromType}> {_methods}", - ParameterType.List => $"this {List}<{fromType}> {_methods}", - ParameterType.IList => $"this {IList}<{fromType}> {_methods}", - ParameterType.ReadOnlySpan => $"this {ReadOnlySpan}<{fromType}> {_methods}", - ParameterType.Enumerable => $"this {IEnumerable}<{fromType}> {_methods}", - _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) - }; - } - - public string Constructor(string fromName) => string.Format(_constructor, fromName); - } - - private enum LengthType - { - None, - Count, - Length, - } - - private enum ParameterType - { - None, - List, - IList, - Array, - Span, - ReadOnlySpan, - Enumerable, - } -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/CreateFrom/CreateFromGenerator.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/CreateFrom/CreateFromGenerator.cs deleted file mode 100644 index 3b77551..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/CreateFrom/CreateFromGenerator.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System.Linq; -using System.Threading; -using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; -using Microsoft.CodeAnalysis.CSharp; -using System.Runtime.CompilerServices; -using Aspid.MVVM.Generators.Descriptions; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Aspid.MVVM.Generators.CreateFrom.Body; -using Aspid.MVVM.Generators.CreateFrom.Data; - -namespace Aspid.MVVM.Generators.CreateFrom; - -[Generator(LanguageNames.CSharp)] -public class CreateFromGenerator : IIncrementalGenerator -{ - public void Initialize(IncrementalGeneratorInitializationContext context) - { - var provider = context.SyntaxProvider.ForAttributeWithMetadataName(Classes.CreateFromAttribute.FullName, SyntacticPredicate, FindCreateFrom). - Where(foundForSourceGenerator => foundForSourceGenerator.IsNeed). - Select((foundForSourceGenerator, _) => foundForSourceGenerator.Container); - - context.RegisterSourceOutput( - source: provider, - action: GenerateCode); - } - - private static bool SyntacticPredicate(SyntaxNode node, CancellationToken cancellationToken) - { - var candidate = node switch - { - ConstructorDeclarationSyntax syntax => syntax, - _ => null - }; - - return candidate is not null && !candidate.Modifiers.Any(SyntaxKind.StaticKeyword); - } - - private static FoundForGenerator FindCreateFrom( - GeneratorAttributeSyntaxContext context, - CancellationToken cancellationToken) - { - if (context.TargetSymbol is not IMethodSymbol constructor) return default; - - var attribute = context.Attributes.First(attribute => - attribute.AttributeClass?.ToDisplayString() == Classes.CreateFromAttribute.FullName); - - if (attribute.ConstructorArguments.First().Value is not ITypeSymbol fromType) return default; - - if (constructor.Parameters.Length == 0) return default; - var candidate = Unsafe.As(context.TargetNode); - return new FoundForGenerator(new CreateFromData(candidate, constructor, fromType)); - } - - private static void GenerateCode(SourceProductionContext context, CreateFromData data) - { - var @namespace = data.Declaration.GetNamespaceName(); - var dataSpan = new CreateFromDataSpan(data); - - if (data.Declaration.Parent is not TypeDeclarationSyntax typeDeclaration) return; - - var i = 0; - var index = -1; - - foreach (var constructor in typeDeclaration.Members.OfType()) - { - if (constructor == data.Declaration) - { - index = i; - break; - } - - i++; - } - - if (index == -1) return; - - var declaration = new DeclarationText( - "public static", - "class", - $"{dataSpan.FromType.ToDisplayString().Replace(".", "_")}To{dataSpan.ToType.ToDisplayString().Replace(".", "_")}_{index}", - null); - - var code = new CodeWriter(); - code.AppendClassBegin(@namespace, declaration) - .AppendCreateFromBody(dataSpan) - .AppendClassEnd(@namespace); - - context.AddSource(declaration.GetFileName(@namespace, "IViewModel"), code.GetSourceText()); - } -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/CreateFrom/Data/CreateFromData.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/CreateFrom/Data/CreateFromData.cs deleted file mode 100644 index b02b9ab..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/CreateFrom/Data/CreateFromData.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Aspid.MVVM.Generators.CreateFrom.Data; - -public readonly struct CreateFromData( - ConstructorDeclarationSyntax declaration, - IMethodSymbol constructor, - ITypeSymbol fromType) -{ - public readonly ITypeSymbol FromType = fromType; - public readonly IMethodSymbol Constructor = constructor; - public readonly ConstructorDeclarationSyntax Declaration = declaration; -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/CreateFrom/Data/CreateFromDataSpan.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/CreateFrom/Data/CreateFromDataSpan.cs deleted file mode 100644 index 82c5741..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/CreateFrom/Data/CreateFromDataSpan.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; - -namespace Aspid.MVVM.Generators.CreateFrom.Data; - -public readonly ref struct CreateFromDataSpan(CreateFromData data) -{ - public readonly CreateFromData Data = data; - public readonly IMethodSymbol Constructor = data.Constructor; - - public readonly ITypeSymbol ToType = data.Constructor.ContainingType; - public readonly string ToName = data.Declaration.Identifier.Text; - public readonly string ToTypeName = data.Constructor.ContainingType.ToDisplayStringGlobal(); - - public readonly ITypeSymbol FromType = data.FromType; - public readonly string FromTypeName = data.FromType.ToDisplayStringGlobal(); - - public readonly string MethodName = $"To{data.Declaration.Identifier.Text}"; - public readonly bool CanBeInherited = data.FromType.TypeKind != TypeKind.Struct && !data.FromType.IsSealed; -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Classes.Aspid.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Classes.Aspid.cs index 4535b0e..05c6017 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Classes.Aspid.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Classes.Aspid.cs @@ -1,19 +1,19 @@ -using Aspid.Generator.Helpers; +using Aspid.Generators.Helper; -namespace Aspid.MVVM.Generators.Descriptions; +namespace Aspid.MVVM.Generators.Generators.Descriptions; // ReSharper disable InconsistentNaming -public static partial class Classes +public static class Classes { #region Views public static readonly TypeText IView = new(nameof(IView), Namespaces.Aspid_MVVM); public static readonly AttributeText ViewAttribute = - new("View", Namespaces.Aspid_MVVM); + new("ViewAttribute", Namespaces.Aspid_MVVM); public static readonly AttributeText AsBinderAttribute = - new("AsBinder", Namespaces.Aspid_MVVM); + new("AsBinderAttribute", Namespaces.Aspid_MVVM); public static readonly TypeText ViewBinder = new (nameof(ViewBinder), Namespaces.Aspid_MVVM); @@ -33,10 +33,10 @@ public static partial class Classes new(nameof(MonoBinder), Namespaces.Aspid_MVVM_UNITY); public static readonly AttributeText BinderLogAttribute = - new("BinderLog", Namespaces.Aspid_MVVM); + new("BinderLogAttribute", Namespaces.Aspid_MVVM); public static readonly AttributeText RequireBinderAttribute = - new("RequireBinder", Namespaces.Aspid_MVVM); + new("RequireBinderAttribute", Namespaces.Aspid_MVVM); #endregion #region View Models @@ -44,7 +44,7 @@ public static partial class Classes new(nameof(IViewModel), Namespaces.Aspid_MVVM); public static readonly AttributeText ViewModelAttribute = - new("ViewModel", Namespaces.Aspid_MVVM); + new("ViewModelAttribute", Namespaces.Aspid_MVVM); public static readonly TypeText FindBindableMemberResult = new(nameof(FindBindableMemberResult), Namespaces.Aspid_MVVM); @@ -55,31 +55,31 @@ public static partial class Classes #region Bind Attributes public static readonly AttributeText BindAttribute = - new("Bind", Namespaces.Aspid_MVVM); + new("BindAttribute", Namespaces.Aspid_MVVM); public static readonly AttributeText IdAttribute = - new("BindId", Namespaces.Aspid_MVVM); + new("BindIdAttribute", Namespaces.Aspid_MVVM); public static readonly AttributeText AccessAttribute = - new("Access", Namespaces.Aspid_MVVM); + new("AccessAttribute", Namespaces.Aspid_MVVM); public static readonly AttributeText BindAlsoAttribute = - new("BindAlso", Namespaces.Aspid_MVVM); + new("BindAlsoAttribute", Namespaces.Aspid_MVVM); public static readonly AttributeText IgnoreAttribute = - new("IgnoreBind", Namespaces.Aspid_MVVM); + new("IgnoreBindAttribute", Namespaces.Aspid_MVVM); public static readonly AttributeText OneWayBindAttribute = - new("OneWayBind", Namespaces.Aspid_MVVM); + new("OneWayBindAttribute", Namespaces.Aspid_MVVM); public static readonly AttributeText TwoWayBindAttribute = - new("TwoWayBind", Namespaces.Aspid_MVVM); + new("TwoWayBindAttribute", Namespaces.Aspid_MVVM); public static readonly AttributeText OneTimeBindAttribute = - new("OneTimeBind", Namespaces.Aspid_MVVM); + new("OneTimeBindAttribute", Namespaces.Aspid_MVVM); public static readonly AttributeText OneWayToSourceBindAttribute = - new("OneWayToSourceBind", Namespaces.Aspid_MVVM); + new("OneWayToSourceBindAttribute", Namespaces.Aspid_MVVM); #endregion #region Bindable Member Events @@ -98,40 +98,40 @@ public static partial class Classes public static readonly TypeText IBinderRemover = new(nameof(IBinderRemover), Namespaces.Aspid_MVVM); - public static readonly TypeText? OneWayBindableMember = + public static readonly TypeText OneWayBindableMember = new(nameof(OneWayBindableMember), Namespaces.Aspid_MVVM); - public static readonly TypeText? OneWayStructBindableMember = + public static readonly TypeText OneWayStructBindableMember = new(nameof(OneWayStructBindableMember), Namespaces.Aspid_MVVM); - public static readonly TypeText? OneWayEnumBindableMember = + public static readonly TypeText OneWayEnumBindableMember = new(nameof(OneWayEnumBindableMember), Namespaces.Aspid_MVVM); - public static readonly TypeText? TwoWayBindableMember = + public static readonly TypeText TwoWayBindableMember = new(nameof(TwoWayBindableMember), Namespaces.Aspid_MVVM); - public static readonly TypeText? TwoWayStructBindableMember = + public static readonly TypeText TwoWayStructBindableMember = new(nameof(TwoWayStructBindableMember), Namespaces.Aspid_MVVM); - public static readonly TypeText? TwoWayEnumBindableMember = + public static readonly TypeText TwoWayEnumBindableMember = new(nameof(TwoWayEnumBindableMember), Namespaces.Aspid_MVVM); - public static readonly TypeText? OneTimeBindableMember = + public static readonly TypeText OneTimeBindableMember = new(nameof(OneTimeBindableMember), Namespaces.Aspid_MVVM); - public static readonly TypeText? OneTimeStructBindableMember = + public static readonly TypeText OneTimeStructBindableMember = new(nameof(OneTimeStructBindableMember), Namespaces.Aspid_MVVM); - public static readonly TypeText? OneTimeEnumBindableMember = + public static readonly TypeText OneTimeEnumBindableMember = new(nameof(OneTimeEnumBindableMember), Namespaces.Aspid_MVVM); - public static readonly TypeText? OneWayToSourceBindableMember = + public static readonly TypeText OneWayToSourceBindableMember = new(nameof(OneWayToSourceBindableMember), Namespaces.Aspid_MVVM); - public static readonly TypeText? OneWayToSourceStructBindableMember = + public static readonly TypeText OneWayToSourceStructBindableMember = new(nameof(OneWayToSourceStructBindableMember), Namespaces.Aspid_MVVM); - public static readonly TypeText? OneWayToSourceEnumBindableMember = + public static readonly TypeText OneWayToSourceEnumBindableMember = new(nameof(OneWayToSourceEnumBindableMember), Namespaces.Aspid_MVVM); #endregion @@ -143,7 +143,7 @@ public static partial class Classes new(nameof(IRelayCommand), Namespaces.Aspid_MVVM); public static readonly AttributeText RelayCommandAttribute = - new("RelayCommand", Namespaces.Aspid_MVVM); + new("RelayCommandAttribute", Namespaces.Aspid_MVVM); #endregion public static readonly TypeText Unsafe = @@ -153,8 +153,8 @@ public static partial class Classes new(nameof(Ids), Namespaces.Aspid_MVVM_Generated); public static readonly AttributeText CreateFromAttribute = - new("CreateFrom", Namespaces.Aspid_MVVM); + new("CreateFromAttribute", Namespaces.Aspid_MVVM); public static readonly AttributeText AddComponentContextMenuAttribute = - new ("AddComponentContextMenu", Namespaces.Aspid_MVVM_UNITY); + new ("AddComponentContextMenuAttribute", Namespaces.Aspid_MVVM_UNITY); } \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Classes.System.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Classes.System.cs deleted file mode 100644 index 3a8d0f2..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Classes.System.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Aspid.Generator.Helpers; - -namespace Aspid.MVVM.Generators.Descriptions; - -public static partial class Classes -{ - public static readonly TypeText Span = new("Span", Namespaces.System); - public static readonly TypeText ReadOnlySpan = new("ReadOnlySpan", Namespaces.System); - public static readonly TypeText Func = new("Func", Namespaces.System); - public static readonly TypeText Action = new("Action", Namespaces.System); - public static readonly TypeText Delegate = new("Delegate", Namespaces.System); - - public static readonly TypeText Exception = new("Exception", Namespaces.System); - public static readonly TypeText ArgumentNullException = new("ArgumentNullException", Namespaces.System); - public static readonly TypeText InvalidOperationException = new("InvalidOperationException", Namespaces.System); - - public static readonly TypeText List = new("List", Namespaces.System_Collections_Generic); - public static readonly TypeText IList = new("IList", Namespaces.System_Collections_Generic); - public static readonly TypeText Dictionary = new("Dictionary", Namespaces.System_Collections_Generic); - public static readonly TypeText IEnumerable = new("IEnumerable", Namespaces.System_Collections_Generic); - public static readonly TypeText EqualityComparer = new("EqualityComparer", Namespaces.System_Collections_Generic); - - public static readonly TypeText EditorBrowsableState = new(nameof(EditorBrowsableState), Namespaces.System_ComponentModel); - public static readonly AttributeText EditorBrowsableAttribute = new("EditorBrowsable", Namespaces.System_ComponentModel); - - public static readonly TypeText MethodImplOptions = new("MethodImplOptions", Namespaces.System_Runtime_CompilerServices); - public static readonly AttributeText MethodImplAttribute = new("MethodImpl", Namespaces.System_Runtime_CompilerServices); -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Classes.UnityEngine.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Classes.UnityEngine.cs deleted file mode 100644 index aa04347..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Classes.UnityEngine.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Aspid.Generator.Helpers; - -namespace Aspid.MVVM.Generators.Descriptions; - -public static partial class Classes -{ - public static readonly TypeText ProfilerMarker = new("ProfilerMarker", Namespaces.Unity_Profiling); - - public static readonly TypeText Object = new("Object", Namespaces.UnityEngine); - public static readonly TypeText Component = new("Component", Namespaces.UnityEngine); - public static readonly TypeText MonoBehaviour = new("MonoBehaviour", Namespaces.UnityEngine); - public static readonly TypeText SerializeField = new("SerializeField", Namespaces.UnityEngine); - - public static readonly TypeText MenuItem = new("MenuItem", Namespaces.UnityEditor); - public static readonly TypeText MenuCommand = new("MenuCommand", Namespaces.UnityEditor); - - public static readonly TypeText Button = new("Button", Namespaces.UnityEngine_UI); - public static readonly TypeText Toggle = new("Toggle", Namespaces.UnityEngine_UI); - public static readonly TypeText Slider = new("Slider", Namespaces.UnityEngine_UI); - public static readonly TypeText ScrollRect = new("ScrollRect", Namespaces.UnityEngine_UI); -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Defines.Aspid.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Defines.Aspid.cs index a15c35a..69ee7e4 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Defines.Aspid.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Defines.Aspid.cs @@ -1,7 +1,7 @@ -namespace Aspid.MVVM.Generators.Descriptions; +namespace Aspid.MVVM.Generators.Generators.Descriptions; // ReSharper disable InconsistentNaming -public static partial class Defines +public static class Defines { public const string ASPID_MVVM_BINDER_LOG_DISABLED = nameof(ASPID_MVVM_BINDER_LOG_DISABLED); diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Defines.UnityEngine.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Defines.UnityEngine.cs deleted file mode 100644 index c5601dc..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Defines.UnityEngine.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Aspid.MVVM.Generators.Descriptions; - -// ReSharper disable InconsistentNaming -public static partial class Defines -{ - public const string UNITY_EDITOR = nameof(UNITY_EDITOR); - - public const string UNITY_IOS = nameof(UNITY_IOS); - public const string UNITY_ANDROID = nameof(UNITY_ANDROID); -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/General.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/General.cs index 7d1aaf3..6fc3ced 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/General.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/General.cs @@ -1,6 +1,6 @@ -namespace Aspid.MVVM.Generators.Descriptions; +namespace Aspid.MVVM.Generators.Generators.Descriptions; -public class General +public static class General { public const string GeneratedCodeIdAttribute = "[global::System.CodeDom.Compiler.GeneratedCode(\"Aspid.MVVM.Generators.IdGenerator\", \"1.0.0\")]"; diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Namespaces.Aspid.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Namespaces.Aspid.cs index 345c9f3..fdc8958 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Namespaces.Aspid.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Namespaces.Aspid.cs @@ -1,9 +1,9 @@ -using Aspid.Generator.Helpers; +using Aspid.Generators.Helper; -namespace Aspid.MVVM.Generators.Descriptions; +namespace Aspid.MVVM.Generators.Generators.Descriptions; // ReSharper disable InconsistentNaming -public static partial class Namespaces +public static class Namespaces { public static readonly NamespaceText Aspid = new(nameof(Aspid)); diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Namespaces.System.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Namespaces.System.cs deleted file mode 100644 index 44f3571..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Namespaces.System.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Aspid.Generator.Helpers; - -namespace Aspid.MVVM.Generators.Descriptions; - -// ReSharper disable InconsistentNaming -public static partial class Namespaces -{ - public static readonly NamespaceText System = new(nameof(System)); - public static readonly NamespaceText System_Runtime = new("Runtime", System); - public static readonly NamespaceText System_Collections = new("Collections", System); - public static readonly NamespaceText System_ComponentModel = new("ComponentModel", System); - public static readonly NamespaceText System_Collections_Generic = new("Generic", System_Collections); - public static readonly NamespaceText System_Runtime_CompilerServices = new("CompilerServices", System_Runtime); -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Namespaces.UnityEngine.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Namespaces.UnityEngine.cs deleted file mode 100644 index cfd9f33..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Namespaces.UnityEngine.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Aspid.Generator.Helpers; - -namespace Aspid.MVVM.Generators.Descriptions; - -// ReSharper disable InconsistentNaming -public static partial class Namespaces -{ - public static readonly NamespaceText Unity = new("Unity"); - public static readonly NamespaceText Unity_Profiling = new("Profiling", Unity); - - public static readonly NamespaceText UnityEngine = new("UnityEngine"); - public static readonly NamespaceText UnityEngine_UI = new("UI", UnityEngine); - - public static readonly NamespaceText UnityEditor = new("UnityEditor"); -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/Data/IdData.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/Data/IdData.cs index 764e4b7..fd45829 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/Data/IdData.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/Data/IdData.cs @@ -1,9 +1,9 @@ using System; using Microsoft.CodeAnalysis; -using Aspid.MVVM.Generators.Descriptions; -using Aspid.MVVM.Generators.Ids.Extensions; +using Aspid.MVVM.Generators.Generators.Descriptions; +using Aspid.MVVM.Generators.Generators.Ids.Extensions; -namespace Aspid.MVVM.Generators.Ids.Data; +namespace Aspid.MVVM.Generators.Generators.Ids.Data; public readonly struct IdData : IEquatable { diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/Extensions/IdGeneratorExtensions.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/Extensions/IdGeneratorExtensions.cs index 06eb234..624595e 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/Extensions/IdGeneratorExtensions.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/Extensions/IdGeneratorExtensions.cs @@ -1,14 +1,15 @@ using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; -using Aspid.MVVM.Generators.Descriptions; +using Aspid.Generators.Helper; +using Classes = Aspid.MVVM.Generators.Generators.Descriptions.Classes; +using SymbolExtensions = Aspid.MVVM.Generators.Helpers.SymbolExtensions; -namespace Aspid.MVVM.Generators.Ids.Extensions; +namespace Aspid.MVVM.Generators.Generators.Ids.Extensions; public static class IdGeneratorExtensions { public static string GetId(this ISymbol member, string prefixName = "") { - if (!member.HasAnyAttribute(out var attribute, Classes.IdAttribute)) + if (!member.TryGetAnyAttributeInSelf(out var attribute, Classes.IdAttribute)) return member.GetName(prefixName); var value = attribute!.ConstructorArguments[0].Value as string; diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.Find.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.Find.cs index 7fd4fb5..ad917ea 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.Find.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.Find.cs @@ -1,26 +1,26 @@ using System.Threading; using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; +using Aspid.Generators.Helper; using System.Collections.Generic; using Microsoft.CodeAnalysis.CSharp; -using Aspid.MVVM.Generators.Descriptions; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Aspid.MVVM.Generators.Views.Factories; -using Aspid.MVVM.Generators.ViewModels.Factories; +using Aspid.MVVM.Generators.Generators.Views.Factories; +using Aspid.MVVM.Generators.Generators.ViewModels.Factories; +using Classes = Aspid.MVVM.Generators.Generators.Descriptions.Classes; -namespace Aspid.MVVM.Generators.Ids; +namespace Aspid.MVVM.Generators.Generators.Ids; public partial class IdGenerator { - private static FoundForGenerator> GetIdsForSourceGeneration( + private static HashSet? GetIdsForSourceGeneration( GeneratorSyntaxContext context, CancellationToken cancellationToken) { var syntax = (TypeDeclarationSyntax)context.Node; - if (context.SemanticModel.GetDeclaredSymbol(syntax) is not { } symbol) return default; + if (context.SemanticModel.GetDeclaredSymbol(syntax) is not { } symbol) return null; var ids = new HashSet(); - if (symbol.HasAnyAttribute(out var attribute, Classes.ViewAttribute, Classes.ViewModelAttribute)) + if (symbol.TryGetAnyAttributeInSelf(out var attribute, Classes.ViewAttribute, Classes.ViewModelAttribute)) { var attributeName = attribute!.AttributeClass!.ToDisplayString(); @@ -40,8 +40,6 @@ private static FoundForGenerator> GetIdsForSourceGeneration( } } - return ids.Count is 0 - ? default - : new FoundForGenerator>(ids); + return ids.Count is 0 ? null : ids; } } \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.Generate.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.Generate.cs index 7daf40f..1222dcf 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.Generate.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.Generate.cs @@ -1,10 +1,10 @@ using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; +using Aspid.Generators.Helper; using System.Collections.Generic; using System.Collections.Immutable; -using static Aspid.MVVM.Generators.Descriptions.General; +using static Aspid.MVVM.Generators.Generators.Descriptions.General; -namespace Aspid.MVVM.Generators.Ids; +namespace Aspid.MVVM.Generators.Generators.Ids; public partial class IdGenerator { diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.cs index 4a768c4..74c2798 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.cs @@ -3,16 +3,17 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace Aspid.MVVM.Generators.Ids; +namespace Aspid.MVVM.Generators.Generators.Ids; [Generator(LanguageNames.CSharp)] public partial class IdGenerator : IIncrementalGenerator { public void Initialize(IncrementalGeneratorInitializationContext context) { + // ReSharper disable once NullableWarningSuppressionIsUsed var provider = context.SyntaxProvider.CreateSyntaxProvider(SyntacticPredicate, GetIdsForSourceGeneration) - .Where(foundFor => foundFor.IsNeed) - .Select((foundFor, _) => foundFor.Container); + .Where(foundFor => foundFor is not null) + .Select((foundFor, _) => foundFor!); context.RegisterSourceOutput(provider.Collect(), GenerateCode); } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembersBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembersBody.cs index 3e7af2e..ea8a752 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembersBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembersBody.cs @@ -1,24 +1,24 @@ using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; -using Aspid.MVVM.Generators.ViewModels.Data; -using static Aspid.MVVM.Generators.Descriptions.Classes; -using static Aspid.MVVM.Generators.Descriptions.General; -using BindMode = Aspid.MVVM.Generators.ViewModels.Data.BindMode; +using Aspid.Generators.Helper; +using Aspid.MVVM.Generators.Generators.ViewModels.Data; +using static Aspid.MVVM.Generators.Generators.Descriptions.General; +using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; +using BindMode = Aspid.MVVM.Generators.Generators.ViewModels.Data.BindMode; -namespace Aspid.MVVM.Generators.ViewModels.Body; +namespace Aspid.MVVM.Generators.Generators.ViewModels.Body; public static class BindableMembersBody { public static void Generate( string @namespace, in ViewModelData data, - in DeclarationText declaration, + DeclarationText declaration, in SourceProductionContext context) { var code = new CodeWriter(); - code.AppendClassBegin(@namespace, declaration) + code.BeginClass(@namespace, declaration) .AppendProperties(data) - .AppendClassEnd(@namespace); + .EndClass(@namespace); context.AddSource(declaration.GetFileName(@namespace, "BindableMembers"), code.GetSourceText()); } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/FindBindableMembersBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/FindBindableMembersBody.cs index 0e73bbd..7473cf9 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/FindBindableMembersBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/FindBindableMembersBody.cs @@ -1,29 +1,31 @@ using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; +using Aspid.Generators.Helper; using System.Collections.Generic; using System.Collections.Immutable; -using Aspid.MVVM.Generators.ViewModels.Data; -using Aspid.MVVM.Generators.ViewModels.Data.Members; -using static Aspid.MVVM.Generators.Descriptions.Classes; -using static Aspid.MVVM.Generators.Descriptions.Defines; -using static Aspid.MVVM.Generators.Descriptions.General; +using Aspid.MVVM.Generators.Generators.ViewModels.Data; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; +using static Aspid.Generators.Helper.Classes; +using static Aspid.Generators.Helper.Unity.UnityClasses; +using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; +using static Aspid.MVVM.Generators.Generators.Descriptions.Defines; +using static Aspid.MVVM.Generators.Generators.Descriptions.General; -namespace Aspid.MVVM.Generators.ViewModels.Body; +namespace Aspid.MVVM.Generators.Generators.ViewModels.Body; public static class FindBindableMembersBody { public static void Generate( string @namespace, in ViewModelData data, - in DeclarationText declaration, + DeclarationText declaration, in SourceProductionContext context) { string[] baseTypes = [IViewModel]; var code = new CodeWriter(); - code.AppendClassBegin(@namespace, declaration, baseTypes) + code.BeginClass(@namespace, declaration, baseTypes) .AppendBody(data) - .AppendClassEnd(@namespace); + .EndClass(@namespace); context.AddSource(declaration.GetFileName(@namespace, "FindBindableMembers"), code.GetSourceText()); } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertiesBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertiesBody.cs index 4187432..2a5006c 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertiesBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertiesBody.cs @@ -1,24 +1,24 @@ using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; -using Aspid.MVVM.Generators.ViewModels.Data; -using Aspid.MVVM.Generators.ViewModels.Data.Members; -using static Aspid.MVVM.Generators.Descriptions.General; +using Aspid.Generators.Helper; +using Aspid.MVVM.Generators.Generators.ViewModels.Data; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; +using static Aspid.MVVM.Generators.Generators.Descriptions.General; -namespace Aspid.MVVM.Generators.ViewModels.Body; +namespace Aspid.MVVM.Generators.Generators.ViewModels.Body; public static class PropertiesBody { public static void Generate( string @namespace, in ViewModelData data, - in DeclarationText declaration, + DeclarationText declaration, in SourceProductionContext context) { var code = new CodeWriter(); - code.AppendClassBegin(@namespace, declaration) + code.BeginClass(@namespace, declaration) .AppendBody(data) - .AppendClassEnd(@namespace); + .EndClass(@namespace); context.AddSource(declaration.GetFileName(@namespace, "Properties"), code.GetSourceText()); } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/RelayCommandBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/RelayCommandBody.cs index a5b134d..7de88ec 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/RelayCommandBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/RelayCommandBody.cs @@ -1,26 +1,26 @@ using System.Linq; using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; -using Aspid.MVVM.Generators.ViewModels.Data; -using Aspid.MVVM.Generators.ViewModels.Data.Members; +using Aspid.Generators.Helper; +using Aspid.MVVM.Generators.Generators.ViewModels.Data; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; -namespace Aspid.MVVM.Generators.ViewModels.Body; +namespace Aspid.MVVM.Generators.Generators.ViewModels.Body; public static class RelayCommandBody { public static void Generate( string @namespace, in ViewModelData data, - in DeclarationText declaration, + DeclarationText declaration, in SourceProductionContext context) { if (!data.Members.OfType().Any()) return; var code = new CodeWriter(); - code.AppendClassBegin(@namespace, declaration) + code.BeginClass(@namespace, declaration) .AppendBody(data) - .AppendClassEnd(@namespace); + .EndClass(@namespace); context.AddSource(declaration.GetFileName(@namespace, "Commands"), code.GetSourceText()); } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/BindMode.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/BindMode.cs index 344db09..af1f4e0 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/BindMode.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/BindMode.cs @@ -1,4 +1,4 @@ -namespace Aspid.MVVM.Generators.ViewModels.Data; +namespace Aspid.MVVM.Generators.Generators.ViewModels.Data; public enum BindMode { diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Inheritor.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Inheritor.cs index 9ea8296..122bb98 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Inheritor.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Inheritor.cs @@ -1,4 +1,4 @@ -namespace Aspid.MVVM.Generators.ViewModels.Data; +namespace Aspid.MVVM.Generators.Generators.ViewModels.Data; public enum Inheritor { diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableBindAlso.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableBindAlso.cs index 966ce75..cbba561 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableBindAlso.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableBindAlso.cs @@ -1,8 +1,8 @@ using System; using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; +using Aspid.Generators.Helper; -namespace Aspid.MVVM.Generators.ViewModels.Data.Members; +namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; public sealed class BindableBindAlso : BindableMember, IEquatable { diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableCommand.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableCommand.cs index 89e99fc..02aaf64 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableCommand.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableCommand.cs @@ -1,11 +1,13 @@ using System.Linq; using System.Text; using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; -using static Aspid.MVVM.Generators.Descriptions.Classes; -using static Aspid.MVVM.Generators.Descriptions.General; +using Aspid.Generators.Helper; +using Aspid.MVVM.Generators.Helpers; +using static Aspid.Generators.Helper.Classes; +using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; +using static Aspid.MVVM.Generators.Generators.Descriptions.General; -namespace Aspid.MVVM.Generators.ViewModels.Data.Members; +namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; public sealed class BindableCommand : BindableMember { diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableField.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableField.cs index d751fc6..e0e5ff5 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableField.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableField.cs @@ -1,11 +1,13 @@ using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; +using Aspid.Generators.Helper; using System.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp; -using static Aspid.MVVM.Generators.Descriptions.Classes; -using static Aspid.MVVM.Generators.Descriptions.General; +using Aspid.MVVM.Generators.Helpers; +using static Aspid.Generators.Helper.Classes; +using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; +using static Aspid.MVVM.Generators.Generators.Descriptions.General; -namespace Aspid.MVVM.Generators.ViewModels.Data.Members; +namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; public class BindableField : BindableMember { @@ -77,7 +79,7 @@ public string ToSetMethodString() {{GeneratedCodeViewModelAttribute}} private void {{setMethod}}({{Type}} value) { - if ({{EqualityComparer}}<{{Type}}>.Default.Equals({{SourceName}}, value)) return; + if ({{EqualityComparer_1}}<{{Type}}>.Default.Equals({{SourceName}}, value)) return; {{onMethodChanging}}({{SourceName}}, value); {{keyWordThis}}{{SourceName}} = value; @@ -104,7 +106,7 @@ public string ToSetMethodString() private static Accessors GetAccessors(IFieldSymbol field) { var accessors = new Accessors(SyntaxKind.PrivateKeyword, SyntaxKind.PrivateKeyword); - if (!field.HasAnyAttribute(out var accessAttribute, AccessAttribute)) return accessors; + if (!field.TryGetAnyAttributeInSelf(out var accessAttribute, AccessAttribute)) return accessors; if (accessAttribute!.ConstructorArguments.Length == 1) { diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableMember.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableMember.cs index d680117..df45ba8 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableMember.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableMember.cs @@ -1,10 +1,11 @@ using Microsoft.CodeAnalysis; -using Aspid.MVVM.Generators.Ids.Data; -using static Aspid.Generator.Helpers.SymbolExtensions; -using static Aspid.MVVM.Generators.Descriptions.General; -using static Aspid.MVVM.Generators.Descriptions.Classes; +using Aspid.MVVM.Generators.Generators.Ids.Data; +using static Aspid.Generators.Helper.Classes; +using static Aspid.MVVM.Generators.Helpers.SymbolExtensions; +using static Aspid.MVVM.Generators.Generators.Descriptions.General; +using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; -namespace Aspid.MVVM.Generators.ViewModels.Data.Members; +namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; public abstract class BindableMember : BindableMember where T : ISymbol diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/Collections/IdLengthMemberGroup.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/Collections/IdLengthMemberGroup.cs index d686f96..ea8dfe6 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/Collections/IdLengthMemberGroup.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/Collections/IdLengthMemberGroup.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Collections.Immutable; -namespace Aspid.MVVM.Generators.ViewModels.Data.Members.Collections; +namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Members.Collections; public readonly struct IdLengthMemberGroup(int length, ImmutableArray members) { diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/CustomViewModelInterface.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/CustomViewModelInterface.cs index 5edb3ef..8c2ac44 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/CustomViewModelInterface.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/CustomViewModelInterface.cs @@ -1,6 +1,6 @@ using Microsoft.CodeAnalysis; -namespace Aspid.MVVM.Generators.ViewModels.Data.Members; +namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; public readonly struct CustomViewModelInterface(string id, IPropertySymbol property, ITypeSymbol @interface) { diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/ViewModelData.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/ViewModelData.cs index d09ff45..7c9ba39 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/ViewModelData.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/ViewModelData.cs @@ -2,10 +2,10 @@ using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Aspid.MVVM.Generators.ViewModels.Data.Members; -using Aspid.MVVM.Generators.ViewModels.Data.Members.Collections; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members.Collections; -namespace Aspid.MVVM.Generators.ViewModels.Data; +namespace Aspid.MVVM.Generators.Generators.ViewModels.Data; public readonly struct ViewModelData( Inheritor inheritor, diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/SymbolExtensions.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/SymbolExtensions.cs index 8ed9654..15d3f17 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/SymbolExtensions.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/SymbolExtensions.cs @@ -1,15 +1,15 @@ using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; -using Aspid.MVVM.Generators.Descriptions; -using Aspid.MVVM.Generators.ViewModels.Data; +using Aspid.Generators.Helper; +using Aspid.MVVM.Generators.Generators.ViewModels.Data; +using Classes = Aspid.MVVM.Generators.Generators.Descriptions.Classes; -namespace Aspid.MVVM.Generators.ViewModels.Extensions; +namespace Aspid.MVVM.Generators.Generators.ViewModels.Extensions; public static class SymbolExtensions { public static BindMode GetBindMode(this ISymbol member) { - if (member.HasAnyAttribute(out var attribute, Classes.BindAttribute, Classes.OneWayBindAttribute, + if (member.TryGetAnyAttributeInSelf(out var attribute, Classes.BindAttribute, Classes.OneWayBindAttribute, Classes.TwoWayBindAttribute, Classes.OneTimeBindAttribute, Classes.OneWayToSourceBindAttribute)) { var attributeName = attribute!.AttributeClass!.ToDisplayString(); diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableBindAlsoFactory.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableBindAlsoFactory.cs index 19ae3c9..92cae1e 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableBindAlsoFactory.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableBindAlsoFactory.cs @@ -1,11 +1,11 @@ using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; +using Aspid.Generators.Helper; using System.Collections.Generic; using System.Collections.Immutable; -using Aspid.MVVM.Generators.ViewModels.Data.Members; -using static Aspid.MVVM.Generators.Descriptions.Classes; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; +using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; -namespace Aspid.MVVM.Generators.ViewModels.Factories; +namespace Aspid.MVVM.Generators.Generators.ViewModels.Factories; public static class BindableBindAlsoFactory { @@ -16,8 +16,8 @@ public static IReadOnlyCollection Create(ImmutableArray Create( foreach (var method in methods) { - if (!method.HasAnyAttribute(out var attribute, Classes.RelayCommandAttribute)) continue; + if (!method.TryGetAnyAttributeInSelf(out var attribute, Classes.RelayCommandAttribute)) continue; var canExecuteArgument = attribute!.NamedArguments .Where(pair => pair.Key == "CanExecute") diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableFieldFactory.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableFieldFactory.cs index df4b51a..9dd2289 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableFieldFactory.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableFieldFactory.cs @@ -1,14 +1,14 @@ using System.Linq; using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; +using Aspid.Generators.Helper; using System.Collections.Generic; using System.Collections.Immutable; -using Aspid.MVVM.Generators.ViewModels.Extensions; -using Aspid.MVVM.Generators.ViewModels.Data.Members; -using static Aspid.MVVM.Generators.Descriptions.Classes; -using BindMode = Aspid.MVVM.Generators.ViewModels.Data.BindMode; +using Aspid.MVVM.Generators.Generators.ViewModels.Extensions; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; +using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; +using BindMode = Aspid.MVVM.Generators.Generators.ViewModels.Data.BindMode; -namespace Aspid.MVVM.Generators.ViewModels.Factories; +namespace Aspid.MVVM.Generators.Generators.ViewModels.Factories; public static class BindableFieldFactory { @@ -48,7 +48,7 @@ private static ImmutableArray GetBindableBindAlso(IFieldSymbol foreach (var attribute in field.GetAttributes()) { if (attribute.AttributeClass != null && - attribute.AttributeClass.ToDisplayStringGlobal() == BindAlsoAttribute.Global) + attribute.AttributeClass.ToDisplayStringGlobal() == BindAlsoAttribute) { var value = attribute.ConstructorArguments[0].Value; if (value is null) continue; diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableMembersFactory.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableMembersFactory.cs index 4c94839..b3cc928 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableMembersFactory.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableMembersFactory.cs @@ -1,11 +1,11 @@ using System.Linq; using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; using System.Collections.Generic; using System.Collections.Immutable; -using Aspid.MVVM.Generators.ViewModels.Data.Members; +using Aspid.MVVM.Generators.Helpers; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; -namespace Aspid.MVVM.Generators.ViewModels.Factories; +namespace Aspid.MVVM.Generators.Generators.ViewModels.Factories; public static class BindableMembersFactory { diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/CustomViewModelInterfacesFactory.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/CustomViewModelInterfacesFactory.cs index 8218543..b21e919 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/CustomViewModelInterfacesFactory.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/CustomViewModelInterfacesFactory.cs @@ -1,12 +1,12 @@ using System.Linq; using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; +using Aspid.Generators.Helper; using System.Collections.Generic; -using Aspid.MVVM.Generators.Ids.Extensions; -using Aspid.MVVM.Generators.ViewModels.Data.Members; -using static Aspid.MVVM.Generators.Descriptions.Classes; +using Aspid.MVVM.Generators.Generators.Ids.Extensions; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; +using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; -namespace Aspid.MVVM.Generators.ViewModels.Factories; +namespace Aspid.MVVM.Generators.Generators.ViewModels.Factories; public static class CustomViewModelInterfacesFactory { @@ -22,7 +22,7 @@ public static Dictionary Create(ITypeSymbol sy void AddMembers(ITypeSymbol @interface) { - if (!@interface.HasInterfaceInSelfOrBases(IViewModel)) return; + if (!@interface.HasAnyInterfaceInSelfAndBases(IViewModel)) return; foreach (var property in @interface.GetMembers() .OfType() @@ -34,7 +34,7 @@ void AddMembers(ITypeSymbol @interface) || type.Contains(IReadOnlyValueBindableMember); })) { - if (property.HasAnyAttribute(IgnoreAttribute)) continue; + if (property.HasAnyAttributeInSelf(IgnoreAttribute)) continue; var id = property.GetId(); dictionary[id] = new CustomViewModelInterface(id, property, @interface); diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/ViewModelGenerator.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/ViewModelGenerator.cs index 62e26d3..f2a7823 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/ViewModelGenerator.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/ViewModelGenerator.cs @@ -1,26 +1,28 @@ +using System; using System.Threading; using System.Diagnostics; using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; +using Aspid.Generators.Helper; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Aspid.MVVM.Generators.ViewModels.Body; -using Aspid.MVVM.Generators.ViewModels.Data; -using Aspid.MVVM.Generators.ViewModels.Factories; -using Aspid.MVVM.Generators.ViewModels.Data.Members.Collections; +using Aspid.MVVM.Generators.Generators.ViewModels.Body; +using Aspid.MVVM.Generators.Generators.ViewModels.Data; +using Aspid.MVVM.Generators.Generators.ViewModels.Factories; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members.Collections; using Unsafe = System.Runtime.CompilerServices.Unsafe; -using static Aspid.MVVM.Generators.Descriptions.Classes; +using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; -namespace Aspid.MVVM.Generators.ViewModels; +namespace Aspid.MVVM.Generators.Generators.ViewModels; [Generator(LanguageNames.CSharp)] public sealed class ViewModelGenerator : IIncrementalGenerator { public void Initialize(IncrementalGeneratorInitializationContext context) { + Console.WriteLine(ViewModelAttribute.FullName); var provider = context.SyntaxProvider.ForAttributeWithMetadataName(ViewModelAttribute.FullName, SyntacticPredicate, FindViewModels) - .Where(static foundForSourceGenerator => foundForSourceGenerator.IsNeed) - .Select(static (foundForSourceGenerator, _) => foundForSourceGenerator.Container); + .Where(static foundForSourceGenerator => foundForSourceGenerator.HasValue) + .Select(static (foundForSourceGenerator, _) => foundForSourceGenerator!.Value); context.RegisterSourceOutput( source: provider, @@ -35,15 +37,15 @@ private static bool SyntacticPredicate(SyntaxNode node, CancellationToken cancel && !candidate.Modifiers.Any(SyntaxKind.StaticKeyword); } - private static FoundForGenerator FindViewModels(GeneratorAttributeSyntaxContext context, + private static ViewModelData? FindViewModels(GeneratorAttributeSyntaxContext context, CancellationToken cancellationToken) { - if (context.TargetSymbol is not INamedTypeSymbol symbol) return default; + if (context.TargetSymbol is not INamedTypeSymbol symbol) return null; Debug.Assert(context.TargetNode is ClassDeclarationSyntax); var candidate = Unsafe.As(context.TargetNode); - var inheritor = symbol.HasAttributeInBases(ViewModelAttribute) + var inheritor = symbol.HasAnyAttributeInBases(ViewModelAttribute) ? Inheritor.Inheritor : Inheritor.None; @@ -51,15 +53,14 @@ private static FoundForGenerator FindViewModels(GeneratorAttribut var memberByGroups = IdLengthMemberGroup.Create(bindableMembers); var customViewModelInterfaces = CustomViewModelInterfacesFactory.Create(symbol); - var data = new ViewModelData(inheritor, symbol, candidate, bindableMembers, memberByGroups, customViewModelInterfaces); - return new FoundForGenerator(data); + return new ViewModelData(inheritor, symbol, candidate, bindableMembers, memberByGroups, customViewModelInterfaces); } private static void GenerateCode(SourceProductionContext context, ViewModelData data) { var declaration = data.Declaration; var @namespace = declaration.GetNamespaceName(); - var declarationText = declaration.GetDeclarationText(); + var declarationText = new DeclarationText(declaration); PropertiesBody.Generate(@namespace, data, declarationText, context); RelayCommandBody.Generate(@namespace, data, declarationText, context); diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/BinderCachedBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/BinderCachedBody.cs index 1533b13..c67d1cf 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/BinderCachedBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/BinderCachedBody.cs @@ -1,28 +1,28 @@ using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; -using Aspid.MVVM.Generators.Descriptions; -using Aspid.MVVM.Generators.Views.Data; -using Aspid.MVVM.Generators.Views.Data.Members; +using Aspid.Generators.Helper; +using Aspid.MVVM.Generators.Generators.Views.Data; +using Aspid.MVVM.Generators.Generators.Descriptions; +using Aspid.MVVM.Generators.Generators.Views.Data.Members; +using static Aspid.Generators.Helper.Classes; -namespace Aspid.MVVM.Generators.Views.Body; +namespace Aspid.MVVM.Generators.Generators.Views.Body; public static class BinderCachedBody { private const string GeneratedAttribute = General.GeneratedCodeViewAttribute; - private static readonly string EditorBrowsableAttribute = $"[{Classes.EditorBrowsableAttribute.Global}({Classes.EditorBrowsableState.Global}.Never)]"; public static void Generate( string @namespace, in ViewDataSpan data, - in DeclarationText declaration, + DeclarationText declaration, in SourceProductionContext context) { if (data.MembersByType.PropertyBinders.Length + data.MembersByType.AsBinders.Length == 0) return; var code = new CodeWriter(); - code.AppendClassBegin(@namespace, declaration) + code.BeginClass(@namespace, declaration) .AppendCachedBinders(data) - .AppendClassEnd(@namespace); + .EndClass(@namespace); context.AddSource(declaration.GetFileName(@namespace, "CachedBinders"), code.GetSourceText()); } @@ -43,7 +43,7 @@ private static CodeWriter AppendCachedBinders(this CodeWriter code, in ViewDataS private static CodeWriter AppendCachedBinderMember(this CodeWriter code, in CachedBinderMember cashedBinderMember) { - code.AppendLine(EditorBrowsableAttribute) + code.AppendLine($"[{EditorBrowsableAttribute}({EditorBrowsableState}.Never)]") .AppendLine(GeneratedAttribute) .AppendLine($"private {cashedBinderMember.Type?.ToDisplayStringGlobal()} {cashedBinderMember.CachedName};") .AppendLine(); @@ -53,7 +53,7 @@ private static CodeWriter AppendCachedBinderMember(this CodeWriter code, in Cach private static CodeWriter AppendAsBinderMember(this CodeWriter code, AsBinderMember asBinderMember) { - code.AppendLine(EditorBrowsableAttribute) + code.AppendLine($"[{EditorBrowsableAttribute}({EditorBrowsableState}.Never)]") .AppendLine(GeneratedAttribute) .AppendLine(asBinderMember.Type is IArrayTypeSymbol ? $"private {asBinderMember.AsBinderType}[] {asBinderMember.CachedName};" diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/Extensions/BindSafelyExtensions.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/Extensions/BindSafelyExtensions.cs index 2ea5ade..c91c2a1 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/Extensions/BindSafelyExtensions.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/Extensions/BindSafelyExtensions.cs @@ -1,9 +1,9 @@ using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; -using Aspid.MVVM.Generators.ViewModels.Data.Members; -using Aspid.MVVM.Generators.Views.Data.Members; +using Aspid.Generators.Helper; +using Aspid.MVVM.Generators.Generators.Views.Data.Members; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; -namespace Aspid.MVVM.Generators.Views.Body.Extensions; +namespace Aspid.MVVM.Generators.Generators.Views.Body.Extensions; public static class BindSafelyExtensions { diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/GenericInitializeView.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/GenericInitializeView.cs index b86e7dc..1c22db9 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/GenericInitializeView.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/GenericInitializeView.cs @@ -1,33 +1,34 @@ using System.Linq; using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; +using Aspid.Generators.Helper; using System.Collections.Generic; -using Aspid.MVVM.Generators.Views.Data; -using Aspid.MVVM.Generators.Descriptions; -using Aspid.MVVM.Generators.ViewModels.Factories; -using Aspid.MVVM.Generators.Views.Body.Extensions; -using Aspid.MVVM.Generators.ViewModels.Data.Members; -using static Aspid.MVVM.Generators.Descriptions.Classes; -using static Aspid.MVVM.Generators.Descriptions.Defines; -using static Aspid.MVVM.Generators.Descriptions.General; +using Aspid.MVVM.Generators.Generators.Views.Data; +using Aspid.MVVM.Generators.Generators.Descriptions; +using Aspid.MVVM.Generators.Generators.ViewModels.Factories; +using Aspid.MVVM.Generators.Generators.Views.Body.Extensions; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; +using static Aspid.Generators.Helper.Classes; +using static Aspid.Generators.Helper.Unity.UnityClasses; +using static Aspid.MVVM.Generators.Generators.Descriptions.Defines; +using static Aspid.MVVM.Generators.Generators.Descriptions.General; -namespace Aspid.MVVM.Generators.Views.Body; +namespace Aspid.MVVM.Generators.Generators.Views.Body; public static class GenericInitializeView { public static void Generate( string @namespace, in ViewDataSpan data, - in DeclarationText declaration, + DeclarationText declaration, in SourceProductionContext context) { foreach (var genericView in data.GenericViews) { var code = new CodeWriter(); - code.AppendClassBegin([Namespaces.Aspid_MVVM], @namespace, declaration, null) + code.BeginClass([Namespaces.Aspid_MVVM], @namespace, declaration, null) .AppendGenericViews(data, genericView) - .AppendClassEnd(@namespace); + .EndClass(@namespace); context.AddSource(declaration.GetFileName(@namespace, genericView.Type.ToDisplayString()), code.GetSourceText()); } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/InitializeBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/InitializeBody.cs index 00440c9..f06ac5c 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/InitializeBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/InitializeBody.cs @@ -1,35 +1,33 @@ -using System; using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; -using Aspid.MVVM.Generators.Views.Data; -using Aspid.MVVM.Generators.Descriptions; -using Aspid.MVVM.Generators.Views.Data.Members; -using Aspid.MVVM.Generators.Views.Body.Extensions; +using Aspid.Generators.Helper; +using Aspid.MVVM.Generators.Generators.Views.Data; +using Aspid.MVVM.Generators.Generators.Descriptions; +using Aspid.MVVM.Generators.Generators.Views.Data.Members; +using Aspid.MVVM.Generators.Generators.Views.Body.Extensions; +using static Aspid.Generators.Helper.Classes; +using static Aspid.Generators.Helper.Unity.UnityClasses; +using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; -namespace Aspid.MVVM.Generators.Views.Body; +namespace Aspid.MVVM.Generators.Generators.Views.Body; // ReSharper disable InconsistentNaming // ReSharper disable once InconsistentNaming public static class InitializeBody { private const string GeneratedAttribute = General.GeneratedCodeViewAttribute; - - private static readonly string IViewModel = Classes.IViewModel.Global; - private static readonly string ProfilerMarker = Classes.ProfilerMarker.Global; - private static readonly string EditorBrowsableAttribute = $"[{Classes.EditorBrowsableAttribute.Global}({Classes.EditorBrowsableState.Global}.Never)]"; public static void Generate( string @namespace, in ViewDataSpan data, - in DeclarationText declaration, + DeclarationText declaration, in SourceProductionContext context) { var code = new CodeWriter(); var baseTypes = GetBaseTypes(data); - code.AppendClassBegin([Namespaces.Aspid_MVVM], @namespace, declaration, baseTypes) + code.BeginClass([Namespaces.Aspid_MVVM], @namespace, declaration, baseTypes) .AppendIView(data) - .AppendClassEnd(@namespace); + .EndClass(@namespace); context.AddSource(declaration.GetFileName(@namespace, "Initialize"), code.GetSourceText()); } @@ -40,7 +38,7 @@ private static CodeWriter AppendIView(this CodeWriter code, in ViewDataSpan data { Inheritor.None => code.AppendNone(data), Inheritor.InheritorViewAttribute => code.AppendHasInterfaceOrInheritor(data), - _ => throw new ArgumentOutOfRangeException() + _ => code }; if (!data.IsInstantiateBinders) return code; @@ -62,7 +60,7 @@ private static CodeWriter AppendNone(this CodeWriter code, in ViewDataSpan data) $""" [global::System.NonSerialized] {GeneratedAttribute} - {EditorBrowsableAttribute} + [{EditorBrowsableAttribute}({EditorBrowsableState}.Never)] private bool __isInitializing; """) @@ -70,7 +68,7 @@ private static CodeWriter AppendNone(this CodeWriter code, in ViewDataSpan data) $""" [global::System.NonSerialized] {GeneratedAttribute} - {EditorBrowsableAttribute} + [{EditorBrowsableAttribute}({EditorBrowsableState}.Never)] private bool __isBindersCached; """) @@ -82,8 +80,8 @@ private static CodeWriter AppendNone(this CodeWriter code, in ViewDataSpan data) {{GeneratedAttribute}} public void Initialize({{IViewModel}} viewModel) { - if (viewModel is null) throw new {{Classes.ArgumentNullException.Global}}(nameof(viewModel)); - if (ViewModel is not null) throw new {{Classes.InvalidOperationException.Global}}("View is already initialized."); + if (viewModel is null) throw new {{ArgumentNullException}}(nameof(viewModel)); + if (ViewModel is not null) throw new {{InvalidOperationException}}("View is already initialized."); ViewModel = viewModel; InitializeInternal(viewModel); @@ -126,7 +124,7 @@ private static CodeWriter AppendHasInterfaceOrInheritor(this CodeWriter code, in $""" [global::System.NonSerialized] {GeneratedAttribute} - {EditorBrowsableAttribute} + [{EditorBrowsableAttribute}({EditorBrowsableState}.Never)] private bool __isInitializing; """) @@ -134,7 +132,7 @@ private static CodeWriter AppendHasInterfaceOrInheritor(this CodeWriter code, in $""" [global::System.NonSerialized] {GeneratedAttribute} - {EditorBrowsableAttribute} + [{EditorBrowsableAttribute}({EditorBrowsableState}.Never)] private bool __isBindersCached; """); @@ -156,11 +154,11 @@ private static CodeWriter AppendProfilerMarkers(this CodeWriter code, string cla return code.AppendMultiline( $""" #if !{Defines.ASPID_MVVM_UNITY_PROFILER_DISABLED} - {EditorBrowsableAttribute} + [{EditorBrowsableAttribute}({EditorBrowsableState}.Never)] {GeneratedAttribute} private static readonly {ProfilerMarker} __initializeMarker = new("{className}.Initialize"); - {EditorBrowsableAttribute} + [{EditorBrowsableAttribute}({EditorBrowsableState}.Never)] {GeneratedAttribute} private static readonly {ProfilerMarker} __deinitializeMarker = new("{className}.Deinitialize"); #endif @@ -341,12 +339,12 @@ private static CodeWriter AppendCreateBinders(this CodeWriter code, in ViewDataS code.AppendLineIf(isAppend) .AppendMultiline( - $$""" - var {{localName}} = {{name}}; - {{binderName}} = new {{binderType}}[{{localName}}.Length]; + $""" + var {localName} = {name}; + {binderName} = new {binderType}[{localName}.Length]; - for (var i = 0; i < {{localName}}.Length; i++) - {{binderName}}[i] = new {{member.AsBinderType}}({{localName}}[i]{{arguments}}); + for (var i = 0; i < {localName}.Length; i++) + {binderName}[i] = new {member.AsBinderType}({localName}[i]{arguments}); """) .AppendLineIf(i + 1 < membersCount); @@ -390,5 +388,5 @@ private static CodeWriter AppendDeinitializeInternalEvents(this CodeWriter code) } private static string[]? GetBaseTypes(in ViewDataSpan data) => - data.Inheritor is Inheritor.None ? [Classes.IView.ToString()] : null; + data.Inheritor is Inheritor.None ? [IView.ToString()] : null; } \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/GenericViewData.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/GenericViewData.cs index 66625af..386bc02 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/GenericViewData.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/GenericViewData.cs @@ -1,7 +1,7 @@ using System; using Microsoft.CodeAnalysis; -namespace Aspid.MVVM.Generators.Views.Data; +namespace Aspid.MVVM.Generators.Generators.Views.Data; public readonly struct GenericViewData(bool isSelf, ITypeSymbol type) : IEquatable { diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/Inheritor.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/Inheritor.cs index f34b095..94219c6 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/Inheritor.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/Inheritor.cs @@ -1,4 +1,4 @@ -namespace Aspid.MVVM.Generators.Views.Data; +namespace Aspid.MVVM.Generators.Generators.Views.Data; public enum Inheritor { diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/Members/AsBinderMember.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/Members/AsBinderMember.cs index 4960011..9a3d2c4 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/Members/AsBinderMember.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/Members/AsBinderMember.cs @@ -1,7 +1,7 @@ using Microsoft.CodeAnalysis; using System.Collections.Generic; -namespace Aspid.MVVM.Generators.Views.Data.Members; +namespace Aspid.MVVM.Generators.Generators.Views.Data.Members; public class AsBinderMember(ISymbol member, string asBinderType, IReadOnlyList? arguments) : CachedBinderMember(member) { diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/Members/BinderMember.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/Members/BinderMember.cs index ff9ff22..a1d676c 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/Members/BinderMember.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/Members/BinderMember.cs @@ -1,8 +1,8 @@ using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; -using Aspid.MVVM.Generators.Ids.Data; +using Aspid.Generators.Helper; +using Aspid.MVVM.Generators.Generators.Ids.Data; -namespace Aspid.MVVM.Generators.Views.Data.Members; +namespace Aspid.MVVM.Generators.Generators.Views.Data.Members; public class BinderMember { diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/Members/CachedBinderMember.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/Members/CachedBinderMember.cs index 1bfa836..9c65a86 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/Members/CachedBinderMember.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/Members/CachedBinderMember.cs @@ -1,7 +1,7 @@ using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; +using Aspid.MVVM.Generators.Helpers; -namespace Aspid.MVVM.Generators.Views.Data.Members; +namespace Aspid.MVVM.Generators.Generators.Views.Data.Members; public class CachedBinderMember(ISymbol member) : BinderMember(member) { diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/Members/Collections/BinderMembersCollectionSpanByType.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/Members/Collections/BinderMembersCollectionSpanByType.cs index e6cfbbe..26b65fb 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/Members/Collections/BinderMembersCollectionSpanByType.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/Members/Collections/BinderMembersCollectionSpanByType.cs @@ -1,9 +1,9 @@ using System; using System.Linq; -using Aspid.Generator.Helpers; using System.Collections.Immutable; +using Aspid.MVVM.Generators.Helpers; -namespace Aspid.MVVM.Generators.Views.Data.Members.Collections; +namespace Aspid.MVVM.Generators.Generators.Views.Data.Members.Collections; public readonly ref struct BinderMembersCollectionSpanByType { diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/ViewData.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/ViewData.cs index cccc8d3..fff5b81 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/ViewData.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/ViewData.cs @@ -1,9 +1,9 @@ using Microsoft.CodeAnalysis; using System.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Aspid.MVVM.Generators.Views.Data.Members; +using Aspid.MVVM.Generators.Generators.Views.Data.Members; -namespace Aspid.MVVM.Generators.Views.Data; +namespace Aspid.MVVM.Generators.Generators.Views.Data; public readonly struct ViewData( INamedTypeSymbol symbol, diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/ViewDataSpan.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/ViewDataSpan.cs index 0eb1720..b7192ca 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/ViewDataSpan.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/ViewDataSpan.cs @@ -1,10 +1,10 @@ using System; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Aspid.MVVM.Generators.Views.Data.Members; -using Aspid.MVVM.Generators.Views.Data.Members.Collections; +using Aspid.MVVM.Generators.Generators.Views.Data.Members; +using Aspid.MVVM.Generators.Generators.Views.Data.Members.Collections; -namespace Aspid.MVVM.Generators.Views.Data; +namespace Aspid.MVVM.Generators.Generators.Views.Data; public readonly ref struct ViewDataSpan(ViewData viewData) { diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Factories/BinderMembersFactory.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Factories/BinderMembersFactory.cs index 7e4bdca..299f76e 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Factories/BinderMembersFactory.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Factories/BinderMembersFactory.cs @@ -1,14 +1,14 @@ using System.Linq; using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; +using Aspid.Generators.Helper; using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp; -using Aspid.MVVM.Generators.Descriptions; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Aspid.MVVM.Generators.Views.Data.Members; +using Aspid.MVVM.Generators.Generators.Views.Data.Members; +using Classes = Aspid.MVVM.Generators.Generators.Descriptions.Classes; -namespace Aspid.MVVM.Generators.Views.Factories; +namespace Aspid.MVVM.Generators.Generators.Views.Factories; public static class BinderMembersFactory { @@ -18,17 +18,17 @@ public static ImmutableArray Create(INamedTypeSymbol symbolClass, foreach (var member in symbolClass.GetMembers()) { - if (member.HasAnyAttribute(Classes.IgnoreAttribute)) continue; + if (member.HasAnyAttributeInSelf()) continue; var type = GetType(member); if (type is null) continue; - if (member.HasAnyAttribute(out var asBinderAttribute, Classes.AsBinderAttribute)) + if (member.TryGetAnyAttributeInSelf(out var asBinderAttribute, Classes.AsBinderAttribute)) { if (asBinderAttribute!.ConstructorArguments[0].Value is not INamedTypeSymbol argumentType) continue; if (argumentType.IsAbstract) continue; - if (!argumentType.HasInterfaceInSelfOrBases(Classes.IBinder)) continue; + if (!argumentType.HasAnyInterfaceInSelfAndBases(Classes.IBinder)) continue; var arguments = new List(); @@ -93,11 +93,11 @@ invocationExpression.Expression is IdentifierNameSyntax identifier && binderMembers.Add(new AsBinderMember(member, argumentType.ToDisplayStringGlobal(), arguments)); } - else if (type.HasAnyAttribute(Classes.ViewAttribute) || type.HasInterfaceInSelfOrBases(Classes.IView)) + else if (type.HasAnyAttributeInSelf(Classes.ViewAttribute) || type.HasAnyInterfaceInSelfAndBases(Classes.IView)) { - binderMembers.Add(new AsBinderMember(member, Classes.ViewBinder.Global, null)); + binderMembers.Add(new AsBinderMember(member, Classes.ViewBinder, null)); } - else if (type.HasInterfaceInSelfOrBases(Classes.IBinder)) + else if (type.HasAnyInterfaceInSelfAndBases(Classes.IBinder)) { switch (member) { @@ -110,7 +110,7 @@ invocationExpression.Expression is IdentifierNameSyntax identifier && var symbols = GetPropertyReturnSymbols(property, semanticModel); if (symbols is null) continue; - if (symbols.Any(symbol => symbol is IFieldSymbol or IPropertySymbol && !symbol.HasAnyAttribute(Classes.IgnoreAttribute))) continue; + if (symbols.Any(symbol => symbol is IFieldSymbol or IPropertySymbol && !symbol.HasAnyAttributeInSelf(Classes.IgnoreAttribute))) continue; binderMembers.Add(new CachedBinderMember(property)); break; diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/ViewGenerator.Find.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/ViewGenerator.Find.cs index 10c18a9..5a2d494 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/ViewGenerator.Find.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/ViewGenerator.Find.cs @@ -1,26 +1,26 @@ using System.Threading; using System.Diagnostics; using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; +using Aspid.Generators.Helper; using System.Collections.Generic; using System.Collections.Immutable; using System.Runtime.CompilerServices; -using Aspid.MVVM.Generators.Views.Data; -using Aspid.MVVM.Generators.Descriptions; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Aspid.MVVM.Generators.Views.Factories; +using Aspid.MVVM.Generators.Generators.Views.Data; +using Aspid.MVVM.Generators.Generators.Views.Factories; +using Classes = Aspid.MVVM.Generators.Generators.Descriptions.Classes; -namespace Aspid.MVVM.Generators.Views; +namespace Aspid.MVVM.Generators.Generators.Views; public partial class ViewGenerator { - private static FoundForGenerator FindView( + private static ViewData? FindView( GeneratorAttributeSyntaxContext context, CancellationToken cancellationToken) { - if (context.TargetSymbol is not INamedTypeSymbol symbol) return default; + if (context.TargetSymbol is not INamedTypeSymbol symbol) return null; - var inheritor = symbol.HasAttributeInBases(Classes.ViewAttribute) + var inheritor = symbol.HasAnyAttributeInBases(Classes.ViewAttribute) ? Inheritor.InheritorViewAttribute : Inheritor.None; @@ -29,8 +29,7 @@ private static FoundForGenerator FindView( Debug.Assert(context.TargetNode is TypeDeclarationSyntax); var candidate = Unsafe.As(context.TargetNode); - var viewData = new ViewData(symbol, inheritor, candidate, members, GetGenericViews(symbol)); - return new FoundForGenerator(viewData); + return new ViewData(symbol, inheritor, candidate, members, GetGenericViews(symbol)); } private static ImmutableArray GetGenericViews(INamedTypeSymbol symbol) diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/ViewGenerator.Generate.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/ViewGenerator.Generate.cs index 4c78aec..cd4a91d 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/ViewGenerator.Generate.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/ViewGenerator.Generate.cs @@ -1,9 +1,9 @@ using Microsoft.CodeAnalysis; -using Aspid.Generator.Helpers; -using Aspid.MVVM.Generators.Views.Body; -using Aspid.MVVM.Generators.Views.Data; +using Aspid.Generators.Helper; +using Aspid.MVVM.Generators.Generators.Views.Body; +using Aspid.MVVM.Generators.Generators.Views.Data; -namespace Aspid.MVVM.Generators.Views; +namespace Aspid.MVVM.Generators.Generators.Views; public partial class ViewGenerator { @@ -13,7 +13,7 @@ private static void GenerateCode(SourceProductionContext context, ViewData data) var declaration = dataSpan.Declaration; var @namespace = declaration.GetNamespaceName(); - var declarationText = declaration.GetDeclarationText(); + var declarationText = new DeclarationText(declaration); InitializeBody.Generate(@namespace, dataSpan, declarationText, context); BinderCachedBody.Generate(@namespace, dataSpan, declarationText, context); diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/ViewGenerator.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/ViewGenerator.cs index 54b8a13..72fe30e 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/ViewGenerator.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/ViewGenerator.cs @@ -1,10 +1,10 @@ using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using Aspid.MVVM.Generators.Descriptions; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Aspid.MVVM.Generators.Generators.Descriptions; -namespace Aspid.MVVM.Generators.Views; +namespace Aspid.MVVM.Generators.Generators.Views; [Generator(LanguageNames.CSharp)] public partial class ViewGenerator : IIncrementalGenerator @@ -12,8 +12,8 @@ public partial class ViewGenerator : IIncrementalGenerator public void Initialize(IncrementalGeneratorInitializationContext context) { var provider = context.SyntaxProvider.ForAttributeWithMetadataName(Classes.ViewAttribute.FullName, SyntacticPredicate, FindView) - .Where(foundForSourceGenerator => foundForSourceGenerator.IsNeed) - .Select((foundForSourceGenerator, _) => foundForSourceGenerator.Container); + .Where(foundForSourceGenerator => foundForSourceGenerator.HasValue) + .Select((foundForSourceGenerator, _) => foundForSourceGenerator!.Value); context.RegisterSourceOutput( source: provider, diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/AttributeText.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/AttributeText.cs deleted file mode 100644 index 183c698..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/AttributeText.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Aspid.Generator.Helpers; - -public class AttributeText(string name, NamespaceText? @namespace = null) : - TypeText(name + "Attribute", @namespace) -{ - public string AttributeName => name; - - public string AttributeFullName => (Namespace != null ? $"{Namespace}." : "") + AttributeName; - - public string AttributeGlobal => $"global::{AttributeFullName}"; - - public override string ToString() => - AttributeGlobal; - - public static implicit operator string(AttributeText type) => - type.ToString(); -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Data/CastedSpan.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/CastedSpan.cs similarity index 95% rename from Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Data/CastedSpan.cs rename to Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/CastedSpan.cs index 520dd1b..450eeb7 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Data/CastedSpan.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/CastedSpan.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.CompilerServices; -namespace Aspid.Generator.Helpers; +namespace Aspid.MVVM.Generators.Helpers; public ref struct CastedSpan(ReadOnlySpan span) { diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/CodeWriter.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/CodeWriter.cs deleted file mode 100644 index 72c76c8..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/CodeWriter.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System; -using System.IO; -using System.Text; -using System.CodeDom.Compiler; -using Microsoft.CodeAnalysis.Text; - -namespace Aspid.Generator.Helpers; - -public sealed class CodeWriter -{ - private readonly MemoryStream _sourceStream; - private readonly IndentedTextWriter _textWriter; - - public int Indent - { - get => _textWriter.Indent; - set => _textWriter.Indent = value; - } - - public CodeWriter() - { - _sourceStream = new MemoryStream(); - var sourceStreamWriter = new StreamWriter(_sourceStream, Encoding.UTF8); - _textWriter = new IndentedTextWriter(sourceStreamWriter); - } - - public CodeWriter Append(string value = "") - { - _textWriter.Write(value); - return this; - } - - public CodeWriter AppendLine(string value = "") - { - _textWriter.WriteLine(value); - return this; - } - - public CodeWriter AppendMultiline(string value) - { - var indent = Indent; - Indent = 0; - - var tab = new string('\t', indent); - value = $"{tab}{value}"; - value = value.Replace("\n", $"\n{tab}"); - AppendLine(value); - - Indent = indent; - return this; - } - - public IDisposable BeginIndentScope() => - new IndentScope(this); - - public IDisposable BeginBlockScope() => - new BlockScope(this); - - public CodeWriter BeginBlock() - { - AppendLine("{"); - IncreaseIndent(); - - return this; - } - - public CodeWriter EndBlock() - { - DecreaseIndent(); - AppendLine("}"); - - return this; - } - - public CodeWriter IncreaseIndent() - { - _textWriter.Indent++; - return this; - } - - public CodeWriter DecreaseIndent() - { - _textWriter.Indent--; - return this; - } - - public SourceText GetSourceText() - { - _textWriter.Flush(); - return SourceText.From(_sourceStream, Encoding.UTF8, canBeEmbedded: true); - } - - private readonly struct IndentScope : IDisposable - { - private readonly CodeWriter _source; - - public IndentScope(CodeWriter source) - { - _source = source; - source.IncreaseIndent(); - } - - public void Dispose() => - _source.DecreaseIndent(); - } - - private readonly struct BlockScope : IDisposable - { - private readonly CodeWriter _source; - - public BlockScope(CodeWriter source) - { - _source = source; - source.BeginBlock(); - } - - public void Dispose() => - _source.EndBlock(); - } -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/DeclarationText.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/DeclarationText.cs deleted file mode 100644 index 751ad04..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/DeclarationText.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Linq; - -namespace Aspid.Generator.Helpers; - -public readonly struct DeclarationText(string? modifiers, string typeType, string name, string? genericArguments) -{ - public string? Modifiers { get; } = modifiers; - - public string TypeType { get; } = typeType; - - public string Name { get; } = name; - - public string? GenericArguments { get; } = genericArguments; - - public string GetFileName(string? namespaceName, string? postfix) - { - postfix ??= ""; - if (postfix.Length > 0 && postfix[0] != '.') postfix = $".{postfix}"; - - namespaceName = string.IsNullOrEmpty(namespaceName) ? "" : $"{namespaceName}."; - - return namespaceName + (string.IsNullOrEmpty(GenericArguments) - ? $"{Name}{postfix}.g.cs" - : $"{Name}`{GenericArguments!.Count(arg => arg == ',') + 1}{postfix}.g.cs"); - } - - public override string ToString() - { - var modifiers = !string.IsNullOrEmpty(Modifiers) ? $"{Modifiers} " : ""; - var arguments = !string.IsNullOrEmpty(GenericArguments) ? $"<{GenericArguments}>" : ""; - - return $"{modifiers}{TypeType} {Name}{arguments}"; - } - - public static implicit operator string(DeclarationText declaration) => - declaration.ToString(); -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Declarations/CSharpSyntaxNodeExtensions.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Declarations/CSharpSyntaxNodeExtensions.cs deleted file mode 100644 index 762e70b..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Declarations/CSharpSyntaxNodeExtensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Aspid.Generator.Helpers; - -public static class CSharpSyntaxNodeExtensions -{ - public static string GetNamespaceName(this CSharpSyntaxNode node) - { - for (var parent = node.Parent; parent != null; parent = parent.Parent) - { - if (parent is BaseNamespaceDeclarationSyntax namespaceDeclaration) - return namespaceDeclaration.Name.ToString(); - } - - return ""; - } -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Declarations/MemberDeclarationSyntaxExtensions.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Declarations/MemberDeclarationSyntaxExtensions.cs deleted file mode 100644 index 0869f7e..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Declarations/MemberDeclarationSyntaxExtensions.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Aspid.Generator.Helpers; - -public static class MemberDeclarationSyntaxExtensions -{ - public static bool HasAttribute(this MemberDeclarationSyntax declaration, SemanticModel semanticModel, string name) - { - foreach (var attribute in declaration.AttributeLists.SelectMany(attributeList => attributeList.Attributes)) - { - if (semanticModel.GetSymbolInfo(attribute).Symbol is not IMethodSymbol attributeSymbol) continue; - if (attributeSymbol.ContainingType?.ToDisplayString() == name) return true; - } - - return false; - } -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Declarations/PropertyDeclarationSyntaxExtensions.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Declarations/PropertyDeclarationSyntaxExtensions.cs deleted file mode 100644 index fabb772..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Declarations/PropertyDeclarationSyntaxExtensions.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Linq; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Aspid.Generator.Helpers; - -public static class PropertyDeclarationSyntaxExtensions -{ - public static bool HasGetAccessor(this PropertyDeclarationSyntax property) => - property.HasAccessor(SyntaxKind.GetAccessorDeclaration); - - public static bool HasSetAccessor(this PropertyDeclarationSyntax property) => - property.HasAccessor(SyntaxKind.SetAccessorDeclaration); - - public static bool HasInitAccessor(this PropertyDeclarationSyntax property) => - property.HasAccessor(SyntaxKind.InitAccessorDeclaration); - - private static bool HasAccessor(this PropertyDeclarationSyntax propertyDeclaration, SyntaxKind accessorKind) - { - var accessorList = propertyDeclaration.AccessorList; - return accessorList != null && accessorList.Accessors.Any(accessor => accessor.Kind() == accessorKind); - } -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Declarations/TypeDeclarationSyntaxExtensions.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Declarations/TypeDeclarationSyntaxExtensions.cs deleted file mode 100644 index 884392c..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Declarations/TypeDeclarationSyntaxExtensions.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Text; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Aspid.Generator.Helpers; - -public static class TypeDeclarationSyntaxExtensions -{ - public static DeclarationText GetDeclarationText(this TypeDeclarationSyntax declaration) - { - var modifiers = declaration.GetModifiersAsText(); - var typeType = declaration is ClassDeclarationSyntax ? "class" : "struct"; - var typeName = declaration.Identifier.Text; - var genericArguments = declaration.GetGenericArgumentsAsText(); - - return new DeclarationText(modifiers, typeType, typeName, genericArguments); - } - - private static string GetModifiersAsText(this TypeDeclarationSyntax declaration) - { - var modifiers = new StringBuilder(); - foreach (var modifier in declaration.Modifiers) - modifiers.Append(modifier.ToString() + " "); - - modifiers.Length--; - return modifiers.ToString(); - } - - private static string GetGenericArgumentsAsText(this TypeDeclarationSyntax declaration) - { - var types = declaration.TypeParameterList; - if (types == null || types.Parameters.Count == 0) return ""; - - var genericTypes = new StringBuilder(); - foreach (var type in types.Parameters) - { - if (genericTypes.Length > 0) - genericTypes.Append(", "); - - genericTypes.Append(type.Identifier.Text); - } - - return genericTypes.ToString(); - } -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Symbols/MethodSymbolExtensions.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Symbols/MethodSymbolExtensions.cs deleted file mode 100644 index 3ed60a4..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Symbols/MethodSymbolExtensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Linq; -using Microsoft.CodeAnalysis; - -namespace Aspid.Generator.Helpers; - -public static class MethodSymbolExtensions -{ - public static bool EqualsSignature(this IMethodSymbol method1, IMethodSymbol method2) - { - if (method1.Parameters.Length != method2.Parameters.Length) return false; - - var method1Name = method1.NameFromExplicitImplementation(); - var method2Name = method2.NameFromExplicitImplementation(); - if (method1Name != method2Name) return false; - - if (!SymbolEqualityComparer.Default.Equals(method1.ReturnType, method2.ReturnType)) return false; - - var areParametersEqual = method1.Parameters - .Where((parameter, i) => SymbolEqualityComparer.Default.Equals(parameter.Type, method2.Parameters[i].Type)) - .Any(); - - return areParametersEqual; - } - - public static string NameFromExplicitImplementation(this IMethodSymbol method) => - method.Name.Substring(method.Name.LastIndexOf('.') + 1); -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Symbols/SymbolExtensions.Attribute.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Symbols/SymbolExtensions.Attribute.cs deleted file mode 100644 index 4eed3fd..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Symbols/SymbolExtensions.Attribute.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Linq; -using Microsoft.CodeAnalysis; -using System.Collections.Generic; - -namespace Aspid.Generator.Helpers; - -public static partial class SymbolExtensions -{ - public static bool HasAnyAttribute(this ISymbol symbol, params IReadOnlyCollection attributeText) => - symbol.HasAnyAttribute(attributeText.Select(attribute => attribute.FullName).ToArray()); - - public static bool HasAnyAttribute(this ISymbol symbol, params IReadOnlyCollection attributeFullName) - { - foreach (var attribute in symbol.GetAttributes()) - { - if (attribute.AttributeClass != null && attributeFullName.Any(name => name == attribute.AttributeClass.ToDisplayString())) - return true; - } - - return false; - } - - public static bool HasAnyAttribute(this ISymbol symbol, out AttributeData? foundAttribute, params IReadOnlyCollection attributeText) => - symbol.HasAnyAttribute(out foundAttribute, attributeText.Select(attribute => attribute.FullName).ToArray()); - - public static bool HasAnyAttribute(this ISymbol symbol, out AttributeData? foundAttribute, params IReadOnlyCollection attributeFullName) - { - foundAttribute = null; - - foreach (var attribute in symbol.GetAttributes()) - { - if (attribute.AttributeClass != null && attributeFullName.Any(name => name == attribute.AttributeClass.ToDisplayString())) - { - foundAttribute = attribute; - return true; - } - } - - return false; - } -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Symbols/TypeSymbolExtensions.Attribute.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Symbols/TypeSymbolExtensions.Attribute.cs deleted file mode 100644 index eb6d80f..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Symbols/TypeSymbolExtensions.Attribute.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Microsoft.CodeAnalysis; - -namespace Aspid.Generator.Helpers; - -public static partial class TypeSymbolExtensions -{ - public static bool HasAttributeInBases(this ITypeSymbol typeSymbol, AttributeText attributeText) => - HasAttributeInBases(typeSymbol, attributeText.FullName); - - public static bool HasAttributeInBases(this ITypeSymbol symbol, string fullName) - { - for (var type = symbol.BaseType; type is not null; type = type.BaseType) - { - if (type.HasAnyAttribute(fullName)) - return true; - } - - return false; - } - - public static bool HasAttributeInBases(this ITypeSymbol typeSymbol, AttributeText attributeText, out AttributeData? attribute) => - HasAttributeInBases(typeSymbol, attributeText.FullName, out attribute); - - public static bool HasAttributeInBases(this ITypeSymbol symbol, string fullName, out AttributeData? attribute) - { - for (var type = symbol.BaseType; type is not null; type = type.BaseType) - { - if (type.HasAnyAttribute(out attribute, fullName)) - return true; - } - - attribute = null; - return false; - } - - public static bool HasAttributeInSelfOrBases(this ITypeSymbol typeSymbol, AttributeText attributeText) => - HasAttributeInSelfOrBases(typeSymbol, attributeText.FullName); - - public static bool HasAttributeInSelfOrBases(this ITypeSymbol symbol, string fullName) - { - for (var type = symbol; type is not null; type = type.BaseType) - { - if (type.HasAnyAttribute(fullName)) - return true; - } - - return false; - } - - public static bool HasAttributeInSelfOrBases(this ITypeSymbol typeSymbol, AttributeText attributeText, out AttributeData? attribute) => - HasAttributeInSelfOrBases(typeSymbol, attributeText.FullName, out attribute); - - public static bool HasAttributeInSelfOrBases(this ITypeSymbol symbol, string fullName, out AttributeData? attribute) - { - for (var type = symbol; type is not null; type = type.BaseType) - { - if (type.HasAnyAttribute(out attribute, fullName)) - return true; - } - - attribute = null; - return false; - } -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Symbols/TypeSymbolExtensions.BaseType.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Symbols/TypeSymbolExtensions.BaseType.cs deleted file mode 100644 index f2a18e9..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Symbols/TypeSymbolExtensions.BaseType.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Linq; -using Microsoft.CodeAnalysis; - -namespace Aspid.Generator.Helpers; - -public static partial class TypeSymbolExtensions -{ - public static bool HasBaseType(this ITypeSymbol symbol, TypeText typeText) => - HasBaseType(symbol, typeText.FullName); - - public static bool HasBaseType(this ITypeSymbol symbol, string baseTypeName) => - HasBaseType(symbol, baseTypeName, out _); - - public static bool HasBaseType(this ITypeSymbol symbol, TypeText typeText, out ITypeSymbol? foundBaseType) => - HasBaseType(symbol, typeText.FullName, out foundBaseType); - - public static bool HasBaseType(this ITypeSymbol symbol, string baseTypeName, out ITypeSymbol? foundBaseType) - { - foundBaseType = null; - - for (var type = symbol; type != null; type = type.BaseType) - { - if (type.ToDisplayString() != baseTypeName) continue; - - foundBaseType = type; - return true; - } - - return false; - } - - public static bool HasBaseType(this ITypeSymbol symbol, params TypeText[] baseTypeNames) => - HasBaseType(symbol, baseTypeNames.Select(baseTypeName => baseTypeName.FullName).ToArray()); - - public static bool HasBaseType(this ITypeSymbol symbol, params string[] baseTypeNames) - { - for (var type = symbol; type != null; type = type.BaseType) - { - if (baseTypeNames.Any(baseTypeName => type.ToDisplayString() == baseTypeName)) - { - return true; - } - } - - return false; - } -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Symbols/TypeSymbolExtensions.Interface.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Symbols/TypeSymbolExtensions.Interface.cs deleted file mode 100644 index 5bbbf87..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Symbols/TypeSymbolExtensions.Interface.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System.Linq; -using Microsoft.CodeAnalysis; - -namespace Aspid.Generator.Helpers; - -public static partial class TypeSymbolExtensions -{ - public static bool HasInterface(this ITypeSymbol type, TypeText typeText) => - type.HasInterface(typeText.FullName); - - public static bool HasInterface(this ITypeSymbol type, string name) => - type.HasInterface(name, out _); - - public static bool HasInterface(this ITypeSymbol type, TypeText typeText, out INamedTypeSymbol? foundInterface) => - type.HasInterface(typeText.FullName, out foundInterface); - - public static bool HasInterface(this ITypeSymbol type, string name, out INamedTypeSymbol? foundInterface) - { - foundInterface = null; - - foreach (var @interface in type.Interfaces) - { - if (@interface.ToDisplayString() != name) continue; - - foundInterface = @interface; - return true; - } - - return false; - } - - public static bool HasInterfaceInBases(this ITypeSymbol type, TypeText typeText) => - type.HasInterfaceInBases(typeText.FullName); - - public static bool HasInterfaceInBases(this ITypeSymbol type, string name) => - type.HasInterfaceInBases(name, out _); - - public static bool HasInterfaceInBases(this ITypeSymbol type, TypeText typeText, out INamedTypeSymbol? foundInterface) => - type.HasInterfaceInBases(typeText.FullName, out foundInterface); - - public static bool HasInterfaceInBases(this ITypeSymbol type, string name, out INamedTypeSymbol? foundInterface) - { - foundInterface = null; - - var baseType = type.BaseType; - if (baseType is null) return false; - - foreach (var @interface in baseType.AllInterfaces) - { - if (@interface.ToDisplayString() != name) continue; - - foundInterface = @interface; - return true; - } - - return false; - } - - public static bool HasInterfaceInSelfOrBases(this ITypeSymbol type, TypeText typeText) => - type.HasInterfaceInSelfOrBases(typeText.FullName); - - public static bool HasInterfaceInSelfOrBases(this ITypeSymbol type, string name) => - type.AllInterfaces.Any(@interface => @interface.ToDisplayString() == name); - - public static bool HasInterfaceInSelfOrBases(this ITypeSymbol type, TypeText typeText, out INamedTypeSymbol? foundInterface) => - type.HasInterfaceInSelfOrBases(typeText.FullName, out foundInterface); - - public static bool HasInterfaceInSelfOrBases(this ITypeSymbol type, string name, out INamedTypeSymbol? foundInterface) - { - foundInterface = null; - - foreach (var @interface in type.AllInterfaces) - { - if (@interface.ToDisplayString() != name) continue; - - foundInterface = @interface; - return true; - } - - return false; - } -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Writer/CodeWriteClassExtension.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Writer/CodeWriteClassExtension.cs deleted file mode 100644 index 24f5e36..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Writer/CodeWriteClassExtension.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; - -namespace Aspid.Generator.Helpers; - -public static class CodeWriteClassExtension -{ - public static CodeWriter AppendClass( - this CodeWriter code, - string @namespace, - DeclarationText declaration, - Action? body, params string[]? baseTypes) - { - code.AppendClassBegin(@namespace, declaration, baseTypes); - body?.Invoke(); - code.AppendClassEnd(@namespace); - - return code; - } - - public static CodeWriter AppendClassBegin( - this CodeWriter code, - string? @namespace, - DeclarationText declaration, - params string[]? baseTypes) - { - var hasNamespace = !string.IsNullOrEmpty(@namespace); - - var baseTypesText = ""; - if (baseTypes is { Length: > 0 }) baseTypesText = $" : {string.Join(",", baseTypes!)}"; - - code.AppendLine("// ") - .AppendChildIf(hasNamespace, () => code - .AppendLine($"namespace {@namespace}") - .BeginBlock()) - .AppendLine($"{declaration}{baseTypesText}") - .BeginBlock(); - - return code; - } - - public static CodeWriter AppendClassBegin( - this CodeWriter code, - string[] imports, - string? @namespace, - DeclarationText declaration, - params string[]? baseTypes) - { - var hasNamespace = !string.IsNullOrEmpty(@namespace); - - var baseTypesText = ""; - if (baseTypes is { Length: > 0 }) baseTypesText = $" : {string.Join(", ", baseTypes!)}"; - - code.AppendLine("// ") - .AppendLine() - .AppendLoop(imports, import => - { - code.AppendLine($"using {import};"); - }) - .AppendLine() - .AppendChildIf(hasNamespace, () => code - .AppendLine($"namespace {@namespace}") - .BeginBlock()) - .AppendLine($"{declaration}{baseTypesText}") - .BeginBlock(); - - return code; - } - - public static CodeWriter AppendClassEnd(this CodeWriter code, string? @namespace) - { - code.AppendClassEnd(!string.IsNullOrEmpty(@namespace)); - return code; - } - - private static CodeWriter AppendClassEnd(this CodeWriter code, bool hasNamespace) - { - code.EndBlock() - .EndBlockIf(hasNamespace); - return code; - } -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Writer/CodeWriteLoopExtensions.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Writer/CodeWriteLoopExtensions.cs deleted file mode 100644 index f3390bb..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Writer/CodeWriteLoopExtensions.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Aspid.Generator.Helpers; - -public static class CodeWriteLoopExtensions -{ - public static CodeWriter AppendLoop(this CodeWriter code, IEnumerable enumerable, Action setValue) - { - foreach (var value in enumerable) - setValue(value); - - return code; - } - - public static CodeWriter AppendLoop(this CodeWriter code, IEnumerable enumerable, Action setValue) - { - var i = 0; - - foreach (var value in enumerable) - { - setValue(i, value); - i++; - } - - return code; - } - - public static CodeWriter AppendLoop(this CodeWriter code, IEnumerable enumerable, Action setValue) - { - foreach (var value in enumerable) - setValue(code, value); - - return code; - } - - public static CodeWriter AppendLoop(this CodeWriter code, IEnumerable enumerable, Action setValue) - { - var i = 0; - - foreach (var value in enumerable) - { - setValue(code, i, value); - i++; - } - - return code; - } - - public static CodeWriter AppendLoop(this CodeWriter code, ReadOnlySpan span, Action setValue) - { - foreach (var value in span) - setValue(value); - - return code; - } - - public static CodeWriter AppendLoop(this CodeWriter code, ReadOnlySpan span, Action setValue) - { - var i = 0; - - foreach (var value in span) - { - setValue(i, value); - i++; - } - - return code; - } - - public static CodeWriter AppendLoop(this CodeWriter code, ReadOnlySpan span, Action setValue) - { - foreach (var value in span) - setValue(code, value); - - return code; - } - - public static CodeWriter AppendLoop(this CodeWriter code, ReadOnlySpan span, Action setValue) - { - var i = 0; - - foreach (var value in span) - { - setValue(code, i, value); - i++; - } - - return code; - } -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Writer/CodeWriterIfExtensions.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Writer/CodeWriterIfExtensions.cs deleted file mode 100644 index 54346be..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Writer/CodeWriterIfExtensions.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; - -namespace Aspid.Generator.Helpers; - -public static class CodeWriterIfExtensions -{ - public static CodeWriter AppendIf(this CodeWriter code, Func condition, string value = "") => - code.AppendIf(condition.Invoke(), value); - - public static CodeWriter AppendIf(this CodeWriter code, bool flag, string value = "") => - !flag ? code : code.Append(value); - - public static CodeWriter AppendLineIf(this CodeWriter code, Func condition, string value = "") => - code.AppendLineIf(condition.Invoke(), value); - - public static CodeWriter AppendLineIf(this CodeWriter code, bool flag, string value = "") => - !flag ? code : code.AppendLine(value); - - public static CodeWriter AppendMultilineIf(this CodeWriter code, Func condition, string value = "") => - code.AppendMultilineIf(condition.Invoke(), value); - - public static CodeWriter AppendMultilineIf(this CodeWriter code, bool flag, string value = "") => - !flag ? code : code.AppendMultiline(value); - - public static CodeWriter AppendChildIf(this CodeWriter code, Func condition, Func childFunctions) => - code.AppendChildIf(condition.Invoke(), childFunctions); - - public static CodeWriter AppendChildIf(this CodeWriter code, bool flag, Func childFunctions) => - !flag ? code : childFunctions.Invoke(); - - public static CodeWriter BeginBlockIf(this CodeWriter code, Func condition) => - code.BeginBlockIf(condition.Invoke()); - - public static CodeWriter BeginBlockIf(this CodeWriter code, bool flag) => - !flag ? code : code.BeginBlock(); - - public static CodeWriter EndBlockIf(this CodeWriter code, Func condition) => - code.EndBlockIf(condition.Invoke()); - - public static CodeWriter EndBlockIf(this CodeWriter code, bool flag) => - !flag ? code : code.EndBlock(); -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/FoundForGenerator.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/FoundForGenerator.cs deleted file mode 100644 index 5a8bfa0..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/FoundForGenerator.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Aspid.Generator.Helpers; - -public readonly struct FoundForGenerator -{ - public readonly bool IsNeed; - public readonly T Container; - - public FoundForGenerator(T container) - { - IsNeed = true; - Container = container; - } -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Data/MembersByGroup.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/MembersByGroup.cs similarity index 97% rename from Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Data/MembersByGroup.cs rename to Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/MembersByGroup.cs index ffff928..f28c590 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Data/MembersByGroup.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/MembersByGroup.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Collections.Immutable; -namespace Aspid.Generator.Helpers; +namespace Aspid.MVVM.Generators.Helpers; public readonly struct MembersByGroup { diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/NamespaceText.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/NamespaceText.cs deleted file mode 100644 index b9f79a9..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/NamespaceText.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Aspid.Generator.Helpers; - -public sealed class NamespaceText(string name, NamespaceText? parent = null) -{ - public string Name { get; } = (parent != null ? $"{parent}." : "") + name; - - public override string ToString() => Name; - - public static implicit operator string(NamespaceText @namespace) => - @namespace.ToString(); -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Symbols/SymbolExtensions.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/SymbolExtensions.cs similarity index 65% rename from Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Symbols/SymbolExtensions.cs rename to Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/SymbolExtensions.cs index beabc6a..71905d0 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/Extensions/Symbols/SymbolExtensions.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/SymbolExtensions.cs @@ -1,28 +1,9 @@ using Microsoft.CodeAnalysis; -namespace Aspid.Generator.Helpers; +namespace Aspid.MVVM.Generators.Helpers; -public static partial class SymbolExtensions +public static class SymbolExtensions { - public static string ToDisplayStringGlobal(this ISymbol symbol) => - symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - - public static ITypeSymbol? GetSymbolType(this ISymbol symbol) => symbol switch - { - ITypeSymbol type => type, - IFieldSymbol field => field.Type, - ILocalSymbol local => local.Type, - IEventSymbol @event => @event.Type, - IDiscardSymbol discard => discard.Type, - - // TODO Delete - IMethodSymbol method => method.ReturnType, - - IPropertySymbol property => property.Type, - IParameterSymbol parameter => parameter.Type, - _ => null - }; - public static string GetFieldName(this ISymbol member, in string? prefix = "_") => GetFieldName(member.Name, prefix); diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/TypeText.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/TypeText.cs deleted file mode 100644 index e4f0dfe..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/TypeText.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Aspid.Generator.Helpers; - -public class TypeText(string name, NamespaceText? @namespace = null) -{ - public string Name { get; } = name; - - public NamespaceText? Namespace { get; } = @namespace; - - public string FullName => (Namespace != null ? $"{Namespace}." : "") + Name; - - public string Global => $"global::{FullName}"; - - public override string ToString() => - Global; - - public static implicit operator string(TypeText? type) => - type.ToString(); -} \ No newline at end of file From 6d3f0b2c149de919a6ccd24e97760e6bf6c18047 Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Mon, 27 Oct 2025 10:07:06 +0300 Subject: [PATCH 02/29] Fix Bug and add launchSettings.json --- .gitignore | 3 --- .../Generators/Binders/BinderGenerator.Generate.cs | 7 +++---- .../Aspid.MVVM.Generators/Properties/launchSettings.json | 9 +++++++++ 3 files changed, 12 insertions(+), 7 deletions(-) create mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Properties/launchSettings.json diff --git a/.gitignore b/.gitignore index 20879b3..0eba03e 100644 --- a/.gitignore +++ b/.gitignore @@ -26,9 +26,6 @@ # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets !packages/*/build/ -# Debug artifacts -launchSettings.json - # Prevent accidental re-checkin of NuGet.exe NuGet.exe diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/BinderGenerator.Generate.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/BinderGenerator.Generate.cs index 258009a..17d6520 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/BinderGenerator.Generate.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/BinderGenerator.Generate.cs @@ -3,7 +3,6 @@ using Aspid.MVVM.Generators.Generators.Binders.Body; using Aspid.MVVM.Generators.Generators.Binders.Data; using Aspid.MVVM.Generators.Generators.Descriptions; -using static Aspid.Generators.Helper.Unity.Defines; namespace Aspid.MVVM.Generators.Generators.Binders; @@ -32,12 +31,12 @@ private static void GenerateBinderLog( #if DEBUG code.AppendLine($"#if !{Defines.ASPID_MVVM_BINDER_LOG_DISABLED}") - .AppendClassBegin(@namespace, declarationText) + .BeginClass(@namespace, declarationText) .AppendBinderLogBody(data) - .AppendClassEnd(@namespace) + .EndClass(@namespace) .AppendLine("#endif"); #else - code.AppendLine($"#if {UNITY_EDITOR} && !{Defines.ASPID_MVVM_BINDER_LOG_DISABLED}") + code.AppendLine($"#if {Aspid.Generators.Helper.Unity.Defines.UNITY_EDITOR} && !{Defines.ASPID_MVVM_BINDER_LOG_DISABLED}") .BeginClass(@namespace, declarationText) .AppendBinderLogBody(data) .EndClass(@namespace) diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Properties/launchSettings.json b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Properties/launchSettings.json new file mode 100644 index 0000000..dc7f06f --- /dev/null +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "DebugRoslynSourceGenerator": { + "commandName": "DebugRoslynComponent", + "targetProject": "../Aspid.MVVM.Generators.Sample/Aspid.MVVM.Generators.Sample.csproj" + } + } +} \ No newline at end of file From 435d9c7d90bb2ba500eb6fee9fc4c836a1c4d841 Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Wed, 26 Nov 2025 09:46:44 +0300 Subject: [PATCH 03/29] Fix Generate Generic Enum And Struct --- .../ViewModels/Data/Members/BindableMember.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableMember.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableMember.cs index df45ba8..926a650 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableMember.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableMember.cs @@ -1,4 +1,5 @@ using Microsoft.CodeAnalysis; +using Aspid.Generators.Helper; using Aspid.MVVM.Generators.Generators.Ids.Data; using static Aspid.Generators.Helper.Classes; using static Aspid.MVVM.Generators.Helpers.SymbolExtensions; @@ -50,6 +51,15 @@ protected BindableMember( GeneratedName = generatedName; Id = new IdData(member, idPostfix); + // Handle generic type parameters with constraints + if (typeKind is TypeKind.TypeParameter) + { + if (member.GetSymbolType() is ITypeParameterSymbol typeParameter) + { + typeKind = GetEffectiveTypeKind(typeParameter); + } + } + switch (mode) { case BindMode.OneWay: @@ -131,4 +141,18 @@ public string ToBindableMemberPropertyDeclarationString() public string ToInvokeBindableMemberString() => Mode is not (BindMode.OneWayToSource or BindMode.OneTime) ? $"this.{_bindableFieldName}?.Invoke({SourceName});" : string.Empty; + + private static TypeKind GetEffectiveTypeKind(ITypeParameterSymbol typeParameter) + { + foreach (var constraint in typeParameter.ConstraintTypes) + { + if (constraint.TypeKind == TypeKind.Enum + || constraint.SpecialType == SpecialType.System_Enum) + { + return TypeKind.Enum; + } + } + + return typeParameter.HasValueTypeConstraint ? TypeKind.Struct : TypeKind.Class; + } } \ No newline at end of file From b69deecbd509aaddf309decc4a8eade105b4218b Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Wed, 26 Nov 2025 10:19:36 +0300 Subject: [PATCH 04/29] Fix Size dll --- .../Aspid.MVVM.Generators/Aspid.MVVM.Generators.csproj | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Aspid.MVVM.Generators.csproj b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Aspid.MVVM.Generators.csproj index a8001cc..69034d8 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Aspid.MVVM.Generators.csproj +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Aspid.MVVM.Generators.csproj @@ -12,15 +12,19 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + + + + + From 39b9fc042e5db3251144dbc2b631c4c9f21d64f0 Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Thu, 27 Nov 2025 09:41:00 +0300 Subject: [PATCH 05/29] Reset fix dll size --- .../Aspid.MVVM.Generators/Aspid.MVVM.Generators.csproj | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Aspid.MVVM.Generators.csproj b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Aspid.MVVM.Generators.csproj index 69034d8..a8001cc 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Aspid.MVVM.Generators.csproj +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Aspid.MVVM.Generators.csproj @@ -12,19 +12,15 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - - - - From 292d96a5aa785e09058f39b07a5c5df3ddbba745 Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Tue, 2 Dec 2025 21:46:09 +0300 Subject: [PATCH 06/29] Refactor --- .../Aspid.MVVM.Generators.Sample.csproj | 510 ++++++++++++++++++ .../Aspid.MVVM.Generators.csproj | 2 +- .../ViewModels/Body/BindableMembersBody.cs | 2 +- .../ViewModels/Body/PropertiesBody.cs | 21 +- .../Data/Infos/GeneratedBindableMembers.cs | 201 +++++++ .../Data/Members/BindableBindAlso.cs | 5 +- .../Data/Members/BindableCommand.cs | 6 +- .../ViewModels/Data/Members/BindableField.cs | 7 +- .../ViewModels/Data/Members/BindableMember.cs | 125 +---- .../Factories/BindableBindAlsoFactory.cs | 4 +- 10 files changed, 737 insertions(+), 146 deletions(-) create mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators.Sample/Aspid.MVVM.Generators.Sample.csproj b/Aspid.MVVM.Generators/Aspid.MVVM.Generators.Sample/Aspid.MVVM.Generators.Sample.csproj index 38d009e..28fce60 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators.Sample/Aspid.MVVM.Generators.Sample.csproj +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators.Sample/Aspid.MVVM.Generators.Sample.csproj @@ -14,4 +14,514 @@ + + + Source\.DS_Store + + + Source\Aspid.MVVM.asmdef + + + Source\Aspid.MVVM.asmdef.meta + + + Source\BindableMembers.meta + + + Source\BindableMembers\.DS_Store + + + Source\BindableMembers\Classes.meta + + + Source\BindableMembers\Classes\OneTimeBindableMember.cs.meta + + + Source\BindableMembers\Classes\OneWayBindableMember.cs.meta + + + Source\BindableMembers\Classes\OneWayToSourceBindableMember.cs.meta + + + Source\BindableMembers\Classes\TwoWayBindableMember.cs.meta + + + Source\BindableMembers\Enum.meta + + + Source\BindableMembers\Enum\OneTimeEnumBindableMember.cs.meta + + + Source\BindableMembers\Enum\OneWayEnumBindableMember.cs.meta + + + Source\BindableMembers\Enum\OneWayToSourceEnumBindableMember.cs.meta + + + Source\BindableMembers\Enum\TwoWayEnumBindableMember.cs.meta + + + Source\BindableMembers\IBindableMember.cs.meta + + + Source\BindableMembers\IBinderAdder.cs.meta + + + Source\BindableMembers\IBinderRemover.cs.meta + + + Source\BindableMembers\IReadOnlyBindableMember.cs.meta + + + Source\BindableMembers\IReadOnlyValueBindableMember.cs.meta + + + Source\BindableMembers\Struct.meta + + + Source\BindableMembers\Struct\OneTimeStructBindableMember.cs.meta + + + Source\BindableMembers\Struct\OneWayStructBindableMember.cs.meta + + + Source\BindableMembers\Struct\OneWayToSourceStructBindableMember.cs.meta + + + Source\BindableMembers\Struct\TwoWayStructBindableMember.cs.meta + + + Source\Binders.meta + + + Source\Binders\Binder.cs.meta + + + Source\Binders\Binder.Debug.cs.meta + + + Source\Binders\Debug.meta + + + Source\Binders\Debug\Extensions.meta + + + Source\Binders\Debug\Extensions\RebindableBinderExtensions.cs.meta + + + Source\Binders\Debug\IRebindableBinder.cs.meta + + + Source\Binders\Extensions.meta + + + Source\Binders\Extensions\BinderExtensions.Bind.cs.meta + + + Source\Binders\Extensions\BinderExtensions.Unbind.cs.meta + + + Source\Binders\Generation.meta + + + Source\Binders\Generation\BinderLogAttribute.cs.meta + + + Source\Binders\IAnyBinder.cs.meta + + + Source\Binders\IAnyReverseBinder.cs.meta + + + Source\Binders\IBinder.cs.meta + + + Source\Binders\IReverseBinder.cs.meta + + + Source\Commands.meta + + + Source\Commands\.DS_Store + + + Source\Commands\Extensions.meta + + + Source\Commands\Extensions\RelayCommandExtensions.Action.cs.meta + + + Source\Commands\Extensions\RelayCommandExtensions.cs.meta + + + Source\Commands\Extensions\RelayCommandExtensions.WithoutParameters.cs.meta + + + Source\Commands\IRelayCommand.cs.meta + + + Source\Commands\RelayCommand.cs.meta + + + Source\csc.rsp + + + Source\csc.rsp.meta + + + Source\Exceptions.meta + + + Source\Exceptions\BinderInvalidCastException.cs.meta + + + Source\Exceptions\BindSafelyNullReferenceException.cs.meta + + + Source\Exceptions\Extensions.meta + + + Source\Exceptions\Extensions\BinderExceptionExtensions.cs.meta + + + Source\Exceptions\ReverseBinderInvalidCastException.cs.meta + + + Source\Exceptions\UnbindSafelyNullReferenceException.cs.meta + + + Source\Generation.meta + + + Source\Generation\BindIdAttribute.cs.meta + + + Source\Generation\IgnoreBindAttribute.cs.meta + + + Source\Helpers.meta + + + Source\Helpers\LoggerHelper.cs.meta + + + Source\Helpers\Unsafe.cs.meta + + + Source\Mode.meta + + + Source\Mode\.DS_Store + + + Source\Mode\BindMode.cs.meta + + + Source\Mode\Extensions.meta + + + Source\Mode\Extensions\BindModeExtensions.cs.meta + + + Source\Mode\Extensions\BindModeExtensions.Throw.cs.meta + + + Source\Mode\Validation.meta + + + Source\Mode\Validation\BindModeAttribute.cs.meta + + + Source\Mode\Validation\BindModeOverrideAttribute.cs.meta + + + Source\ViewModels.meta + + + Source\ViewModels\.DS_Store + + + Source\ViewModels\Extensions.meta + + + Source\ViewModels\Extensions\ViewModelExtensions.cs.meta + + + Source\ViewModels\FindBindableMemberParameters.cs.meta + + + Source\ViewModels\FindBindableMemberResult.cs.meta + + + Source\ViewModels\Generation.meta + + + Source\ViewModels\Generation\Access.cs.meta + + + Source\ViewModels\Generation\AccessAttribute.cs.meta + + + Source\ViewModels\Generation\BaseBindAttribute.cs.meta + + + Source\ViewModels\Generation\BindAlsoAttribute.cs.meta + + + Source\ViewModels\Generation\BindAttribute.cs.meta + + + Source\ViewModels\Generation\OneTimeBindAttribute.cs.meta + + + Source\ViewModels\Generation\OneWayBindAttribute.cs.meta + + + Source\ViewModels\Generation\OneWayToSourceBindAttribute.cs.meta + + + Source\ViewModels\Generation\RelayCommandAttribute.cs.meta + + + Source\ViewModels\Generation\TwoWayBindAttribute.cs.meta + + + Source\ViewModels\Generation\ViewModelAttribute.cs.meta + + + Source\ViewModels\IViewModel.cs.meta + + + Source\Views.meta + + + Source\Views\.DS_Store + + + Source\Views\Extensions.meta + + + Source\Views\Extensions\ViewExtensions.cs.meta + + + Source\Views\Generation.meta + + + Source\Views\Generation\AsBinderAttribute.cs.meta + + + Source\Views\Generation\ViewAttribute.cs.meta + + + Source\Views\IView.cs.meta + + + Source\Views\ViewBinder.cs.meta + + + + + + Source\BindableMembers\Classes\OneTimeBindableMember.cs + + + Source\BindableMembers\Classes\OneWayBindableMember.cs + + + Source\BindableMembers\Classes\OneWayToSourceBindableMember.cs + + + Source\BindableMembers\Classes\TwoWayBindableMember.cs + + + Source\BindableMembers\Enum\OneTimeEnumBindableMember.cs + + + Source\BindableMembers\Enum\OneWayEnumBindableMember.cs + + + Source\BindableMembers\Enum\OneWayToSourceEnumBindableMember.cs + + + Source\BindableMembers\Enum\TwoWayEnumBindableMember.cs + + + Source\BindableMembers\IBindableMember.cs + + + Source\BindableMembers\IBinderAdder.cs + + + Source\BindableMembers\IBinderRemover.cs + + + Source\BindableMembers\IReadOnlyBindableMember.cs + + + Source\BindableMembers\IReadOnlyValueBindableMember.cs + + + Source\BindableMembers\Struct\OneTimeStructBindableMember.cs + + + Source\BindableMembers\Struct\OneWayStructBindableMember.cs + + + Source\BindableMembers\Struct\OneWayToSourceStructBindableMember.cs + + + Source\BindableMembers\Struct\TwoWayStructBindableMember.cs + + + Source\Binders\Binder.cs + + + Source\Binders\Binder.Debug.cs + + + Source\Binders\Debug\Extensions\RebindableBinderExtensions.cs + + + Source\Binders\Debug\IRebindableBinder.cs + + + Source\Binders\Extensions\BinderExtensions.Bind.cs + + + Source\Binders\Extensions\BinderExtensions.Unbind.cs + + + Source\Binders\Generation\BinderLogAttribute.cs + + + Source\Binders\IAnyBinder.cs + + + Source\Binders\IAnyReverseBinder.cs + + + Source\Binders\IBinder.cs + + + Source\Binders\IReverseBinder.cs + + + Source\Commands\Extensions\RelayCommandExtensions.Action.cs + + + Source\Commands\Extensions\RelayCommandExtensions.cs + + + Source\Commands\Extensions\RelayCommandExtensions.WithoutParameters.cs + + + Source\Commands\IRelayCommand.cs + + + Source\Commands\RelayCommand.cs + + + Source\Exceptions\BinderInvalidCastException.cs + + + Source\Exceptions\BindSafelyNullReferenceException.cs + + + Source\Exceptions\Extensions\BinderExceptionExtensions.cs + + + Source\Exceptions\ReverseBinderInvalidCastException.cs + + + Source\Exceptions\UnbindSafelyNullReferenceException.cs + + + Source\Generation\BindIdAttribute.cs + + + Source\Generation\IgnoreBindAttribute.cs + + + Source\Helpers\LoggerHelper.cs + + + Source\Helpers\Unsafe.cs + + + Source\Mode\BindMode.cs + + + Source\Mode\Extensions\BindModeExtensions.cs + + + Source\Mode\Extensions\BindModeExtensions.Throw.cs + + + Source\Mode\Validation\BindModeAttribute.cs + + + Source\Mode\Validation\BindModeOverrideAttribute.cs + + + Source\ViewModels\Extensions\ViewModelExtensions.cs + + + Source\ViewModels\FindBindableMemberParameters.cs + + + Source\ViewModels\FindBindableMemberResult.cs + + + Source\ViewModels\Generation\Access.cs + + + Source\ViewModels\Generation\AccessAttribute.cs + + + Source\ViewModels\Generation\BaseBindAttribute.cs + + + Source\ViewModels\Generation\BindAlsoAttribute.cs + + + Source\ViewModels\Generation\BindAttribute.cs + + + Source\ViewModels\Generation\OneTimeBindAttribute.cs + + + Source\ViewModels\Generation\OneWayBindAttribute.cs + + + Source\ViewModels\Generation\OneWayToSourceBindAttribute.cs + + + Source\ViewModels\Generation\RelayCommandAttribute.cs + + + Source\ViewModels\Generation\TwoWayBindAttribute.cs + + + Source\ViewModels\Generation\ViewModelAttribute.cs + + + Source\ViewModels\IViewModel.cs + + + Source\Views\Extensions\ViewExtensions.cs + + + Source\Views\Generation\AsBinderAttribute.cs + + + Source\Views\Generation\ViewAttribute.cs + + + Source\Views\IView.cs + + + Source\Views\ViewBinder.cs + + + diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Aspid.MVVM.Generators.csproj b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Aspid.MVVM.Generators.csproj index a8001cc..51ae120 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Aspid.MVVM.Generators.csproj +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Aspid.MVVM.Generators.csproj @@ -4,7 +4,7 @@ netstandard2.0 false enable - 13 + 14 true true diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembersBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembersBody.cs index ea8a752..d168d8f 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembersBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembersBody.cs @@ -39,7 +39,7 @@ private static CodeWriter AppendProperties(this CodeWriter code, in ViewModelDat if (!propertyType.Contains(IReadOnlyValueBindableMember)) { - if (!member.BindableMemberPropertyType.Contains(IReadOnlyBindableMember)) + if (!member.Bindable.PropertyType.Contains(IReadOnlyBindableMember)) continue; } } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertiesBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertiesBody.cs index 2a5006c..6d3b50e 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertiesBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertiesBody.cs @@ -27,8 +27,7 @@ private static CodeWriter AppendBody(this CodeWriter code, in ViewModelData data { if (!data.Members.IsEmpty) { - code.AppendFieldEvents(data) - .AppendProperties(data) + code.AppendProperties(data) .AppendBindableMembers(data) .AppendSetMethods(data); } @@ -40,27 +39,13 @@ private static CodeWriter AppendBindableMembers(this CodeWriter code, in ViewMod { foreach (var member in data.Members) { - code.AppendMultiline(member.ToBindableMemberPropertyDeclarationString()) + code.AppendMultiline(member.Bindable.Declaration) .AppendLine(); } return code; } - private static CodeWriter AppendFieldEvents(this CodeWriter code, in ViewModelData data) - { - foreach (var member in data.Members) - { - var fieldDeclaration = member.ToBindableMemberFieldDeclarationString(); - if (fieldDeclaration is null) continue; - - code.AppendMultiline(fieldDeclaration) - .AppendLine(); - } - - return code; - } - private static CodeWriter AppendProperties(this CodeWriter code, in ViewModelData data) { foreach (var field in data.Members.OfType()) @@ -100,7 +85,7 @@ private static CodeWriter AppendNotifyAll(this CodeWriter code, in ViewModelData foreach (var member in data.Members) { - var invoke = member.ToInvokeBindableMemberString(); + var invoke = member.Bindable.Invoke; code.AppendLineIf(!string.IsNullOrWhiteSpace(invoke), invoke); } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs new file mode 100644 index 0000000..157a1ad --- /dev/null +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs @@ -0,0 +1,201 @@ +using Microsoft.CodeAnalysis; +using Aspid.Generators.Helper; +using Aspid.MVVM.Generators.Helpers; +using Aspid.MVVM.Generators.Generators.ViewModels.Extensions; +using static Aspid.Generators.Helper.Classes; +using static Aspid.MVVM.Generators.Generators.Descriptions.General; +using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; + +namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; + +public readonly struct GeneratedBindableMembers +{ + // TODO Aspid.MVVM.Generators – Write summary + public readonly string Declaration; + public readonly string? Invoke; + + public readonly string PropertyType; + + private GeneratedBindableMembers( + string? invoke, + string propertyType, + string declaration) + { + Invoke = invoke; + Declaration = declaration; + PropertyType = propertyType; + } + + public static GeneratedBindableMembers CreateForRelayCommand( + string type, + string fieldName, + string propertyName) + { + const BindMode mode = BindMode.OneTime; + + var fieldType = $"{OneTimeBindableMember}<{type}>"; + var propertyType = $"{IReadOnlyValueBindableMember}<{type}>"; + + var declaration = RecognizeDeclaration(mode, fieldName, null, fieldType, $"{propertyName}Bindable", propertyType); + + return new GeneratedBindableMembers(null, propertyType, declaration); + } + + public static GeneratedBindableMembers CreateForField(IFieldSymbol fieldSymbol) + { + var mode = fieldSymbol.GetBindMode(); + var memberName = fieldSymbol.Name; + + // __fieldName; + var fieldName = $"__{fieldSymbol.RemoveFieldPrefix()}Bindable"; + var fieldType = RecognizeFieldType(fieldSymbol.Type, mode); + + // _fieldName -> FieldNameBindable + var propertyName = $"{fieldSymbol.GetPropertyName()}Bindable"; + var propertyType = RecognizePropertyType(fieldSymbol.Type, mode); + + var invoke = RecognizeInvoke(mode, memberName, fieldName); + var declaration = RecognizeDeclaration(mode, memberName, fieldName, fieldType, propertyName, propertyType); + + return new GeneratedBindableMembers(invoke, propertyType, declaration); + } + + public static GeneratedBindableMembers CreateForBindAlso(IPropertySymbol propertySymbol) + { + const BindMode mode = BindMode.OneWay; + var memberName = propertySymbol.Name; + + // PropertyName -> __propertyNameBindable + var fieldName = $"{propertySymbol.GetFieldName(prefix: "__")}Bindable"; + var fieldType = RecognizeFieldType(propertySymbol.Type, mode); + + // PropertyName -> PropertyNameBindable + var propertyName = $"{propertySymbol.Name}Bindable"; + var propertyType = RecognizePropertyType(propertySymbol.Type, mode); + + var invoke = RecognizeInvoke(mode, memberName, fieldName); + var declaration = RecognizeDeclaration(mode, memberName, fieldName, fieldType, propertyName, propertyType); + + return new GeneratedBindableMembers(invoke, propertyType, declaration); + } + + private static string RecognizeFieldType(ITypeSymbol type, BindMode mode) + { + var typeKind = type.TypeKind; + + if (typeKind is TypeKind.TypeParameter) + { + if (type is ITypeParameterSymbol typeParameter) + { + typeKind = GetEffectiveTypeKind(typeParameter); + } + } + + string result = mode switch + { + BindMode.OneWay => typeKind switch + { + TypeKind.Enum => OneWayEnumBindableMember, + TypeKind.Struct => OneWayStructBindableMember, + _ => OneWayBindableMember + }, + BindMode.TwoWay => typeKind switch + { + TypeKind.Enum => TwoWayEnumBindableMember, + TypeKind.Struct => TwoWayStructBindableMember, + _ => TwoWayBindableMember + }, + BindMode.OneTime => typeKind switch + { + TypeKind.Enum => OneTimeEnumBindableMember, + TypeKind.Struct => OneTimeStructBindableMember, + _ => OneTimeBindableMember + }, + BindMode.OneWayToSource => typeKind switch + { + TypeKind.Enum => OneWayToSourceEnumBindableMember, + TypeKind.Struct => OneWayToSourceStructBindableMember, + _ => OneWayToSourceBindableMember + }, + _ => string.Empty + }; + + return string.IsNullOrWhiteSpace(result) + ? string.Empty + : $"{result}<{type.ToDisplayStringGlobal()}>"; + + TypeKind GetEffectiveTypeKind(ITypeParameterSymbol typeParameter) + { + foreach (var constraint in typeParameter.ConstraintTypes) + { + if (constraint.TypeKind == TypeKind.Enum + || constraint.SpecialType == SpecialType.System_Enum) + { + return TypeKind.Enum; + } + } + + return typeParameter.HasValueTypeConstraint ? TypeKind.Struct : TypeKind.Class; + } + } + + private static string RecognizePropertyType(ITypeSymbol type, BindMode mode) + { + string result = mode switch + { + BindMode.TwoWay => IBindableMember, + BindMode.OneWay => IReadOnlyBindableMember, + BindMode.OneTime or BindMode.OneWayToSource => IReadOnlyValueBindableMember, + _ => string.Empty + }; + + return string.IsNullOrWhiteSpace(result) + ? string.Empty + : $"{result}<{type.ToDisplayStringGlobal()}>"; + } + + private static string RecognizeDeclaration( + BindMode mode, + string memberName, + string? fieldName, + string fieldType, + string propertyName, + string propertyType) + { + if (mode is BindMode.OneTime) + { + return + $""" + {GeneratedCodeViewModelAttribute} + public {propertyType} {propertyName} => + {fieldType}.Get({memberName}); + """; + } + + var instantiate = mode switch + { + BindMode.OneWay => $"{fieldName} ??= new({memberName})", + BindMode.TwoWay => $"{fieldName} ??= new({memberName}, Set{memberName})", + BindMode.OneWayToSource => $"{fieldName} ??= new(Set{memberName})", + _ => string.Empty + }; + + return + $""" + [{EditorBrowsableAttribute}({EditorBrowsableState}.Never)] + {GeneratedCodeViewModelAttribute} + private {fieldType} {fieldName}; + + {GeneratedCodeViewModelAttribute} + public {propertyType} {propertyName} => + {instantiate}; + """; + } + + private static string? RecognizeInvoke(BindMode mode, string memberName, string fieldName) + { + return mode is not (BindMode.OneWayToSource or BindMode.OneTime) + ? $"this.{fieldName}?.Invoke({memberName});" + : null; + } +} diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableBindAlso.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableBindAlso.cs index cbba561..c654c06 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableBindAlso.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableBindAlso.cs @@ -1,13 +1,14 @@ using System; using Microsoft.CodeAnalysis; using Aspid.Generators.Helper; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; public sealed class BindableBindAlso : BindableMember, IEquatable { - public BindableBindAlso(ISymbol member) - : base(member, BindMode.OneWay, member.GetSymbolType()?.ToDisplayStringGlobal() ?? string.Empty, member.Name, member.Name, string.Empty, member.GetSymbolType()?.TypeKind ?? TypeKind.Class) { } + public BindableBindAlso(IPropertySymbol member) + : base(member, BindMode.OneWay, member.GetSymbolType()?.ToDisplayStringGlobal() ?? string.Empty, member.Name, member.Name, string.Empty, GeneratedBindableMembers.CreateForBindAlso(member)) { } public override bool Equals(object? obj) => obj is BindableBindAlso other && Equals(other); diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableCommand.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableCommand.cs index 02aaf64..ce39234 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableCommand.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableCommand.cs @@ -3,6 +3,7 @@ using Microsoft.CodeAnalysis; using Aspid.Generators.Helper; using Aspid.MVVM.Generators.Helpers; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; using static Aspid.Generators.Helper.Classes; using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; using static Aspid.MVVM.Generators.Generators.Descriptions.General; @@ -14,7 +15,10 @@ public sealed class BindableCommand : BindableMember public readonly string CanExecute; public BindableCommand(IMethodSymbol command, string? canExecute, bool isLambda, bool isMethod) - : base(command, BindMode.OneTime, GetTypeName(command), $"{command.GetFieldName("__")}Command", $"{command.GetPropertyName()}Command", "Command") + : this(command, GetTypeName(command), $"{command.GetFieldName("__")}Command", $"{command.GetPropertyName()}Command", canExecute, isLambda, isMethod) { } + + private BindableCommand(IMethodSymbol command, string type, string fieldName, string propertyName, string? canExecute, bool isLambda, bool isMethod) + : base(command, BindMode.OneTime, type, fieldName, propertyName, "Command", GeneratedBindableMembers.CreateForRelayCommand(type, fieldName, propertyName)) { CanExecute = GetCanExecuteAction(command, isLambda, isMethod, canExecute); } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableField.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableField.cs index e0e5ff5..3f7185f 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableField.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableField.cs @@ -3,6 +3,7 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp; using Aspid.MVVM.Generators.Helpers; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; using static Aspid.Generators.Helper.Classes; using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; using static Aspid.MVVM.Generators.Generators.Descriptions.General; @@ -24,7 +25,7 @@ public BindableField(IFieldSymbol field, BindMode mode, ImmutableArray : BindableMember { public readonly T Member; - protected BindableMember(T member, BindMode mode, string type, string sourceName, string generatedName, string idPostfix, TypeKind typeKind = TypeKind.Class) - : base(member, mode, type, sourceName, generatedName, idPostfix, typeKind) + protected BindableMember(T member, BindMode mode, string type, string sourceName, string generatedName, string idPostfix, GeneratedBindableMembers bindable) + : base(member, mode, type, sourceName, generatedName, idPostfix, bindable) { Member = member; } @@ -25,17 +21,11 @@ public abstract class BindableMember public readonly string Type; public readonly string SourceName; public readonly string GeneratedName; - public readonly string BindableMemberPropertyType; + public readonly GeneratedBindableMembers Bindable; public readonly IdData Id; public readonly BindMode Mode; - private readonly string? _bindableType; - private readonly string _bindableFieldName; - - protected BindableMember(ISymbol member, BindMode mode, string type, string name, string idPostfix, TypeKind typeKind = TypeKind.Class) - : this(member, mode, type, name, name, idPostfix, typeKind) { } - protected BindableMember( ISymbol member, BindMode mode, @@ -43,116 +33,13 @@ protected BindableMember( string sourceName, string generatedName, string idPostfix, - TypeKind typeKind = TypeKind.Class) + GeneratedBindableMembers bindable) { Type = type; Mode = mode; SourceName = sourceName; GeneratedName = generatedName; Id = new IdData(member, idPostfix); - - // Handle generic type parameters with constraints - if (typeKind is TypeKind.TypeParameter) - { - if (member.GetSymbolType() is ITypeParameterSymbol typeParameter) - { - typeKind = GetEffectiveTypeKind(typeParameter); - } - } - - switch (mode) - { - case BindMode.OneWay: - _bindableType = typeKind switch - { - TypeKind.Enum => OneWayEnumBindableMember, - TypeKind.Struct => OneWayStructBindableMember, - _ => OneWayBindableMember - }; - break; - - case BindMode.TwoWay: - _bindableType = typeKind switch - { - TypeKind.Enum => TwoWayEnumBindableMember, - TypeKind.Struct => TwoWayStructBindableMember, - _ => TwoWayBindableMember - }; - break; - - case BindMode.OneTime: - _bindableType = typeKind switch - { - TypeKind.Enum => OneTimeEnumBindableMember, - TypeKind.Struct => OneTimeStructBindableMember, - _ => OneTimeBindableMember - }; - break; - - case BindMode.OneWayToSource: - _bindableType = typeKind switch - { - TypeKind.Enum => OneWayToSourceEnumBindableMember, - TypeKind.Struct => OneWayToSourceStructBindableMember, - _ => OneWayToSourceBindableMember - }; - break; - } - - BindableMemberPropertyType = Mode is BindMode.OneTime - ? IReadOnlyValueBindableMember - : IReadOnlyBindableMember; - - _bindableFieldName = $"__{RemoveFieldPrefix(GetFieldName(generatedName, null))}Bindable"; - } - - public string? ToBindableMemberFieldDeclarationString() - { - if (Mode is BindMode.OneTime) return null; - - return _bindableType is null - ? null - : $""" - [{EditorBrowsableAttribute}({EditorBrowsableState}.Never)] - {GeneratedCodeViewModelAttribute} - private {_bindableType}<{Type}> {_bindableFieldName}; - """; - } - - public string ToBindableMemberPropertyDeclarationString() - { - var instantiate = Mode switch - { - BindMode.OneWay => $"{_bindableFieldName} ??= new({GeneratedName})", - BindMode.TwoWay => $"{_bindableFieldName} ??= new({GeneratedName}, Set{GeneratedName})", - BindMode.OneTime => $"{_bindableType}<{Type}>.Get({GeneratedName})", - BindMode.OneWayToSource => $"{_bindableFieldName} ??= new(Set{GeneratedName})", - _ => string.Empty - }; - - return $""" - {GeneratedCodeViewModelAttribute} - public {BindableMemberPropertyType}<{Type}> {GeneratedName}Bindable => - {instantiate}; - """; - } - - // TODO Nullable? - public string ToInvokeBindableMemberString() => Mode is not (BindMode.OneWayToSource or BindMode.OneTime) - ? $"this.{_bindableFieldName}?.Invoke({SourceName});" - : string.Empty; - - private static TypeKind GetEffectiveTypeKind(ITypeParameterSymbol typeParameter) - { - foreach (var constraint in typeParameter.ConstraintTypes) - { - if (constraint.TypeKind == TypeKind.Enum - || constraint.SpecialType == SpecialType.System_Enum) - { - return TypeKind.Enum; - } - } - - return typeParameter.HasValueTypeConstraint ? TypeKind.Struct : TypeKind.Class; + Bindable = bindable; } } \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableBindAlsoFactory.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableBindAlsoFactory.cs index 92cae1e..61fe359 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableBindAlsoFactory.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableBindAlsoFactory.cs @@ -27,8 +27,10 @@ public static IReadOnlyCollection Create(ImmutableArray Date: Tue, 2 Dec 2025 22:21:12 +0300 Subject: [PATCH 07/29] Refactor --- .../ViewModels/Body/BindableMembersBody.cs | 2 +- .../Body/FindBindableMembersBody.cs | 7 +++-- .../Data/Infos/BindableBindAlsoInfo.cs | 30 +++++++++++++++++++ .../Data/Infos/GeneratedBindableMembers.cs | 19 +++++++----- .../Data/Infos/IBindableMemberInfo.cs | 16 ++++++++++ .../Data/Members/BindableBindAlso.cs | 21 ------------- .../Data/Members/BindableCommand.cs | 4 +-- .../ViewModels/Data/Members/BindableField.cs | 14 ++++----- .../ViewModels/Data/Members/BindableMember.cs | 18 ++++++----- .../Collections/IdLengthMemberGroup.cs | 9 +++--- .../ViewModels/Data/ViewModelData.cs | 5 ++-- .../Factories/BindableBindAlsoFactory.cs | 8 ++--- .../Factories/BindableFieldFactory.cs | 7 +++-- .../Factories/BindableMembersFactory.cs | 6 ++-- .../Body/Extensions/BindSafelyExtensions.cs | 6 ++-- .../Views/Body/GenericInitializeView.cs | 4 +-- 16 files changed, 106 insertions(+), 70 deletions(-) create mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableBindAlsoInfo.cs create mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/IBindableMemberInfo.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableBindAlso.cs diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembersBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembersBody.cs index d168d8f..f3cd43a 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembersBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembersBody.cs @@ -47,7 +47,7 @@ private static CodeWriter AppendProperties(this CodeWriter code, in ViewModelDat var interfaceType = customInterface.Interface.ToDisplayStringGlobal(); code.AppendLine(GeneratedCodeViewModelAttribute) - .AppendLine($"{propertyType} {interfaceType}.{property.Name} => {member.GeneratedName}Bindable;") + .AppendLine($"{propertyType} {interfaceType}.{property.Name} => {member.Bindable.PropertyName};") .AppendLine(); } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/FindBindableMembersBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/FindBindableMembersBody.cs index 7473cf9..b084bd2 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/FindBindableMembersBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/FindBindableMembersBody.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using Aspid.MVVM.Generators.Generators.ViewModels.Data; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; using static Aspid.Generators.Helper.Classes; using static Aspid.Generators.Helper.Unity.UnityClasses; @@ -54,7 +55,7 @@ private static CodeWriter AppendMarkers(this CodeWriter code, in ViewModelData d private static CodeWriter AppendFindBindableMember(this CodeWriter code, in ViewModelData data) { - var addedMembers = new HashSet(); + var addedMembers = new HashSet(); var modifiers = "public"; if (data.Inheritor is not Inheritor.None) modifiers = "public override"; @@ -119,7 +120,7 @@ private static CodeWriter AppendFindBindableMember(this CodeWriter code, in View return code; - void AppendIdBlock(ImmutableArray members) + void AppendIdBlock(ImmutableArray members) { foreach (var member in members) { @@ -129,7 +130,7 @@ void AppendIdBlock(ImmutableArray members) $$""" case {{member.Id}}: { - return new({{member.GeneratedName}}Bindable); + return new({{member.Bindable.PropertyName}}); } """); diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableBindAlsoInfo.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableBindAlsoInfo.cs new file mode 100644 index 0000000..9b13819 --- /dev/null +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableBindAlsoInfo.cs @@ -0,0 +1,30 @@ +using System; +using Microsoft.CodeAnalysis; +using Aspid.Generators.Helper; +using Aspid.MVVM.Generators.Generators.Ids.Data; + +namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; + +public sealed class BindableBindAlsoInfo(IPropertySymbol propertySymbol) : IBindableMemberInfo, IEquatable +{ + private readonly IPropertySymbol _propertySymbol = propertySymbol; + + public string Type { get; } = propertySymbol.Type.ToDisplayStringGlobal(); + + public string Name { get; } = propertySymbol.Name; + + public IdData Id { get; } = new(propertySymbol); + + public BindMode Mode => BindMode.OneWay; + + public GeneratedBindableMembers Bindable { get; } = GeneratedBindableMembers.CreateForBindAlso(propertySymbol); + + public override bool Equals(object? obj) => + obj is BindableBindAlsoInfo other && Equals(other); + + public bool Equals(BindableBindAlsoInfo other) => + SymbolEqualityComparer.Default.Equals(_propertySymbol, other._propertySymbol); + + public override int GetHashCode() => + SymbolEqualityComparer.Default.GetHashCode(_propertySymbol); +} diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs index 157a1ad..e1ff749 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs @@ -11,18 +11,20 @@ namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; public readonly struct GeneratedBindableMembers { // TODO Aspid.MVVM.Generators – Write summary - public readonly string Declaration; public readonly string? Invoke; - + public readonly string Declaration; + public readonly string PropertyName; public readonly string PropertyType; private GeneratedBindableMembers( string? invoke, - string propertyType, - string declaration) + string declaration, + string propertyName, + string propertyType) { Invoke = invoke; Declaration = declaration; + PropertyName = propertyName; PropertyType = propertyType; } @@ -35,10 +37,11 @@ public static GeneratedBindableMembers CreateForRelayCommand( var fieldType = $"{OneTimeBindableMember}<{type}>"; var propertyType = $"{IReadOnlyValueBindableMember}<{type}>"; + var bindablePropertyName = $"{propertyName}Bindable"; - var declaration = RecognizeDeclaration(mode, fieldName, null, fieldType, $"{propertyName}Bindable", propertyType); + var declaration = RecognizeDeclaration(mode, fieldName, null, fieldType, bindablePropertyName, propertyType); - return new GeneratedBindableMembers(null, propertyType, declaration); + return new GeneratedBindableMembers(null, declaration, bindablePropertyName, propertyType); } public static GeneratedBindableMembers CreateForField(IFieldSymbol fieldSymbol) @@ -57,7 +60,7 @@ public static GeneratedBindableMembers CreateForField(IFieldSymbol fieldSymbol) var invoke = RecognizeInvoke(mode, memberName, fieldName); var declaration = RecognizeDeclaration(mode, memberName, fieldName, fieldType, propertyName, propertyType); - return new GeneratedBindableMembers(invoke, propertyType, declaration); + return new GeneratedBindableMembers(invoke, declaration, propertyName, propertyType); } public static GeneratedBindableMembers CreateForBindAlso(IPropertySymbol propertySymbol) @@ -76,7 +79,7 @@ public static GeneratedBindableMembers CreateForBindAlso(IPropertySymbol propert var invoke = RecognizeInvoke(mode, memberName, fieldName); var declaration = RecognizeDeclaration(mode, memberName, fieldName, fieldType, propertyName, propertyType); - return new GeneratedBindableMembers(invoke, propertyType, declaration); + return new GeneratedBindableMembers(invoke, declaration, propertyName, propertyType); } private static string RecognizeFieldType(ITypeSymbol type, BindMode mode) diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/IBindableMemberInfo.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/IBindableMemberInfo.cs new file mode 100644 index 0000000..a64d1c5 --- /dev/null +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/IBindableMemberInfo.cs @@ -0,0 +1,16 @@ +using Aspid.MVVM.Generators.Generators.Ids.Data; + +namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; + +public interface IBindableMemberInfo +{ + public string Type { get; } + + public string Name { get; } + + public IdData Id { get; } + + public BindMode Mode { get; } + + public GeneratedBindableMembers Bindable { get; } +} diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableBindAlso.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableBindAlso.cs deleted file mode 100644 index c654c06..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableBindAlso.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using Microsoft.CodeAnalysis; -using Aspid.Generators.Helper; -using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; - -namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; - -public sealed class BindableBindAlso : BindableMember, IEquatable -{ - public BindableBindAlso(IPropertySymbol member) - : base(member, BindMode.OneWay, member.GetSymbolType()?.ToDisplayStringGlobal() ?? string.Empty, member.Name, member.Name, string.Empty, GeneratedBindableMembers.CreateForBindAlso(member)) { } - - public override bool Equals(object? obj) => - obj is BindableBindAlso other && Equals(other); - - public bool Equals(BindableBindAlso other) => - SymbolEqualityComparer.Default.Equals(Member, other.Member); - - public override int GetHashCode() => - SymbolEqualityComparer.Default.GetHashCode(Member); -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableCommand.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableCommand.cs index ce39234..547813b 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableCommand.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableCommand.cs @@ -29,10 +29,10 @@ public string ToDeclarationCommandString() $""" {GeneratedCodeViewModelAttribute} [{EditorBrowsableAttribute}({EditorBrowsableState}.Never)] - private {Type} {SourceName}; + private {Type} {Name}; {GeneratedCodeViewModelAttribute} - private {Type} {GeneratedName} => {SourceName} ??= new {Type}({Member.Name}{CanExecute}); + private {Type} {GeneratedName} => {Name} ??= new {Type}({Member.Name}{CanExecute}); """; } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableField.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableField.cs index 3f7185f..6055a08 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableField.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableField.cs @@ -16,9 +16,9 @@ public class BindableField : BindableMember public readonly string GetAccessAsText; public readonly string SetAccessAsText; public readonly string GeneralAccessAsText; - public readonly ImmutableArray BindAlso; + public readonly ImmutableArray BindAlso; - public BindableField(IFieldSymbol field, BindMode mode, ImmutableArray bindAlso) + public BindableField(IFieldSymbol field, BindMode mode, ImmutableArray bindAlso) : base(field, mode, field.Type.ToDisplayStringGlobal(), @@ -48,13 +48,13 @@ public string ToDeclarationPropertyString() return IsReadOnly ? $""" {GeneratedCodeViewModelAttribute} - {GeneralAccessAsText}{Type} {GeneratedName} => {SourceName}; + {GeneralAccessAsText}{Type} {GeneratedName} => {Name}; """ : $$""" {{GeneratedCodeViewModelAttribute}} {{GeneralAccessAsText}}{{Type}} {{GeneratedName}} { - {{GetAccessAsText}}get => {{SourceName}}; + {{GetAccessAsText}}get => {{Name}}; {{SetAccessAsText}}set => Set{{GeneratedName}}(value); } """; @@ -80,10 +80,10 @@ public string ToSetMethodString() {{GeneratedCodeViewModelAttribute}} private void {{setMethod}}({{Type}} value) { - if ({{EqualityComparer_1}}<{{Type}}>.Default.Equals({{SourceName}}, value)) return; + if ({{EqualityComparer_1}}<{{Type}}>.Default.Equals({{Name}}, value)) return; - {{onMethodChanging}}({{SourceName}}, value); - {{keyWordThis}}{{SourceName}} = value; + {{onMethodChanging}}({{Name}}, value); + {{keyWordThis}}{{Name}} = value; {{eventInvoke}} {{onMethodChanged}}(value); } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableMember.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableMember.cs index 82772de..4aa00f6 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableMember.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableMember.cs @@ -16,15 +16,19 @@ protected BindableMember(T member, BindMode mode, string type, string sourceName } } -public abstract class BindableMember +public abstract class BindableMember : IBindableMemberInfo { - public readonly string Type; - public readonly string SourceName; public readonly string GeneratedName; - public readonly GeneratedBindableMembers Bindable; + + public string Type { get; } + + public string Name { get; } - public readonly IdData Id; - public readonly BindMode Mode; + public IdData Id { get; } + + public BindMode Mode { get; } + + public GeneratedBindableMembers Bindable { get; } protected BindableMember( ISymbol member, @@ -37,7 +41,7 @@ protected BindableMember( { Type = type; Mode = mode; - SourceName = sourceName; + Name = sourceName; GeneratedName = generatedName; Id = new IdData(member, idPostfix); Bindable = bindable; diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/Collections/IdLengthMemberGroup.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/Collections/IdLengthMemberGroup.cs index ea8dfe6..047566e 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/Collections/IdLengthMemberGroup.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/Collections/IdLengthMemberGroup.cs @@ -1,15 +1,16 @@ using System.Linq; using System.Collections.Generic; using System.Collections.Immutable; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Members.Collections; -public readonly struct IdLengthMemberGroup(int length, ImmutableArray members) +public readonly struct IdLengthMemberGroup(int length, ImmutableArray members) { public readonly int Length = length; - public readonly ImmutableArray Members = members; + public readonly ImmutableArray Members = members; - public static ImmutableArray Create(ImmutableArray bindableMembers) + public static ImmutableArray Create(ImmutableArray bindableMembers) { var bindableMembersCountByLength = new Dictionary(); @@ -22,7 +23,7 @@ public static ImmutableArray Create(ImmutableArray>(); + var idGroups = new Dictionary>(); foreach (var bindableMember in bindableMembers) { diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/ViewModelData.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/ViewModelData.cs index 7c9ba39..09b97e4 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/ViewModelData.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/ViewModelData.cs @@ -1,6 +1,7 @@ using Microsoft.CodeAnalysis; using System.Collections.Generic; using System.Collections.Immutable; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; using Microsoft.CodeAnalysis.CSharp.Syntax; using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members.Collections; @@ -11,7 +12,7 @@ public readonly struct ViewModelData( Inheritor inheritor, INamedTypeSymbol symbol, ClassDeclarationSyntax declaration, - ImmutableArray members, + ImmutableArray members, ImmutableArray idGroups, Dictionary customViewModelInterfaces) { @@ -21,7 +22,7 @@ public readonly struct ViewModelData( public readonly INamedTypeSymbol Symbol = symbol; public readonly ClassDeclarationSyntax Declaration = declaration; - public readonly ImmutableArray Members = members; + public readonly ImmutableArray Members = members; public readonly ImmutableArray IdGroups = idGroups; public readonly Dictionary CustomViewModelInterfaces = customViewModelInterfaces; } \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableBindAlsoFactory.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableBindAlsoFactory.cs index 61fe359..7518abe 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableBindAlsoFactory.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableBindAlsoFactory.cs @@ -2,17 +2,17 @@ using Aspid.Generators.Helper; using System.Collections.Generic; using System.Collections.Immutable; -using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; namespace Aspid.MVVM.Generators.Generators.ViewModels.Factories; public static class BindableBindAlsoFactory { - public static IReadOnlyCollection Create(ImmutableArray members) + public static IReadOnlyCollection Create(ImmutableArray members) { var set = new HashSet(); - var bindableBindAlso = new List(); + var bindableBindAlso = new List(); foreach (var member in members) { @@ -30,7 +30,7 @@ public static IReadOnlyCollection Create(ImmutableArray Create(ImmutableArray fields, IReadOnlyCollection bindableBindAlsos) + public static IReadOnlyCollection Create(ImmutableArray fields, IReadOnlyCollection bindableBindAlsos) { var bindableFields = new List(); @@ -41,7 +42,7 @@ public static IReadOnlyCollection Create(ImmutableArray GetBindableBindAlso(IFieldSymbol field, IReadOnlyCollection allBindableBindAlsos) + private static ImmutableArray GetBindableBindAlso(IFieldSymbol field, IReadOnlyCollection allBindableBindAlsos) { var set = new HashSet(); @@ -58,6 +59,6 @@ private static ImmutableArray GetBindableBindAlso(IFieldSymbol } return allBindableBindAlsos.Where(bindableBindAlso => - set.Contains(bindableBindAlso.SourceName) || set.Contains(bindableBindAlso.GeneratedName)).ToImmutableArray(); + set.Contains(bindableBindAlso.Name)).ToImmutableArray(); } } \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableMembersFactory.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableMembersFactory.cs index b3cc928..950f32c 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableMembersFactory.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableMembersFactory.cs @@ -3,13 +3,13 @@ using System.Collections.Generic; using System.Collections.Immutable; using Aspid.MVVM.Generators.Helpers; -using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; namespace Aspid.MVVM.Generators.Generators.ViewModels.Factories; public static class BindableMembersFactory { - public static ImmutableArray Create(ITypeSymbol symbol) + public static ImmutableArray Create(ITypeSymbol symbol) { var members = new MembersByGroup(symbol); @@ -22,7 +22,7 @@ public static ImmutableArray Create(ITypeSymbol symbol) .ToImmutableArray(); var bindableCommands = BindableCommandFactory.Create(members.Methods, members.Properties, generatedProperties); - var bindableMembers = new List(bindableBindAlso.Count + bindableFields.Count + bindableCommands.Count); + var bindableMembers = new List(bindableBindAlso.Count + bindableFields.Count + bindableCommands.Count); bindableMembers.AddRange(bindableFields); bindableMembers.AddRange(bindableCommands); diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/Extensions/BindSafelyExtensions.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/Extensions/BindSafelyExtensions.cs index c91c2a1..34b2a3c 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/Extensions/BindSafelyExtensions.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/Extensions/BindSafelyExtensions.cs @@ -1,14 +1,14 @@ using Microsoft.CodeAnalysis; using Aspid.Generators.Helper; using Aspid.MVVM.Generators.Generators.Views.Data.Members; -using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; namespace Aspid.MVVM.Generators.Generators.Views.Body.Extensions; public static class BindSafelyExtensions { - public static CodeWriter AppendBindSafely(this CodeWriter code, BinderMember member, BindableMember bindableMember) => - code.AppendBindSafely(member, bindableMember?.GeneratedName + "Bindable"); + public static CodeWriter AppendBindSafely(this CodeWriter code, BinderMember member, IBindableMemberInfo bindableMember) => + code.AppendBindSafely(member, bindableMember.Bindable.PropertyName); public static CodeWriter AppendBindSafely(this CodeWriter code, BinderMember member, string? bindableMemberName = null) { diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/GenericInitializeView.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/GenericInitializeView.cs index 1c22db9..d45bdee 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/GenericInitializeView.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/GenericInitializeView.cs @@ -4,9 +4,9 @@ using System.Collections.Generic; using Aspid.MVVM.Generators.Generators.Views.Data; using Aspid.MVVM.Generators.Generators.Descriptions; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; using Aspid.MVVM.Generators.Generators.ViewModels.Factories; using Aspid.MVVM.Generators.Generators.Views.Body.Extensions; -using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; using static Aspid.Generators.Helper.Classes; using static Aspid.Generators.Helper.Unity.UnityClasses; using static Aspid.MVVM.Generators.Generators.Descriptions.Defines; @@ -98,7 +98,7 @@ public void Initialize({{typeName}} viewModel) if (genericView.Type.TypeKind is not TypeKind.Interface) { - var bindableMembers = new Dictionary(); + var bindableMembers = new Dictionary(); for (var viewModelType = genericView.Type; viewModelType is not null; viewModelType = viewModelType.BaseType) { From 62dff38bfc5f7736352dcaf97a8de6bbfcb63058 Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Tue, 2 Dec 2025 22:33:14 +0300 Subject: [PATCH 08/29] Refactor BindableCommand --- .../ViewModels/Body/RelayCommandBody.cs | 7 +-- .../BindableCommandInfo.cs} | 47 +++++++++++-------- .../Data/Infos/GeneratedBindableMembers.cs | 9 ++-- .../Factories/BindableCommandFactory.cs | 9 ++-- 4 files changed, 41 insertions(+), 31 deletions(-) rename Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/{Members/BindableCommand.cs => Infos/BindableCommandInfo.cs} (61%) diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/RelayCommandBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/RelayCommandBody.cs index 7de88ec..9e0d816 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/RelayCommandBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/RelayCommandBody.cs @@ -2,6 +2,7 @@ using Microsoft.CodeAnalysis; using Aspid.Generators.Helper; using Aspid.MVVM.Generators.Generators.ViewModels.Data; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; namespace Aspid.MVVM.Generators.Generators.ViewModels.Body; @@ -14,7 +15,7 @@ public static void Generate( DeclarationText declaration, in SourceProductionContext context) { - if (!data.Members.OfType().Any()) return; + if (!data.Members.OfType().Any()) return; var code = new CodeWriter(); @@ -27,9 +28,9 @@ public static void Generate( private static CodeWriter AppendBody(this CodeWriter code, in ViewModelData data) { - foreach (var command in data.Members.OfType()) + foreach (var command in data.Members.OfType()) { - code.AppendMultiline(command.ToDeclarationCommandString()) + code.AppendMultiline(command.CommandDeclaration) .AppendLine(); } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableCommand.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableCommandInfo.cs similarity index 61% rename from Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableCommand.cs rename to Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableCommandInfo.cs index 547813b..a464b4d 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableCommand.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableCommandInfo.cs @@ -1,41 +1,50 @@ using System.Linq; using System.Text; -using Microsoft.CodeAnalysis; using Aspid.Generators.Helper; +using Aspid.MVVM.Generators.Generators.Ids.Data; using Aspid.MVVM.Generators.Helpers; -using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; +using Microsoft.CodeAnalysis; using static Aspid.Generators.Helper.Classes; using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; using static Aspid.MVVM.Generators.Generators.Descriptions.General; -namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; +namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; -public sealed class BindableCommand : BindableMember +public sealed class BindableCommandInfo : IBindableMemberInfo { - public readonly string CanExecute; + public string Type { get; } + + public string Name { get; } + + public IdData Id { get; } - public BindableCommand(IMethodSymbol command, string? canExecute, bool isLambda, bool isMethod) - : this(command, GetTypeName(command), $"{command.GetFieldName("__")}Command", $"{command.GetPropertyName()}Command", canExecute, isLambda, isMethod) { } + public BindMode Mode => BindMode.OneTime; + + public GeneratedBindableMembers Bindable { get; } + + public string CommandDeclaration { get; } - private BindableCommand(IMethodSymbol command, string type, string fieldName, string propertyName, string? canExecute, bool isLambda, bool isMethod) - : base(command, BindMode.OneTime, type, fieldName, propertyName, "Command", GeneratedBindableMembers.CreateForRelayCommand(type, fieldName, propertyName)) + public BindableCommandInfo(IMethodSymbol methodSymbol, string? canExecute, bool isLambda, bool isMethod) { - CanExecute = GetCanExecuteAction(command, isLambda, isMethod, canExecute); - } + Type = GetTypeName(methodSymbol); + Id = new IdData(methodSymbol, "Command"); + Name = $"{methodSymbol.GetPropertyName()}Command"; + Bindable = GeneratedBindableMembers.CreateForRelayCommand(Type, Name); - public string ToDeclarationCommandString() - { - return + var fieldName = $"{methodSymbol.GetFieldName("__")}Command"; + canExecute = GetCanExecuteAction(methodSymbol, isLambda, isMethod, canExecute); + + CommandDeclaration = $""" {GeneratedCodeViewModelAttribute} [{EditorBrowsableAttribute}({EditorBrowsableState}.Never)] - private {Type} {Name}; - + private {Type} {fieldName}; + {GeneratedCodeViewModelAttribute} - private {Type} {GeneratedName} => {Name} ??= new {Type}({Member.Name}{CanExecute}); + private {Type} {Name} => {fieldName} ??= new {Type}({methodSymbol.Name}{canExecute}); """; } - + private static string GetTypeName(IMethodSymbol command) { var type = new StringBuilder(RelayCommand); @@ -75,4 +84,4 @@ private static string GetCanExecuteAction(IMethodSymbol command, bool isLambda, return canExecuteName.ToString(); } -} \ No newline at end of file +} diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs index e1ff749..1470297 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs @@ -30,18 +30,17 @@ private GeneratedBindableMembers( public static GeneratedBindableMembers CreateForRelayCommand( string type, - string fieldName, - string propertyName) + string memberName) { const BindMode mode = BindMode.OneTime; var fieldType = $"{OneTimeBindableMember}<{type}>"; var propertyType = $"{IReadOnlyValueBindableMember}<{type}>"; - var bindablePropertyName = $"{propertyName}Bindable"; + var propertyName = $"{memberName}Bindable"; - var declaration = RecognizeDeclaration(mode, fieldName, null, fieldType, bindablePropertyName, propertyType); + var declaration = RecognizeDeclaration(mode, memberName, null, fieldType, propertyName, propertyType); - return new GeneratedBindableMembers(null, declaration, bindablePropertyName, propertyType); + return new GeneratedBindableMembers(null, declaration, propertyName, propertyType); } public static GeneratedBindableMembers CreateForField(IFieldSymbol fieldSymbol) diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableCommandFactory.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableCommandFactory.cs index 5f0333b..6f1c505 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableCommandFactory.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableCommandFactory.cs @@ -3,6 +3,7 @@ using Aspid.Generators.Helper; using System.Collections.Generic; using System.Collections.Immutable; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; using Classes = Aspid.MVVM.Generators.Generators.Descriptions.Classes; @@ -12,12 +13,12 @@ public static class BindableCommandFactory { private const string CanExecuteReturnType = "bool"; - public static IReadOnlyCollection Create( + public static IReadOnlyCollection Create( ImmutableArray methods, ImmutableArray properties, ImmutableArray generatedBoolProperties) { - var bindableCommands = new List(); + var bindableCommands = new List(); var boolMethods = methods.Where(boolMethods => boolMethods.ReturnType.ToString() is CanExecuteReturnType).ToImmutableArray(); @@ -37,8 +38,8 @@ public static IReadOnlyCollection Create( var canExecute = GetCanExecute(canExecuteArgument, method, boolMethods, boolProperties, generatedBoolProperties); bindableCommands.Add(canExecute.isEixst ? - new BindableCommand(method, canExecuteArgument, canExecute.isLamda, canExecute.isMethod) : - new BindableCommand(method, null, false, false)); + new BindableCommandInfo(method, canExecuteArgument, canExecute.isLamda, canExecute.isMethod) : + new BindableCommandInfo(method, null, false, false)); } return bindableCommands; From 9d3105f7980898c7a1661e81d9609924f45ff050 Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Wed, 3 Dec 2025 11:03:55 +0300 Subject: [PATCH 09/29] Refactor BindableField --- .../ViewModels/Body/PropertiesBody.cs | 94 ++++----- .../Data/Infos/BindableFieldInfo.cs | 195 ++++++++++++++++++ .../ViewModels/Data/Members/BindableField.cs | 149 ------------- .../Factories/BindableFieldFactory.cs | 6 +- .../Factories/BindableMembersFactory.cs | 2 +- 5 files changed, 239 insertions(+), 207 deletions(-) create mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableFieldInfo.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableField.cs diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertiesBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertiesBody.cs index 6d3b50e..2959ebf 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertiesBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertiesBody.cs @@ -1,7 +1,7 @@ using Microsoft.CodeAnalysis; using Aspid.Generators.Helper; using Aspid.MVVM.Generators.Generators.ViewModels.Data; -using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; using static Aspid.MVVM.Generators.Generators.Descriptions.General; namespace Aspid.MVVM.Generators.Generators.ViewModels.Body; @@ -23,72 +23,58 @@ public static void Generate( context.AddSource(declaration.GetFileName(@namespace, "Properties"), code.GetSourceText()); } - private static CodeWriter AppendBody(this CodeWriter code, in ViewModelData data) + extension(CodeWriter code) { - if (!data.Members.IsEmpty) + private CodeWriter AppendBody(in ViewModelData data) { - code.AppendProperties(data) - .AppendBindableMembers(data) - .AppendSetMethods(data); - } + if (!data.Members.IsEmpty) + { + code.AppendProperties(data) + .AppendBindableMembers(data); + } - return code.AppendNotifyAll(data); - } - - private static CodeWriter AppendBindableMembers(this CodeWriter code, in ViewModelData data) - { - foreach (var member in data.Members) - { - code.AppendMultiline(member.Bindable.Declaration) - .AppendLine(); + return code.AppendNotifyAll(data); } - return code; - } - - private static CodeWriter AppendProperties(this CodeWriter code, in ViewModelData data) - { - foreach (var field in data.Members.OfType()) + private CodeWriter AppendBindableMembers(in ViewModelData data) { - if (field.Member.IsConst) continue; - - code.AppendMultiline(field.ToDeclarationPropertyString()) - .AppendLine(); + foreach (var member in data.Members) + { + code.AppendMultiline(member.Bindable.Declaration) + .AppendLine(); + } + + return code; } - return code; - } - - private static CodeWriter AppendSetMethods(this CodeWriter code, in ViewModelData data) - { - foreach (var field in data.Members.OfType()) + private CodeWriter AppendProperties(in ViewModelData data) { - if (field.Mode is BindMode.OneTime) continue; - - code.AppendMultiline(field.ToSetMethodString()) - .AppendLine(); + foreach (var field in data.Members.OfType()) + { + code.AppendMultiline(field.Declaration); + } + + return code; } - return code; - } - - private static CodeWriter AppendNotifyAll(this CodeWriter code, in ViewModelData data) - { - var modifiers = "private"; - if (data.Inheritor is not Inheritor.None) modifiers = "protected override"; - else if (!data.Symbol.IsSealed) modifiers = "protected virtual"; + private CodeWriter AppendNotifyAll(in ViewModelData data) + { + var modifiers = "private"; + if (data.Inheritor is not Inheritor.None) modifiers = "protected override"; + else if (!data.Symbol.IsSealed) modifiers = "protected virtual"; - code.AppendLine(GeneratedCodeViewModelAttribute) - .AppendLine($"{modifiers} void NotifyAll()") - .BeginBlock() - .AppendLineIf(data.Inheritor is Inheritor.Inheritor, "base.NotifyAll();"); + code.AppendLine(GeneratedCodeViewModelAttribute) + .AppendLine($"{modifiers} void NotifyAll()") + .BeginBlock() + .AppendLineIf(data.Inheritor is Inheritor.Inheritor, "base.NotifyAll();"); - foreach (var member in data.Members) - { - var invoke = member.Bindable.Invoke; - code.AppendLineIf(!string.IsNullOrWhiteSpace(invoke), invoke); - } + foreach (var member in data.Members) + { + var invoke = member.Bindable.Invoke; + code.AppendLineIf(!string.IsNullOrWhiteSpace(invoke), invoke); + } - return code.EndBlock(); + return code.EndBlock(); + } } } \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableFieldInfo.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableFieldInfo.cs new file mode 100644 index 0000000..bea2ec0 --- /dev/null +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableFieldInfo.cs @@ -0,0 +1,195 @@ +using System.Text; +using Microsoft.CodeAnalysis; +using Aspid.Generators.Helper; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.CSharp; +using Aspid.MVVM.Generators.Helpers; +using Aspid.MVVM.Generators.Generators.Ids.Data; +using static Aspid.Generators.Helper.Classes; +using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; +using static Aspid.MVVM.Generators.Generators.Descriptions.General; + +namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; + +public sealed class BindableFieldInfo : IBindableMemberInfo +{ + public string Type { get; } + + public string Name { get; } + + public IdData Id { get; } + + public BindMode Mode { get; } + + public GeneratedBindableMembers Bindable { get; } + + public string Declaration { get; } + + public BindableFieldInfo(IFieldSymbol fieldSymbol, BindMode mode, ImmutableArray bindAlso) + { + Mode = mode; + Id = new IdData(fieldSymbol); + Type = fieldSymbol.Type.ToDisplayStringGlobal(); + Name = fieldSymbol.IsConst ? fieldSymbol.Name : fieldSymbol.GetPropertyName(); + Bindable = GeneratedBindableMembers.CreateForField(fieldSymbol); + var accessors = Accessors.GetAccessors(fieldSymbol); + + var setMethodName = $"Set{Name}"; + StringBuilder declaration = new(); + + if (!fieldSymbol.IsConst) + { + declaration.AppendLine($"#region {Name}"); + + declaration.AppendLine(Mode is BindMode.OneTime + ? $""" + {GeneratedCodeViewModelAttribute} + {accessors.General}{Type} {Name} => {fieldSymbol.Name}; + """ + : $$""" + {{GeneratedCodeViewModelAttribute}} + {{accessors.General}}{{Type}} {{Name}} + { + {{accessors.Get}}get => {{fieldSymbol.Name}}; + {{accessors.Set}}set => {{setMethodName}}(value); + } + """); + + if (Mode is not BindMode.OneTime && Mode is not BindMode.None) + { + var onChangedMethod = $"On{Name}Changed"; + var onChangingMethod = $"On{Name}Changing"; + var methodModifier = Accessors.ConvertAccessorToString(accessors.SetKind); + var keywordThis = !fieldSymbol.IsStatic ? "this." : string.Empty; + + var eventInvoke = Bindable.Invoke; + + foreach (var property in bindAlso) + eventInvoke += $"\n\t\t{property.Bindable.Invoke}"; + + declaration.AppendLine(); + declaration.AppendLine( + $$""" + {{GeneratedCodeViewModelAttribute}} + {{methodModifier}}void {{setMethodName}}({{Type}} value) + { + if ({{EqualityComparer_1}}<{{Type}}>.Default.Equals({{fieldSymbol.Name}}, value)) return; + + {{Type}} oldValue = {{fieldSymbol.Name}}; + + {{onChangingMethod}}(value); + {{onChangingMethod}}(oldValue, value); + { + {{keywordThis}}{{fieldSymbol.Name}} = value; + {{eventInvoke}} + } + {{onChangedMethod}}(value); + {{onChangedMethod}}(oldValue, value); + } + + {{GeneratedCodeViewModelAttribute}} + partial void {{onChangingMethod}}({{Type}} newValue); + + {{GeneratedCodeViewModelAttribute}} + partial void {{onChangingMethod}}({{Type}} oldValue, {{Type}} newValue); + + {{GeneratedCodeViewModelAttribute}} + partial void {{onChangedMethod}}({{Type}} newValue); + + {{GeneratedCodeViewModelAttribute}} + partial void {{onChangedMethod}}({{Type}} oldValue, {{Type}} newValue); + """); + } + + declaration.AppendLine("#endregion"); + } + + Declaration = declaration.ToString(); + } + + private readonly ref struct Accessors + { + public readonly SyntaxKind GetKind; + public readonly SyntaxKind SetKind; + + public readonly string Get; + public readonly string Set; + public readonly string General; + + private Accessors(SyntaxKind get, SyntaxKind set) + { + GetKind = get; + SetKind = set; + + switch (Compare(get, set)) + { + case 0: + Get = string.Empty; + Set = string.Empty; + General = ConvertAccessorToString(get); + break; + + case > 0: + Get = string.Empty; + Set = ConvertAccessorToString(set); + General = ConvertAccessorToString(get); + break; + + case < 0: + Get = ConvertAccessorToString(get); + Set = string.Empty; + General = ConvertAccessorToString(set); + break; + } + } + + public static Accessors GetAccessors(IFieldSymbol field) + { + var get = SyntaxKind.PrivateKeyword; + var set = SyntaxKind.PrivateKeyword; + + if (field.TryGetAnyAttributeInSelf(out var accessAttribute, AccessAttribute)) + { + if (accessAttribute.ConstructorArguments.Length == 1) + { + var value = (SyntaxKind)(int)(accessAttribute.ConstructorArguments[0].Value ?? SyntaxKind.PrivateKeyword); + get = value; + set = value; + } + + foreach (var argument in accessAttribute!.NamedArguments) + { + switch (argument.Key) + { + case "Get": get = (SyntaxKind)(int)(argument.Value.Value ?? SyntaxKind.PrivateKeyword); break; + case "Set": set = (SyntaxKind)(int)(argument.Value.Value ?? SyntaxKind.PrivateKeyword); break; + } + } + } + + return new Accessors(get, set); + } + + public static string ConvertAccessorToString(SyntaxKind syntaxKind) => syntaxKind switch + { + SyntaxKind.PublicKeyword => "public ", + SyntaxKind.PrivateKeyword => "private ", + SyntaxKind.ProtectedKeyword => "protected ", + _ => string.Empty + }; + + private static int Compare(in SyntaxKind get, in SyntaxKind set) + { + if (get == set) return 0; + + if (set is SyntaxKind.PrivateKeyword) return 1; + if (get is SyntaxKind.PrivateKeyword) return -1; + + if (set is SyntaxKind.ProtectedKeyword) return 1; + if (get is SyntaxKind.ProtectedKeyword) return -1; + + // This is not impossible. + return 0; + } + } +} diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableField.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableField.cs deleted file mode 100644 index 6055a08..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableField.cs +++ /dev/null @@ -1,149 +0,0 @@ -using Microsoft.CodeAnalysis; -using Aspid.Generators.Helper; -using System.Collections.Immutable; -using Microsoft.CodeAnalysis.CSharp; -using Aspid.MVVM.Generators.Helpers; -using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; -using static Aspid.Generators.Helper.Classes; -using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; -using static Aspid.MVVM.Generators.Generators.Descriptions.General; - -namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; - -public class BindableField : BindableMember -{ - public readonly bool IsReadOnly; - public readonly string GetAccessAsText; - public readonly string SetAccessAsText; - public readonly string GeneralAccessAsText; - public readonly ImmutableArray BindAlso; - - public BindableField(IFieldSymbol field, BindMode mode, ImmutableArray bindAlso) - : base(field, - mode, - field.Type.ToDisplayStringGlobal(), - field.Name, - field.IsConst ? field.Name : field.GetPropertyName(), - string.Empty, - GeneratedBindableMembers.CreateForField(field)) - { - BindAlso = bindAlso; - IsReadOnly = mode is BindMode.OneTime; - - var accessors = GetAccessors(field); - - GetAccessAsText = accessors.Get == accessors.General - ? string.Empty - : ConvertAccessToText(accessors.Get); - - SetAccessAsText = accessors.Set == accessors.General - ? string.Empty - : ConvertAccessToText(accessors.Set); - - GeneralAccessAsText = ConvertAccessToText(accessors.General); - } - - public string ToDeclarationPropertyString() - { - return IsReadOnly - ? $""" - {GeneratedCodeViewModelAttribute} - {GeneralAccessAsText}{Type} {GeneratedName} => {Name}; - """ - : $$""" - {{GeneratedCodeViewModelAttribute}} - {{GeneralAccessAsText}}{{Type}} {{GeneratedName}} - { - {{GetAccessAsText}}get => {{Name}}; - {{SetAccessAsText}}set => Set{{GeneratedName}}(value); - } - """; - } - - // TODO Nullable? - public string ToSetMethodString() - { - if (Mode is BindMode.OneTime) return string.Empty; - - var setMethod = $"Set{GeneratedName}"; - var onMethodChanged = $"On{GeneratedName}Changed"; - var onMethodChanging = $"On{GeneratedName}Changing"; - - var eventInvoke = Bindable.Invoke; - var keyWordThis = !Member.IsStatic ? "this." : string.Empty; - - foreach (var property in BindAlso) - eventInvoke += $"\n\t{property.Bindable.Invoke}"; - - return - $$""" - {{GeneratedCodeViewModelAttribute}} - private void {{setMethod}}({{Type}} value) - { - if ({{EqualityComparer_1}}<{{Type}}>.Default.Equals({{Name}}, value)) return; - - {{onMethodChanging}}({{Name}}, value); - {{keyWordThis}}{{Name}} = value; - {{eventInvoke}} - {{onMethodChanged}}(value); - } - - {{GeneratedCodeViewModelAttribute}} - partial void {{onMethodChanging}}({{Type}} oldValue, {{Type}} newValue); - - {{GeneratedCodeViewModelAttribute}} - partial void {{onMethodChanged}}({{Type}} newValue); - """; - } - - private static string ConvertAccessToText(SyntaxKind syntaxKind) => syntaxKind switch - { - SyntaxKind.PrivateKeyword => "private ", - SyntaxKind.ProtectedKeyword => "protected ", - SyntaxKind.PublicKeyword => "public ", - _ => "" - }; - - private static Accessors GetAccessors(IFieldSymbol field) - { - var accessors = new Accessors(SyntaxKind.PrivateKeyword, SyntaxKind.PrivateKeyword); - if (!field.TryGetAnyAttributeInSelf(out var accessAttribute, AccessAttribute)) return accessors; - - if (accessAttribute!.ConstructorArguments.Length == 1) - { - var value = (SyntaxKind)(int)(accessAttribute.ConstructorArguments[0].Value ?? SyntaxKind.PrivateKeyword); - accessors.Get = value; - accessors.Set = value; - } - - foreach (var argument in accessAttribute!.NamedArguments) - { - switch (argument.Key) - { - case "Get": accessors.Get = (SyntaxKind)(int)(argument.Value.Value ?? SyntaxKind.PrivateKeyword); break; - case "Set": accessors.Set = (SyntaxKind)(int)(argument.Value.Value ?? SyntaxKind.PrivateKeyword); break; - } - } - - return accessors; - } - - private ref struct Accessors(SyntaxKind get, SyntaxKind set) - { - public SyntaxKind Get = get; - public SyntaxKind Set = set; - - public SyntaxKind General => GetGeneralAccessor(Get, Set); - - private static SyntaxKind GetGeneralAccessor(SyntaxKind getAccessor, SyntaxKind setAccessor) - { - if (setAccessor == getAccessor) return getAccessor; - if (getAccessor == SyntaxKind.PublicKeyword) return getAccessor; - if (setAccessor == SyntaxKind.PublicKeyword) return setAccessor; - if (getAccessor == SyntaxKind.ProtectedKeyword) return getAccessor; - if (setAccessor == SyntaxKind.ProtectedKeyword) return setAccessor; - - return SyntaxKind.PrivateKeyword; - } - } -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableFieldFactory.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableFieldFactory.cs index 4390e18..665c150 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableFieldFactory.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableFieldFactory.cs @@ -13,9 +13,9 @@ namespace Aspid.MVVM.Generators.Generators.ViewModels.Factories; public static class BindableFieldFactory { - public static IReadOnlyCollection Create(ImmutableArray fields, IReadOnlyCollection bindableBindAlsos) + public static IReadOnlyCollection Create(ImmutableArray fields, IReadOnlyCollection bindableBindAlsos) { - var bindableFields = new List(); + var bindableFields = new List(); foreach (var field in fields) { @@ -36,7 +36,7 @@ public static IReadOnlyCollection Create(ImmutableArray Create(ITypeSymbol symbol) var generatedProperties = bindableFields .Where(field => field.Type.ToString() == "bool") - .Select(field => field.GeneratedName) + .Select(field => field.Name) .ToImmutableArray(); var bindableCommands = BindableCommandFactory.Create(members.Methods, members.Properties, generatedProperties); From 02579a53050f7028d42ec37c4ae391f72c295dc3 Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Wed, 3 Dec 2025 11:39:50 +0300 Subject: [PATCH 10/29] Add #region in generated code --- ...ody.cs => BindableInterfaceMembersBody.cs} | 4 +- .../{PropertiesBody.cs => BindableMembers.cs} | 24 +++-------- .../Body/GeneratedPropertiesBody.cs | 41 +++++++++++++++++++ .../ViewModels/Body/RelayCommandBody.cs | 1 - .../Data/Infos/BindableCommandInfo.cs | 2 + .../Data/Infos/GeneratedBindableMembers.cs | 6 +++ .../ViewModels/ViewModelGenerator.cs | 5 ++- 7 files changed, 60 insertions(+), 23 deletions(-) rename Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/{BindableMembersBody.cs => BindableInterfaceMembersBody.cs} (95%) rename Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/{PropertiesBody.cs => BindableMembers.cs} (71%) create mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/GeneratedPropertiesBody.cs diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembersBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableInterfaceMembersBody.cs similarity index 95% rename from Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembersBody.cs rename to Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableInterfaceMembersBody.cs index f3cd43a..453d0d4 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembersBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableInterfaceMembersBody.cs @@ -7,7 +7,7 @@ namespace Aspid.MVVM.Generators.Generators.ViewModels.Body; -public static class BindableMembersBody +public static class BindableInterfaceMembersBody { public static void Generate( string @namespace, @@ -20,7 +20,7 @@ public static void Generate( .AppendProperties(data) .EndClass(@namespace); - context.AddSource(declaration.GetFileName(@namespace, "BindableMembers"), code.GetSourceText()); + context.AddSource(declaration.GetFileName(@namespace, "BindableInterfaceMembers"), code.GetSourceText()); } private static CodeWriter AppendProperties(this CodeWriter code, in ViewModelData data) diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertiesBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembers.cs similarity index 71% rename from Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertiesBody.cs rename to Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembers.cs index 2959ebf..441e18a 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertiesBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembers.cs @@ -1,12 +1,11 @@ using Microsoft.CodeAnalysis; using Aspid.Generators.Helper; using Aspid.MVVM.Generators.Generators.ViewModels.Data; -using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; using static Aspid.MVVM.Generators.Generators.Descriptions.General; namespace Aspid.MVVM.Generators.Generators.ViewModels.Body; -public static class PropertiesBody +public static class BindableMembers { public static void Generate( string @namespace, @@ -20,7 +19,7 @@ public static void Generate( .AppendBody(data) .EndClass(@namespace); - context.AddSource(declaration.GetFileName(@namespace, "Properties"), code.GetSourceText()); + context.AddSource(declaration.GetFileName(@namespace, "BindableMembers"), code.GetSourceText()); } extension(CodeWriter code) @@ -29,8 +28,7 @@ private CodeWriter AppendBody(in ViewModelData data) { if (!data.Members.IsEmpty) { - code.AppendProperties(data) - .AppendBindableMembers(data); + code.AppendBindableMembers(data); } return code.AppendNotifyAll(data); @@ -47,21 +45,11 @@ private CodeWriter AppendBindableMembers(in ViewModelData data) return code; } - private CodeWriter AppendProperties(in ViewModelData data) - { - foreach (var field in data.Members.OfType()) - { - code.AppendMultiline(field.Declaration); - } - - return code; - } - private CodeWriter AppendNotifyAll(in ViewModelData data) { - var modifiers = "private"; - if (data.Inheritor is not Inheritor.None) modifiers = "protected override"; - else if (!data.Symbol.IsSealed) modifiers = "protected virtual"; + var modifiers = "public"; + if (data.Inheritor is not Inheritor.None) modifiers += " override"; + else if (!data.Symbol.IsSealed) modifiers += " virtual"; code.AppendLine(GeneratedCodeViewModelAttribute) .AppendLine($"{modifiers} void NotifyAll()") diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/GeneratedPropertiesBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/GeneratedPropertiesBody.cs new file mode 100644 index 0000000..73d35c2 --- /dev/null +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/GeneratedPropertiesBody.cs @@ -0,0 +1,41 @@ +using Microsoft.CodeAnalysis; +using Aspid.Generators.Helper; +using System.Collections.Immutable; +using Aspid.MVVM.Generators.Generators.ViewModels.Data; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; + +namespace Aspid.MVVM.Generators.Generators.ViewModels.Body; + +public static class GeneratedPropertiesBody +{ + public static void Generate( + string namespaceName, + in ViewModelData data, + DeclarationText declaration, + in SourceProductionContext context) + { + var fields = data.Members.OfType().ToImmutableArray(); + if (fields.Length is 0) return; + + var code = new CodeWriter(); + + code.BeginClass(namespaceName, declaration) + .AppendBody(fields) + .EndClass(namespaceName); + + context.AddSource(declaration.GetFileName(namespaceName, "GeneratedProperties"), code.GetSourceText()); + } + + extension(CodeWriter code) + { + private CodeWriter AppendBody(in ImmutableArray fields) + { + foreach (var field in fields) + { + code.AppendMultiline(field.Declaration); + } + + return code; + } + } +} diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/RelayCommandBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/RelayCommandBody.cs index 9e0d816..02400e8 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/RelayCommandBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/RelayCommandBody.cs @@ -3,7 +3,6 @@ using Aspid.Generators.Helper; using Aspid.MVVM.Generators.Generators.ViewModels.Data; using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; -using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; namespace Aspid.MVVM.Generators.Generators.ViewModels.Body; diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableCommandInfo.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableCommandInfo.cs index a464b4d..af1b6a8 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableCommandInfo.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableCommandInfo.cs @@ -36,12 +36,14 @@ public BindableCommandInfo(IMethodSymbol methodSymbol, string? canExecute, bool CommandDeclaration = $""" + #region {Name} {GeneratedCodeViewModelAttribute} [{EditorBrowsableAttribute}({EditorBrowsableState}.Never)] private {Type} {fieldName}; {GeneratedCodeViewModelAttribute} private {Type} {Name} => {fieldName} ??= new {Type}({methodSymbol.Name}{canExecute}); + #endregion """; } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs index 1470297..e88cc37 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs @@ -164,13 +164,17 @@ private static string RecognizeDeclaration( string propertyName, string propertyType) { + + if (mode is BindMode.OneTime) { return $""" + #region {propertyName} {GeneratedCodeViewModelAttribute} public {propertyType} {propertyName} => {fieldType}.Get({memberName}); + #endregion """; } @@ -184,6 +188,7 @@ private static string RecognizeDeclaration( return $""" + #region {propertyName} [{EditorBrowsableAttribute}({EditorBrowsableState}.Never)] {GeneratedCodeViewModelAttribute} private {fieldType} {fieldName}; @@ -191,6 +196,7 @@ private static string RecognizeDeclaration( {GeneratedCodeViewModelAttribute} public {propertyType} {propertyName} => {instantiate}; + #endregion """; } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/ViewModelGenerator.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/ViewModelGenerator.cs index f2a7823..6aea30a 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/ViewModelGenerator.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/ViewModelGenerator.cs @@ -62,9 +62,10 @@ private static void GenerateCode(SourceProductionContext context, ViewModelData var @namespace = declaration.GetNamespaceName(); var declarationText = new DeclarationText(declaration); - PropertiesBody.Generate(@namespace, data, declarationText, context); + BindableMembers.Generate(@namespace, data, declarationText, context); RelayCommandBody.Generate(@namespace, data, declarationText, context); - BindableMembersBody.Generate(@namespace, data, declarationText, context); FindBindableMembersBody.Generate(@namespace, data, declarationText, context); + GeneratedPropertiesBody.Generate(@namespace, data, declarationText, context); + BindableInterfaceMembersBody.Generate(@namespace, data, declarationText, context); } } \ No newline at end of file From 299385b5dc450a608b409e66874e0d60e33975ec Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Sat, 20 Dec 2025 17:03:32 +0300 Subject: [PATCH 11/29] Add support for property --- .../ViewModels/Body/BindableMembers.cs | 46 ++++++++- .../Body/GeneratedPropertyMethodsBody.cs | 42 ++++++++ .../Data/Infos/BindableBindAlsoInfo.cs | 10 ++ .../Data/Infos/BindableCommandInfo.cs | 7 +- .../Data/Infos/BindableFieldInfo.cs | 21 ++-- .../Data/Infos/BindablePropertyInfo.cs | 95 +++++++++++++++++++ .../Data/Infos/GeneratedBindableMembers.cs | 69 ++++++++++---- .../Data/Infos/IBindableMemberInfo.cs | 3 + .../ViewModels/Data/Members/BindableMember.cs | 49 ---------- .../ViewModels/Data/ViewModelData.cs | 3 +- .../ViewModels/Extensions/SymbolExtensions.cs | 28 ++++-- .../Factories/BindableFieldFactory.cs | 31 +----- .../Factories/BindableMembersFactory.cs | 13 ++- .../Factories/BindablePropertyFactory.cs | 40 ++++++++ .../ViewModels/ViewModelGenerator.cs | 1 + 15 files changed, 341 insertions(+), 117 deletions(-) create mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/GeneratedPropertyMethodsBody.cs create mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindablePropertyInfo.cs delete mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableMember.cs create mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindablePropertyFactory.cs diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembers.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembers.cs index 441e18a..ba67422 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembers.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembers.cs @@ -1,7 +1,12 @@ +using System.Linq; using Microsoft.CodeAnalysis; using Aspid.Generators.Helper; +using System.Collections.Generic; using Aspid.MVVM.Generators.Generators.ViewModels.Data; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; +using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; using static Aspid.MVVM.Generators.Generators.Descriptions.General; +using BindMode = Aspid.MVVM.Generators.Generators.ViewModels.Data.BindMode; namespace Aspid.MVVM.Generators.Generators.ViewModels.Body; @@ -22,6 +27,26 @@ public static void Generate( context.AddSource(declaration.GetFileName(@namespace, "BindableMembers"), code.GetSourceText()); } + private static IEnumerable GetBindableBindAlso(ISymbol member, in ViewModelData data) + { + var set = new HashSet(); + + foreach (var attribute in member.GetAttributes()) + { + if (attribute.AttributeClass is not null && + attribute.AttributeClass.ToDisplayStringGlobal() == BindAlsoAttribute) + { + var value = attribute.ConstructorArguments[0].Value; + if (value is null) continue; + + set.Add(value.ToString()); + } + } + + return data.Members.Where(bindAlso => + set.Contains(bindAlso.Name) && bindAlso.Mode is not BindMode.OneTime and not BindMode.None); + } + extension(CodeWriter code) { private CodeWriter AppendBody(in ViewModelData data) @@ -38,7 +63,26 @@ private CodeWriter AppendBindableMembers(in ViewModelData data) { foreach (var member in data.Members) { - code.AppendMultiline(member.Bindable.Declaration) + code.AppendLine($"#region {member.Name}") + .AppendMultiline(member.Bindable.Declaration) + .AppendLine(); + + if (!string.IsNullOrWhiteSpace(member.Bindable.OnPropertyChangedName)) + { + code.AppendLine($"{GeneratedCodeViewModelAttribute}") + .AppendLine($"private void On{member.Name}PropertyChanged()") + .BeginBlock() + .AppendLineIf(!string.IsNullOrWhiteSpace(member.Bindable.Invoke), member.Bindable.Invoke); + + foreach (var bindAlso in GetBindableBindAlso(member.Member, data)) + { + code.AppendLineIf(!string.IsNullOrWhiteSpace(member.Bindable.OnPropertyChangedName), $"{bindAlso.Bindable.OnPropertyChangedName}();"); + } + + code.EndBlock(); + } + + code.AppendLine("#endregion") .AppendLine(); } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/GeneratedPropertyMethodsBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/GeneratedPropertyMethodsBody.cs new file mode 100644 index 0000000..74347e1 --- /dev/null +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/GeneratedPropertyMethodsBody.cs @@ -0,0 +1,42 @@ +using Microsoft.CodeAnalysis; +using Aspid.Generators.Helper; +using System.Collections.Immutable; +using Aspid.MVVM.Generators.Generators.ViewModels.Data; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; + +namespace Aspid.MVVM.Generators.Generators.ViewModels.Body; + +public static class GeneratedPropertyMethodsBody +{ + public static void Generate( + string namespaceName, + in ViewModelData data, + DeclarationText declaration, + in SourceProductionContext context) + { + var properties = data.Members.OfType().ToImmutableArray(); + if (properties.Length is 0) return; + + var code = new CodeWriter(); + + code.BeginClass(namespaceName, declaration) + .AppendBody(properties) + .EndClass(namespaceName); + + context.AddSource(declaration.GetFileName(namespaceName, "GeneratedPropertyMethods"), code.GetSourceText()); + } + + extension(CodeWriter code) + { + private CodeWriter AppendBody(in ImmutableArray properties) + { + foreach (var property in properties) + { + code.AppendMultiline(property.Declaration); + } + + return code; + } + } +} + diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableBindAlsoInfo.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableBindAlsoInfo.cs index 9b13819..95ef2db 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableBindAlsoInfo.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableBindAlsoInfo.cs @@ -2,12 +2,15 @@ using Microsoft.CodeAnalysis; using Aspid.Generators.Helper; using Aspid.MVVM.Generators.Generators.Ids.Data; +using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; public sealed class BindableBindAlsoInfo(IPropertySymbol propertySymbol) : IBindableMemberInfo, IEquatable { private readonly IPropertySymbol _propertySymbol = propertySymbol; + + public ISymbol Member { get; } = propertySymbol; public string Type { get; } = propertySymbol.Type.ToDisplayStringGlobal(); @@ -16,6 +19,13 @@ public sealed class BindableBindAlsoInfo(IPropertySymbol propertySymbol) : IBind public IdData Id { get; } = new(propertySymbol); public BindMode Mode => BindMode.OneWay; + + public bool HasBindAttribute { get; } = propertySymbol.HasAnyAttributeInSelf( + BindAttribute, + OneWayBindAttribute, + TwoWayBindAttribute, + OneTimeBindAttribute, + OneWayToSourceBindAttribute); public GeneratedBindableMembers Bindable { get; } = GeneratedBindableMembers.CreateForBindAlso(propertySymbol); diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableCommandInfo.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableCommandInfo.cs index af1b6a8..6c7fd45 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableCommandInfo.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableCommandInfo.cs @@ -1,9 +1,9 @@ using System.Linq; using System.Text; +using Microsoft.CodeAnalysis; using Aspid.Generators.Helper; -using Aspid.MVVM.Generators.Generators.Ids.Data; using Aspid.MVVM.Generators.Helpers; -using Microsoft.CodeAnalysis; +using Aspid.MVVM.Generators.Generators.Ids.Data; using static Aspid.Generators.Helper.Classes; using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; using static Aspid.MVVM.Generators.Generators.Descriptions.General; @@ -12,6 +12,8 @@ namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; public sealed class BindableCommandInfo : IBindableMemberInfo { + public ISymbol Member { get; } + public string Type { get; } public string Name { get; } @@ -26,6 +28,7 @@ public sealed class BindableCommandInfo : IBindableMemberInfo public BindableCommandInfo(IMethodSymbol methodSymbol, string? canExecute, bool isLambda, bool isMethod) { + Member = methodSymbol; Type = GetTypeName(methodSymbol); Id = new IdData(methodSymbol, "Command"); Name = $"{methodSymbol.GetPropertyName()}Command"; diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableFieldInfo.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableFieldInfo.cs index bea2ec0..ea11c7e 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableFieldInfo.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableFieldInfo.cs @@ -1,7 +1,6 @@ using System.Text; using Microsoft.CodeAnalysis; using Aspid.Generators.Helper; -using System.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp; using Aspid.MVVM.Generators.Helpers; using Aspid.MVVM.Generators.Generators.Ids.Data; @@ -13,6 +12,8 @@ namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; public sealed class BindableFieldInfo : IBindableMemberInfo { + public ISymbol Member { get; } + public string Type { get; } public string Name { get; } @@ -25,9 +26,10 @@ public sealed class BindableFieldInfo : IBindableMemberInfo public string Declaration { get; } - public BindableFieldInfo(IFieldSymbol fieldSymbol, BindMode mode, ImmutableArray bindAlso) + public BindableFieldInfo(IFieldSymbol fieldSymbol, BindMode mode) { Mode = mode; + Member = fieldSymbol; Id = new IdData(fieldSymbol); Type = fieldSymbol.Type.ToDisplayStringGlobal(); Name = fieldSymbol.IsConst ? fieldSymbol.Name : fieldSymbol.GetPropertyName(); @@ -61,11 +63,10 @@ public BindableFieldInfo(IFieldSymbol fieldSymbol, BindMode mode, ImmutableArray var onChangingMethod = $"On{Name}Changing"; var methodModifier = Accessors.ConvertAccessorToString(accessors.SetKind); var keywordThis = !fieldSymbol.IsStatic ? "this." : string.Empty; - - var eventInvoke = Bindable.Invoke; - - foreach (var property in bindAlso) - eventInvoke += $"\n\t\t{property.Bindable.Invoke}"; + + var invoke = !string.IsNullOrWhiteSpace(Bindable.OnPropertyChangedName) + ? $"{Bindable.OnPropertyChangedName}();" + : string.Empty; declaration.AppendLine(); declaration.AppendLine( @@ -81,21 +82,25 @@ public BindableFieldInfo(IFieldSymbol fieldSymbol, BindMode mode, ImmutableArray {{onChangingMethod}}(oldValue, value); { {{keywordThis}}{{fieldSymbol.Name}} = value; - {{eventInvoke}} + {{invoke}} } {{onChangedMethod}}(value); {{onChangedMethod}}(oldValue, value); } + [{{EditorBrowsableAttribute}}({{EditorBrowsableState}}.Never)] {{GeneratedCodeViewModelAttribute}} partial void {{onChangingMethod}}({{Type}} newValue); + [{{EditorBrowsableAttribute}}({{EditorBrowsableState}}.Never)] {{GeneratedCodeViewModelAttribute}} partial void {{onChangingMethod}}({{Type}} oldValue, {{Type}} newValue); + [{{EditorBrowsableAttribute}}({{EditorBrowsableState}}.Never)] {{GeneratedCodeViewModelAttribute}} partial void {{onChangedMethod}}({{Type}} newValue); + [{{EditorBrowsableAttribute}}({{EditorBrowsableState}}.Never)] {{GeneratedCodeViewModelAttribute}} partial void {{onChangedMethod}}({{Type}} oldValue, {{Type}} newValue); """); diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindablePropertyInfo.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindablePropertyInfo.cs new file mode 100644 index 0000000..4af86d7 --- /dev/null +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindablePropertyInfo.cs @@ -0,0 +1,95 @@ +using System.Text; +using Microsoft.CodeAnalysis; +using Aspid.Generators.Helper; +using Aspid.MVVM.Generators.Generators.Ids.Data; +using static Aspid.Generators.Helper.Classes; +using static Aspid.MVVM.Generators.Generators.Descriptions.General; + +namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; + +public sealed class BindablePropertyInfo : IBindableMemberInfo +{ + public ISymbol Member { get; } + + public string Type { get; } + + public string Name { get; } + + public IdData Id { get; } + + public BindMode Mode { get; } + + public GeneratedBindableMembers Bindable { get; } + + public string Declaration { get; } + + public BindablePropertyInfo(IPropertySymbol propertySymbol, BindMode mode) + { + Member = propertySymbol; + Mode = mode; + Id = new IdData(propertySymbol); + Type = propertySymbol.Type.ToDisplayStringGlobal(); + Name = propertySymbol.Name; + Bindable = GeneratedBindableMembers.CreateForProperty(propertySymbol); + + var setMethodName = $"Set{Name}"; + StringBuilder declaration = new(); + + if (Mode is not BindMode.OneTime && Mode is not BindMode.None) + { + var onPropertyChangedMethod = $"On{Name}PropertyChanged"; + + // Для readonly свойств (OneWay) генерируем только метод OnPropertyChanged + if (propertySymbol is { IsReadOnly: false, IsWriteOnly: false }) + { + // Для свойств с сеттером генерируем полный набор методов + var onChangedMethod = $"On{Name}Changed"; + var onChangingMethod = $"On{Name}Changing"; + + declaration.AppendLine($"#region {Name} SetMethod"); + declaration.AppendLine( + $$""" + {{GeneratedCodeViewModelAttribute}} + private bool {{setMethodName}}(ref {{Type}} field, {{Type}} value) + { + if ({{EqualityComparer_1}}<{{Type}}>.Default.Equals(field, value)) return false; + + {{Type}} oldValue = field; + + {{onChangingMethod}}(value); + {{onChangingMethod}}(oldValue, value); + { + field = value; + {{onPropertyChangedMethod}}(); + } + {{onChangedMethod}}(value); + {{onChangedMethod}}(oldValue, value); + + return true; + } + + [{{EditorBrowsableAttribute}}({{EditorBrowsableState}}.Never)] + {{GeneratedCodeViewModelAttribute}} + partial void {{onChangingMethod}}({{Type}} newValue); + + [{{EditorBrowsableAttribute}}({{EditorBrowsableState}}.Never)] + {{GeneratedCodeViewModelAttribute}} + partial void {{onChangingMethod}}({{Type}} oldValue, {{Type}} newValue); + + [{{EditorBrowsableAttribute}}({{EditorBrowsableState}}.Never)] + {{GeneratedCodeViewModelAttribute}} + partial void {{onChangedMethod}}({{Type}} newValue); + + [{{EditorBrowsableAttribute}}({{EditorBrowsableState}}.Never)] + {{GeneratedCodeViewModelAttribute}} + partial void {{onChangedMethod}}({{Type}} oldValue, {{Type}} newValue); + """); + + declaration.AppendLine("#endregion"); + } + } + + Declaration = declaration.ToString(); + } +} + diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs index e88cc37..278b4ad 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs @@ -5,6 +5,7 @@ using static Aspid.Generators.Helper.Classes; using static Aspid.MVVM.Generators.Generators.Descriptions.General; using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; +using SymbolExtensions = Aspid.MVVM.Generators.Helpers.SymbolExtensions; namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; @@ -15,17 +16,20 @@ public readonly struct GeneratedBindableMembers public readonly string Declaration; public readonly string PropertyName; public readonly string PropertyType; + public readonly string? OnPropertyChangedName; private GeneratedBindableMembers( string? invoke, string declaration, string propertyName, - string propertyType) + string propertyType, + string? onPropertyChangedName) { Invoke = invoke; Declaration = declaration; PropertyName = propertyName; PropertyType = propertyType; + OnPropertyChangedName = onPropertyChangedName; } public static GeneratedBindableMembers CreateForRelayCommand( @@ -40,7 +44,7 @@ public static GeneratedBindableMembers CreateForRelayCommand( var declaration = RecognizeDeclaration(mode, memberName, null, fieldType, propertyName, propertyType); - return new GeneratedBindableMembers(null, declaration, propertyName, propertyType); + return new GeneratedBindableMembers(null, declaration, propertyName, propertyType, null); } public static GeneratedBindableMembers CreateForField(IFieldSymbol fieldSymbol) @@ -56,10 +60,37 @@ public static GeneratedBindableMembers CreateForField(IFieldSymbol fieldSymbol) var propertyName = $"{fieldSymbol.GetPropertyName()}Bindable"; var propertyType = RecognizePropertyType(fieldSymbol.Type, mode); + var invoke = RecognizeInvoke(mode, memberName, fieldName); + var declaration = RecognizeDeclaration(mode, memberName, fieldName, fieldType, propertyName, propertyType, $"Set{SymbolExtensions.GetPropertyName(memberName)}"); + + var onPropertyChangedName = mode is not BindMode.OneTime and not BindMode.OneWayToSource and not BindMode.None + ? $"On{fieldSymbol.GetPropertyName()}PropertyChanged" + : null; + + return new GeneratedBindableMembers(invoke, declaration, propertyName, propertyType, onPropertyChangedName); + } + + public static GeneratedBindableMembers CreateForProperty(IPropertySymbol propertySymbol) + { + var mode = propertySymbol.GetBindMode(); + var memberName = propertySymbol.Name; + + // PropertyName -> __propertyNameBindable + var fieldName = $"{propertySymbol.GetFieldName(prefix: "__")}Bindable"; + var fieldType = RecognizeFieldType(propertySymbol.Type, mode); + + // PropertyName -> PropertyNameBindable + var propertyName = $"{propertySymbol.Name}Bindable"; + var propertyType = RecognizePropertyType(propertySymbol.Type, mode); + var invoke = RecognizeInvoke(mode, memberName, fieldName); var declaration = RecognizeDeclaration(mode, memberName, fieldName, fieldType, propertyName, propertyType); - return new GeneratedBindableMembers(invoke, declaration, propertyName, propertyType); + var onPropertyChangedName = mode is not BindMode.OneTime and not BindMode.OneWayToSource and not BindMode.None + ? $"On{propertySymbol.Name}PropertyChanged" + : null; + + return new GeneratedBindableMembers(invoke, declaration, propertyName, propertyType, onPropertyChangedName); } public static GeneratedBindableMembers CreateForBindAlso(IPropertySymbol propertySymbol) @@ -78,7 +109,7 @@ public static GeneratedBindableMembers CreateForBindAlso(IPropertySymbol propert var invoke = RecognizeInvoke(mode, memberName, fieldName); var declaration = RecognizeDeclaration(mode, memberName, fieldName, fieldType, propertyName, propertyType); - return new GeneratedBindableMembers(invoke, declaration, propertyName, propertyType); + return new GeneratedBindableMembers(invoke, declaration, propertyName, propertyType, $"On{propertySymbol.Name}PropertyChanged"); } private static string RecognizeFieldType(ITypeSymbol type, BindMode mode) @@ -156,39 +187,45 @@ private static string RecognizePropertyType(ITypeSymbol type, BindMode mode) : $"{result}<{type.ToDisplayStringGlobal()}>"; } + private static string? RecognizeInvoke(BindMode mode, string memberName, string fieldName) + { + return mode is not (BindMode.OneWayToSource or BindMode.OneTime) + ? $"this.{fieldName}?.Invoke({memberName});" + : null; + } + private static string RecognizeDeclaration( BindMode mode, string memberName, string? fieldName, string fieldType, string propertyName, - string propertyType) + string propertyType, + string? setMethod = null) { - - if (mode is BindMode.OneTime) { return $""" - #region {propertyName} {GeneratedCodeViewModelAttribute} public {propertyType} {propertyName} => {fieldType}.Get({memberName}); - #endregion """; } + setMethod ??= $"value => {memberName} = value"; + + // For properties, we use a property setter directly instead of a method reference var instantiate = mode switch { BindMode.OneWay => $"{fieldName} ??= new({memberName})", - BindMode.TwoWay => $"{fieldName} ??= new({memberName}, Set{memberName})", - BindMode.OneWayToSource => $"{fieldName} ??= new(Set{memberName})", + BindMode.TwoWay => $"{fieldName} ??= new({memberName}, {setMethod})", + BindMode.OneWayToSource => $"{fieldName} ??= new({setMethod})", _ => string.Empty }; return $""" - #region {propertyName} [{EditorBrowsableAttribute}({EditorBrowsableState}.Never)] {GeneratedCodeViewModelAttribute} private {fieldType} {fieldName}; @@ -196,14 +233,6 @@ private static string RecognizeDeclaration( {GeneratedCodeViewModelAttribute} public {propertyType} {propertyName} => {instantiate}; - #endregion """; } - - private static string? RecognizeInvoke(BindMode mode, string memberName, string fieldName) - { - return mode is not (BindMode.OneWayToSource or BindMode.OneTime) - ? $"this.{fieldName}?.Invoke({memberName});" - : null; - } } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/IBindableMemberInfo.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/IBindableMemberInfo.cs index a64d1c5..624d468 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/IBindableMemberInfo.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/IBindableMemberInfo.cs @@ -1,9 +1,12 @@ +using Microsoft.CodeAnalysis; using Aspid.MVVM.Generators.Generators.Ids.Data; namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; public interface IBindableMemberInfo { + public ISymbol Member { get; } + public string Type { get; } public string Name { get; } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableMember.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableMember.cs deleted file mode 100644 index 4aa00f6..0000000 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Members/BindableMember.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Microsoft.CodeAnalysis; -using Aspid.MVVM.Generators.Generators.Ids.Data; -using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; - -namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; - -public abstract class BindableMember : BindableMember - where T : ISymbol -{ - public readonly T Member; - - protected BindableMember(T member, BindMode mode, string type, string sourceName, string generatedName, string idPostfix, GeneratedBindableMembers bindable) - : base(member, mode, type, sourceName, generatedName, idPostfix, bindable) - { - Member = member; - } -} - -public abstract class BindableMember : IBindableMemberInfo -{ - public readonly string GeneratedName; - - public string Type { get; } - - public string Name { get; } - - public IdData Id { get; } - - public BindMode Mode { get; } - - public GeneratedBindableMembers Bindable { get; } - - protected BindableMember( - ISymbol member, - BindMode mode, - string type, - string sourceName, - string generatedName, - string idPostfix, - GeneratedBindableMembers bindable) - { - Type = type; - Mode = mode; - Name = sourceName; - GeneratedName = generatedName; - Id = new IdData(member, idPostfix); - Bindable = bindable; - } -} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/ViewModelData.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/ViewModelData.cs index 09b97e4..753e9bf 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/ViewModelData.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/ViewModelData.cs @@ -1,8 +1,8 @@ using Microsoft.CodeAnalysis; using System.Collections.Generic; using System.Collections.Immutable; -using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members.Collections; @@ -24,5 +24,6 @@ public readonly struct ViewModelData( public readonly ImmutableArray Members = members; public readonly ImmutableArray IdGroups = idGroups; + public readonly Dictionary CustomViewModelInterfaces = customViewModelInterfaces; } \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/SymbolExtensions.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/SymbolExtensions.cs index 15d3f17..18ea793 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/SymbolExtensions.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/SymbolExtensions.cs @@ -12,19 +12,28 @@ public static BindMode GetBindMode(this ISymbol member) if (member.TryGetAnyAttributeInSelf(out var attribute, Classes.BindAttribute, Classes.OneWayBindAttribute, Classes.TwoWayBindAttribute, Classes.OneTimeBindAttribute, Classes.OneWayToSourceBindAttribute)) { - var attributeName = attribute!.AttributeClass!.ToDisplayString(); + var attributeName = attribute!.AttributeClass.ToDisplayString(); if (attributeName == Classes.BindAttribute.FullName) { - if (attribute!.ConstructorArguments.Length is 0) + if (attribute.ConstructorArguments.Length is 0) { - if (member is not IFieldSymbol field) return BindMode.TwoWay; - if (field.IsReadOnly || field.IsConst) return BindMode.OneTime; - + if (member is IFieldSymbol field) + { + if (field.IsReadOnly || field.IsConst) return BindMode.OneTime; + return BindMode.TwoWay; + } + + if (member is IPropertySymbol property) + { + if (property.IsReadOnly) return Determine(BindMode.OneWay); + if (property.IsWriteOnly) return Determine(BindMode.OneWayToSource); + } + return BindMode.TwoWay; } - return Determine((BindMode)(int)attribute!.ConstructorArguments[0].Value!); + return Determine((BindMode)(int)attribute.ConstructorArguments[0].Value!); } if (attributeName == Classes.OneWayBindAttribute.FullName) @@ -51,6 +60,13 @@ BindMode Determine(BindMode current) if (field.IsReadOnly && current is not BindMode.OneTime) return BindMode.None; break; } + + case IPropertySymbol property: + { + if (property.IsReadOnly && current is not (BindMode.OneTime or BindMode.OneWay)) return BindMode.None; + if (property.IsWriteOnly && current is not BindMode.OneWayToSource) return BindMode.None; + break; + } } return current; diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableFieldFactory.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableFieldFactory.cs index 665c150..35670bd 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableFieldFactory.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableFieldFactory.cs @@ -1,19 +1,15 @@ -using System.Linq; using Microsoft.CodeAnalysis; -using Aspid.Generators.Helper; using System.Collections.Generic; using System.Collections.Immutable; using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; using Aspid.MVVM.Generators.Generators.ViewModels.Extensions; -using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; -using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; using BindMode = Aspid.MVVM.Generators.Generators.ViewModels.Data.BindMode; namespace Aspid.MVVM.Generators.Generators.ViewModels.Factories; public static class BindableFieldFactory { - public static IReadOnlyCollection Create(ImmutableArray fields, IReadOnlyCollection bindableBindAlsos) + public static IReadOnlyCollection Create(ImmutableArray fields) { var bindableFields = new List(); @@ -32,33 +28,14 @@ public static IReadOnlyCollection Create(ImmutableArray GetBindableBindAlso(IFieldSymbol field, IReadOnlyCollection allBindableBindAlsos) - { - var set = new HashSet(); - - foreach (var attribute in field.GetAttributes()) - { - if (attribute.AttributeClass != null && - attribute.AttributeClass.ToDisplayStringGlobal() == BindAlsoAttribute) - { - var value = attribute.ConstructorArguments[0].Value; - if (value is null) continue; - - set.Add(value.ToString()); - } - } - - return allBindableBindAlsos.Where(bindableBindAlso => - set.Contains(bindableBindAlso.Name)).ToImmutableArray(); - } } \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableMembersFactory.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableMembersFactory.cs index e18fa04..fc3bd05 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableMembersFactory.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableMembersFactory.cs @@ -13,20 +13,27 @@ public static ImmutableArray Create(ITypeSymbol symbol) { var members = new MembersByGroup(symbol); + var bindableFields = BindableFieldFactory.Create(members.Fields); var bindableBindAlso = BindableBindAlsoFactory.Create(members.All); - var bindableFields = BindableFieldFactory.Create(members.Fields, bindableBindAlso); + var bindableProperties = BindablePropertyFactory.Create(members.Properties); var generatedProperties = bindableFields .Where(field => field.Type.ToString() == "bool") .Select(field => field.Name) + .Concat(bindableProperties + .Where(property => property.Type.ToString() == "bool") + .Select(property => property.Name)) .ToImmutableArray(); var bindableCommands = BindableCommandFactory.Create(members.Methods, members.Properties, generatedProperties); - var bindableMembers = new List(bindableBindAlso.Count + bindableFields.Count + bindableCommands.Count); + + var filteredBindableBindAlso = bindableBindAlso.Where(b => !b.HasBindAttribute).ToList(); + var bindableMembers = new List(filteredBindableBindAlso.Count + bindableFields.Count + bindableProperties.Count + bindableCommands.Count); bindableMembers.AddRange(bindableFields); + bindableMembers.AddRange(bindableProperties); bindableMembers.AddRange(bindableCommands); - bindableMembers.AddRange(bindableBindAlso); + bindableMembers.AddRange(filteredBindableBindAlso); return bindableMembers.ToImmutableArray(); } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindablePropertyFactory.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindablePropertyFactory.cs new file mode 100644 index 0000000..4d6fd52 --- /dev/null +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindablePropertyFactory.cs @@ -0,0 +1,40 @@ +using Microsoft.CodeAnalysis; +using System.Collections.Generic; +using System.Collections.Immutable; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; +using Aspid.MVVM.Generators.Generators.ViewModels.Extensions; +using BindMode = Aspid.MVVM.Generators.Generators.ViewModels.Data.BindMode; + +namespace Aspid.MVVM.Generators.Generators.ViewModels.Factories; + +public static class BindablePropertyFactory +{ + public static IReadOnlyCollection Create(ImmutableArray properties) + { + var bindableProperties = new List(); + + foreach (var property in properties) + { + var mode = property.GetBindMode(); + + switch (mode) + { + case BindMode.OneTime: + case BindMode.OneWay: break; + + case BindMode.TwoWay: + case BindMode.OneWayToSource: + { + if (property.IsReadOnly) continue; + break; + } + + default: continue; + } + + bindableProperties.Add(new BindablePropertyInfo(property, mode)); + } + + return bindableProperties; + } +} diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/ViewModelGenerator.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/ViewModelGenerator.cs index 6aea30a..ba413e8 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/ViewModelGenerator.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/ViewModelGenerator.cs @@ -66,6 +66,7 @@ private static void GenerateCode(SourceProductionContext context, ViewModelData RelayCommandBody.Generate(@namespace, data, declarationText, context); FindBindableMembersBody.Generate(@namespace, data, declarationText, context); GeneratedPropertiesBody.Generate(@namespace, data, declarationText, context); + GeneratedPropertyMethodsBody.Generate(@namespace, data, declarationText, context); BindableInterfaceMembersBody.Generate(@namespace, data, declarationText, context); } } \ No newline at end of file From e4cdf1c9ed61266a2be69aac57b37d70cb585124 Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Mon, 22 Dec 2025 10:39:57 +0300 Subject: [PATCH 12/29] Upgrade Bindable Property generated --- .../Generators/Ids/IdGenerator.Find.cs | 2 +- .../Generators/Ids/IdGenerator.cs | 2 +- .../Data/Infos/BindablePropertyInfo.cs | 97 +++++++++++-------- .../Extensions/MethodUsageAnalyzer.cs | 31 ++++++ .../Factories/BindableMembersFactory.cs | 5 +- .../Factories/BindablePropertyFactory.cs | 14 ++- .../ViewModels/ViewModelGenerator.cs | 2 +- .../Views/Body/GenericInitializeView.cs | 2 +- 8 files changed, 104 insertions(+), 51 deletions(-) create mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/MethodUsageAnalyzer.cs diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.Find.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.Find.cs index ad917ea..7bf4807 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.Find.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.Find.cs @@ -33,7 +33,7 @@ public partial class IdGenerator } else { - var members = BindableMembersFactory.Create(symbol); + var members = BindableMembersFactory.Create(symbol, syntax); foreach (var member in members) ids.Add(member.Id.SourceValue); diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.cs index 74c2798..2f71648 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.cs @@ -22,7 +22,7 @@ private static bool SyntacticPredicate(SyntaxNode node, CancellationToken cancel { var candidate = node switch { - ClassDeclarationSyntax or StructDeclarationSyntax => node as TypeDeclarationSyntax, + ClassDeclarationSyntax => node as TypeDeclarationSyntax, _ => null }; diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindablePropertyInfo.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindablePropertyInfo.cs index 4af86d7..13d8f14 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindablePropertyInfo.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindablePropertyInfo.cs @@ -23,67 +23,78 @@ public sealed class BindablePropertyInfo : IBindableMemberInfo public string Declaration { get; } - public BindablePropertyInfo(IPropertySymbol propertySymbol, BindMode mode) + public BindablePropertyInfo(IPropertySymbol propertySymbol, BindMode mode, bool isSetMethodUsed) { - Member = propertySymbol; Mode = mode; + Member = propertySymbol; + Name = propertySymbol.Name; Id = new IdData(propertySymbol); Type = propertySymbol.Type.ToDisplayStringGlobal(); - Name = propertySymbol.Name; Bindable = GeneratedBindableMembers.CreateForProperty(propertySymbol); - var setMethodName = $"Set{Name}"; + var setMethodName = $"Set{Name}Field"; StringBuilder declaration = new(); if (Mode is not BindMode.OneTime && Mode is not BindMode.None) { var onPropertyChangedMethod = $"On{Name}PropertyChanged"; - - // Для readonly свойств (OneWay) генерируем только метод OnPropertyChanged + if (propertySymbol is { IsReadOnly: false, IsWriteOnly: false }) { - // Для свойств с сеттером генерируем полный набор методов - var onChangedMethod = $"On{Name}Changed"; - var onChangingMethod = $"On{Name}Changing"; + declaration.AppendLine($"#region {Name}"); + + if (isSetMethodUsed) + { + var onChangedMethod = $"On{Name}Changed"; + var onChangingMethod = $"On{Name}Changing"; - declaration.AppendLine($"#region {Name} SetMethod"); - declaration.AppendLine( - $$""" - {{GeneratedCodeViewModelAttribute}} - private bool {{setMethodName}}(ref {{Type}} field, {{Type}} value) - { - if ({{EqualityComparer_1}}<{{Type}}>.Default.Equals(field, value)) return false; - - {{Type}} oldValue = field; - - {{onChangingMethod}}(value); - {{onChangingMethod}}(oldValue, value); - { - field = value; - {{onPropertyChangedMethod}}(); - } - {{onChangedMethod}}(value); - {{onChangedMethod}}(oldValue, value); - - return true; - } + declaration.AppendLine( + $$""" + {{GeneratedCodeViewModelAttribute}} + private bool {{setMethodName}}(ref {{Type}} field, {{Type}} value) + { + if ({{EqualityComparer_1}}<{{Type}}>.Default.Equals(field, value)) return false; + + {{Type}} oldValue = field; + + {{onChangingMethod}}(value); + {{onChangingMethod}}(oldValue, value); + { + field = value; + {{onPropertyChangedMethod}}(); + } + {{onChangedMethod}}(value); + {{onChangedMethod}}(oldValue, value); + + return true; + } - [{{EditorBrowsableAttribute}}({{EditorBrowsableState}}.Never)] - {{GeneratedCodeViewModelAttribute}} - partial void {{onChangingMethod}}({{Type}} newValue); + [{{EditorBrowsableAttribute}}({{EditorBrowsableState}}.Never)] + {{GeneratedCodeViewModelAttribute}} + partial void {{onChangingMethod}}({{Type}} newValue); - [{{EditorBrowsableAttribute}}({{EditorBrowsableState}}.Never)] - {{GeneratedCodeViewModelAttribute}} - partial void {{onChangingMethod}}({{Type}} oldValue, {{Type}} newValue); + [{{EditorBrowsableAttribute}}({{EditorBrowsableState}}.Never)] + {{GeneratedCodeViewModelAttribute}} + partial void {{onChangingMethod}}({{Type}} oldValue, {{Type}} newValue); - [{{EditorBrowsableAttribute}}({{EditorBrowsableState}}.Never)] - {{GeneratedCodeViewModelAttribute}} - partial void {{onChangedMethod}}({{Type}} newValue); + [{{EditorBrowsableAttribute}}({{EditorBrowsableState}}.Never)] + {{GeneratedCodeViewModelAttribute}} + partial void {{onChangedMethod}}({{Type}} newValue); - [{{EditorBrowsableAttribute}}({{EditorBrowsableState}}.Never)] - {{GeneratedCodeViewModelAttribute}} - partial void {{onChangedMethod}}({{Type}} oldValue, {{Type}} newValue); - """); + [{{EditorBrowsableAttribute}}({{EditorBrowsableState}}.Never)] + {{GeneratedCodeViewModelAttribute}} + partial void {{onChangedMethod}}({{Type}} oldValue, {{Type}} newValue); + """); + } + else + { + declaration.AppendLine( + $""" + {GeneratedCodeViewModelAttribute} + private bool {setMethodName}(ref {Type} field, {Type} value) => + throw new {Classes.NotImplementedException}("Generator Error: SetMethod is not used."); + """); + } declaration.AppendLine("#endregion"); } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/MethodUsageAnalyzer.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/MethodUsageAnalyzer.cs new file mode 100644 index 0000000..4c31b38 --- /dev/null +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/MethodUsageAnalyzer.cs @@ -0,0 +1,31 @@ +using System.Linq; +using System.Collections.Generic; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Aspid.MVVM.Generators.Generators.ViewModels.Extensions; + +public static class MethodUsageAnalyzer +{ + public static HashSet GetUsedMethods(TypeDeclarationSyntax declaration, HashSet methodNames) + { + var usedMethods = new HashSet(); + + foreach (var invocation in declaration.DescendantNodes().OfType()) + { + var invokedMethodName = GetMethodName(invocation); + + if (invokedMethodName is not null && methodNames.Contains(invokedMethodName)) + usedMethods.Add(invokedMethodName); + } + + return usedMethods; + } + + private static string? GetMethodName(InvocationExpressionSyntax invocation) => invocation.Expression switch + { + IdentifierNameSyntax identifier => identifier.Identifier.Text, + MemberAccessExpressionSyntax memberAccess => memberAccess.Name.Identifier.Text, + _ => null + }; +} + diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableMembersFactory.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableMembersFactory.cs index fc3bd05..36c05d1 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableMembersFactory.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableMembersFactory.cs @@ -3,19 +3,20 @@ using System.Collections.Generic; using System.Collections.Immutable; using Aspid.MVVM.Generators.Helpers; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; namespace Aspid.MVVM.Generators.Generators.ViewModels.Factories; public static class BindableMembersFactory { - public static ImmutableArray Create(ITypeSymbol symbol) + public static ImmutableArray Create(ITypeSymbol symbol, TypeDeclarationSyntax declaration) { var members = new MembersByGroup(symbol); var bindableFields = BindableFieldFactory.Create(members.Fields); var bindableBindAlso = BindableBindAlsoFactory.Create(members.All); - var bindableProperties = BindablePropertyFactory.Create(members.Properties); + var bindableProperties = BindablePropertyFactory.Create(declaration, members.Properties); var generatedProperties = bindableFields .Where(field => field.Type.ToString() == "bool") diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindablePropertyFactory.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindablePropertyFactory.cs index 4d6fd52..4b4fed6 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindablePropertyFactory.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindablePropertyFactory.cs @@ -1,6 +1,8 @@ +using System.Linq; using Microsoft.CodeAnalysis; using System.Collections.Generic; using System.Collections.Immutable; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; using Aspid.MVVM.Generators.Generators.ViewModels.Extensions; using BindMode = Aspid.MVVM.Generators.Generators.ViewModels.Data.BindMode; @@ -9,9 +11,14 @@ namespace Aspid.MVVM.Generators.Generators.ViewModels.Factories; public static class BindablePropertyFactory { - public static IReadOnlyCollection Create(ImmutableArray properties) + public static IReadOnlyCollection Create(TypeDeclarationSyntax declaration, ImmutableArray properties) { var bindableProperties = new List(); + + var setMethodNames = new HashSet(properties + .Select(p => $"Set{p.Name}Field")); + + var usedSetMethods = MethodUsageAnalyzer.GetUsedMethods(declaration, setMethodNames); foreach (var property in properties) { @@ -32,7 +39,10 @@ public static IReadOnlyCollection Create(ImmutableArray bindable.Id.SourceValue, bindable => bindable)) { bindableMembers.Add(memberPair.Key, memberPair.Value); From 698f3df9363f1eb0179603f3c786e79127bea017 Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Mon, 22 Dec 2025 13:56:44 +0300 Subject: [PATCH 13/29] Add Invocation PropertyChanged Generator support --- .../Generators/Ids/IdGenerator.Find.cs | 2 +- .../Body/PropertyNotificationBody.cs | 140 ++++++++++++++++++ .../Data/PropertyNotificationData.cs | 47 ++++++ .../ViewModels/Data/ViewModelData.cs | 4 +- .../InvocationExpressionSyntaxExtensions.cs | 13 ++ .../Extensions/MethodUsageAnalyzer.cs | 9 +- .../PropertyNotificationAnalyzer.cs | 137 +++++++++++++++++ .../Factories/BindableMembersFactory.cs | 31 +++- .../Factories/BindablePropertyFactory.cs | 9 +- .../ViewModels/ViewModelGenerator.cs | 7 +- .../Views/Body/GenericInitializeView.cs | 2 +- 11 files changed, 383 insertions(+), 18 deletions(-) create mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertyNotificationBody.cs create mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/PropertyNotificationData.cs create mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/InvocationExpressionSyntaxExtensions.cs create mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/PropertyNotificationAnalyzer.cs diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.Find.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.Find.cs index 7bf4807..55c6621 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.Find.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.Find.cs @@ -33,7 +33,7 @@ public partial class IdGenerator } else { - var members = BindableMembersFactory.Create(symbol, syntax); + var members = BindableMembersFactory.Create(symbol, syntax, out _); foreach (var member in members) ids.Add(member.Id.SourceValue); diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertyNotificationBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertyNotificationBody.cs new file mode 100644 index 0000000..f2c0309 --- /dev/null +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertyNotificationBody.cs @@ -0,0 +1,140 @@ +using System.Linq; +using Microsoft.CodeAnalysis; +using Aspid.Generators.Helper; +using Aspid.MVVM.Generators.Generators.ViewModels.Data; +using static Aspid.Generators.Helper.Classes; +using static Aspid.MVVM.Generators.Generators.Descriptions.General; + +namespace Aspid.MVVM.Generators.Generators.ViewModels.Body; + +public static class PropertyNotificationBody +{ + private const string CallerLineNumberAttribute = "[global::System.Runtime.CompilerServices.CallerLineNumber]"; + + public static void Generate( + string namespaceName, + in ViewModelData data, + DeclarationText declaration, + in SourceProductionContext context) + { + var code = new CodeWriter(); + + code.BeginClass(namespaceName, declaration) + .AppendBody(data) + .EndClass(namespaceName); + + context.AddSource(declaration.GetFileName(namespaceName, postfix: "PropertyNotification"), code.GetSourceText()); + } + + extension(CodeWriter code) + { + private CodeWriter AppendBody(in ViewModelData data) + { + return code + .AppendOnPropertyChanged(data) + .AppendLine() + .AppendSetField(data); + } + + private CodeWriter AppendOnPropertyChanged(in ViewModelData data) + { + var notificationData = data.PropertyNotificationData; + + code.AppendLine("#region OnPropertyChanged"); + + code.AppendLine(GeneratedCodeViewModelAttribute) + .AppendLine($"private void OnPropertyChanged({CallerLineNumberAttribute} int line = -1)") + .BeginBlock(); + + if (notificationData.HasOnPropertyChangedCalls) + { + var first = true; + + foreach (var kvp in notificationData.OnPropertyChangedCalls) + { + var propertyName = kvp.Key; + var lines = kvp.Value; + var linesPattern = string.Join(" or ", lines.Select(line => line.ToString())); + var keyword = first ? "if" : "else if"; + + first = false; + code.AppendLine($"{keyword} (line is {linesPattern}) On{propertyName}PropertyChanged();"); + } + + code.AppendLine($"else throw new {NotImplementedException}($\"OnPropertyChanged: No property found for line {{line}}\");"); + } + else + { + code.AppendLine($"throw new {NotImplementedException}($\"OnPropertyChanged: No property found for line {{line}}\");"); + } + + code.EndBlock() + .AppendLine(); + + code.AppendMultiline( + $""" + {GeneratedCodeViewModelAttribute} + private void OnPropertyChanged(string propertyName, {CallerLineNumberAttribute} int line = -1) => + OnPropertyChanged(line); + """) + .AppendLine("#endregion"); + + return code; + } + + private CodeWriter AppendSetField(in ViewModelData data) + { + var notificationData = data.PropertyNotificationData; + + code.AppendLine("#region SetField") + .AppendMultiline( + $$""" + {{GeneratedCodeViewModelAttribute}} + private bool SetField(ref T field, T newValue, {{CallerLineNumberAttribute}} int line = -1) => + throw new {{NotImplementedException}}("SetField: No property found for line {line}. Use typed overload."); + + {{GeneratedCodeViewModelAttribute}} + private bool SetField(ref T field, T newValue, string propertyName, {{CallerLineNumberAttribute}} int line = -1) => + throw new {{NotImplementedException}}($"SetField: No property {propertyName} found for line {line}. Use typed overload."); + """); + + if (notificationData.HasSetFieldCalls) + { + foreach (var typeGroup in notificationData.SetFieldCallsByType) + { + var type = typeGroup.Key; + var propertyCalls = typeGroup.Value; + + code.AppendLine(GeneratedCodeViewModelAttribute) + .AppendLine($"private bool SetField(ref {type} field, {type} newValue, {CallerLineNumberAttribute} int line = -1)") + .BeginBlock(); + + var first = true; + foreach (var kvp in propertyCalls) + { + var propertyName = kvp.Key; + var lines = kvp.Value; + var linesPattern = string.Join(" or ", lines.Select(l => l.ToString())); + var keyword = first ? "if" : "else if"; + first = false; + + code.AppendLine($"{keyword} (line is {linesPattern}) return Set{propertyName}Field(ref field, newValue);"); + } + + code.AppendLine($"else throw new {NotImplementedException}($\"SetField: No property found for line {{line}}\");") + .EndBlock() + .AppendLine() + .AppendMultiline( + $""" + {GeneratedCodeViewModelAttribute} + private bool SetField(ref {type} field, {type} newValue, string propertyName, {CallerLineNumberAttribute} int line = -1) => + SetField(ref field, newValue, line); + """); + } + } + + return code.AppendLine("#endregion"); + } + } +} + diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/PropertyNotificationData.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/PropertyNotificationData.cs new file mode 100644 index 0000000..65b990d --- /dev/null +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/PropertyNotificationData.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; + +namespace Aspid.MVVM.Generators.Generators.ViewModels.Data; + +public sealed class PropertyNotificationData +{ + // Key - propertyName, Value - List of line numbers + public Dictionary> OnPropertyChangedCalls { get; } = []; + + // Key - type, Value - Dictionary (propertyName -> List of line numbers) + public Dictionary>> SetFieldCallsByType { get; } = []; + + public HashSet PropertiesRequiringSetFieldBody { get; } = []; + + public bool HasOnPropertyChangedCalls => OnPropertyChangedCalls.Count > 0; + + public bool HasSetFieldCalls => SetFieldCallsByType.Count > 0; + + public void AddOnPropertyChangedCall(string propertyName, int line) + { + if (!OnPropertyChangedCalls.TryGetValue(propertyName, out var lines)) + { + lines = []; + OnPropertyChangedCalls[propertyName] = lines; + } + + lines.Add(line); + } + + public void AddSetFieldCall(string propertyName, int line, string type) + { + if (!SetFieldCallsByType.TryGetValue(type, out var propertyCalls)) + { + propertyCalls = []; + SetFieldCallsByType[type] = propertyCalls; + } + + if (!propertyCalls.TryGetValue(propertyName, out var lines)) + { + lines = []; + propertyCalls[propertyName] = lines; + } + + lines.Add(line); + PropertiesRequiringSetFieldBody.Add(propertyName); + } +} diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/ViewModelData.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/ViewModelData.cs index 753e9bf..32385ae 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/ViewModelData.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/ViewModelData.cs @@ -14,7 +14,8 @@ public readonly struct ViewModelData( ClassDeclarationSyntax declaration, ImmutableArray members, ImmutableArray idGroups, - Dictionary customViewModelInterfaces) + Dictionary customViewModelInterfaces, + PropertyNotificationData propertyNotificationData) { public readonly string Name = symbol.Name; @@ -26,4 +27,5 @@ public readonly struct ViewModelData( public readonly ImmutableArray IdGroups = idGroups; public readonly Dictionary CustomViewModelInterfaces = customViewModelInterfaces; + public readonly PropertyNotificationData PropertyNotificationData = propertyNotificationData; } \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/InvocationExpressionSyntaxExtensions.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/InvocationExpressionSyntaxExtensions.cs new file mode 100644 index 0000000..9b7a069 --- /dev/null +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/InvocationExpressionSyntaxExtensions.cs @@ -0,0 +1,13 @@ +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Aspid.MVVM.Generators.Generators.ViewModels.Extensions; + +public static class InvocationExpressionSyntaxExtensions +{ + public static string? GetMethodName(this InvocationExpressionSyntax invocation) => invocation.Expression switch + { + IdentifierNameSyntax identifier => identifier.Identifier.Text, + MemberAccessExpressionSyntax memberAccess => memberAccess.Name.Identifier.Text, + _ => null + }; +} diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/MethodUsageAnalyzer.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/MethodUsageAnalyzer.cs index 4c31b38..614979f 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/MethodUsageAnalyzer.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/MethodUsageAnalyzer.cs @@ -12,7 +12,7 @@ public static HashSet GetUsedMethods(TypeDeclarationSyntax declaration, foreach (var invocation in declaration.DescendantNodes().OfType()) { - var invokedMethodName = GetMethodName(invocation); + var invokedMethodName = invocation.GetMethodName(); if (invokedMethodName is not null && methodNames.Contains(invokedMethodName)) usedMethods.Add(invokedMethodName); @@ -20,12 +20,5 @@ public static HashSet GetUsedMethods(TypeDeclarationSyntax declaration, return usedMethods; } - - private static string? GetMethodName(InvocationExpressionSyntax invocation) => invocation.Expression switch - { - IdentifierNameSyntax identifier => identifier.Identifier.Text, - MemberAccessExpressionSyntax memberAccess => memberAccess.Name.Identifier.Text, - _ => null - }; } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/PropertyNotificationAnalyzer.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/PropertyNotificationAnalyzer.cs new file mode 100644 index 0000000..020028e --- /dev/null +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/PropertyNotificationAnalyzer.cs @@ -0,0 +1,137 @@ +using System.Linq; +using Microsoft.CodeAnalysis; +using System.Collections.Generic; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Aspid.MVVM.Generators.Generators.ViewModels.Extensions; + +public static class PropertyNotificationAnalyzer +{ + /// + /// Анализирует класс и возвращает данные о вызовах OnPropertyChanged и SetField. + /// + public static Data.PropertyNotificationData Analyze( + TypeDeclarationSyntax classDeclaration, + IReadOnlyDictionary bindableProperties) // propertyName -> type + { + var data = new Data.PropertyNotificationData(); + + foreach (var invocation in classDeclaration.DescendantNodes().OfType()) + { + var methodName = invocation.GetMethodName(); + + switch (methodName) + { + case "OnPropertyChanged": + AnalyzeOnPropertyChanged(invocation, bindableProperties, data); + break; + + case "SetField": + AnalyzeSetField(invocation, bindableProperties, data); + break; + } + } + + return data; + } + + private static void AnalyzeOnPropertyChanged( + InvocationExpressionSyntax invocation, + IReadOnlyDictionary bindableProperties, + Data.PropertyNotificationData data) + { + var line = invocation.GetLocation().GetLineSpan().StartLinePosition.Line + 1; + var arguments = invocation.ArgumentList.Arguments; + + if (arguments.Count == 0) + { + // OnPropertyChanged() - определяем свойство по контексту (ищем свойство в котором вызов) + var propertyName = FindContainingPropertyName(invocation); + if (propertyName is not null && bindableProperties.ContainsKey(propertyName)) + { + data.AddOnPropertyChangedCall(propertyName, line); + } + } + else + { + // OnPropertyChanged(nameof(FirstName)) или OnPropertyChanged("FirstName") + var propertyName = ExtractPropertyName(arguments[0].Expression); + if (propertyName is not null && bindableProperties.ContainsKey(propertyName)) + { + data.AddOnPropertyChangedCall(propertyName, line); + } + } + } + + private static void AnalyzeSetField( + InvocationExpressionSyntax invocation, + IReadOnlyDictionary bindableProperties, + Data.PropertyNotificationData data) + { + var line = invocation.GetLocation().GetLineSpan().StartLinePosition.Line + 1; + var arguments = invocation.ArgumentList.Arguments; + + // SetField(ref _field, value) - минимум 2 аргумента + // SetField(ref _field, value, nameof(Property)) - 3 аргумента + if (arguments.Count < 2) return; + + string? propertyName; + + if (arguments.Count >= 3) + { + // Третий аргумент - имя свойства + propertyName = ExtractPropertyName(arguments[2].Expression); + } + else + { + // Определяем по контексту свойства (вызов внутри свойства) + propertyName = FindContainingPropertyName(invocation); + } + + if (propertyName is not null && bindableProperties.TryGetValue(propertyName, out var type)) + { + data.AddSetFieldCall(propertyName, line, type); + } + } + + private static string? FindContainingPropertyName(SyntaxNode node) + { + var current = node.Parent; + while (current is not null) + { + if (current is PropertyDeclarationSyntax property) + { + return property.Identifier.Text; + } + current = current.Parent; + } + return null; + } + + private static string? ExtractPropertyName(ExpressionSyntax expression) + { + return expression switch + { + // nameof(FirstName) + InvocationExpressionSyntax { Expression: IdentifierNameSyntax { Identifier.Text: "nameof" } } nameofExpr + when nameofExpr.ArgumentList.Arguments.Count > 0 + => GetIdentifierFromExpression(nameofExpr.ArgumentList.Arguments[0].Expression), + + // "FirstName" + LiteralExpressionSyntax literal => literal.Token.ValueText, + + _ => null + }; + } + + private static string? GetIdentifierFromExpression(ExpressionSyntax expression) + { + return expression switch + { + IdentifierNameSyntax identifier => identifier.Identifier.Text, + MemberAccessExpressionSyntax memberAccess => memberAccess.Name.Identifier.Text, + _ => null + }; + } +} + diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableMembersFactory.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableMembersFactory.cs index 36c05d1..8eb7fb0 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableMembersFactory.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableMembersFactory.cs @@ -1,22 +1,29 @@ using System.Linq; using Microsoft.CodeAnalysis; +using Aspid.Generators.Helper; using System.Collections.Generic; using System.Collections.Immutable; using Aspid.MVVM.Generators.Helpers; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Aspid.MVVM.Generators.Generators.ViewModels.Data; using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; +using Aspid.MVVM.Generators.Generators.ViewModels.Extensions; namespace Aspid.MVVM.Generators.Generators.ViewModels.Factories; public static class BindableMembersFactory { - public static ImmutableArray Create(ITypeSymbol symbol, TypeDeclarationSyntax declaration) + public static ImmutableArray Create( + ITypeSymbol symbol, + TypeDeclarationSyntax declaration, + out PropertyNotificationData propertyNotificationData) { var members = new MembersByGroup(symbol); + propertyNotificationData = CreatePropertyNotificationData(symbol, declaration); var bindableFields = BindableFieldFactory.Create(members.Fields); var bindableBindAlso = BindableBindAlsoFactory.Create(members.All); - var bindableProperties = BindablePropertyFactory.Create(declaration, members.Properties); + var bindableProperties = BindablePropertyFactory.Create(declaration, members.Properties, propertyNotificationData); var generatedProperties = bindableFields .Where(field => field.Type.ToString() == "bool") @@ -38,4 +45,24 @@ public static ImmutableArray Create(ITypeSymbol symbol, Typ return bindableMembers.ToImmutableArray(); } + + private static PropertyNotificationData CreatePropertyNotificationData(ITypeSymbol symbol, TypeDeclarationSyntax candidate) + { + var members = new MembersByGroup(symbol); + + var bindablePropertiesDict = members.Properties + .Where(property => property.GetBindMode() is not BindMode.None) + .ToDictionary(property => property.Name, property => property.Type.ToDisplayStringGlobal()); + + var bindableFieldsDict = members.Fields + .Where(field => field.GetBindMode() is not BindMode.None) + .ToDictionary(field => field.GetPropertyName(), field => field.Type.ToDisplayStringGlobal()); + + foreach (var pair in bindableFieldsDict) + { + bindablePropertiesDict[pair.Key] = pair.Value; + } + + return PropertyNotificationAnalyzer.Analyze(candidate, bindablePropertiesDict); + } } \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindablePropertyFactory.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindablePropertyFactory.cs index 4b4fed6..782dbf8 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindablePropertyFactory.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindablePropertyFactory.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Aspid.MVVM.Generators.Generators.ViewModels.Data; using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; using Aspid.MVVM.Generators.Generators.ViewModels.Extensions; using BindMode = Aspid.MVVM.Generators.Generators.ViewModels.Data.BindMode; @@ -11,7 +12,10 @@ namespace Aspid.MVVM.Generators.Generators.ViewModels.Factories; public static class BindablePropertyFactory { - public static IReadOnlyCollection Create(TypeDeclarationSyntax declaration, ImmutableArray properties) + public static IReadOnlyCollection Create( + TypeDeclarationSyntax declaration, + ImmutableArray properties, + PropertyNotificationData propertyNotificationData) { var bindableProperties = new List(); @@ -40,7 +44,8 @@ public static IReadOnlyCollection Create(TypeDeclarationSy } var setMethodName = $"Set{property.Name}Field"; - var isSetMethodUsed = usedSetMethods.Contains(setMethodName); + var isSetMethodUsed = usedSetMethods.Contains(setMethodName) + || propertyNotificationData.PropertiesRequiringSetFieldBody.Contains(property.Name); bindableProperties.Add(new BindablePropertyInfo(property, mode, isSetMethodUsed)); } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/ViewModelGenerator.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/ViewModelGenerator.cs index 7247723..0118c96 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/ViewModelGenerator.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/ViewModelGenerator.cs @@ -48,12 +48,12 @@ private static bool SyntacticPredicate(SyntaxNode node, CancellationToken cancel var inheritor = symbol.HasAnyAttributeInBases(ViewModelAttribute) ? Inheritor.Inheritor : Inheritor.None; - - var bindableMembers = BindableMembersFactory.Create(symbol, candidate); + + var bindableMembers = BindableMembersFactory.Create(symbol, candidate, out var propertyNotificationData); var memberByGroups = IdLengthMemberGroup.Create(bindableMembers); var customViewModelInterfaces = CustomViewModelInterfacesFactory.Create(symbol); - return new ViewModelData(inheritor, symbol, candidate, bindableMembers, memberByGroups, customViewModelInterfaces); + return new ViewModelData(inheritor, symbol, candidate, bindableMembers, memberByGroups, customViewModelInterfaces, propertyNotificationData); } private static void GenerateCode(SourceProductionContext context, ViewModelData data) @@ -66,6 +66,7 @@ private static void GenerateCode(SourceProductionContext context, ViewModelData RelayCommandBody.Generate(@namespace, data, declarationText, context); FindBindableMembersBody.Generate(@namespace, data, declarationText, context); GeneratedPropertiesBody.Generate(@namespace, data, declarationText, context); + PropertyNotificationBody.Generate(@namespace, data, declarationText, context); GeneratedPropertyMethodsBody.Generate(@namespace, data, declarationText, context); BindableInterfaceMembersBody.Generate(@namespace, data, declarationText, context); } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/GenericInitializeView.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/GenericInitializeView.cs index 325d586..5de1944 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/GenericInitializeView.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/GenericInitializeView.cs @@ -103,7 +103,7 @@ public void Initialize({{typeName}} viewModel) for (var viewModelType = genericView.Type; viewModelType is not null; viewModelType = viewModelType.BaseType) { foreach (var memberPair in - BindableMembersFactory.Create(viewModelType, data.Declaration) + BindableMembersFactory.Create(viewModelType, data.Declaration, out _) .ToDictionary(bindable => bindable.Id.SourceValue, bindable => bindable)) { bindableMembers.Add(memberPair.Key, memberPair.Value); From b4aa94fce8b7237561b71bf116af219c65738abf Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Mon, 22 Dec 2025 14:15:14 +0300 Subject: [PATCH 14/29] Add Samples --- .../ExProperty1Vm.cs | 36 +++++++++++++++++++ .../ExProperty2Vm.cs | 36 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators.Sample/ExProperty1Vm.cs create mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators.Sample/ExProperty2Vm.cs diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators.Sample/ExProperty1Vm.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators.Sample/ExProperty1Vm.cs new file mode 100644 index 0000000..5a76410 --- /dev/null +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators.Sample/ExProperty1Vm.cs @@ -0,0 +1,36 @@ +namespace Aspid.MVVM.Generators.Sample; + +[ViewModel] +public partial class ExProperty1Vm +{ + private string _firstName = string.Empty; + private string _lastName = string.Empty; + + [Bind] + public string FirstName + { + get => _firstName; + private set + { + if (SetFirstNameField(ref _firstName, value)) + OnFullNamePropertyChanged(); + } + } + + [Bind] + [BindAlso(nameof(FullName))] + public string LastName + { + get => _lastName; + private set + { + if (_lastName == value) return; + + _lastName = value; + OnLastNamePropertyChanged(); + } + } + + [Bind] + public string FullName => $"{FirstName} {LastName}"; +} diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators.Sample/ExProperty2Vm.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators.Sample/ExProperty2Vm.cs new file mode 100644 index 0000000..2d080d6 --- /dev/null +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators.Sample/ExProperty2Vm.cs @@ -0,0 +1,36 @@ +namespace Aspid.MVVM.Generators.Sample; + +[ViewModel] +public partial class ExProperty2Vm +{ + private string _firstName = string.Empty; + private string _lastName = string.Empty; + + [Bind] + public string FirstName + { + get => _firstName; + private set + { + if (SetField(ref _firstName, value)) + OnPropertyChanged(nameof(FullName)); + } + } + + [Bind] + [BindAlso(nameof(FullName))] + public string LastName + { + get => _lastName; + private set + { + if (_lastName == value) return; + + _lastName = value; + OnPropertyChanged(); + } + } + + [Bind] + public string FullName => $"{_firstName} {_lastName}"; +} From ca513344cb9b6965ab65148a8a828d8739fe2dca Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Mon, 12 Jan 2026 17:40:49 +0300 Subject: [PATCH 15/29] Add public void NotifyCanExecuteChanged() for ViewModel --- .../Generators/Descriptions/General.cs | 10 ++++---- .../ViewModels/Body/BindableMembers.cs | 24 ++++++++++++++++++- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/General.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/General.cs index 6fc3ced..5251594 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/General.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/General.cs @@ -3,17 +3,17 @@ namespace Aspid.MVVM.Generators.Generators.Descriptions; public static class General { public const string GeneratedCodeIdAttribute = - "[global::System.CodeDom.Compiler.GeneratedCode(\"Aspid.MVVM.Generators.IdGenerator\", \"1.0.0\")]"; + "[global::System.CodeDom.Compiler.GeneratedCode(\"Aspid.MVVM.Generators.IdGenerator\", \"1.1.0\")]"; public const string GeneratedCodeViewAttribute = - "[global::System.CodeDom.Compiler.GeneratedCode(\"Aspid.MVVM.Generators.ViewGenerator\", \"1.0.0\")]"; + "[global::System.CodeDom.Compiler.GeneratedCode(\"Aspid.MVVM.Generators.ViewGenerator\", \"1.1.0\")]"; public const string GeneratedCodeLogBinderAttribute = - "[global::System.CodeDom.Compiler.GeneratedCode(\"Aspid.MVVM.Generators.LogBinderGenerator\", \"1.0.0\")]"; + "[global::System.CodeDom.Compiler.GeneratedCode(\"Aspid.MVVM.Generators.LogBinderGenerator\", \"1.1.0\")]"; public const string GeneratedCodeCreateFromAttribute = - "[global::System.CodeDom.Compiler.GeneratedCode(\"Aspid.MVVM.Generators.CreateFromGenerator\", \"1.0.0\")]"; + "[global::System.CodeDom.Compiler.GeneratedCode(\"Aspid.MVVM.Generators.CreateFromGenerator\", \"1.1.0\")]"; public const string GeneratedCodeViewModelAttribute = - "[global::System.CodeDom.Compiler.GeneratedCode(\"Aspid.MVVM.Generators.ViewModelGenerator\", \"1.0.0\")]"; + "[global::System.CodeDom.Compiler.GeneratedCode(\"Aspid.MVVM.Generators.ViewModelGenerator\", \"1.1.0\")]"; } \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembers.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembers.cs index ba67422..4235bf0 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembers.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembers.cs @@ -56,7 +56,10 @@ private CodeWriter AppendBody(in ViewModelData data) code.AppendBindableMembers(data); } - return code.AppendNotifyAll(data); + return code + .AppendNotifyAll(data) + .AppendLine() + .AppendNotifyCanExecuteChangedAll(data); } private CodeWriter AppendBindableMembers(in ViewModelData data) @@ -108,5 +111,24 @@ private CodeWriter AppendNotifyAll(in ViewModelData data) return code.EndBlock(); } + + private CodeWriter AppendNotifyCanExecuteChangedAll(in ViewModelData data) + { + var modifiers = "public"; + if (data.Inheritor is not Inheritor.None) modifiers += " override"; + else if (!data.Symbol.IsSealed) modifiers += " virtual"; + + code.AppendLine(GeneratedCodeViewModelAttribute) + .AppendLine($"{modifiers} void NotifyCanExecuteChangedAll()") + .BeginBlock() + .AppendLineIf(data.Inheritor is Inheritor.Inheritor, "base.NotifyCanExecuteChangedAll();"); + + foreach (var command in data.Members.OfType()) + { + code.AppendLine($"{command.Name}.NotifyCanExecuteChanged();"); + } + + return code.EndBlock(); + } } } \ No newline at end of file From b31dcb0212eca7f76478a43c7c81df41bb356133 Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Sun, 18 Jan 2026 13:47:29 +0300 Subject: [PATCH 16/29] Fix NotifyCanExecuteChanged Generated --- .../ViewModels/Body/BindableMembers.cs | 24 ++++++++++++-- .../Data/Infos/BindableCommandInfo.cs | 31 ++++++++++--------- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembers.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembers.cs index 4235bf0..a8b0721 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembers.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembers.cs @@ -6,6 +6,7 @@ using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; using static Aspid.MVVM.Generators.Generators.Descriptions.General; +using SymbolExtensions = Aspid.MVVM.Generators.Helpers.SymbolExtensions; using BindMode = Aspid.MVVM.Generators.Generators.ViewModels.Data.BindMode; namespace Aspid.MVVM.Generators.Generators.ViewModels.Body; @@ -123,9 +124,28 @@ private CodeWriter AppendNotifyCanExecuteChangedAll(in ViewModelData data) .BeginBlock() .AppendLineIf(data.Inheritor is Inheritor.Inheritor, "base.NotifyCanExecuteChangedAll();"); - foreach (var command in data.Members.OfType()) + foreach (var member in data.Members) { - code.AppendLine($"{command.Name}.NotifyCanExecuteChanged();"); + var name = member.Name; + + if (member is BindableCommandInfo command) + { + if (string.IsNullOrWhiteSpace(command.CanExecute)) continue; + name = $"{SymbolExtensions.GetFieldName(name, prefix: "__")}"; + } + else + { + var memberType = member.Member.GetSymbolType(); + if (memberType is null) continue; + + if (!memberType.ToDisplayStringGlobal().Contains(IRelayCommand)) + { + if (!memberType.AllInterfaces.Any(i => i.ToDisplayStringGlobal().Contains(IRelayCommand))) + continue; + } + } + + code.AppendLine($"{name}?.NotifyCanExecuteChanged();"); } return code.EndBlock(); diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableCommandInfo.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableCommandInfo.cs index 6c7fd45..5b0f3f3 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableCommandInfo.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableCommandInfo.cs @@ -18,6 +18,8 @@ public sealed class BindableCommandInfo : IBindableMemberInfo public string Name { get; } + public string CanExecute { get; } + public IdData Id { get; } public BindMode Mode => BindMode.OneTime; @@ -35,7 +37,7 @@ public BindableCommandInfo(IMethodSymbol methodSymbol, string? canExecute, bool Bindable = GeneratedBindableMembers.CreateForRelayCommand(Type, Name); var fieldName = $"{methodSymbol.GetFieldName("__")}Command"; - canExecute = GetCanExecuteAction(methodSymbol, isLambda, isMethod, canExecute); + CanExecute = GetCanExecuteAction(methodSymbol, isLambda, isMethod, canExecute); CommandDeclaration = $""" @@ -45,7 +47,7 @@ public BindableCommandInfo(IMethodSymbol methodSymbol, string? canExecute, bool private {Type} {fieldName}; {GeneratedCodeViewModelAttribute} - private {Type} {Name} => {fieldName} ??= new {Type}({methodSymbol.Name}{canExecute}); + private {Type} {Name} => {fieldName} ??= new {Type}({methodSymbol.Name}{CanExecute}); #endregion """; } @@ -69,22 +71,21 @@ private static string GetTypeName(IMethodSymbol command) private static string GetCanExecuteAction(IMethodSymbol command, bool isLambda, bool isMethod, string? canExecute) { - var canExecuteName = new StringBuilder(canExecute ?? ""); + if (canExecute is null || string.IsNullOrWhiteSpace(canExecute)) return string.Empty; + + var canExecuteName = new StringBuilder(canExecute); - if (canExecuteName.Length != 0) + if (!isLambda) + { + canExecuteName.Insert(0, ", "); + } + else { - if (!isLambda) - { - canExecuteName.Insert(0, ", "); - } - else - { - var parameters = command.Parameters; - var missingParameters = string.Join(", ", Enumerable.Repeat("_", parameters.Length)); + var parameters = command.Parameters; + var missingParameters = string.Join(", ", Enumerable.Repeat("_", parameters.Length)); - canExecuteName.Insert(0, $", ({missingParameters}) => "); - if (isLambda && isMethod) canExecuteName.Append("()"); - } + canExecuteName.Insert(0, $", ({missingParameters}) => "); + if (isLambda && isMethod) canExecuteName.Append("()"); } return canExecuteName.ToString(); From 60e62239af0a495cc26688a45a2ae9cab12e730c Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Thu, 22 Jan 2026 10:14:27 +0300 Subject: [PATCH 17/29] Add Keyword For Set Methods and Refactor --- .../Generators/Binders/Body/BinderLogBody.cs | 14 ++--- .../Descriptions/{General.cs => Constants.cs} | 11 ++-- .../Generators/Ids/IdGenerator.Generate.cs | 2 +- .../Body/BindableInterfaceMembersBody.cs | 2 +- .../ViewModels/Body/BindableMembers.cs | 2 +- .../Body/FindBindableMembersBody.cs | 6 +- .../Body/GeneratedPropertiesBody.cs | 2 +- .../Body/GeneratedPropertyMethodsBody.cs | 3 +- .../Body/PropertyNotificationBody.cs | 7 +-- .../Data/Infos/BindableBindAlsoInfo.cs | 2 +- .../Data/Infos/BindableCommandInfo.cs | 7 +-- .../Data/Infos/BindableFieldInfo.cs | 26 +++++---- .../Data/Infos/BindablePropertyInfo.cs | 13 ++--- .../Data/Infos/GeneratedBindableMembers.cs | 7 +-- .../Data/Infos/IBindableMemberInfo.cs | 2 +- .../Data/PropertyNotificationData.cs | 2 +- .../InvocationExpressionSyntaxExtensions.cs | 2 +- .../Extensions/MethodUsageAnalyzer.cs | 3 +- .../PropertyNotificationAnalyzer.cs | 3 +- .../Factories/BindablePropertyFactory.cs | 2 +- .../Generators/Views/Body/BinderCachedBody.cs | 13 ++--- .../Views/Body/GenericInitializeView.cs | 6 +- .../Generators/Views/Body/InitializeBody.cs | 55 +++++++++---------- .../Generators/Views/Data/Inheritor.cs | 2 +- 24 files changed, 91 insertions(+), 103 deletions(-) rename Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/{General.cs => Constants.cs} (69%) diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/Body/BinderLogBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/Body/BinderLogBody.cs index def4d21..8102378 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/Body/BinderLogBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/Body/BinderLogBody.cs @@ -2,9 +2,9 @@ using Microsoft.CodeAnalysis; using Aspid.Generators.Helper; using Aspid.MVVM.Generators.Generators.Binders.Data; -using Aspid.MVVM.Generators.Generators.Descriptions; using static Aspid.Generators.Helper.Classes; using static Aspid.Generators.Helper.Unity.UnityClasses; +using static Aspid.MVVM.Generators.Generators.Descriptions.Constants; using Classes = Aspid.MVVM.Generators.Generators.Descriptions.Classes; namespace Aspid.MVVM.Generators.Generators.Binders.Body; @@ -12,8 +12,6 @@ namespace Aspid.MVVM.Generators.Generators.Binders.Body; // ReSharper disable InconsistentNaming public static class BinderLogBody { - private const string GeneratedAttribute = General.GeneratedCodeLogBinderAttribute; - private static readonly string IBinder = Classes.IBinder; private static readonly string Exception = Aspid.Generators.Helper.Classes.Exception; @@ -50,14 +48,14 @@ private static CodeWriter AppendProperties(this CodeWriter code, in BinderDataSp code.AppendMultiline( $""" - {GeneratedAttribute} + {GeneratedCodeLogBinderAttribute} [{SerializeField}] private bool _isDebug; // TODO Add Custom Property - {GeneratedAttribute} + {GeneratedCodeLogBinderAttribute} [{SerializeField}] private {List_1} _log; - {GeneratedAttribute} + {GeneratedCodeLogBinderAttribute} {modifier} bool IsDebug => _isDebug; """) .AppendLine(); @@ -74,7 +72,7 @@ private static CodeWriter AppendSetValueMethods(this CodeWriter code, in ReadOnl code.AppendMultiline( $$""" - {{GeneratedAttribute}} + {{GeneratedCodeLogBinderAttribute}} void {{IBinder}}<{{parameterType}}>.{{method.Name}}({{parameterType}} {{parameterName}}) { if (IsDebug) @@ -115,7 +113,7 @@ private static CodeWriter AppendAddLogMethod(this CodeWriter code, in BinderData code.AppendMultiline( $$""" - {{GeneratedAttribute}} + {{GeneratedCodeLogBinderAttribute}} {{modifier}} void AddLog(string log) { _log ??= new {{List_1}}(); diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/General.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Constants.cs similarity index 69% rename from Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/General.cs rename to Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Constants.cs index 5251594..7121902 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/General.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Constants.cs @@ -1,6 +1,8 @@ +using static Aspid.Generators.Helper.Classes; + namespace Aspid.MVVM.Generators.Generators.Descriptions; -public static class General +public static class Constants { public const string GeneratedCodeIdAttribute = "[global::System.CodeDom.Compiler.GeneratedCode(\"Aspid.MVVM.Generators.IdGenerator\", \"1.1.0\")]"; @@ -11,9 +13,10 @@ public static class General public const string GeneratedCodeLogBinderAttribute = "[global::System.CodeDom.Compiler.GeneratedCode(\"Aspid.MVVM.Generators.LogBinderGenerator\", \"1.1.0\")]"; - public const string GeneratedCodeCreateFromAttribute = - "[global::System.CodeDom.Compiler.GeneratedCode(\"Aspid.MVVM.Generators.CreateFromGenerator\", \"1.1.0\")]"; - public const string GeneratedCodeViewModelAttribute = "[global::System.CodeDom.Compiler.GeneratedCode(\"Aspid.MVVM.Generators.ViewModelGenerator\", \"1.1.0\")]"; + + public const string CallerLineNumberAttribute = "[global::System.Runtime.CompilerServices.CallerLineNumber]"; + + public static readonly string EditorBrowsableAttributeNever = $"[{EditorBrowsableAttribute}({EditorBrowsableState}.Never)]"; } \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.Generate.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.Generate.cs index 1222dcf..ed41e0d 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.Generate.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Ids/IdGenerator.Generate.cs @@ -2,7 +2,7 @@ using Aspid.Generators.Helper; using System.Collections.Generic; using System.Collections.Immutable; -using static Aspid.MVVM.Generators.Generators.Descriptions.General; +using static Aspid.MVVM.Generators.Generators.Descriptions.Constants; namespace Aspid.MVVM.Generators.Generators.Ids; diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableInterfaceMembersBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableInterfaceMembersBody.cs index 453d0d4..32b4766 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableInterfaceMembersBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableInterfaceMembersBody.cs @@ -1,8 +1,8 @@ using Microsoft.CodeAnalysis; using Aspid.Generators.Helper; using Aspid.MVVM.Generators.Generators.ViewModels.Data; -using static Aspid.MVVM.Generators.Generators.Descriptions.General; using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; +using static Aspid.MVVM.Generators.Generators.Descriptions.Constants; using BindMode = Aspid.MVVM.Generators.Generators.ViewModels.Data.BindMode; namespace Aspid.MVVM.Generators.Generators.ViewModels.Body; diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembers.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembers.cs index a8b0721..0ec49d0 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembers.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembers.cs @@ -5,7 +5,7 @@ using Aspid.MVVM.Generators.Generators.ViewModels.Data; using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; -using static Aspid.MVVM.Generators.Generators.Descriptions.General; +using static Aspid.MVVM.Generators.Generators.Descriptions.Constants; using SymbolExtensions = Aspid.MVVM.Generators.Helpers.SymbolExtensions; using BindMode = Aspid.MVVM.Generators.Generators.ViewModels.Data.BindMode; diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/FindBindableMembersBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/FindBindableMembersBody.cs index b084bd2..0705266 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/FindBindableMembersBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/FindBindableMembersBody.cs @@ -4,12 +4,10 @@ using System.Collections.Immutable; using Aspid.MVVM.Generators.Generators.ViewModels.Data; using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; -using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; -using static Aspid.Generators.Helper.Classes; using static Aspid.Generators.Helper.Unity.UnityClasses; using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; using static Aspid.MVVM.Generators.Generators.Descriptions.Defines; -using static Aspid.MVVM.Generators.Generators.Descriptions.General; +using static Aspid.MVVM.Generators.Generators.Descriptions.Constants; namespace Aspid.MVVM.Generators.Generators.ViewModels.Body; @@ -46,8 +44,8 @@ private static CodeWriter AppendMarkers(this CodeWriter code, in ViewModelData d return code.AppendMultiline( $""" #if !{ASPID_MVVM_UNITY_PROFILER_DISABLED} + {EditorBrowsableAttributeNever} {GeneratedCodeViewModelAttribute} - [{EditorBrowsableAttribute}({EditorBrowsableState}.Never)] private static readonly {ProfilerMarker} __findBindableMemberMarker = new("{className}.FindBindableMember"); #endif """); diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/GeneratedPropertiesBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/GeneratedPropertiesBody.cs index 73d35c2..a31b510 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/GeneratedPropertiesBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/GeneratedPropertiesBody.cs @@ -38,4 +38,4 @@ private CodeWriter AppendBody(in ImmutableArray fields) return code; } } -} +} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/GeneratedPropertyMethodsBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/GeneratedPropertyMethodsBody.cs index 74347e1..f41063e 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/GeneratedPropertyMethodsBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/GeneratedPropertyMethodsBody.cs @@ -38,5 +38,4 @@ private CodeWriter AppendBody(in ImmutableArray properties return code; } } -} - +} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertyNotificationBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertyNotificationBody.cs index f2c0309..dc36f4e 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertyNotificationBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertyNotificationBody.cs @@ -3,14 +3,12 @@ using Aspid.Generators.Helper; using Aspid.MVVM.Generators.Generators.ViewModels.Data; using static Aspid.Generators.Helper.Classes; -using static Aspid.MVVM.Generators.Generators.Descriptions.General; +using static Aspid.MVVM.Generators.Generators.Descriptions.Constants; namespace Aspid.MVVM.Generators.Generators.ViewModels.Body; public static class PropertyNotificationBody { - private const string CallerLineNumberAttribute = "[global::System.Runtime.CompilerServices.CallerLineNumber]"; - public static void Generate( string namespaceName, in ViewModelData data, @@ -136,5 +134,4 @@ private bool SetField(ref {type} field, {type} newValue, string propertyName, {C return code.AppendLine("#endregion"); } } -} - +} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableBindAlsoInfo.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableBindAlsoInfo.cs index 95ef2db..e1ee6d9 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableBindAlsoInfo.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableBindAlsoInfo.cs @@ -37,4 +37,4 @@ public bool Equals(BindableBindAlsoInfo other) => public override int GetHashCode() => SymbolEqualityComparer.Default.GetHashCode(_propertySymbol); -} +} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableCommandInfo.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableCommandInfo.cs index 5b0f3f3..ad02fed 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableCommandInfo.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableCommandInfo.cs @@ -4,9 +4,8 @@ using Aspid.Generators.Helper; using Aspid.MVVM.Generators.Helpers; using Aspid.MVVM.Generators.Generators.Ids.Data; -using static Aspid.Generators.Helper.Classes; using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; -using static Aspid.MVVM.Generators.Generators.Descriptions.General; +using static Aspid.MVVM.Generators.Generators.Descriptions.Constants; namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; @@ -42,8 +41,8 @@ public BindableCommandInfo(IMethodSymbol methodSymbol, string? canExecute, bool CommandDeclaration = $""" #region {Name} + {EditorBrowsableAttributeNever} {GeneratedCodeViewModelAttribute} - [{EditorBrowsableAttribute}({EditorBrowsableState}.Never)] private {Type} {fieldName}; {GeneratedCodeViewModelAttribute} @@ -90,4 +89,4 @@ private static string GetCanExecuteAction(IMethodSymbol command, bool isLambda, return canExecuteName.ToString(); } -} +} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableFieldInfo.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableFieldInfo.cs index ea11c7e..35c4add 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableFieldInfo.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindableFieldInfo.cs @@ -6,7 +6,7 @@ using Aspid.MVVM.Generators.Generators.Ids.Data; using static Aspid.Generators.Helper.Classes; using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; -using static Aspid.MVVM.Generators.Generators.Descriptions.General; +using static Aspid.MVVM.Generators.Generators.Descriptions.Constants; namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; @@ -43,16 +43,19 @@ public BindableFieldInfo(IFieldSymbol fieldSymbol, BindMode mode) { declaration.AppendLine($"#region {Name}"); + var keywordThis = !fieldSymbol.IsStatic ? "this." : string.Empty; + var fieldInvoke = $"{keywordThis}{fieldSymbol.Name}"; + declaration.AppendLine(Mode is BindMode.OneTime ? $""" {GeneratedCodeViewModelAttribute} - {accessors.General}{Type} {Name} => {fieldSymbol.Name}; + {accessors.General}{Type} {Name} => {fieldInvoke}; """ : $$""" {{GeneratedCodeViewModelAttribute}} {{accessors.General}}{{Type}} {{Name}} { - {{accessors.Get}}get => {{fieldSymbol.Name}}; + {{accessors.Get}}get => {{fieldInvoke}}; {{accessors.Set}}set => {{setMethodName}}(value); } """); @@ -62,7 +65,6 @@ public BindableFieldInfo(IFieldSymbol fieldSymbol, BindMode mode) var onChangedMethod = $"On{Name}Changed"; var onChangingMethod = $"On{Name}Changing"; var methodModifier = Accessors.ConvertAccessorToString(accessors.SetKind); - var keywordThis = !fieldSymbol.IsStatic ? "this." : string.Empty; var invoke = !string.IsNullOrWhiteSpace(Bindable.OnPropertyChangedName) ? $"{Bindable.OnPropertyChangedName}();" @@ -74,33 +76,33 @@ public BindableFieldInfo(IFieldSymbol fieldSymbol, BindMode mode) {{GeneratedCodeViewModelAttribute}} {{methodModifier}}void {{setMethodName}}({{Type}} value) { - if ({{EqualityComparer_1}}<{{Type}}>.Default.Equals({{fieldSymbol.Name}}, value)) return; + if ({{EqualityComparer_1}}<{{Type}}>.Default.Equals({{fieldInvoke}}, value)) return; - {{Type}} oldValue = {{fieldSymbol.Name}}; + {{Type}} oldValue = {{fieldInvoke}}; {{onChangingMethod}}(value); {{onChangingMethod}}(oldValue, value); { - {{keywordThis}}{{fieldSymbol.Name}} = value; + {{fieldInvoke}} = value; {{invoke}} } {{onChangedMethod}}(value); {{onChangedMethod}}(oldValue, value); } - [{{EditorBrowsableAttribute}}({{EditorBrowsableState}}.Never)] + {{EditorBrowsableAttributeNever}} {{GeneratedCodeViewModelAttribute}} partial void {{onChangingMethod}}({{Type}} newValue); - [{{EditorBrowsableAttribute}}({{EditorBrowsableState}}.Never)] + {{EditorBrowsableAttributeNever}} {{GeneratedCodeViewModelAttribute}} partial void {{onChangingMethod}}({{Type}} oldValue, {{Type}} newValue); - [{{EditorBrowsableAttribute}}({{EditorBrowsableState}}.Never)] + {{EditorBrowsableAttributeNever}} {{GeneratedCodeViewModelAttribute}} partial void {{onChangedMethod}}({{Type}} newValue); - [{{EditorBrowsableAttribute}}({{EditorBrowsableState}}.Never)] + {{EditorBrowsableAttributeNever}} {{GeneratedCodeViewModelAttribute}} partial void {{onChangedMethod}}({{Type}} oldValue, {{Type}} newValue); """); @@ -197,4 +199,4 @@ private static int Compare(in SyntaxKind get, in SyntaxKind set) return 0; } } -} +} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindablePropertyInfo.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindablePropertyInfo.cs index 13d8f14..b2490d7 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindablePropertyInfo.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindablePropertyInfo.cs @@ -3,7 +3,7 @@ using Aspid.Generators.Helper; using Aspid.MVVM.Generators.Generators.Ids.Data; using static Aspid.Generators.Helper.Classes; -using static Aspid.MVVM.Generators.Generators.Descriptions.General; +using static Aspid.MVVM.Generators.Generators.Descriptions.Constants; namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; @@ -69,19 +69,19 @@ public BindablePropertyInfo(IPropertySymbol propertySymbol, BindMode mode, bool return true; } - [{{EditorBrowsableAttribute}}({{EditorBrowsableState}}.Never)] + {{EditorBrowsableAttributeNever}} {{GeneratedCodeViewModelAttribute}} partial void {{onChangingMethod}}({{Type}} newValue); - [{{EditorBrowsableAttribute}}({{EditorBrowsableState}}.Never)] + {{EditorBrowsableAttributeNever}} {{GeneratedCodeViewModelAttribute}} partial void {{onChangingMethod}}({{Type}} oldValue, {{Type}} newValue); - [{{EditorBrowsableAttribute}}({{EditorBrowsableState}}.Never)] + {{EditorBrowsableAttributeNever}} {{GeneratedCodeViewModelAttribute}} partial void {{onChangedMethod}}({{Type}} newValue); - [{{EditorBrowsableAttribute}}({{EditorBrowsableState}}.Never)] + {{EditorBrowsableAttributeNever}} {{GeneratedCodeViewModelAttribute}} partial void {{onChangedMethod}}({{Type}} oldValue, {{Type}} newValue); """); @@ -102,5 +102,4 @@ public BindablePropertyInfo(IPropertySymbol propertySymbol, BindMode mode, bool Declaration = declaration.ToString(); } -} - +} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs index 278b4ad..0f3dfe7 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs @@ -2,9 +2,8 @@ using Aspid.Generators.Helper; using Aspid.MVVM.Generators.Helpers; using Aspid.MVVM.Generators.Generators.ViewModels.Extensions; -using static Aspid.Generators.Helper.Classes; -using static Aspid.MVVM.Generators.Generators.Descriptions.General; using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; +using static Aspid.MVVM.Generators.Generators.Descriptions.Constants; using SymbolExtensions = Aspid.MVVM.Generators.Helpers.SymbolExtensions; namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; @@ -226,7 +225,7 @@ private static string RecognizeDeclaration( return $""" - [{EditorBrowsableAttribute}({EditorBrowsableState}.Never)] + {EditorBrowsableAttributeNever} {GeneratedCodeViewModelAttribute} private {fieldType} {fieldName}; @@ -235,4 +234,4 @@ private static string RecognizeDeclaration( {instantiate}; """; } -} +} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/IBindableMemberInfo.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/IBindableMemberInfo.cs index 624d468..20ec3ef 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/IBindableMemberInfo.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/IBindableMemberInfo.cs @@ -16,4 +16,4 @@ public interface IBindableMemberInfo public BindMode Mode { get; } public GeneratedBindableMembers Bindable { get; } -} +} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/PropertyNotificationData.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/PropertyNotificationData.cs index 65b990d..d0c6ac8 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/PropertyNotificationData.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/PropertyNotificationData.cs @@ -44,4 +44,4 @@ public void AddSetFieldCall(string propertyName, int line, string type) lines.Add(line); PropertiesRequiringSetFieldBody.Add(propertyName); } -} +} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/InvocationExpressionSyntaxExtensions.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/InvocationExpressionSyntaxExtensions.cs index 9b7a069..5fdb9e6 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/InvocationExpressionSyntaxExtensions.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/InvocationExpressionSyntaxExtensions.cs @@ -10,4 +10,4 @@ public static class InvocationExpressionSyntaxExtensions MemberAccessExpressionSyntax memberAccess => memberAccess.Name.Identifier.Text, _ => null }; -} +} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/MethodUsageAnalyzer.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/MethodUsageAnalyzer.cs index 614979f..7e01898 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/MethodUsageAnalyzer.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/MethodUsageAnalyzer.cs @@ -20,5 +20,4 @@ public static HashSet GetUsedMethods(TypeDeclarationSyntax declaration, return usedMethods; } -} - +} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/PropertyNotificationAnalyzer.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/PropertyNotificationAnalyzer.cs index 020028e..78df8b1 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/PropertyNotificationAnalyzer.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Extensions/PropertyNotificationAnalyzer.cs @@ -133,5 +133,4 @@ when nameofExpr.ArgumentList.Arguments.Count > 0 _ => null }; } -} - +} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindablePropertyFactory.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindablePropertyFactory.cs index 782dbf8..178c4d4 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindablePropertyFactory.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindablePropertyFactory.cs @@ -52,4 +52,4 @@ public static IReadOnlyCollection Create( return bindableProperties; } -} +} \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/BinderCachedBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/BinderCachedBody.cs index c67d1cf..9a3bf71 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/BinderCachedBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/BinderCachedBody.cs @@ -1,16 +1,13 @@ using Microsoft.CodeAnalysis; using Aspid.Generators.Helper; using Aspid.MVVM.Generators.Generators.Views.Data; -using Aspid.MVVM.Generators.Generators.Descriptions; using Aspid.MVVM.Generators.Generators.Views.Data.Members; -using static Aspid.Generators.Helper.Classes; +using static Aspid.MVVM.Generators.Generators.Descriptions.Constants; namespace Aspid.MVVM.Generators.Generators.Views.Body; public static class BinderCachedBody { - private const string GeneratedAttribute = General.GeneratedCodeViewAttribute; - public static void Generate( string @namespace, in ViewDataSpan data, @@ -43,8 +40,8 @@ private static CodeWriter AppendCachedBinders(this CodeWriter code, in ViewDataS private static CodeWriter AppendCachedBinderMember(this CodeWriter code, in CachedBinderMember cashedBinderMember) { - code.AppendLine($"[{EditorBrowsableAttribute}({EditorBrowsableState}.Never)]") - .AppendLine(GeneratedAttribute) + code.AppendLine(GeneratedCodeViewAttribute) + .AppendLine(EditorBrowsableAttributeNever) .AppendLine($"private {cashedBinderMember.Type?.ToDisplayStringGlobal()} {cashedBinderMember.CachedName};") .AppendLine(); @@ -53,8 +50,8 @@ private static CodeWriter AppendCachedBinderMember(this CodeWriter code, in Cach private static CodeWriter AppendAsBinderMember(this CodeWriter code, AsBinderMember asBinderMember) { - code.AppendLine($"[{EditorBrowsableAttribute}({EditorBrowsableState}.Never)]") - .AppendLine(GeneratedAttribute) + code.AppendLine(GeneratedCodeViewAttribute) + .AppendLine(EditorBrowsableAttributeNever) .AppendLine(asBinderMember.Type is IArrayTypeSymbol ? $"private {asBinderMember.AsBinderType}[] {asBinderMember.CachedName};" : $"private {asBinderMember.AsBinderType} {asBinderMember.CachedName};") diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/GenericInitializeView.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/GenericInitializeView.cs index 5de1944..ed20df7 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/GenericInitializeView.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/GenericInitializeView.cs @@ -4,13 +4,13 @@ using System.Collections.Generic; using Aspid.MVVM.Generators.Generators.Views.Data; using Aspid.MVVM.Generators.Generators.Descriptions; -using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; using Aspid.MVVM.Generators.Generators.ViewModels.Factories; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; using Aspid.MVVM.Generators.Generators.Views.Body.Extensions; using static Aspid.Generators.Helper.Classes; using static Aspid.Generators.Helper.Unity.UnityClasses; using static Aspid.MVVM.Generators.Generators.Descriptions.Defines; -using static Aspid.MVVM.Generators.Generators.Descriptions.General; +using static Aspid.MVVM.Generators.Generators.Descriptions.Constants; namespace Aspid.MVVM.Generators.Generators.Views.Body; @@ -157,7 +157,7 @@ private static CodeWriter AppendProfilerMarker(this CodeWriter code, in ViewData $""" #if !{ASPID_MVVM_UNITY_PROFILER_DISABLED} {GeneratedCodeViewAttribute} - [{EditorBrowsableAttribute}({EditorBrowsableState}.Never)] + {EditorBrowsableAttributeNever} private readonly static {ProfilerMarker} __initialize{viewModelTypeName.Replace(".", "_")}Marker = new("{data.Declaration.Identifier.Text}.{viewModelTypeName}.Initialize"); #endif """); diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/InitializeBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/InitializeBody.cs index f06ac5c..4e3b7c5 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/InitializeBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/InitializeBody.cs @@ -7,6 +7,7 @@ using static Aspid.Generators.Helper.Classes; using static Aspid.Generators.Helper.Unity.UnityClasses; using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; +using static Aspid.MVVM.Generators.Generators.Descriptions.Constants; namespace Aspid.MVVM.Generators.Generators.Views.Body; @@ -14,8 +15,6 @@ namespace Aspid.MVVM.Generators.Generators.Views.Body; // ReSharper disable once InconsistentNaming public static class InitializeBody { - private const string GeneratedAttribute = General.GeneratedCodeViewAttribute; - public static void Generate( string @namespace, in ViewDataSpan data, @@ -59,25 +58,25 @@ private static CodeWriter AppendNone(this CodeWriter code, in ViewDataSpan data) .AppendMultiline( $""" [global::System.NonSerialized] - {GeneratedAttribute} - [{EditorBrowsableAttribute}({EditorBrowsableState}.Never)] + {GeneratedCodeViewAttribute} + {EditorBrowsableAttributeNever} private bool __isInitializing; """) .AppendMultilineIf(data.IsInstantiateBinders, $""" [global::System.NonSerialized] - {GeneratedAttribute} - [{EditorBrowsableAttribute}({EditorBrowsableState}.Never)] + {GeneratedCodeViewAttribute} + {EditorBrowsableAttributeNever} private bool __isBindersCached; """) .AppendMultiline( $$""" - {{GeneratedAttribute}} + {{GeneratedCodeViewAttribute}} public {{IViewModel}} ViewModel { get; protected set; } - {{GeneratedAttribute}} + {{GeneratedCodeViewAttribute}} public void Initialize({{IViewModel}} viewModel) { if (viewModel is null) throw new {{ArgumentNullException}}(nameof(viewModel)); @@ -87,7 +86,7 @@ public void Initialize({{IViewModel}} viewModel) InitializeInternal(viewModel); } - {{GeneratedAttribute}} + {{GeneratedCodeViewAttribute}} {{modifiers}} void InitializeInternal({{IViewModel}} viewModel) """) .AppendInitializeBody(data) @@ -95,7 +94,7 @@ public void Initialize({{IViewModel}} viewModel) .AppendLine() .AppendMultiline( $$""" - {{GeneratedAttribute}} + {{GeneratedCodeViewAttribute}} public void Deinitialize() { if (ViewModel is null) return; @@ -104,7 +103,7 @@ public void Deinitialize() ViewModel = null; } - {{GeneratedAttribute}} + {{GeneratedCodeViewAttribute}} {{modifiers}} void DeinitializeInternal() """) .AppendDeinitializeBody(data) @@ -123,16 +122,16 @@ private static CodeWriter AppendHasInterfaceOrInheritor(this CodeWriter code, in .AppendMultiline( $""" [global::System.NonSerialized] - {GeneratedAttribute} - [{EditorBrowsableAttribute}({EditorBrowsableState}.Never)] + {GeneratedCodeViewAttribute} + {EditorBrowsableAttributeNever} private bool __isInitializing; """) .AppendMultilineIf(data.IsInstantiateBinders, $""" [global::System.NonSerialized] - {GeneratedAttribute} - [{EditorBrowsableAttribute}({EditorBrowsableState}.Never)] + {GeneratedCodeViewAttribute} + {EditorBrowsableAttributeNever} private bool __isBindersCached; """); @@ -154,12 +153,12 @@ private static CodeWriter AppendProfilerMarkers(this CodeWriter code, string cla return code.AppendMultiline( $""" #if !{Defines.ASPID_MVVM_UNITY_PROFILER_DISABLED} - [{EditorBrowsableAttribute}({EditorBrowsableState}.Never)] - {GeneratedAttribute} + {GeneratedCodeViewAttribute} + {EditorBrowsableAttributeNever} private static readonly {ProfilerMarker} __initializeMarker = new("{className}.Initialize"); - [{EditorBrowsableAttribute}({EditorBrowsableState}.Never)] - {GeneratedAttribute} + {GeneratedCodeViewAttribute} + {EditorBrowsableAttributeNever} private static readonly {ProfilerMarker} __deinitializeMarker = new("{className}.Deinitialize"); #endif """); @@ -173,7 +172,7 @@ private static CodeWriter AppendInitializeInternalDeclaration(this CodeWriter co code.AppendMultiline( $""" - {GeneratedAttribute} + {GeneratedCodeViewAttribute} {modifiers} void InitializeInternal({IViewModel} viewModel) """); @@ -188,7 +187,7 @@ private static CodeWriter AppendDeinitializeInternalDeclaration(this CodeWriter code.AppendMultiline( $""" - {GeneratedAttribute} + {GeneratedCodeViewAttribute} {modifiers} void DeinitializeInternal() """); @@ -281,7 +280,7 @@ private static CodeWriter AppendInstantiateBindersMethods(this CodeWriter code, { code.AppendMultiline( $""" - {GeneratedAttribute} + {GeneratedCodeViewAttribute} private void InstantiateBinders() """) .BeginBlock() @@ -296,10 +295,10 @@ private void InstantiateBinders() .AppendMultiline( $""" - {GeneratedAttribute} + {GeneratedCodeViewAttribute} partial void OnInstantiatingBinders(); - {GeneratedAttribute} + {GeneratedCodeViewAttribute} partial void OnInstantiatedBinders(); """); @@ -366,10 +365,10 @@ private static CodeWriter AppendInitializeInternalEvents(this CodeWriter code) return code.AppendMultiline( $""" - {GeneratedAttribute} + {GeneratedCodeViewAttribute} partial void OnInitializingInternal({IViewModel} viewModel); - {GeneratedAttribute} + {GeneratedCodeViewAttribute} partial void OnInitializedInternal({IViewModel} viewModel); """); } @@ -379,10 +378,10 @@ private static CodeWriter AppendDeinitializeInternalEvents(this CodeWriter code) return code.AppendMultiline( $""" - {GeneratedAttribute} + {GeneratedCodeViewAttribute} partial void OnDeinitializingInternal(); - {GeneratedAttribute} + {GeneratedCodeViewAttribute} partial void OnDeinitializedInternal(); """); } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/Inheritor.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/Inheritor.cs index 94219c6..9a4d9bd 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/Inheritor.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/Inheritor.cs @@ -4,4 +4,4 @@ public enum Inheritor { None, InheritorViewAttribute, -} +} \ No newline at end of file From dec29698eca5b3658afc42418da03ffd4201f74b Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Thu, 22 Jan 2026 10:29:18 +0300 Subject: [PATCH 18/29] Remove .this in bindable properties --- .../ViewModels/Data/Infos/GeneratedBindableMembers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs index 0f3dfe7..d3fe0c3 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs @@ -189,7 +189,7 @@ private static string RecognizePropertyType(ITypeSymbol type, BindMode mode) private static string? RecognizeInvoke(BindMode mode, string memberName, string fieldName) { return mode is not (BindMode.OneWayToSource or BindMode.OneTime) - ? $"this.{fieldName}?.Invoke({memberName});" + ? $"{fieldName}?.Invoke({memberName});" : null; } From 88f73cb556395e6ce8b881e2bac96d577d23da7e Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Thu, 22 Jan 2026 10:30:18 +0300 Subject: [PATCH 19/29] Fix Generated const and properties --- .../ViewModels/Factories/BindableFieldFactory.cs | 1 + .../Factories/BindablePropertyFactory.cs | 14 +++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableFieldFactory.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableFieldFactory.cs index 35670bd..ae6cdb3 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableFieldFactory.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableFieldFactory.cs @@ -25,6 +25,7 @@ public static IReadOnlyCollection Create(ImmutableArray Create( switch (mode) { case BindMode.OneTime: - case BindMode.OneWay: break; - + case BindMode.OneWay: + { + if (property.IsWriteOnly) continue; + break; + } case BindMode.TwoWay: + { + if (property.IsReadOnly || property.IsWriteOnly) continue; + break; + } case BindMode.OneWayToSource: { if (property.IsReadOnly) continue; break; } - + + case BindMode.None: default: continue; } From a1bc4deb73fae5697927affb65c0ebb6fba7a7a7 Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Sun, 22 Feb 2026 21:11:25 +0300 Subject: [PATCH 20/29] Fix name for bindable properties and add IAnyBinder BinderlLog support --- .../Binders/BinderGenerator.Find.cs | 26 +- .../Generators/Binders/Body/BinderLogBody.cs | 224 +++++++++++------- .../Generators/Descriptions/Classes.Aspid.cs | 5 +- .../Body/PropertyNotificationBody.cs | 6 +- .../Data/Infos/BindablePropertyInfo.cs | 17 +- .../Data/Infos/GeneratedBindableMembers.cs | 22 +- .../Factories/BindablePropertyFactory.cs | 15 +- .../Helpers/SymbolExtensions.cs | 15 +- Directory.Build.targets | 11 + 9 files changed, 215 insertions(+), 126 deletions(-) create mode 100644 Directory.Build.targets diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/BinderGenerator.Find.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/BinderGenerator.Find.cs index 34eeefa..b1518ce 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/BinderGenerator.Find.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/BinderGenerator.Find.cs @@ -35,14 +35,20 @@ public partial class BinderGenerator .Any(binderMethod => { if (binderMethod.Name is not setValueName) return false; - + return binderMethod.EqualsSignature(method) && method.ExplicitInterfaceImplementations.Length != 0; }); - + if (methodsExplicitImplemented) return null; - if (!hasBinderLogInBaseType + // Check if IAnyBinder.SetValue is already explicitly implemented + if (method.IsGenericMethod && method.Name == setValueName + && method.ExplicitInterfaceImplementations.Any(m => + m.ContainingType.Name is "IAnyBinder")) + return null; + + if (!hasBinderLogInBaseType && !SymbolEqualityComparer.Default.Equals(type, symbol) && method.HasAnyAttributeInSelf(Classes.BinderLogAttribute)) { @@ -55,10 +61,18 @@ public partial class BinderGenerator foreach (var method in symbol.GetMembers().OfType()) { - if (method.Parameters.Length != 1) continue; + if (method.Parameters.Length is not 1) continue; if (method.NameFromExplicitImplementation() != setValueName) continue; - if (!symbol.HasAnyInterfaceInSelfAndBases($"{Classes.IBinder.FullName}<{method.Parameters[0].Type.ToDisplayString()}>")) continue; - + + var isIBinderMethod = !method.IsGenericMethod + && symbol.HasAnyInterfaceInSelfAndBases( + $"{Classes.IBinder.FullName}<{method.Parameters[0].Type.ToDisplayString()}>"); + + var isIAnyBinderMethod = method is { IsGenericMethod: true, TypeParameters.Length: 1 } + && symbol.HasAnyInterfaceInSelfAndBases(Classes.IAnyBinder.FullName); + + if (!isIBinderMethod && !isIAnyBinderMethod) continue; + if (method.HasAnyAttributeInSelf(Classes.BinderLogAttribute) && !method.ExplicitInterfaceImplementations.Any()) binderLogMethods.Add(method); diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/Body/BinderLogBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/Body/BinderLogBody.cs index 8102378..06bba5e 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/Body/BinderLogBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Binders/Body/BinderLogBody.cs @@ -13,114 +13,166 @@ namespace Aspid.MVVM.Generators.Generators.Binders.Body; public static class BinderLogBody { private static readonly string IBinder = Classes.IBinder; + private static readonly string IAnyBinder = Classes.IAnyBinder; private static readonly string Exception = Aspid.Generators.Helper.Classes.Exception; - public static CodeWriter AppendBinderLogBody(this CodeWriter code, in BinderDataSpan data) + extension(CodeWriter code) { - var hasBinderLogInBaseType = data.HasBinderLogInBaseType; - - if (!hasBinderLogInBaseType) + public CodeWriter AppendBinderLogBody(in BinderDataSpan data) { - code.AppendProfilerMarkers(data) - .AppendProperties(data); - } + var hasBinderLogInBaseType = data.HasBinderLogInBaseType; + + if (!hasBinderLogInBaseType) + { + code.AppendProfilerMarkers(data) + .AppendProperties(data); + } - code.AppendSetValueMethods(data.Methods); + code.AppendSetValueMethods(data.Methods); - if (!hasBinderLogInBaseType) - code.AppendAddLogMethod(data); + if (!hasBinderLogInBaseType) + code.AppendAddLogMethod(data); - return code; - } + return code; + } - private static CodeWriter AppendProfilerMarkers(this CodeWriter code, in BinderDataSpan data) - { - var modifier = data.Symbol.IsSealed ? "private" : "protected"; - var className = data.Declaration.Identifier.Text; + private CodeWriter AppendProfilerMarkers(in BinderDataSpan data) + { + var modifier = data.Symbol.IsSealed ? "private" : "protected"; + var className = data.Declaration.Identifier.Text; - code.AppendLine($"{modifier} static readonly {ProfilerMarker} SetValueMarker = new(\"{className}.SetValue\");"); - return code.AppendLine(); - } + code.AppendLine($"{modifier} static readonly {ProfilerMarker} SetValueMarker = new(\"{className}.SetValue\");"); + return code.AppendLine(); + } - private static CodeWriter AppendProperties(this CodeWriter code, in BinderDataSpan data) - { - var modifier = data.Symbol.IsSealed ? "private" : "protected"; + private CodeWriter AppendProperties(in BinderDataSpan data) + { + var modifier = data.Symbol.IsSealed ? "private" : "protected"; - code.AppendMultiline( - $""" - {GeneratedCodeLogBinderAttribute} - [{SerializeField}] private bool _isDebug; - - // TODO Add Custom Property - {GeneratedCodeLogBinderAttribute} - [{SerializeField}] private {List_1} _log; - - {GeneratedCodeLogBinderAttribute} - {modifier} bool IsDebug => _isDebug; - """) - .AppendLine(); + code.AppendMultiline( + $""" + {GeneratedCodeLogBinderAttribute} + [{SerializeField}] private bool _isDebug; + + // TODO Add Custom Property + {GeneratedCodeLogBinderAttribute} + [{SerializeField}] private {List_1} _log; + + {GeneratedCodeLogBinderAttribute} + {modifier} bool IsDebug => _isDebug; + """) + .AppendLine(); - return code; - } + return code; + } - private static CodeWriter AppendSetValueMethods(this CodeWriter code, in ReadOnlySpan methods) - { - foreach (var method in methods) + private CodeWriter AppendSetValueMethods(in ReadOnlySpan methods) + { + foreach (var method in methods) + { + if (method.IsGenericMethod) + code.AppendIAnyBinderSetValueMethod(method); + else + code.AppendIBinderSetValueMethod(method); + + code.AppendLine(); + } + + return code; + } + + private void AppendIBinderSetValueMethod(IMethodSymbol method) { var parameterName = method.Parameters[0].Name; var parameterType = method.Parameters[0].Type.ToDisplayStringGlobal(); - + code.AppendMultiline( $$""" - {{GeneratedCodeLogBinderAttribute}} - void {{IBinder}}<{{parameterType}}>.{{method.Name}}({{parameterType}} {{parameterName}}) - { - if (IsDebug) - { - try - { - using (SetValueMarker.Auto()) - { - SetValue({{parameterName}}); - } - - AddLog($"SetValue: {{{parameterName}}}"); - } - catch ({{Exception}} e) - { - AddLog($"Exception: {e}. {nameof({{parameterName}})}: {{parameterName}}"); - throw; - } - } - else - { - using (SetValueMarker.Auto()) - { - SetValue({{parameterName}}); - } - } - } - """) - .AppendLine(); + {{GeneratedCodeLogBinderAttribute}} + void {{IBinder}}<{{parameterType}}>.{{method.Name}}({{parameterType}} {{parameterName}}) + { + if (IsDebug) + { + try + { + using (SetValueMarker.Auto()) + { + SetValue({{parameterName}}); + } + + AddLog($"SetValue: {{{parameterName}}}"); + } + catch ({{Exception}} e) + { + AddLog($"Exception: {e}. {nameof({{parameterName}})}: {{parameterName}}"); + throw; + } + } + else + { + using (SetValueMarker.Auto()) + { + SetValue({{parameterName}}); + } + } + } + """); } - return code; - } + private void AppendIAnyBinderSetValueMethod(IMethodSymbol method) + { + var typeParamName = method.TypeParameters[0].Name; + var parameterName = method.Parameters[0].Name; - private static CodeWriter AppendAddLogMethod(this CodeWriter code, in BinderDataSpan data) - { - var modifier = data.Symbol.IsSealed ? "private" : "protected"; + code.AppendMultiline( + $$""" + {{GeneratedCodeLogBinderAttribute}} + void {{IAnyBinder}}.{{method.Name}}<{{typeParamName}}>({{typeParamName}} {{parameterName}}) + { + if (IsDebug) + { + try + { + using (SetValueMarker.Auto()) + { + SetValue({{parameterName}}); + } + + AddLog($"SetValue: {{{parameterName}}}"); + } + catch ({{Exception}} e) + { + AddLog($"Exception: {e}. {nameof({{parameterName}})}: {{parameterName}}"); + throw; + } + } + else + { + using (SetValueMarker.Auto()) + { + SetValue({{parameterName}}); + } + } + } + """); + } + + private CodeWriter AppendAddLogMethod(in BinderDataSpan data) + { + var modifier = data.Symbol.IsSealed ? "private" : "protected"; - code.AppendMultiline( - $$""" - {{GeneratedCodeLogBinderAttribute}} - {{modifier}} void AddLog(string log) - { - _log ??= new {{List_1}}(); - _log.Add(log); - } - """); + code.AppendMultiline( + $$""" + {{GeneratedCodeLogBinderAttribute}} + {{modifier}} void AddLog(string log) + { + _log ??= new {{List_1}}(); + _log.Add(log); + } + """); - return code; + return code; + } } + } \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Classes.Aspid.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Classes.Aspid.cs index 05c6017..52269c3 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Classes.Aspid.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Classes.Aspid.cs @@ -25,7 +25,10 @@ public static class Classes public static readonly TypeText IBinder = new(nameof(IBinder), Namespaces.Aspid_MVVM); - + + public static readonly TypeText IAnyBinder = + new(nameof(IAnyBinder), Namespaces.Aspid_MVVM); + public static readonly TypeText IReverseBinder = new(nameof(IReverseBinder), Namespaces.Aspid_MVVM); diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertyNotificationBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertyNotificationBody.cs index dc36f4e..322e07f 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertyNotificationBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertyNotificationBody.cs @@ -1,9 +1,11 @@ using System.Linq; using Microsoft.CodeAnalysis; using Aspid.Generators.Helper; +using Aspid.MVVM.Generators.Helpers; using Aspid.MVVM.Generators.Generators.ViewModels.Data; using static Aspid.Generators.Helper.Classes; using static Aspid.MVVM.Generators.Generators.Descriptions.Constants; +using SymbolExtensions = Aspid.MVVM.Generators.Helpers.SymbolExtensions; namespace Aspid.MVVM.Generators.Generators.ViewModels.Body; @@ -56,7 +58,7 @@ private CodeWriter AppendOnPropertyChanged(in ViewModelData data) var keyword = first ? "if" : "else if"; first = false; - code.AppendLine($"{keyword} (line is {linesPattern}) On{propertyName}PropertyChanged();"); + code.AppendLine($"{keyword} (line is {linesPattern}) On{propertyName.CapitalizeFirstLetter()}PropertyChanged();"); } code.AppendLine($"else throw new {NotImplementedException}($\"OnPropertyChanged: No property found for line {{line}}\");"); @@ -116,7 +118,7 @@ private bool SetField(ref T field, T newValue, string propertyName, {{CallerL var keyword = first ? "if" : "else if"; first = false; - code.AppendLine($"{keyword} (line is {linesPattern}) return Set{propertyName}Field(ref field, newValue);"); + code.AppendLine($"{keyword} (line is {linesPattern}) return Set{propertyName.CapitalizeFirstLetter()}Field(ref field, newValue);"); } code.AppendLine($"else throw new {NotImplementedException}($\"SetField: No property found for line {{line}}\");") diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindablePropertyInfo.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindablePropertyInfo.cs index b2490d7..c26e0ef 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindablePropertyInfo.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/BindablePropertyInfo.cs @@ -1,6 +1,7 @@ using System.Text; using Microsoft.CodeAnalysis; using Aspid.Generators.Helper; +using Aspid.MVVM.Generators.Helpers; using Aspid.MVVM.Generators.Generators.Ids.Data; using static Aspid.Generators.Helper.Classes; using static Aspid.MVVM.Generators.Generators.Descriptions.Constants; @@ -10,25 +11,25 @@ namespace Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; public sealed class BindablePropertyInfo : IBindableMemberInfo { public ISymbol Member { get; } - + public string Type { get; } - + public string Name { get; } - + public IdData Id { get; } - + public BindMode Mode { get; } - + public GeneratedBindableMembers Bindable { get; } - + public string Declaration { get; } public BindablePropertyInfo(IPropertySymbol propertySymbol, BindMode mode, bool isSetMethodUsed) { Mode = mode; Member = propertySymbol; - Name = propertySymbol.Name; Id = new IdData(propertySymbol); + Name = propertySymbol.Name.CapitalizeFirstLetter(); Type = propertySymbol.Type.ToDisplayStringGlobal(); Bindable = GeneratedBindableMembers.CreateForProperty(propertySymbol); @@ -92,7 +93,7 @@ public BindablePropertyInfo(IPropertySymbol propertySymbol, BindMode mode, bool $""" {GeneratedCodeViewModelAttribute} private bool {setMethodName}(ref {Type} field, {Type} value) => - throw new {Classes.NotImplementedException}("Generator Error: SetMethod is not used."); + throw new {NotImplementedException}("Generator Error: SetMethod is not used."); """); } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs index d3fe0c3..d3c8624 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Data/Infos/GeneratedBindableMembers.cs @@ -79,36 +79,38 @@ public static GeneratedBindableMembers CreateForProperty(IPropertySymbol propert var fieldType = RecognizeFieldType(propertySymbol.Type, mode); // PropertyName -> PropertyNameBindable - var propertyName = $"{propertySymbol.Name}Bindable"; + var capitalizedName = propertySymbol.Name.CapitalizeFirstLetter(); + var propertyName = $"{capitalizedName}Bindable"; var propertyType = RecognizePropertyType(propertySymbol.Type, mode); var invoke = RecognizeInvoke(mode, memberName, fieldName); var declaration = RecognizeDeclaration(mode, memberName, fieldName, fieldType, propertyName, propertyType); - + var onPropertyChangedName = mode is not BindMode.OneTime and not BindMode.OneWayToSource and not BindMode.None - ? $"On{propertySymbol.Name}PropertyChanged" + ? $"On{capitalizedName}PropertyChanged" : null; - + return new GeneratedBindableMembers(invoke, declaration, propertyName, propertyType, onPropertyChangedName); } - + public static GeneratedBindableMembers CreateForBindAlso(IPropertySymbol propertySymbol) { const BindMode mode = BindMode.OneWay; var memberName = propertySymbol.Name; - + // PropertyName -> __propertyNameBindable var fieldName = $"{propertySymbol.GetFieldName(prefix: "__")}Bindable"; var fieldType = RecognizeFieldType(propertySymbol.Type, mode); - + // PropertyName -> PropertyNameBindable - var propertyName = $"{propertySymbol.Name}Bindable"; + var capitalizedName = propertySymbol.Name.CapitalizeFirstLetter(); + var propertyName = $"{capitalizedName}Bindable"; var propertyType = RecognizePropertyType(propertySymbol.Type, mode); var invoke = RecognizeInvoke(mode, memberName, fieldName); var declaration = RecognizeDeclaration(mode, memberName, fieldName, fieldType, propertyName, propertyType); - - return new GeneratedBindableMembers(invoke, declaration, propertyName, propertyType, $"On{propertySymbol.Name}PropertyChanged"); + + return new GeneratedBindableMembers(invoke, declaration, propertyName, propertyType, $"On{capitalizedName}PropertyChanged"); } private static string RecognizeFieldType(ITypeSymbol type, BindMode mode) diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindablePropertyFactory.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindablePropertyFactory.cs index 8b9254b..03b70e6 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindablePropertyFactory.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindablePropertyFactory.cs @@ -2,6 +2,7 @@ using Microsoft.CodeAnalysis; using System.Collections.Generic; using System.Collections.Immutable; +using Aspid.MVVM.Generators.Helpers; using Microsoft.CodeAnalysis.CSharp.Syntax; using Aspid.MVVM.Generators.Generators.ViewModels.Data; using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; @@ -13,15 +14,15 @@ namespace Aspid.MVVM.Generators.Generators.ViewModels.Factories; public static class BindablePropertyFactory { public static IReadOnlyCollection Create( - TypeDeclarationSyntax declaration, + TypeDeclarationSyntax declaration, ImmutableArray properties, PropertyNotificationData propertyNotificationData) { var bindableProperties = new List(); - + var setMethodNames = new HashSet(properties - .Select(p => $"Set{p.Name}Field")); - + .Select(p => $"Set{p.Name.CapitalizeFirstLetter()}Field")); + var usedSetMethods = MethodUsageAnalyzer.GetUsedMethods(declaration, setMethodNames); foreach (var property in properties) @@ -50,9 +51,9 @@ public static IReadOnlyCollection Create( case BindMode.None: default: continue; } - - var setMethodName = $"Set{property.Name}Field"; - var isSetMethodUsed = usedSetMethods.Contains(setMethodName) + + var setMethodName = $"Set{property.Name.CapitalizeFirstLetter()}Field"; + var isSetMethodUsed = usedSetMethods.Contains(setMethodName) || propertyNotificationData.PropertiesRequiringSetFieldBody.Contains(property.Name); bindableProperties.Add(new BindablePropertyInfo(property, mode, isSetMethodUsed)); diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/SymbolExtensions.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/SymbolExtensions.cs index 71905d0..21ec8e4 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/SymbolExtensions.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Helpers/SymbolExtensions.cs @@ -40,14 +40,17 @@ public static string RemoveFieldPrefix(this ISymbol member) => public static string RemoveFieldPrefix(in string name) { - var prefixCount = name.StartsWith("_") + var prefixCount = name.StartsWith("_") ? 1 - : name.StartsWith("m_") || name.StartsWith("s_") - ? 2 + : name.StartsWith("m_") || name.StartsWith("s_") + ? 2 : 0; - - return prefixCount > 0 - ? name.Remove(0, prefixCount) + + return prefixCount > 0 + ? name.Remove(0, prefixCount) : name; } + + public static string CapitalizeFirstLetter(this string name) => + name.Length == 0 ? name : char.ToUpper(name[0]) + name.Substring(1); } \ No newline at end of file diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 0000000..c386f97 --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,11 @@ + + + + + <_UnityDestination>$(MSBuildThisFileDirectory)../Aspid.MVVM/Assets/Plugins/Aspid/MVVM/ + + + + + + From 640009d3a9d176bd510517ae4ab1eff687b3b74a Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Tue, 24 Feb 2026 09:47:18 +0300 Subject: [PATCH 21/29] Fix BindAlso --- .../Generators/ViewModels/Body/BindableMembers.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembers.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembers.cs index 0ec49d0..4f59f5c 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembers.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/BindableMembers.cs @@ -2,6 +2,7 @@ using Microsoft.CodeAnalysis; using Aspid.Generators.Helper; using System.Collections.Generic; +using Aspid.MVVM.Generators.Helpers; using Aspid.MVVM.Generators.Generators.ViewModels.Data; using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; using static Aspid.MVVM.Generators.Generators.Descriptions.Classes; @@ -67,14 +68,16 @@ private CodeWriter AppendBindableMembers(in ViewModelData data) { foreach (var member in data.Members) { - code.AppendLine($"#region {member.Name}") + var capitalizeName = member.Name.CapitalizeFirstLetter(); + + code.AppendLine($"#region {capitalizeName}") .AppendMultiline(member.Bindable.Declaration) .AppendLine(); if (!string.IsNullOrWhiteSpace(member.Bindable.OnPropertyChangedName)) { code.AppendLine($"{GeneratedCodeViewModelAttribute}") - .AppendLine($"private void On{member.Name}PropertyChanged()") + .AppendLine($"private void On{capitalizeName}PropertyChanged()") .BeginBlock() .AppendLineIf(!string.IsNullOrWhiteSpace(member.Bindable.Invoke), member.Bindable.Invoke); From a1b0219a47a95555a8d82e847d21bd61ea1e4f53 Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Thu, 5 Mar 2026 12:51:21 +0300 Subject: [PATCH 22/29] Update Aspid.Generators.Helper --- .../Aspid.MVVM.Generators.csproj | 4 ++-- .../Generators/Descriptions/Constants.cs | 2 -- .../ViewModels/Body/PropertyNotificationBody.cs | 13 ++++++------- .../ViewModels/Factories/BindableCommandFactory.cs | 1 - .../Views/Body/Extensions/BindSafelyExtensions.cs | 11 ----------- 5 files changed, 8 insertions(+), 23 deletions(-) diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Aspid.MVVM.Generators.csproj b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Aspid.MVVM.Generators.csproj index 51ae120..0441d35 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Aspid.MVVM.Generators.csproj +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Aspid.MVVM.Generators.csproj @@ -12,8 +12,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Constants.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Constants.cs index 7121902..37b90bf 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Constants.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Constants.cs @@ -16,7 +16,5 @@ public static class Constants public const string GeneratedCodeViewModelAttribute = "[global::System.CodeDom.Compiler.GeneratedCode(\"Aspid.MVVM.Generators.ViewModelGenerator\", \"1.1.0\")]"; - public const string CallerLineNumberAttribute = "[global::System.Runtime.CompilerServices.CallerLineNumber]"; - public static readonly string EditorBrowsableAttributeNever = $"[{EditorBrowsableAttribute}({EditorBrowsableState}.Never)]"; } \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertyNotificationBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertyNotificationBody.cs index 322e07f..a80c4f7 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertyNotificationBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Body/PropertyNotificationBody.cs @@ -5,7 +5,6 @@ using Aspid.MVVM.Generators.Generators.ViewModels.Data; using static Aspid.Generators.Helper.Classes; using static Aspid.MVVM.Generators.Generators.Descriptions.Constants; -using SymbolExtensions = Aspid.MVVM.Generators.Helpers.SymbolExtensions; namespace Aspid.MVVM.Generators.Generators.ViewModels.Body; @@ -43,7 +42,7 @@ private CodeWriter AppendOnPropertyChanged(in ViewModelData data) code.AppendLine("#region OnPropertyChanged"); code.AppendLine(GeneratedCodeViewModelAttribute) - .AppendLine($"private void OnPropertyChanged({CallerLineNumberAttribute} int line = -1)") + .AppendLine($"private void OnPropertyChanged([{CallerLineNumberAttribute}] int line = -1)") .BeginBlock(); if (notificationData.HasOnPropertyChangedCalls) @@ -74,7 +73,7 @@ private CodeWriter AppendOnPropertyChanged(in ViewModelData data) code.AppendMultiline( $""" {GeneratedCodeViewModelAttribute} - private void OnPropertyChanged(string propertyName, {CallerLineNumberAttribute} int line = -1) => + private void OnPropertyChanged(string propertyName, [{CallerLineNumberAttribute}] int line = -1) => OnPropertyChanged(line); """) .AppendLine("#endregion"); @@ -90,11 +89,11 @@ private CodeWriter AppendSetField(in ViewModelData data) .AppendMultiline( $$""" {{GeneratedCodeViewModelAttribute}} - private bool SetField(ref T field, T newValue, {{CallerLineNumberAttribute}} int line = -1) => + private bool SetField(ref T field, T newValue, [{{CallerLineNumberAttribute}}] int line = -1) => throw new {{NotImplementedException}}("SetField: No property found for line {line}. Use typed overload."); {{GeneratedCodeViewModelAttribute}} - private bool SetField(ref T field, T newValue, string propertyName, {{CallerLineNumberAttribute}} int line = -1) => + private bool SetField(ref T field, T newValue, string propertyName, [{{CallerLineNumberAttribute}}] int line = -1) => throw new {{NotImplementedException}}($"SetField: No property {propertyName} found for line {line}. Use typed overload."); """); @@ -106,7 +105,7 @@ private bool SetField(ref T field, T newValue, string propertyName, {{CallerL var propertyCalls = typeGroup.Value; code.AppendLine(GeneratedCodeViewModelAttribute) - .AppendLine($"private bool SetField(ref {type} field, {type} newValue, {CallerLineNumberAttribute} int line = -1)") + .AppendLine($"private bool SetField(ref {type} field, {type} newValue, [{CallerLineNumberAttribute}] int line = -1)") .BeginBlock(); var first = true; @@ -127,7 +126,7 @@ private bool SetField(ref T field, T newValue, string propertyName, {{CallerL .AppendMultiline( $""" {GeneratedCodeViewModelAttribute} - private bool SetField(ref {type} field, {type} newValue, string propertyName, {CallerLineNumberAttribute} int line = -1) => + private bool SetField(ref {type} field, {type} newValue, string propertyName, [{CallerLineNumberAttribute}] int line = -1) => SetField(ref field, newValue, line); """); } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableCommandFactory.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableCommandFactory.cs index 6f1c505..4364b0e 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableCommandFactory.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/ViewModels/Factories/BindableCommandFactory.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; -using Aspid.MVVM.Generators.Generators.ViewModels.Data.Members; using Classes = Aspid.MVVM.Generators.Generators.Descriptions.Classes; namespace Aspid.MVVM.Generators.Generators.ViewModels.Factories; diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/Extensions/BindSafelyExtensions.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/Extensions/BindSafelyExtensions.cs index 34b2a3c..cb8d0d6 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/Extensions/BindSafelyExtensions.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/Extensions/BindSafelyExtensions.cs @@ -1,4 +1,3 @@ -using Microsoft.CodeAnalysis; using Aspid.Generators.Helper; using Aspid.MVVM.Generators.Generators.Views.Data.Members; using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; @@ -20,14 +19,4 @@ public static CodeWriter AppendBindSafely(this CodeWriter code, BinderMember mem : $"{name}.BindSafely(viewModel.FindBindableMember({parameters}));"); } - - private static string GetBinderMemberType(this BinderMember member) - { - if (member is AsBinderMember asBinderMember) - return asBinderMember.AsBinderType; - - return member.Type is IArrayTypeSymbol arrayType - ? arrayType.ElementType.ToDisplayStringGlobal() - : member.Type.ToDisplayStringGlobal(); - } } \ No newline at end of file From 2f3720588bc25eff67e091528dfb16837369a7c0 Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Mon, 20 Apr 2026 11:09:28 +0300 Subject: [PATCH 23/29] Pass owner and bindable Id to BindSafely/UnbindSafely Generated View `InitializeInternal`/`DeinitializeInternal` now forward the View instance and the bindable member Id (`Ids.X` const) to runtime binder helpers, so a null entry in a binders array logs the offending View/GameObject and identifier instead of an opaque message. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Views/Body/Extensions/BindSafelyExtensions.cs | 6 +++--- .../Generators/Views/Body/InitializeBody.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/Extensions/BindSafelyExtensions.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/Extensions/BindSafelyExtensions.cs index cb8d0d6..c68fc9d 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/Extensions/BindSafelyExtensions.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/Extensions/BindSafelyExtensions.cs @@ -14,9 +14,9 @@ public static CodeWriter AppendBindSafely(this CodeWriter code, BinderMember mem var parameters = $"new({member.Id})"; var name = member is CachedBinderMember cachedMember ? cachedMember.CachedName : member.Name; - return code.AppendLine(bindableMemberName is not null - ? $"{name}.BindSafely(viewModel.{bindableMemberName});" - : $"{name}.BindSafely(viewModel.FindBindableMember({parameters}));"); + return code.AppendLine(bindableMemberName is not null + ? $"{name}.BindSafely(viewModel.{bindableMemberName}, this, {member.Id});" + : $"{name}.BindSafely(viewModel.FindBindableMember({parameters}), this, {member.Id});"); } } \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/InitializeBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/InitializeBody.cs index 4e3b7c5..f4f1fb1 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/InitializeBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/InitializeBody.cs @@ -260,11 +260,11 @@ private static CodeWriter AppendDeinitializeBody(this CodeWriter code, in ViewDa { if (member is CachedBinderMember cachedBinderMember) { - code.AppendLine($"{cachedBinderMember.CachedName}.UnbindSafely();"); + code.AppendLine($"{cachedBinderMember.CachedName}.UnbindSafely(this, {member.Id});"); } else { - code.AppendLine($"{member.Name}.UnbindSafely();"); + code.AppendLine($"{member.Name}.UnbindSafely(this, {member.Id});"); } } From efb01e2e1effd0748607b1500e8cf4f5907beb1f Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Mon, 20 Apr 2026 11:52:10 +0300 Subject: [PATCH 24/29] Skip owner/Id args for single-binder BindSafely emission The BindSafely/UnbindSafely runtime overloads for single binders (`this T?`) do not accept owner/memberName params, so emitting them broke compilation for any view holding a non-collection binder field. Emit the diagnostic args only when the member resolves to a collection overload (T[], List, IEnumerable). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Body/Extensions/BindSafelyExtensions.cs | 26 ++++++++++++++++--- .../Generators/Views/Body/InitializeBody.cs | 5 ++-- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/Extensions/BindSafelyExtensions.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/Extensions/BindSafelyExtensions.cs index c68fc9d..57f9e88 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/Extensions/BindSafelyExtensions.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/Extensions/BindSafelyExtensions.cs @@ -1,3 +1,4 @@ +using Microsoft.CodeAnalysis; using Aspid.Generators.Helper; using Aspid.MVVM.Generators.Generators.Views.Data.Members; using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; @@ -8,15 +9,32 @@ public static class BindSafelyExtensions { public static CodeWriter AppendBindSafely(this CodeWriter code, BinderMember member, IBindableMemberInfo bindableMember) => code.AppendBindSafely(member, bindableMember.Bindable.PropertyName); - + public static CodeWriter AppendBindSafely(this CodeWriter code, BinderMember member, string? bindableMemberName = null) { var parameters = $"new({member.Id})"; var name = member is CachedBinderMember cachedMember ? cachedMember.CachedName : member.Name; + var diagnostics = IsBinderCollection(member.Type) ? $", this, {member.Id}" : string.Empty; return code.AppendLine(bindableMemberName is not null - ? $"{name}.BindSafely(viewModel.{bindableMemberName}, this, {member.Id});" - : $"{name}.BindSafely(viewModel.FindBindableMember({parameters}), this, {member.Id});"); + ? $"{name}.BindSafely(viewModel.{bindableMemberName}{diagnostics});" + : $"{name}.BindSafely(viewModel.FindBindableMember({parameters}){diagnostics});"); + } + + // True when `type` resolves to one of the BindSafely/UnbindSafely collection overloads + // (T[], List, IEnumerable or anything implementing IEnumerable). + internal static bool IsBinderCollection(ITypeSymbol? type) + { + if (type is null) return false; + if (type is IArrayTypeSymbol) return true; + if (type.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T) return true; + + foreach (var i in type.AllInterfaces) + { + if (i.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T) + return true; + } + return false; } -} \ No newline at end of file +} diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/InitializeBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/InitializeBody.cs index f4f1fb1..e9935ec 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/InitializeBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/InitializeBody.cs @@ -258,13 +258,14 @@ private static CodeWriter AppendDeinitializeBody(this CodeWriter code, in ViewDa foreach (var member in data.Members) { + var diagnostics = BindSafelyExtensions.IsBinderCollection(member.Type) ? $"this, {member.Id}" : string.Empty; if (member is CachedBinderMember cachedBinderMember) { - code.AppendLine($"{cachedBinderMember.CachedName}.UnbindSafely(this, {member.Id});"); + code.AppendLine($"{cachedBinderMember.CachedName}.UnbindSafely({diagnostics});"); } else { - code.AppendLine($"{member.Name}.UnbindSafely(this, {member.Id});"); + code.AppendLine($"{member.Name}.UnbindSafely({diagnostics});"); } } From 4ed04b48cabf582754ff9f8a1d28455a7d4269c5 Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Mon, 20 Apr 2026 11:57:55 +0300 Subject: [PATCH 25/29] Always emit owner/Id args for BindSafely/UnbindSafely Reverts the IsBinderCollection split: the runtime now exposes the same optional `owner`/`memberName` pair on single-binder overloads, so the generator can emit a single uniform call shape regardless of binder arity. Keeps the emitter trivial. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Body/Extensions/BindSafelyExtensions.cs | 23 ++----------------- .../Generators/Views/Body/InitializeBody.cs | 5 ++-- 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/Extensions/BindSafelyExtensions.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/Extensions/BindSafelyExtensions.cs index 57f9e88..a6d8819 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/Extensions/BindSafelyExtensions.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/Extensions/BindSafelyExtensions.cs @@ -1,4 +1,3 @@ -using Microsoft.CodeAnalysis; using Aspid.Generators.Helper; using Aspid.MVVM.Generators.Generators.Views.Data.Members; using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; @@ -14,27 +13,9 @@ public static CodeWriter AppendBindSafely(this CodeWriter code, BinderMember mem { var parameters = $"new({member.Id})"; var name = member is CachedBinderMember cachedMember ? cachedMember.CachedName : member.Name; - var diagnostics = IsBinderCollection(member.Type) ? $", this, {member.Id}" : string.Empty; return code.AppendLine(bindableMemberName is not null - ? $"{name}.BindSafely(viewModel.{bindableMemberName}{diagnostics});" - : $"{name}.BindSafely(viewModel.FindBindableMember({parameters}){diagnostics});"); - } - - // True when `type` resolves to one of the BindSafely/UnbindSafely collection overloads - // (T[], List, IEnumerable or anything implementing IEnumerable). - internal static bool IsBinderCollection(ITypeSymbol? type) - { - if (type is null) return false; - if (type is IArrayTypeSymbol) return true; - if (type.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T) return true; - - foreach (var i in type.AllInterfaces) - { - if (i.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T) - return true; - } - - return false; + ? $"{name}.BindSafely(viewModel.{bindableMemberName}, this, {member.Id});" + : $"{name}.BindSafely(viewModel.FindBindableMember({parameters}), this, {member.Id});"); } } diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/InitializeBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/InitializeBody.cs index e9935ec..f4f1fb1 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/InitializeBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/InitializeBody.cs @@ -258,14 +258,13 @@ private static CodeWriter AppendDeinitializeBody(this CodeWriter code, in ViewDa foreach (var member in data.Members) { - var diagnostics = BindSafelyExtensions.IsBinderCollection(member.Type) ? $"this, {member.Id}" : string.Empty; if (member is CachedBinderMember cachedBinderMember) { - code.AppendLine($"{cachedBinderMember.CachedName}.UnbindSafely({diagnostics});"); + code.AppendLine($"{cachedBinderMember.CachedName}.UnbindSafely(this, {member.Id});"); } else { - code.AppendLine($"{member.Name}.UnbindSafely({diagnostics});"); + code.AppendLine($"{member.Name}.UnbindSafely(this, {member.Id});"); } } From 16cdf5215c6645a7ad98613a06a47a383f2bc6a4 Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Fri, 22 May 2026 14:19:00 +0300 Subject: [PATCH 26/29] Add virtual binder fields generation with HeaderGroup support - VirtualBinderFields collects bindable VM members not yet declared as binder fields on a [View] type and BinderFieldsBody emits a `_ : MonoBinder[]` slot for each one. Opt out via [View(AutoBinderFields = false)] or by deriving from ScriptableObject. - Propagate inspector grouping metadata from the VM: [Header], [HeaderGroup], [HeaderGroupStart], [HeaderGroupEnd] on VM members tag the corresponding generated binder fields so the editor can render foldout groups. - Wire the collector through ViewGenerator / BinderMembersFactory / ViewData(Span) / InitializeBody / GenericInitializeView, and align Descriptions.Classes with the current Aspid.MVVM namespace for MonoBinder and AddComponentContextMenuAttribute. --- .../Generators/Descriptions/Classes.Aspid.cs | 18 +- .../Descriptions/Namespaces.Aspid.cs | 1 - .../Generators/Views/Body/BinderFieldsBody.cs | 49 +++++ .../Views/Body/GenericInitializeView.cs | 8 + .../Generators/Views/Body/InitializeBody.cs | 11 +- .../Generators/Views/Data/ViewData.cs | 8 +- .../Generators/Views/Data/ViewDataSpan.cs | 1 + .../Views/Factories/BinderMembersFactory.cs | 15 ++ .../Views/Helpers/VirtualBinderFields.cs | 177 ++++++++++++++++++ .../Generators/Views/ViewGenerator.Find.cs | 3 +- .../Views/ViewGenerator.Generate.cs | 1 + 11 files changed, 284 insertions(+), 8 deletions(-) create mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/BinderFieldsBody.cs create mode 100644 Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Helpers/VirtualBinderFields.cs diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Classes.Aspid.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Classes.Aspid.cs index 52269c3..b8c58d6 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Classes.Aspid.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Classes.Aspid.cs @@ -18,6 +18,20 @@ public static class Classes public static readonly TypeText ViewBinder = new (nameof(ViewBinder), Namespaces.Aspid_MVVM); #endregion + + #region Headers + public static readonly AttributeText HeaderAttribute = + new(nameof(HeaderAttribute), "UnityEngine"); + + public static readonly AttributeText HeaderGroupAttribute = + new (nameof(HeaderGroupAttribute), Namespaces.Aspid_MVVM); + + public static readonly AttributeText HeaderGroupStartAttribute = + new (nameof(HeaderGroupStartAttribute), Namespaces.Aspid_MVVM); + + public static readonly AttributeText HeaderGroupEndAttribute = + new (nameof(HeaderGroupEndAttribute), Namespaces.Aspid_MVVM); + #endregion #region Binders public static readonly TypeText BindMode = @@ -33,7 +47,7 @@ public static class Classes new(nameof(IReverseBinder), Namespaces.Aspid_MVVM); public static readonly TypeText MonoBinder = - new(nameof(MonoBinder), Namespaces.Aspid_MVVM_UNITY); + new(nameof(MonoBinder), Namespaces.Aspid_MVVM); public static readonly AttributeText BinderLogAttribute = new("BinderLogAttribute", Namespaces.Aspid_MVVM); @@ -159,5 +173,5 @@ public static class Classes new("CreateFromAttribute", Namespaces.Aspid_MVVM); public static readonly AttributeText AddComponentContextMenuAttribute = - new ("AddComponentContextMenuAttribute", Namespaces.Aspid_MVVM_UNITY); + new ("AddComponentContextMenuAttribute", Namespaces.Aspid_MVVM); } \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Namespaces.Aspid.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Namespaces.Aspid.cs index fdc8958..44cf406 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Namespaces.Aspid.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Descriptions/Namespaces.Aspid.cs @@ -8,6 +8,5 @@ public static class Namespaces public static readonly NamespaceText Aspid = new(nameof(Aspid)); public static readonly NamespaceText Aspid_MVVM = new("MVVM", Aspid); - public static readonly NamespaceText Aspid_MVVM_UNITY = new("Unity", Aspid_MVVM); public static readonly NamespaceText Aspid_MVVM_Generated = new("Generated", Aspid_MVVM); } \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/BinderFieldsBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/BinderFieldsBody.cs new file mode 100644 index 0000000..9b03155 --- /dev/null +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/BinderFieldsBody.cs @@ -0,0 +1,49 @@ +using System.Linq; +using Microsoft.CodeAnalysis; +using Aspid.Generators.Helper; +using Aspid.Generators.Helper.Unity; +using Aspid.MVVM.Generators.Generators.Views.Data; +using Aspid.MVVM.Generators.Generators.Views.Helpers; +using static Aspid.MVVM.Generators.Generators.Descriptions.Constants; +using Classes = Aspid.MVVM.Generators.Generators.Descriptions.Classes; + +namespace Aspid.MVVM.Generators.Generators.Views.Body; + +public static class BinderFieldsBody +{ + public static void Generate( + string @namespace, + in ViewDataSpan data, + DeclarationText declaration, + in SourceProductionContext context) + { + var virtualFields = VirtualBinderFields.Collect(data); + if (virtualFields.Count is 0) return; + + var code = new CodeWriter(); + code.BeginClass(@namespace, declaration); + + foreach (var info in virtualFields.Values) + { + var requireBinderArgs = string.Join(", ", info.RequireBinderTypes.Select(type => $"typeof({type})")); + + code.AppendLine(GeneratedCodeViewAttribute); + + if (info.HeaderGroup is not null) + code.AppendLine($"[{Classes.HeaderGroupAttribute}({EscapeStringLiteral(info.HeaderGroup)})]"); + + if (info.Header is not null) + code.AppendLine($"[{Classes.HeaderAttribute}({EscapeStringLiteral(info.Header)})]"); + + code.AppendLine($"[{Classes.RequireBinderAttribute}({requireBinderArgs})]") + .AppendLine($"[{UnityClasses.SerializeField}] private {Classes.MonoBinder}[] {info.FieldName};") + .AppendLine(); + } + + code.EndClass(@namespace); + context.AddSource(declaration.GetFileName(@namespace, "BinderFields"), code.GetSourceText()); + } + + private static string EscapeStringLiteral(string value) => + $"\"{value.Replace("\\", "\\\\").Replace("\"", "\\\"")}\""; +} diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/GenericInitializeView.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/GenericInitializeView.cs index ed20df7..43e9b38 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/GenericInitializeView.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/GenericInitializeView.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using Aspid.MVVM.Generators.Generators.Views.Data; using Aspid.MVVM.Generators.Generators.Descriptions; +using Aspid.MVVM.Generators.Generators.Views.Helpers; using Aspid.MVVM.Generators.Generators.ViewModels.Factories; using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; using Aspid.MVVM.Generators.Generators.Views.Body.Extensions; @@ -121,6 +122,13 @@ public void Initialize({{typeName}} viewModel) code.AppendBindSafely(member); } } + + var virtualFields = VirtualBinderFields.Collect(data); + foreach (var info in virtualFields.Values) + { + if (bindableMembers.TryGetValue(info.Id.SourceValue, out var bindableMember)) + code.AppendLine($"{info.FieldName}.BindSafely(viewModel.{bindableMember.Bindable.PropertyName}, this, {info.Id.Value});"); + } } else { diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/InitializeBody.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/InitializeBody.cs index f4f1fb1..b9d5c9f 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/InitializeBody.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Body/InitializeBody.cs @@ -2,6 +2,7 @@ using Aspid.Generators.Helper; using Aspid.MVVM.Generators.Generators.Views.Data; using Aspid.MVVM.Generators.Generators.Descriptions; +using Aspid.MVVM.Generators.Generators.Views.Helpers; using Aspid.MVVM.Generators.Generators.Views.Data.Members; using Aspid.MVVM.Generators.Generators.Views.Body.Extensions; using static Aspid.Generators.Helper.Classes; @@ -233,7 +234,11 @@ private static CodeWriter AppendInitializeBody(this CodeWriter code, in ViewData foreach (var member in data.Members) code.AppendBindSafely(member); - + + var virtualFields = VirtualBinderFields.Collect(data); + foreach (var info in virtualFields.Values) + code.AppendLine($"{info.FieldName}.BindSafely(viewModel.FindBindableMember(new({info.Id.Value})), this, {info.Id.Value});"); + return code.AppendLine() .AppendLine("OnInitializedInternal(viewModel);") .AppendLine("__isInitializing = false;") @@ -268,6 +273,10 @@ private static CodeWriter AppendDeinitializeBody(this CodeWriter code, in ViewDa } } + var virtualFields = VirtualBinderFields.Collect(data); + foreach (var info in virtualFields.Values) + code.AppendLine($"{info.FieldName}.UnbindSafely(this, {info.Id.Value});"); + code.AppendLine() .AppendLine("OnDeinitializedInternal();") .EndBlock() diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/ViewData.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/ViewData.cs index fff5b81..4186338 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/ViewData.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/ViewData.cs @@ -7,14 +7,16 @@ namespace Aspid.MVVM.Generators.Generators.Views.Data; public readonly struct ViewData( INamedTypeSymbol symbol, - Inheritor inheritor, + Inheritor inheritor, TypeDeclarationSyntax declaration, ImmutableArray members, - ImmutableArray genericViews) + ImmutableArray genericViews, + ImmutableArray inheritedDeclaredIds) { - public readonly INamedTypeSymbol Symbol = symbol; + public readonly INamedTypeSymbol Symbol = symbol; public readonly Inheritor Inheritor = inheritor; public readonly ImmutableArray Members = members; public readonly TypeDeclarationSyntax Declaration = declaration; public readonly ImmutableArray GenericViews = genericViews; + public readonly ImmutableArray InheritedDeclaredIds = inheritedDeclaredIds; } \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/ViewDataSpan.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/ViewDataSpan.cs index b7192ca..0c567ea 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/ViewDataSpan.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Data/ViewDataSpan.cs @@ -14,6 +14,7 @@ public readonly ref struct ViewDataSpan(ViewData viewData) public readonly ReadOnlySpan Members = viewData.Members.AsSpan(); public readonly BinderMembersCollectionSpanByType MembersByType = new(viewData.Members); public readonly ReadOnlySpan GenericViews = viewData.GenericViews.AsSpan(); + public readonly ReadOnlySpan InheritedDeclaredIds = viewData.InheritedDeclaredIds.AsSpan(); public bool IsInstantiateBinders => MembersByType.AsBinders.Length + MembersByType.PropertyBinders.Length > 0; } \ No newline at end of file diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Factories/BinderMembersFactory.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Factories/BinderMembersFactory.cs index 299f76e..fa3c1a3 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Factories/BinderMembersFactory.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Factories/BinderMembersFactory.cs @@ -12,6 +12,21 @@ namespace Aspid.MVVM.Generators.Generators.Views.Factories; public static class BinderMembersFactory { + public static ImmutableArray CollectInheritedIds(INamedTypeSymbol symbol, SemanticModel semanticModel) + { + var ids = ImmutableArray.CreateBuilder(); + + for (var baseType = symbol.BaseType; baseType is not null; baseType = baseType.BaseType) + { + if (!baseType.HasAnyAttributeInSelf(Classes.ViewAttribute)) continue; + + foreach (var member in Create(baseType, semanticModel)) + ids.Add(member.Id.SourceValue); + } + + return ids.ToImmutable(); + } + public static ImmutableArray Create(INamedTypeSymbol symbolClass, SemanticModel semanticModel) { var binderMembers = new List(); diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Helpers/VirtualBinderFields.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Helpers/VirtualBinderFields.cs new file mode 100644 index 0000000..0c212b4 --- /dev/null +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/Helpers/VirtualBinderFields.cs @@ -0,0 +1,177 @@ +using Microsoft.CodeAnalysis; +using System.Collections.Generic; +using Aspid.Generators.Helper; +using Aspid.Generators.Helper.Unity; +using Aspid.MVVM.Generators.Generators.Ids.Data; +using Aspid.MVVM.Generators.Generators.Views.Data; +using Aspid.MVVM.Generators.Generators.ViewModels.Factories; +using Aspid.MVVM.Generators.Generators.ViewModels.Data.Infos; +using Classes = Aspid.MVVM.Generators.Generators.Descriptions.Classes; +using SymbolExtensions = Aspid.MVVM.Generators.Helpers.SymbolExtensions; + +namespace Aspid.MVVM.Generators.Generators.Views.Helpers; + +public static class VirtualBinderFields +{ + public static Dictionary Collect(in ViewDataSpan data) + { + var result = new Dictionary(); + if (data.GenericViews.Length == 0) return result; + if (!IsAutoBinderFieldsEnabled(data.Symbol)) return result; + if (InheritsFromScriptableObject(data.Symbol)) return result; + + var declared = new HashSet(); + foreach (var member in data.Members) + declared.Add(member.Id.SourceValue); + foreach (var id in data.InheritedDeclaredIds) + declared.Add(id); + + foreach (var genericView in data.GenericViews) + { + if (genericView.Type.TypeKind is TypeKind.Interface) continue; + + for (var viewModelType = genericView.Type; viewModelType is not null; viewModelType = viewModelType.BaseType) + { + CollectFromType(viewModelType, data, declared, result); + } + } + + return result; + } + + private static void CollectFromType( + ITypeSymbol viewModelType, + in ViewDataSpan data, + HashSet declared, + Dictionary result) + { + var bindableMembers = BindableMembersFactory.Create(viewModelType, data.Declaration, out _); + if (bindableMembers.Length == 0) return; + + var membersBySymbol = new Dictionary>(SymbolEqualityComparer.Default); + foreach (var bindable in bindableMembers) + { + if (!membersBySymbol.TryGetValue(bindable.Member, out var list)) + { + list = []; + membersBySymbol[bindable.Member] = list; + } + + list.Add(bindable); + } + + string? activeGroup = null; + + foreach (var symbol in viewModelType.GetMembers()) + { + var groupStart = GetAttributeStringArgument(symbol, Classes.HeaderGroupStartAttribute); + var groupName = GetAttributeStringArgument(symbol, Classes.HeaderGroupAttribute); + var groupEnd = HasAttribute(symbol, Classes.HeaderGroupEndAttribute); + + if (groupStart is not null) activeGroup = groupStart; + else if (groupName is not null) activeGroup = groupName; + + if (membersBySymbol.TryGetValue(symbol, out var bindablesForSymbol)) + { + foreach (var bindableMember in bindablesForSymbol) + { + var sourceValue = bindableMember.Id.SourceValue; + if (declared.Contains(sourceValue)) continue; + + if (!result.TryGetValue(sourceValue, out var info)) + { + info = new Info(bindableMember.Id, SymbolExtensions.GetFieldName(sourceValue, "_")); + result[sourceValue] = info; + } + + var requireType = StripNullable(bindableMember.Type); + if (!info.RequireBinderTypes.Contains(requireType)) + info.RequireBinderTypes.Add(requireType); + + if (bindableMember is not BindableCommandInfo) + info.Header ??= GetAttributeStringArgument(symbol, Classes.HeaderAttribute); + + if (activeGroup is not null) + info.HeaderGroup ??= activeGroup; + + if (groupEnd && !info.HeaderGroupEnd) + info.HeaderGroupEnd = true; + } + } + + if (groupEnd) activeGroup = null; + } + } + + private static bool IsAutoBinderFieldsEnabled(INamedTypeSymbol viewSymbol) + { + foreach (var attribute in viewSymbol.GetAttributes()) + { + if (attribute.AttributeClass?.ToDisplayString() != Classes.ViewAttribute.FullName) continue; + + foreach (var named in attribute.NamedArguments) + { + if (named.Key != "AutoBinderFields") continue; + if (named.Value.Value is bool enabled) return enabled; + } + + return true; + } + + return true; + } + + private static bool InheritsFromScriptableObject(INamedTypeSymbol viewSymbol) + { + var scriptableObjectFullName = UnityClasses.ScriptableObject.FullName; + + for (var type = viewSymbol.BaseType; type is not null; type = type.BaseType) + { + if (type.ToDisplayString() == scriptableObjectFullName) return true; + } + + return false; + } + + private static string? GetAttributeStringArgument(ISymbol symbol, AttributeText attributeText) + { + if (symbol.TryGetAnyAttributeInSelf(out var attribute, attributeText)) + { + if (attribute.ConstructorArguments.Length is 0) return null; + + var value = attribute.ConstructorArguments[0].Value as string; + if (!string.IsNullOrWhiteSpace(value)) return value; + } + + return null; + } + + private static bool HasAttribute(ISymbol symbol, string attributeFullName) + { + foreach (var attribute in symbol.GetAttributes()) + { + if (attribute.AttributeClass?.ToDisplayString() == attributeFullName) + return true; + } + + return false; + } + + private static string StripNullable(string type) => + type.EndsWith("?") ? type.Substring(0, type.Length - 1) : type; + + public sealed class Info(IdData id, string fieldName) + { + public IdData Id { get; } = id; + + public string FieldName { get; } = fieldName; + + public List RequireBinderTypes { get; } = []; + + public string? Header { get; set; } + + public string? HeaderGroup { get; set; } + + public bool HeaderGroupEnd { get; set; } + } +} diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/ViewGenerator.Find.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/ViewGenerator.Find.cs index 5a2d494..846765f 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/ViewGenerator.Find.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/ViewGenerator.Find.cs @@ -25,11 +25,12 @@ public partial class ViewGenerator : Inheritor.None; var members = BinderMembersFactory.Create(symbol, context.SemanticModel); + var inheritedDeclaredIds = BinderMembersFactory.CollectInheritedIds(symbol, context.SemanticModel); Debug.Assert(context.TargetNode is TypeDeclarationSyntax); var candidate = Unsafe.As(context.TargetNode); - return new ViewData(symbol, inheritor, candidate, members, GetGenericViews(symbol)); + return new ViewData(symbol, inheritor, candidate, members, GetGenericViews(symbol), inheritedDeclaredIds); } private static ImmutableArray GetGenericViews(INamedTypeSymbol symbol) diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/ViewGenerator.Generate.cs b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/ViewGenerator.Generate.cs index cd4a91d..79e266c 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/ViewGenerator.Generate.cs +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators/Generators/Views/ViewGenerator.Generate.cs @@ -17,6 +17,7 @@ private static void GenerateCode(SourceProductionContext context, ViewData data) InitializeBody.Generate(@namespace, dataSpan, declarationText, context); BinderCachedBody.Generate(@namespace, dataSpan, declarationText, context); + BinderFieldsBody.Generate(@namespace, dataSpan, declarationText, context); GenericInitializeView.Generate(@namespace, dataSpan, declarationText, context); } } \ No newline at end of file From 5aa2d138ae328406052e96d379409851134b4408 Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Fri, 22 May 2026 15:16:19 +0300 Subject: [PATCH 27/29] Update Unity copy path to Aspid/MVVM after folder move --- Directory.Build.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.targets b/Directory.Build.targets index c386f97..93f892a 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -2,7 +2,7 @@ - <_UnityDestination>$(MSBuildThisFileDirectory)../Aspid.MVVM/Assets/Plugins/Aspid/MVVM/ + <_UnityDestination>$(MSBuildThisFileDirectory)../Aspid.MVVM/Assets/Aspid/MVVM/ From c151e2c7f7a2b5e064e69b48a7bfcbf80b773063 Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Sat, 30 May 2026 16:36:56 +0300 Subject: [PATCH 28/29] fix(build): glob Source paths in Generators.Sample after package move The Sample project pinned the pre-move Assets\Plugins\Aspid\MVVM source paths via an explicit, now-stale file list, so dotnet build failed with CS2001 (missing source files). Replace the explicit list with a recursive glob over Assets\Aspid\MVVM\Source, which is Unity-independent, so the project tracks file add/remove/rename automatically. --- .../Aspid.MVVM.Generators.Sample.csproj | 517 +----------------- 1 file changed, 9 insertions(+), 508 deletions(-) diff --git a/Aspid.MVVM.Generators/Aspid.MVVM.Generators.Sample/Aspid.MVVM.Generators.Sample.csproj b/Aspid.MVVM.Generators/Aspid.MVVM.Generators.Sample/Aspid.MVVM.Generators.Sample.csproj index 28fce60..6ee56e6 100644 --- a/Aspid.MVVM.Generators/Aspid.MVVM.Generators.Sample/Aspid.MVVM.Generators.Sample.csproj +++ b/Aspid.MVVM.Generators/Aspid.MVVM.Generators.Sample/Aspid.MVVM.Generators.Sample.csproj @@ -1,4 +1,4 @@ - + net9.0 @@ -14,514 +14,15 @@ + - - Source\.DS_Store - - - Source\Aspid.MVVM.asmdef - - - Source\Aspid.MVVM.asmdef.meta - - - Source\BindableMembers.meta - - - Source\BindableMembers\.DS_Store - - - Source\BindableMembers\Classes.meta - - - Source\BindableMembers\Classes\OneTimeBindableMember.cs.meta - - - Source\BindableMembers\Classes\OneWayBindableMember.cs.meta - - - Source\BindableMembers\Classes\OneWayToSourceBindableMember.cs.meta - - - Source\BindableMembers\Classes\TwoWayBindableMember.cs.meta - - - Source\BindableMembers\Enum.meta - - - Source\BindableMembers\Enum\OneTimeEnumBindableMember.cs.meta - - - Source\BindableMembers\Enum\OneWayEnumBindableMember.cs.meta - - - Source\BindableMembers\Enum\OneWayToSourceEnumBindableMember.cs.meta - - - Source\BindableMembers\Enum\TwoWayEnumBindableMember.cs.meta - - - Source\BindableMembers\IBindableMember.cs.meta - - - Source\BindableMembers\IBinderAdder.cs.meta - - - Source\BindableMembers\IBinderRemover.cs.meta - - - Source\BindableMembers\IReadOnlyBindableMember.cs.meta - - - Source\BindableMembers\IReadOnlyValueBindableMember.cs.meta - - - Source\BindableMembers\Struct.meta - - - Source\BindableMembers\Struct\OneTimeStructBindableMember.cs.meta - - - Source\BindableMembers\Struct\OneWayStructBindableMember.cs.meta - - - Source\BindableMembers\Struct\OneWayToSourceStructBindableMember.cs.meta - - - Source\BindableMembers\Struct\TwoWayStructBindableMember.cs.meta - - - Source\Binders.meta - - - Source\Binders\Binder.cs.meta - - - Source\Binders\Binder.Debug.cs.meta - - - Source\Binders\Debug.meta - - - Source\Binders\Debug\Extensions.meta - - - Source\Binders\Debug\Extensions\RebindableBinderExtensions.cs.meta - - - Source\Binders\Debug\IRebindableBinder.cs.meta - - - Source\Binders\Extensions.meta - - - Source\Binders\Extensions\BinderExtensions.Bind.cs.meta - - - Source\Binders\Extensions\BinderExtensions.Unbind.cs.meta - - - Source\Binders\Generation.meta - - - Source\Binders\Generation\BinderLogAttribute.cs.meta - - - Source\Binders\IAnyBinder.cs.meta - - - Source\Binders\IAnyReverseBinder.cs.meta - - - Source\Binders\IBinder.cs.meta - - - Source\Binders\IReverseBinder.cs.meta - - - Source\Commands.meta - - - Source\Commands\.DS_Store - - - Source\Commands\Extensions.meta - - - Source\Commands\Extensions\RelayCommandExtensions.Action.cs.meta - - - Source\Commands\Extensions\RelayCommandExtensions.cs.meta - - - Source\Commands\Extensions\RelayCommandExtensions.WithoutParameters.cs.meta - - - Source\Commands\IRelayCommand.cs.meta - - - Source\Commands\RelayCommand.cs.meta - - - Source\csc.rsp - - - Source\csc.rsp.meta - - - Source\Exceptions.meta - - - Source\Exceptions\BinderInvalidCastException.cs.meta - - - Source\Exceptions\BindSafelyNullReferenceException.cs.meta - - - Source\Exceptions\Extensions.meta - - - Source\Exceptions\Extensions\BinderExceptionExtensions.cs.meta - - - Source\Exceptions\ReverseBinderInvalidCastException.cs.meta - - - Source\Exceptions\UnbindSafelyNullReferenceException.cs.meta - - - Source\Generation.meta - - - Source\Generation\BindIdAttribute.cs.meta - - - Source\Generation\IgnoreBindAttribute.cs.meta - - - Source\Helpers.meta - - - Source\Helpers\LoggerHelper.cs.meta - - - Source\Helpers\Unsafe.cs.meta - - - Source\Mode.meta - - - Source\Mode\.DS_Store - - - Source\Mode\BindMode.cs.meta - - - Source\Mode\Extensions.meta - - - Source\Mode\Extensions\BindModeExtensions.cs.meta - - - Source\Mode\Extensions\BindModeExtensions.Throw.cs.meta - - - Source\Mode\Validation.meta - - - Source\Mode\Validation\BindModeAttribute.cs.meta - - - Source\Mode\Validation\BindModeOverrideAttribute.cs.meta - - - Source\ViewModels.meta - - - Source\ViewModels\.DS_Store - - - Source\ViewModels\Extensions.meta - - - Source\ViewModels\Extensions\ViewModelExtensions.cs.meta - - - Source\ViewModels\FindBindableMemberParameters.cs.meta - - - Source\ViewModels\FindBindableMemberResult.cs.meta - - - Source\ViewModels\Generation.meta - - - Source\ViewModels\Generation\Access.cs.meta - - - Source\ViewModels\Generation\AccessAttribute.cs.meta - - - Source\ViewModels\Generation\BaseBindAttribute.cs.meta - - - Source\ViewModels\Generation\BindAlsoAttribute.cs.meta - - - Source\ViewModels\Generation\BindAttribute.cs.meta - - - Source\ViewModels\Generation\OneTimeBindAttribute.cs.meta - - - Source\ViewModels\Generation\OneWayBindAttribute.cs.meta - - - Source\ViewModels\Generation\OneWayToSourceBindAttribute.cs.meta - - - Source\ViewModels\Generation\RelayCommandAttribute.cs.meta - - - Source\ViewModels\Generation\TwoWayBindAttribute.cs.meta - - - Source\ViewModels\Generation\ViewModelAttribute.cs.meta - - - Source\ViewModels\IViewModel.cs.meta - - - Source\Views.meta - - - Source\Views\.DS_Store - - - Source\Views\Extensions.meta - - - Source\Views\Extensions\ViewExtensions.cs.meta - - - Source\Views\Generation.meta - - - Source\Views\Generation\AsBinderAttribute.cs.meta - - - Source\Views\Generation\ViewAttribute.cs.meta - - - Source\Views\IView.cs.meta - - - Source\Views\ViewBinder.cs.meta - - - - - - Source\BindableMembers\Classes\OneTimeBindableMember.cs - - - Source\BindableMembers\Classes\OneWayBindableMember.cs - - - Source\BindableMembers\Classes\OneWayToSourceBindableMember.cs - - - Source\BindableMembers\Classes\TwoWayBindableMember.cs - - - Source\BindableMembers\Enum\OneTimeEnumBindableMember.cs - - - Source\BindableMembers\Enum\OneWayEnumBindableMember.cs - - - Source\BindableMembers\Enum\OneWayToSourceEnumBindableMember.cs - - - Source\BindableMembers\Enum\TwoWayEnumBindableMember.cs - - - Source\BindableMembers\IBindableMember.cs - - - Source\BindableMembers\IBinderAdder.cs - - - Source\BindableMembers\IBinderRemover.cs - - - Source\BindableMembers\IReadOnlyBindableMember.cs - - - Source\BindableMembers\IReadOnlyValueBindableMember.cs - - - Source\BindableMembers\Struct\OneTimeStructBindableMember.cs - - - Source\BindableMembers\Struct\OneWayStructBindableMember.cs - - - Source\BindableMembers\Struct\OneWayToSourceStructBindableMember.cs - - - Source\BindableMembers\Struct\TwoWayStructBindableMember.cs - - - Source\Binders\Binder.cs - - - Source\Binders\Binder.Debug.cs - - - Source\Binders\Debug\Extensions\RebindableBinderExtensions.cs - - - Source\Binders\Debug\IRebindableBinder.cs - - - Source\Binders\Extensions\BinderExtensions.Bind.cs - - - Source\Binders\Extensions\BinderExtensions.Unbind.cs - - - Source\Binders\Generation\BinderLogAttribute.cs - - - Source\Binders\IAnyBinder.cs - - - Source\Binders\IAnyReverseBinder.cs - - - Source\Binders\IBinder.cs - - - Source\Binders\IReverseBinder.cs - - - Source\Commands\Extensions\RelayCommandExtensions.Action.cs - - - Source\Commands\Extensions\RelayCommandExtensions.cs - - - Source\Commands\Extensions\RelayCommandExtensions.WithoutParameters.cs - - - Source\Commands\IRelayCommand.cs - - - Source\Commands\RelayCommand.cs - - - Source\Exceptions\BinderInvalidCastException.cs - - - Source\Exceptions\BindSafelyNullReferenceException.cs - - - Source\Exceptions\Extensions\BinderExceptionExtensions.cs - - - Source\Exceptions\ReverseBinderInvalidCastException.cs - - - Source\Exceptions\UnbindSafelyNullReferenceException.cs - - - Source\Generation\BindIdAttribute.cs - - - Source\Generation\IgnoreBindAttribute.cs - - - Source\Helpers\LoggerHelper.cs - - - Source\Helpers\Unsafe.cs - - - Source\Mode\BindMode.cs - - - Source\Mode\Extensions\BindModeExtensions.cs - - - Source\Mode\Extensions\BindModeExtensions.Throw.cs - - - Source\Mode\Validation\BindModeAttribute.cs - - - Source\Mode\Validation\BindModeOverrideAttribute.cs - - - Source\ViewModels\Extensions\ViewModelExtensions.cs - - - Source\ViewModels\FindBindableMemberParameters.cs - - - Source\ViewModels\FindBindableMemberResult.cs - - - Source\ViewModels\Generation\Access.cs - - - Source\ViewModels\Generation\AccessAttribute.cs - - - Source\ViewModels\Generation\BaseBindAttribute.cs - - - Source\ViewModels\Generation\BindAlsoAttribute.cs - - - Source\ViewModels\Generation\BindAttribute.cs - - - Source\ViewModels\Generation\OneTimeBindAttribute.cs - - - Source\ViewModels\Generation\OneWayBindAttribute.cs - - - Source\ViewModels\Generation\OneWayToSourceBindAttribute.cs - - - Source\ViewModels\Generation\RelayCommandAttribute.cs - - - Source\ViewModels\Generation\TwoWayBindAttribute.cs - - - Source\ViewModels\Generation\ViewModelAttribute.cs - - - Source\ViewModels\IViewModel.cs - - - Source\Views\Extensions\ViewExtensions.cs - - - Source\Views\Generation\AsBinderAttribute.cs - - - Source\Views\Generation\ViewAttribute.cs - - - Source\Views\IView.cs - - - Source\Views\ViewBinder.cs - + + Source\%(RecursiveDir)%(Filename)%(Extension) + From 4809f9a542de02a436c524605680a8f3c591b061 Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Sat, 30 May 2026 16:36:56 +0300 Subject: [PATCH 29/29] build(ci): exclude Generators.Sample from headless solution build The Sample compiles generator output that references Unity.* (profiler markers), so it only compiles inside Unity. Drop its Build.0 entries so dotnet build/test of the solution (release CI) no longer fail on it; the project still loads in the IDE. --- Aspid.MVVM.Generators.sln | 2 -- 1 file changed, 2 deletions(-) diff --git a/Aspid.MVVM.Generators.sln b/Aspid.MVVM.Generators.sln index 3c82bd3..a882cb7 100644 --- a/Aspid.MVVM.Generators.sln +++ b/Aspid.MVVM.Generators.sln @@ -17,9 +17,7 @@ Global {8396B53C-70A1-422E-9E40-9AEBB223F6B3}.Release|Any CPU.ActiveCfg = Release|Any CPU {8396B53C-70A1-422E-9E40-9AEBB223F6B3}.Release|Any CPU.Build.0 = Release|Any CPU {A1DE8BA3-CAEC-4823-99D1-720059565792}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1DE8BA3-CAEC-4823-99D1-720059565792}.Debug|Any CPU.Build.0 = Debug|Any CPU {A1DE8BA3-CAEC-4823-99D1-720059565792}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1DE8BA3-CAEC-4823-99D1-720059565792}.Release|Any CPU.Build.0 = Release|Any CPU {837E3102-786A-44C6-9490-1C77BE8D26C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {837E3102-786A-44C6-9490-1C77BE8D26C3}.Debug|Any CPU.Build.0 = Debug|Any CPU {837E3102-786A-44C6-9490-1C77BE8D26C3}.Release|Any CPU.ActiveCfg = Release|Any CPU