Skip to content

Commit 03a13cf

Browse files
authored
21.2.2 add command attribute transfer (#15)
* Add_MvvmLight_Support * Add mvvm light tests + fix relayCommand namespace * remove KeepTargetAlive command property + refactoring * Refactoring * fix format * Add command attribute generation * refactoring * Refactoring
1 parent cf7bed8 commit 03a13cf

File tree

9 files changed

+105
-13
lines changed

9 files changed

+105
-13
lines changed

DevExpress.Mvvm.CodeGenerators.Tests/SharedTests/DxTest/CommandGenerationTests.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using NUnit.Framework;
22
using System;
3+
using System.Linq;
34
using System.Reflection;
45

56
namespace DevExpress.Mvvm.CodeGenerators.Tests {
@@ -38,7 +39,17 @@ void WithNullableString3(string? str) { }
3839
void WithNullableString4(string str) { }
3940
bool CanWithNullableString1(string str) => str?.Length > 0;
4041
bool CanWithNullableString2(string str) => str.Length > 0;
42+
[First]
43+
[Second]
44+
[Third]
45+
[GenerateCommand]
46+
void AttributeTest() { }
4147
}
48+
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
49+
public sealed class FirstAttribute : Attribute { }
50+
[AttributeUsage(AttributeTargets.Method)]
51+
public sealed class SecondAttribute : Attribute { }
52+
public sealed class ThirdAttribute : Attribute { }
4253

4354
[TestFixture]
4455
public class CommandGenerationTests {
@@ -134,5 +145,14 @@ public void NullableReferenceType() {
134145
Assert.IsTrue(generated.WithNullableString3Command.CanExecute(""));
135146
Assert.IsFalse(generated.WithNullableString4Command.CanExecute(null));
136147
}
148+
[Test]
149+
public void AttributeGenerationTest() {
150+
var generated = new GenerateCommands();
151+
152+
var attributes = generated.GetType().GetProperty("AttributeTestCommand").GetCustomAttributes().ToList();
153+
Assert.AreEqual(2, attributes.Count);
154+
Assert.IsTrue(attributes[0] is FirstAttribute);
155+
Assert.IsTrue(attributes[1] is ThirdAttribute);
156+
}
137157
}
138158
}

DevExpress.Mvvm.CodeGenerators.Tests/SharedTests/MvvmLightTest/CommandGenerationTests.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
using System.Reflection;
44
using DevExpress.Mvvm.CodeGenerators.MvvmLight;
55
using GalaSoft.MvvmLight.Helpers;
6+
using System.Linq;
7+
68
#if NETCOREAPP
79
using GalaSoft.MvvmLight.Command;
810
#else
@@ -39,7 +41,17 @@ void WithNullableString3(string? str) { }
3941
void WithNullableString4(string str) { }
4042
bool CanWithNullableString1(string str) => str?.Length > 0;
4143
bool CanWithNullableString2(string str) => str.Length > 0;
44+
[First]
45+
[Second]
46+
[Third]
47+
[GenerateCommand]
48+
void AttributeTest() { }
4249
}
50+
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
51+
public sealed class FirstAttribute : Attribute { }
52+
[AttributeUsage(AttributeTargets.Method)]
53+
public sealed class SecondAttribute : Attribute { }
54+
public sealed class ThirdAttribute : Attribute { }
4355

4456
[TestFixture]
4557
public class CommandGenerationTests {
@@ -111,5 +123,15 @@ public void NullableReferenceType() {
111123
Assert.IsTrue(generated.WithNullableString3Command.CanExecute(""));
112124
Assert.IsFalse(generated.WithNullableString4Command.CanExecute(null));
113125
}
126+
127+
[Test]
128+
public void AttributeGenerationTest() {
129+
var generated = new GenerateCommands();
130+
131+
var attributes = generated.GetType().GetProperty("AttributeTestCommand").GetCustomAttributes().ToList();
132+
Assert.AreEqual(2, attributes.Count);
133+
Assert.IsTrue(attributes[0] is FirstAttribute);
134+
Assert.IsTrue(attributes[1] is ThirdAttribute);
135+
}
114136
}
115137
}

DevExpress.Mvvm.CodeGenerators.Tests/SharedTests/PrismTest/CommandGenerationTests.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using DevExpress.Mvvm.CodeGenerators.Prism;
55
using Prism.Commands;
66
using System.Collections.Generic;
7+
using System.Linq;
78

89
namespace Prism.Mvvm.Tests {
910
[GenerateViewModel]
@@ -41,7 +42,17 @@ public void WithCanExecuteAndCanExecuteProperty() { }
4142
void WithCanExecutePropertyAndOservesProperty() { }
4243

4344
public void SomeMethod() { }
45+
[First]
46+
[Second]
47+
[Third]
48+
[GenerateCommand]
49+
void AttributeTest() { }
4450
}
51+
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
52+
public sealed class FirstAttribute : Attribute { }
53+
[AttributeUsage(AttributeTargets.Method)]
54+
public sealed class SecondAttribute : Attribute { }
55+
public sealed class ThirdAttribute : Attribute { }
4556

