Skip to content

Commit

Permalink
Merge pull request #90 from thomasclaudiushuber/issue/77
Browse files Browse the repository at this point in the history
Generate correct constructor when inheriting from another ViewModel w…
  • Loading branch information
thomasclaudiushuber committed Dec 10, 2023
2 parents adf89ec + 4cdce00 commit c7685d3
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 11 deletions.
Expand Up @@ -282,6 +282,49 @@ public void ShouldBeEqual5()
Assert.Equal(_viewModelToGenerate1, _viewModelToGenerate2);
}

[Fact]
public void ShouldNotBeEqualDifferentBaseClassInjectionsToGenerate1()
{
_viewModelToGenerate2.BaseClassInjectionsToGenerate = new List<InjectionToGenerate>();

Assert.NotEqual(_viewModelToGenerate1, _viewModelToGenerate2);
}

[Fact]
public void ShouldNotBeEqualDifferentBaseClassInjectionsToGenerate2()
{
var list = (List<InjectionToGenerate>)_viewModelToGenerate2.BaseClassInjectionsToGenerate!;

list.Add(new InjectionToGenerate("IDataProvider", "DataProvider") { PropertyAccessModifier = "private" });

Assert.NotEqual(_viewModelToGenerate1, _viewModelToGenerate2);
}

[Fact]
public void ShouldNotBeEqualDifferentBaseClassInjectionsToGenerate3()
{
var list = (List<InjectionToGenerate>)_viewModelToGenerate2.BaseClassInjectionsToGenerate!;

var originalInjectionToGenerate = list[0];

list.Clear();
list.Add(new InjectionToGenerate("IChangedDataProvider", originalInjectionToGenerate.PropertyName)
{
PropertyAccessModifier = originalInjectionToGenerate.PropertyAccessModifier
});

Assert.NotEqual(_viewModelToGenerate1, _viewModelToGenerate2);
}

[Fact]
public void ShouldBeEqual6()
{
_viewModelToGenerate1.BaseClassInjectionsToGenerate = null;
_viewModelToGenerate2.BaseClassInjectionsToGenerate = null;

Assert.Equal(_viewModelToGenerate1, _viewModelToGenerate2);
}

[Fact]
public void ShouldNotBeEqualDifferentViewModelFactoryToGenerate1()
{
Expand All @@ -299,7 +342,7 @@ public void ShouldNotBeEqualDifferentViewModelFactoryToGenerate2()
}

[Fact]
public void ShouldBeEqual6()
public void ShouldBeEqual7()
{
_viewModelToGenerate1.ViewModelFactoryToGenerate = null;
_viewModelToGenerate2.ViewModelFactoryToGenerate = null;
Expand All @@ -324,7 +367,7 @@ public void ShouldNotBeEqualDifferentViewModelInterfaceToGenerate2()
}

[Fact]
public void ShouldBeEqual7()
public void ShouldBeEqual8()
{
_viewModelToGenerate1.ViewModelInterfaceToGenerate = null;
_viewModelToGenerate2.ViewModelInterfaceToGenerate = null;
Expand Down Expand Up @@ -360,6 +403,10 @@ private static void FillAllProperties(ViewModelToGenerate viewModelToGenerate)
new InjectionToGenerate("IEventAggregator","EventAggregator"){ PropertyAccessModifier ="public"}
};

viewModelToGenerate.BaseClassInjectionsToGenerate = new List<InjectionToGenerate> {
new InjectionToGenerate("IEventAggregator","EventAggregator"){ PropertyAccessModifier ="public"}
};

