99namespace ThunderDesign . Net_PCL . SourceGenerators
1010{
1111 [ Generator ]
12- public class BindablePropertyGenerator : IIncrementalGenerator
12+ public class UnifiedPropertyGenerator : IIncrementalGenerator
1313 {
1414 public void Initialize ( IncrementalGeneratorInitializationContext context )
1515 {
16+ // Collect all fields with [BindableProperty] or [Property]
1617 var fieldsWithAttribute = context . SyntaxProvider
1718 . CreateSyntaxProvider (
1819 predicate : static ( node , _ ) => node is FieldDeclarationSyntax fds && fds . AttributeLists . Count > 0 ,
19- transform : static ( ctx , _ ) => GetBindableField ( ctx )
20+ transform : static ( ctx , _ ) =>
21+ {
22+ var bindable = GetBindableField ( ctx ) ;
23+ if ( ! bindable . Equals ( default ( BindableFieldInfo ) ) )
24+ return ( Class : bindable . ContainingClass , Bindable : bindable , Property : default ( PropertyFieldInfo ) ) ;
25+ var prop = PropertyGeneratorHelpers . GetFieldWithAttribute ( ctx , "PropertyAttribute" ) ;
26+ if ( ! prop . Equals ( default ( PropertyFieldInfo ) ) )
27+ return ( Class : prop . ContainingClass , Bindable : default ( BindableFieldInfo ) , Property : prop ) ;
28+ return default ;
29+ }
2030 )
21- . Where ( static info => ! info . Equals ( default ( BindableFieldInfo ) ) ) ;
31+ . Where ( static info => ! info . Equals ( default ( ( INamedTypeSymbol , BindableFieldInfo , PropertyFieldInfo ) ) ) ) ;
2232
23- // Group fields by containing class
33+ // Group by class
2434 var grouped = fieldsWithAttribute . Collect ( )
2535 . Select ( ( list , _ ) => list
26- . GroupBy ( info => info . ContainingClass , SymbolEqualityComparer . Default )
27- . Select ( g => ( ClassSymbol : g . Key , Fields : g . ToList ( ) ) )
36+ . Where ( info => info . Class is INamedTypeSymbol )
37+ . GroupBy ( info => info . Class , SymbolEqualityComparer . Default )
38+ . Select ( g => (
39+ ClassSymbol : g . Key ,
40+ BindableFields : g . Select ( x => x . Bindable ) . Where ( b => ! b . Equals ( default ( BindableFieldInfo ) ) ) . ToList ( ) ,
41+ PropertyFields : g . Select ( x => x . Property ) . Where ( p => ! p . Equals ( default ( PropertyFieldInfo ) ) ) . ToList ( )
42+ ) )
2843 . ToList ( )
2944 ) ;
3045
@@ -36,10 +51,9 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
3651 foreach ( var group in classGroups )
3752 {
3853 var classSymbol = group . ClassSymbol as INamedTypeSymbol ;
39- var fields = group . Fields ;
4054 if ( classSymbol != null )
4155 {
42- GenerateBindablePropertyClass ( spc , classSymbol , fields , compilation ) ;
56+ GenerateUnifiedPropertyClass ( spc , classSymbol , group . BindableFields , group . PropertyFields , compilation ) ;
4357 }
4458 }
4559 } ) ;
@@ -74,11 +88,11 @@ private static BindableFieldInfo GetBindableField(GeneratorSyntaxContext context
7488 return default ( BindableFieldInfo ) ;
7589 }
7690
77- // New method to generate all properties and shared members for a class
78- private static void GenerateBindablePropertyClass (
91+ private static void GenerateUnifiedPropertyClass (
7992 SourceProductionContext context ,
8093 INamedTypeSymbol classSymbol ,
81- List < BindableFieldInfo > fields ,
94+ List < BindableFieldInfo > bindableFields ,
95+ List < PropertyFieldInfo > propertyFields ,
8296 Compilation compilation )
8397 {
8498 var implementsINotify = ImplementsInterface ( classSymbol , "System.ComponentModel.INotifyPropertyChanged" ) ;
@@ -96,28 +110,31 @@ private static void GenerateBindablePropertyClass(
96110 source . AppendLine ( $ "namespace { ns } {{") ;
97111
98112 source . AppendLine ( "using ThunderDesign.Net.Threading.Extentions;" ) ;
99- source . AppendLine ( "using ThunderDesign.Net.Threading.Interfaces;" ) ;
100113 source . AppendLine ( "using ThunderDesign.Net.Threading.Objects;" ) ;
114+ if ( bindableFields . Count > 0 )
115+ {
116+ source . AppendLine ( "using ThunderDesign.Net.Threading.Interfaces;" ) ;
117+ }
101118
102119 source . Append ( $ "partial class { classSymbol . Name } ") ;
103120 var interfaces = new List < string > ( ) ;
104- if ( ! implementsIBindable )
121+ if ( bindableFields . Count > 0 && ! implementsIBindable )
105122 interfaces . Add ( "IBindableObject" ) ;
106123 if ( interfaces . Count > 0 )
107124 source . Append ( " : " + string . Join ( ", " , interfaces ) ) ;
108125 source . AppendLine ( ) ;
109126 source . AppendLine ( "{" ) ;
110127
111128 // Add event if needed
112- if ( ! implementsINotify && ! PropertyGeneratorHelpers . EventExists ( classSymbol , "PropertyChanged" , propertyChangedEventType ) )
129+ if ( bindableFields . Count > 0 && ! implementsINotify && ! PropertyGeneratorHelpers . EventExists ( classSymbol , "PropertyChanged" , propertyChangedEventType ) )
113130 source . AppendLine ( " public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;" ) ;
114131
115132 // Add _Locker if needed
116- if ( ! inheritsThreadObject && ! PropertyGeneratorHelpers . FieldExists ( classSymbol , "_Locker" ) )
133+ if ( ( ! inheritsThreadObject ) && ! PropertyGeneratorHelpers . FieldExists ( classSymbol , "_Locker" ) )
117134 source . AppendLine ( " protected readonly object _Locker = new object();" ) ;
118135
119136 // Add OnPropertyChanged if needed
120- if ( ! implementsIBindable && ! PropertyGeneratorHelpers . MethodExists (
137+ if ( bindableFields . Count > 0 && ! implementsIBindable && ! PropertyGeneratorHelpers . MethodExists (
121138 classSymbol ,
122139 "OnPropertyChanged" ,
123140 new ITypeSymbol [ ] { stringTypeSymbol } ,
@@ -130,8 +147,8 @@ public virtual void OnPropertyChanged([System.Runtime.CompilerServices.CallerMem
130147 }" ) ;
131148 }
132149
133- // Generate all properties
134- foreach ( var info in fields )
150+ // Generate all bindable properties
151+ foreach ( var info in bindableFields )
135152 {
136153 var fieldSymbol = info . FieldSymbol ;
137154 var fieldName = fieldSymbol . Name ;
@@ -200,12 +217,43 @@ public virtual void OnPropertyChanged([System.Runtime.CompilerServices.CallerMem
200217 }
201218 }
202219
220+ // Generate all regular properties
221+ foreach ( var info in propertyFields )
222+ {
223+ var fieldSymbol = info . FieldSymbol ;
224+ var fieldName = fieldSymbol . Name ;
225+ var propertyName = PropertyGeneratorHelpers . ToPropertyName ( fieldName ) ;
226+ var typeName = fieldSymbol . Type . ToDisplayString ( ) ;
227+
228+ var readOnly = info . AttributeData . ConstructorArguments . Length > 0 && ( bool ) info . AttributeData . ConstructorArguments [ 0 ] . Value ! ;
229+ var threadSafe = info . AttributeData . ConstructorArguments . Length > 1 && ( bool ) info . AttributeData . ConstructorArguments [ 1 ] . Value ! ;
230+
231+ var lockerArg = threadSafe ? "_Locker" : "null" ;
232+ if ( readOnly )
233+ {
234+ source . AppendLine ( $@ "
235+ public { typeName } { propertyName }
236+ {{
237+ get {{ return this.GetProperty(ref { fieldName } , { lockerArg } ); }}
238+ }}" ) ;
239+ }
240+ else
241+ {
242+ source . AppendLine ( $@ "
243+ public { typeName } { propertyName }
244+ {{
245+ get {{ return this.GetProperty(ref { fieldName } , { lockerArg } ); }}
246+ set {{ this.SetProperty(ref { fieldName } , value, { lockerArg } ); }}
247+ }}" ) ;
248+ }
249+ }
250+
203251 source . AppendLine ( "}" ) ;
204252
205253 if ( ! string . IsNullOrEmpty ( ns ) )
206254 source . AppendLine ( "}" ) ;
207255
208- context . AddSource ( $ "{ classSymbol . Name } _BindableProperties .g.cs", SourceText . From ( source . ToString ( ) , Encoding . UTF8 ) ) ;
256+ context . AddSource ( $ "{ classSymbol . Name } _AllProperties .g.cs", SourceText . From ( source . ToString ( ) , Encoding . UTF8 ) ) ;
209257 }
210258
211259 private static bool ImplementsInterface ( INamedTypeSymbol type , string interfaceName )
0 commit comments