Skip to content

Commit 778e779

Browse files
committed
feat: add interface-based observation plugin system with affinity-based selection
Replace 7 static observation generator classes with an interface-based plugin system where plugins compete by affinity to generate platform-specific, reflection-free observation code. Affinity values now match ReactiveUI's exact runtime values (KVO=15, ReactiveObject=10, WinForms=8, WinUI=6, INPC=5, Android=5, WPF=4). Each plugin generates code that mirrors the corresponding ReactiveUI runtime implementation but without reflection: - WPF: EventObservable with DependencyPropertyDescriptor.AddValueChanged - WinUI: inline __WinUIDPObservable with RegisterPropertyChangedCallback - WinForms: EventObservable with direct {Property}Changed event subscription - KVO: inline __KVOObservable with NSObject.AddObserver and compile-time resolved key paths (lowercase first char, boolean "is" prefix) - INPC/ReactiveObject: PropertyObservable/PropertyChangingObservable - Android: ReturnObservable fallback (no INPC on Android View) Pipeline A simplified from 7 separate filter calls + Consolidate() to a single plugin-based step via ObservationPluginRegistry.GetBestPlugin(). New runtime type EventObservable<T> provides platform-agnostic event-based observation using pure BCL types (Action<EventHandler> add/remove pattern). 100% line and branch coverage maintained across the full solution (7236 tests).
1 parent 4c5e858 commit 778e779

53 files changed

Lines changed: 3605 additions & 651 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/ReactiveUI.Binding.SourceGenerators/BindingGenerator.cs

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
using Microsoft.CodeAnalysis.CSharp;
77

88
using ReactiveUI.Binding.SourceGenerators.Generators;
9-
using ReactiveUI.Binding.SourceGenerators.Generators.Observation;
109
using ReactiveUI.Binding.SourceGenerators.Helpers;
1110
using ReactiveUI.Binding.SourceGenerators.Invocations;
11+
using ReactiveUI.Binding.SourceGenerators.Models;
12+
using ReactiveUI.Binding.SourceGenerators.Plugins;
1213

1314
namespace ReactiveUI.Binding.SourceGenerators;
1415

@@ -60,18 +61,31 @@ internal static partial class __ReactiveUIGeneratedBindings
6061
.Where(static x => x is not null)
6162
.Select(static (x, _) => x!);
6263

63-
// Per-kind fallback generators FILTER from allClasses (no independent RegisterSourceOutput)
64-
var reactiveObjTypes = ReactiveObjectBindingGenerator.Filter(allClasses);
65-
var inpcTypes = INPCBindingGenerator.Filter(allClasses);
66-
var wpfTypes = WpfBindingGenerator.Filter(allClasses);
67-
var winuiTypes = WinUIBindingGenerator.Filter(allClasses);
68-
var kvoTypes = KVOBindingGenerator.Filter(allClasses);
69-
var winformsTypes = WinFormsBindingGenerator.Filter(allClasses);
70-
var androidTypes = AndroidBindingGenerator.Filter(allClasses);
64+
// Single plugin-based step replaces 7 separate filter calls.
65+
// Each type is matched against the plugin registry; the highest-affinity
66+
// matching plugin determines the observation kind and capabilities.
67+
var allObservableTypes = allClasses
68+
.Select(static (classInfo, _) =>
69+
{
70+
var plugin = ObservationPluginRegistry.GetBestPlugin(classInfo);
71+
if (plugin is null)
72+
{
73+
return null;
74+
}
75+
76+
return new ObservableTypeInfo(
77+
classInfo.FullyQualifiedName,
78+
classInfo.MetadataName,
79+
plugin.ObservationKind,
80+
plugin.Affinity,
81+
plugin.SupportsBeforeChanged,
82+
classInfo.Properties);
83+
})
84+
.Where(static x => x is not null)
85+
.Select(static (x, _) => x!);
7186

72-
// Consolidate all per-kind results → single RegisterSourceOutput
73-
var consolidated = RegistrationGenerator.Consolidate(
74-
reactiveObjTypes, inpcTypes, wpfTypes, winuiTypes, kvoTypes, winformsTypes, androidTypes);
87+
// Consolidate all observable types → single RegisterSourceOutput
88+
var consolidated = allObservableTypes.Collect();
7589

7690
context.RegisterSourceOutput(
7791
consolidated,

src/ReactiveUI.Binding.SourceGenerators/CodeGeneration/ObservationCodeGenerator.cs

Lines changed: 131 additions & 135 deletions
Large diffs are not rendered by default.

src/ReactiveUI.Binding.SourceGenerators/Generators/Observation/AndroidBindingGenerator.cs

Lines changed: 0 additions & 42 deletions
This file was deleted.

src/ReactiveUI.Binding.SourceGenerators/Generators/Observation/INPCBindingGenerator.cs

Lines changed: 0 additions & 42 deletions
This file was deleted.

src/ReactiveUI.Binding.SourceGenerators/Generators/Observation/KVOBindingGenerator.cs

Lines changed: 0 additions & 42 deletions
This file was deleted.

src/ReactiveUI.Binding.SourceGenerators/Generators/Observation/ReactiveObjectBindingGenerator.cs

Lines changed: 0 additions & 42 deletions
This file was deleted.

src/ReactiveUI.Binding.SourceGenerators/Generators/Observation/WinFormsBindingGenerator.cs

Lines changed: 0 additions & 42 deletions
This file was deleted.

src/ReactiveUI.Binding.SourceGenerators/Generators/Observation/WinUIBindingGenerator.cs

Lines changed: 0 additions & 42 deletions
This file was deleted.

src/ReactiveUI.Binding.SourceGenerators/Generators/Observation/WpfBindingGenerator.cs

Lines changed: 0 additions & 42 deletions
This file was deleted.

0 commit comments

Comments
 (0)