4657
[TestFixture]
4758
public class CommandGenerationTests {
@@ -120,5 +131,14 @@ public void ObservesProperties() {
120131
Assert.AreEqual(3, expressionsWithCanExecuteProperty.Count);
121132
Assert.IsTrue(expressionsWithCanExecuteProperty.Contains($"() => value({generated.GetType().FullName}).CanExecuteProperty"));
122133
}
134+
[Test]
135+
public void AttributeGenerationTest() {
136+
var generated = new GenerateCommands();
137+
138+
var attributes = generated.GetType().GetProperty("AttributeTestCommand").GetCustomAttributes().ToList();
139+
Assert.AreEqual(2, attributes.Count);
140+
Assert.IsTrue(attributes[0] is FirstAttribute);
141+
Assert.IsTrue(attributes[1] is ThirdAttribute);
142+
}
123143
}
124144
}

DevExpress.Mvvm.CodeGenerators/Generators/CommandGenerator.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ public static void Generate(SourceBuilder source, ContextInfo info, INamedTypeSy
3535
CSharpSyntaxNode commandSyntaxNode = (CSharpSyntaxNode)methodSymbol.DeclaringSyntaxReferences[0].GetSyntax();
3636
XMLCommentHelper.AppendComment(source, commandSyntaxNode);
3737

38+
AttributeHelper.AppendMethodAttriutes(source, methodSymbol, info);
39+
3840
source.Append("public ").AppendCommandGenericType(mvvm, isCommand, genericArgumentType).Append(" ").Append(name);
3941

4042
switch(mvvm) {

DevExpress.Mvvm.CodeGenerators/Generators/PropertyGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ static class PropertyGenerator {
1818
CSharpSyntaxNode fieldSyntaxNode = (CSharpSyntaxNode)fieldSymbol.DeclaringSyntaxReferences[0].GetSyntax().Parent!.Parent!;
1919
XMLCommentHelper.AppendComment(source, fieldSyntaxNode);
2020

21-
PropertyHelper.AppendAttributesList(source, fieldSymbol);
21+
AttributeHelper.AppendFieldAttriutes(source, fieldSymbol, info);
2222

2323
bool isVirtual = PropertyHelper.GetIsVirtualValue(fieldSymbol, propertyAttributeSymbol);
2424
string virtuality = isVirtual ? "virtual " : string.Empty;

DevExpress.Mvvm.CodeGenerators/Helpers/AttributeHelper.cs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Microsoft.CodeAnalysis;
2-
using System.Collections.Generic;
2+
using System;
3+
using System.Collections.Immutable;
34
using System.Linq;
45

56
namespace DevExpress.Mvvm.CodeGenerators {
@@ -30,5 +31,28 @@ public static bool HasAttribute(ISymbol sourceSymbol, INamedTypeSymbol? attribut
3031
return null;
3132
return sourceSymbol.GetAttributes().FirstOrDefault(ad => SymbolEqualityComparer.Default.Equals(ad.AttributeClass, attributeSymbol));
3233
}
34+
35+
public static void AppendFieldAttriutes(SourceBuilder source, ISymbol symbol, ContextInfo info) {
36+
AppendAttributesListCore(source, symbol, info, CanAppendFieldAttribute);
37+
}
38+
static bool CanAppendFieldAttribute(ContextInfo _, AttributeData attribute) {
39+
return PropertyHelper.CanAppendAttribute(attribute.ToString());
40+
}
41+
public static void AppendMethodAttriutes(SourceBuilder source, ISymbol symbol, ContextInfo info) {
42+
AppendAttributesListCore(source, symbol, info, CanAppendMethodAttribute);
43+
}
44+
static bool CanAppendMethodAttribute(ContextInfo info, AttributeData attribute) {
45+
return CommandHelper.CanAppendAttribute(attribute, info);
46+
}
47+
static void AppendAttributesListCore(SourceBuilder source, ISymbol symbol, ContextInfo info, Func<ContextInfo, AttributeData, bool> predicate) {
48+
ImmutableArray<AttributeData> attributeList = symbol.GetAttributes();
49+
if(attributeList.Length == 1)
50+
return;
51+
foreach(AttributeData attribute in attributeList) {
52+
string attributeName = attribute.ToString();
53+
if(predicate(info, attribute))
54+
source.Append('[').Append(attributeName).AppendLine("]");
55+
}
56+
}
3357
}
3458
}

DevExpress.Mvvm.CodeGenerators/Helpers/CommandHelper.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,5 +69,13 @@ static bool HaveSameParametersList(ImmutableArray<IParameterSymbol> parameters,
6969
return parameters.Count() == 0;
7070
return parameters.Count() == 1 && PropertyHelper.IsСompatibleType(parameters.First().Type, parameterType);
7171
}
72+
public static bool CanAppendAttribute(AttributeData attribute, ContextInfo info) {
73+
ImmutableArray<AttributeData> innerAttributeList = attribute.AttributeClass!.GetAttributes();
74+
if(innerAttributeList.Length == 0)
75+
return true;
76+
object? attributeTargets = innerAttributeList.FirstOrDefault(attr => IsAttributeUsageSymbol(attr, info))?.ConstructorArguments[0].Value;
77+
return attributeTargets != null && (((AttributeTargets)attributeTargets & AttributeTargets.Property) != 0);
78+
}
79+
static bool IsAttributeUsageSymbol(AttributeData attribute, ContextInfo info) => SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, info.AttributeUsageSymbol);
7280
}
7381
}