viewModelToGenerate.ViewModelFactoryToGenerate = new FactoryToGenerate("FactoryClassName", "FactoryInterfaceName", "CustomReturnType");
viewModelToGenerate.ViewModelInterfaceToGenerate = new InterfaceToGenerate("InterfaceName")
{
Expand Down
Expand Up @@ -107,7 +107,7 @@ public void GeneratePropertyOfInjectedType2()
namespace MyCode
{{
[Inject(typeof(MvvmGen.Events.IEventAggregator)))]
[Inject(typeof(MvvmGen.Events.IEventAggregator))]
[ViewModel]
public partial class EmployeeViewModel
{{
Expand Down Expand Up @@ -258,5 +258,59 @@ public EmployeeViewModel(MyCode.IDataProvider<MyCode.Customer> dataProvider1, My
}}
");
}

[Fact]
public void GenerateConstructorForBaseClassCall()
{
ShouldGenerateExpectedCode(
$@"using MvvmGen;
namespace MyCode
{{
[Inject(typeof(MvvmGen.Events.IEventAggregator))]
[ViewModel]
public partial class MyBaseViewModel
{{
}}
[ViewModel
public partial class EmployeeViewModel : MyBaseViewModel
{{
}}
}}",
$@"{AutoGeneratedTopContent}
namespace MyCode
{{
partial class MyBaseViewModel : global::MvvmGen.ViewModels.ViewModelBase
{{
public MyBaseViewModel(MvvmGen.Events.IEventAggregator eventAggregator)
{{
this.EventAggregator = eventAggregator;
this.OnInitialize();
}}
partial void OnInitialize();
protected MvvmGen.Events.IEventAggregator EventAggregator {{ get; private set; }}
}}
}}
",
$@"{AutoGeneratedTopContent}
namespace MyCode
{{
partial class EmployeeViewModel
{{
public EmployeeViewModel(MvvmGen.Events.IEventAggregator eventAggregator) : base(eventAggregator)
{{
this.OnInitialize();
}}
partial void OnInitialize();
}}
}}
" );
}
}
}
48 changes: 42 additions & 6 deletions src/MvvmGen.SourceGenerators/Generators/ConstructorGenerator.cs
Expand Up @@ -19,22 +19,26 @@ internal static void GenerateConstructor(this ViewModelBuilder vmBuilder, ViewMo
{
Generate(vmBuilder, viewModelToGenerate.ClassName,
viewModelToGenerate.InjectionsToGenerate,
viewModelToGenerate.BaseClassInjectionsToGenerate,
viewModelToGenerate.IsEventSubscriber);
}
}

private static void Generate(ViewModelBuilder vmBuilder, string viewModelClassName,
IEnumerable<InjectionToGenerate>? injectionsToGenerate, bool isEventSubscriber)
IEnumerable<InjectionToGenerate>? directInjectionsToGenerate,
IEnumerable<InjectionToGenerate>? baseClassInjectionsToGenerate,
bool isEventSubscriber)
{
vmBuilder.AppendLineBeforeMember();
vmBuilder.Append($"public {viewModelClassName}(");
injectionsToGenerate ??= Enumerable.Empty<InjectionToGenerate>();
directInjectionsToGenerate ??= Enumerable.Empty<InjectionToGenerate>();
baseClassInjectionsToGenerate ??= Enumerable.Empty<InjectionToGenerate>();

var first = true;
string? eventAggregatorAccessForSubscription = null;
if (isEventSubscriber)
{
var eventAggregatorInjection = injectionsToGenerate.FirstOrDefault(x => x.Type == "MvvmGen.Events.IEventAggregator");
var eventAggregatorInjection = directInjectionsToGenerate.FirstOrDefault(x => x.Type == "MvvmGen.Events.IEventAggregator");
if (eventAggregatorInjection is not null)
{
eventAggregatorAccessForSubscription = $"this.{eventAggregatorInjection.PropertyName}";
Expand All @@ -47,7 +51,7 @@ internal static void GenerateConstructor(this ViewModelBuilder vmBuilder, ViewMo
}
}

foreach (var injectionToGenerate in injectionsToGenerate)
foreach (var injectionToGenerate in directInjectionsToGenerate)
{
if (!first)
{
Expand All @@ -57,10 +61,42 @@ internal static void GenerateConstructor(this ViewModelBuilder vmBuilder, ViewMo
vmBuilder.Append($"{injectionToGenerate.Type} {injectionToGenerate.PropertyName.ToCamelCase()}");
}

vmBuilder.AppendLine(")");
var hasBaseClassInjections = false;
foreach (var injectionToGenerate in baseClassInjectionsToGenerate)
{
hasBaseClassInjections = true;
if (!first)
{
vmBuilder.Append(", ");
}
first = false;
vmBuilder.Append($"{injectionToGenerate.Type} {injectionToGenerate.PropertyName.ToCamelCase()}");
}

vmBuilder.Append(")");

if (hasBaseClassInjections)
{
vmBuilder.Append(" : base(");

first = true;
foreach (var injectionToGenerate in baseClassInjectionsToGenerate)
{
if (!first)
{
vmBuilder.Append(", ");
}
first = false;
vmBuilder.Append(injectionToGenerate.PropertyName.ToCamelCase());
}

vmBuilder.Append(")");
}

vmBuilder.AppendLine();
vmBuilder.AppendLine("{");
vmBuilder.IncreaseIndent();
foreach (var injectionToGenerate in injectionsToGenerate)
foreach (var injectionToGenerate in directInjectionsToGenerate)
{
vmBuilder.AppendLine($"this.{injectionToGenerate.PropertyName} = {injectionToGenerate.PropertyName.ToCamelCase()};");
}
Expand Down
Expand Up @@ -13,8 +13,13 @@ namespace MvvmGen.Inspectors
{
internal static class ViewModelInjectAttributeInspector
{
internal static IEnumerable<InjectionToGenerate> Inspect(INamedTypeSymbol viewModelClassSymbol)
internal static IEnumerable<InjectionToGenerate> Inspect(INamedTypeSymbol? viewModelClassSymbol)
{
if(viewModelClassSymbol is null)
{
return Enumerable.Empty<InjectionToGenerate>();
}

List<InjectionToGenerate> injectionsToGenerate = new();
var injectAttributeDatas = viewModelClassSymbol.GetAttributes()
.Where(x => x.AttributeClass?.ToDisplayString() == "MvvmGen.InjectAttribute")
Expand Down
5 changes: 4 additions & 1 deletion src/MvvmGen.SourceGenerators/Model/ViewModelToGenerate.cs
Expand Up @@ -45,6 +45,8 @@ public ViewModelToGenerate(string className, string namespaceName)

public IEnumerable<InjectionToGenerate>? InjectionsToGenerate { get; set; }

public IEnumerable<InjectionToGenerate>? BaseClassInjectionsToGenerate { get; set; }

public FactoryToGenerate? ViewModelFactoryToGenerate { get; set; }

public InterfaceToGenerate? ViewModelInterfaceToGenerate { get; set; }
Expand All @@ -70,7 +72,8 @@ public bool Equals(ViewModelToGenerate? other)
CommandsToGenerate.SequenceEqualWithNullCheck(other.CommandsToGenerate) &&
CommandInvalidationsToGenerate.SequenceEqualWithNullCheck(other.CommandInvalidationsToGenerate) &&
PropertiesToGenerate.SequenceEqualWithNullCheck(other.PropertiesToGenerate) &&
InjectionsToGenerate.SequenceEqualWithNullCheck(other.InjectionsToGenerate);
InjectionsToGenerate.SequenceEqualWithNullCheck(other.InjectionsToGenerate) &&
BaseClassInjectionsToGenerate.SequenceEqualWithNullCheck(other.BaseClassInjectionsToGenerate);
}

public override int GetHashCode()
Expand Down
1 change: 1 addition & 0 deletions src/MvvmGen.SourceGenerators/ViewModelGenerator.cs
Expand Up @@ -81,6 +81,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
{
ClassAccessModifier = accessModifier,
InjectionsToGenerate = ViewModelInjectAttributeInspector.Inspect(viewModelClassSymbol),
BaseClassInjectionsToGenerate=ViewModelInjectAttributeInspector.Inspect(viewModelClassSymbol.BaseType),
GenerateConstructor = ViewModelAttributeInspector.InspectGenerateConstructor(viewModelAttributeData),
ViewModelFactoryToGenerate = ViewModelGenerateFactoryAttributeInspector.Inspect(viewModelClassSymbol),
InheritFromViewModelBase = ViewModelBaseClassInspector.Inspect(viewModelClassSymbol, viewModelBaseClassSymbol),
Expand Down

0 comments on commit c7685d3

Please sign in to comment.