DevExpress.Mvvm.CodeGenerators/Helpers/PropertyHelper.cs

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using Microsoft.CodeAnalysis;
22
using System.Collections.Generic;
3-
using System.Collections.Immutable;
43
using System.Linq;
54

65
namespace DevExpress.Mvvm.CodeGenerators {
@@ -25,16 +24,6 @@ public static string GetSetterAccessModifierValue(IFieldSymbol fieldSymbol, INam
2524
int enumIndex = AttributeHelper.GetPropertyActualValue(fieldSymbol, propertySymbol, nameofSetterAccessModifier, 0);
2625
return AccessModifierGenerator.GetCodeRepresentation((AccessModifier)enumIndex);
2726
}
28-
public static void AppendAttributesList(SourceBuilder source, IFieldSymbol fieldSymbol) {
29-
ImmutableArray<AttributeData> attributeList = fieldSymbol.GetAttributes();
30-
if(attributeList.Length == 1)
31-
return;
32-
foreach(AttributeData attribute in attributeList) {
33-
string attributeName = attribute.ToString();
34-
if(!(attributeName.StartsWith(AttributesGenerator.DxPropertyAttributeFullName!) || attributeName.StartsWith(AttributesGenerator.PrismPropertyAttributeFullName!) || attributeName.StartsWith(AttributesGenerator.MvvmLightPropertyAttributeFullName!)))
35-
source.Append('[').Append(attributeName).AppendLine("]");
36-
}
37-
}
3827
public static NullableAnnotation GetNullableAnnotation(ITypeSymbol type) =>
3928
type.IsReferenceType && type.NullableAnnotation == NullableAnnotation.None
4029
? NullableAnnotation.Annotated
@@ -90,5 +79,10 @@ static IEnumerable<IMethodSymbol> GetOnChangedMethods(INamedTypeSymbol classSymb
9079
CommandHelper.GetMethods(classSymbol,
9180
methodSymbol => methodSymbol.ReturnsVoid && methodSymbol.Name == methodName && methodSymbol.Parameters.Length < 2 &&
9281
(methodSymbol.Parameters.Length == 0 || IsСompatibleType(methodSymbol.Parameters.First().Type, fieldType)));
82+
public static bool CanAppendAttribute(string attributeName) {
83+
return !(attributeName.StartsWith(AttributesGenerator.DxPropertyAttributeFullName!) ||
84+
attributeName.StartsWith(AttributesGenerator.PrismPropertyAttributeFullName!) ||
85+
attributeName.StartsWith(AttributesGenerator.MvvmLightPropertyAttributeFullName!));
86+
}
9387
}
9488
}

DevExpress.Mvvm.CodeGenerators/Info/ContextInfo.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class ContextInfo {
5959
public INamedTypeSymbol INPCingSymbol { get; }
6060
public INamedTypeSymbol TaskSymbol { get; }
6161
public INamedTypeSymbol BoolSymbol { get; }
62+
public INamedTypeSymbol AttributeUsageSymbol { get; }
6263

6364
public bool IsWinUI { get; }
6465
public List<SupportedMvvm> AvailableMvvm { get; }
@@ -81,6 +82,7 @@ public ContextInfo(GeneratorExecutionContext context, Compilation compilation) {
8182

8283
TaskSymbol = compilation.GetTypeByMetadataName("System.Threading.Tasks.Task")!;
8384
BoolSymbol = compilation.GetTypeByMetadataName("System.Boolean")!;
85+
AttributeUsageSymbol = compilation.GetTypeByMetadataName("System.AttributeUsageAttribute")!;
8486

8587
IsWinUI = GetIsWinUI(compilation);
8688
}

0 commit comments

Comments
 (0)