diff --git a/WpfExtended.SourceGeneration.Tests/Class1.cs b/WpfExtended.SourceGeneration.Tests/Class1.cs
index a23b402..174e9f2 100644
--- a/WpfExtended.SourceGeneration.Tests/Class1.cs
+++ b/WpfExtended.SourceGeneration.Tests/Class1.cs
@@ -1,24 +1,41 @@
using FluentAssertions;
+using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Extensions;
using System.Windows.Media.Effects;
-namespace WpfExtended.SourceGeneration.Tests
-{
- internal partial class Class1 : UserControl
- {
- [GenerateDependencyProperty]
- public int someF;
- [GenerateDependencyProperty(InitialValue = "This has a value")]
- public string someValue;
- [GenerateDependencyProperty]
- public Effect effect;
+namespace WpfExtended.SourceGeneration.Tests;
- public void TestValues()
- {
- this.SomeF.Should().Be(0);
- this.SomeValue.Should().Be("This has a value");
- this.Effect.Should().BeNull();
- }
+internal partial class Class1 : UserControl, INotifyPropertyChanged
+{
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ private bool calledEvent;
+
+ [GenerateDependencyProperty]
+ public int someF;
+ [GenerateDependencyProperty(InitialValue = "This has a value")]
+ public string someValue;
+ [GenerateDependencyProperty]
+ public Effect effect;
+ [GenerateNotifyPropertyChanged]
+ public string someNotifyingValue;
+
+
+ public void TestValues()
+ {
+ this.PropertyChanged += Class1_PropertyChanged;
+ this.SomeF.Should().Be(0);
+ this.SomeValue.Should().Be("This has a value");
+ this.Effect.Should().BeNull();
+
+ this.SomeNotifyingValue = "NewValue";
+ this.calledEvent.Should().BeTrue();
+ this.someNotifyingValue.Should().Be("NewValue");
+ }
+
+ private void Class1_PropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ this.calledEvent = true;
}
}
diff --git a/WpfExtended.SourceGeneration.Tests/WpfExtended.SourceGeneration.Tests.csproj b/WpfExtended.SourceGeneration.Tests/WpfExtended.SourceGeneration.Tests.csproj
index f62b91d..afe6b99 100644
--- a/WpfExtended.SourceGeneration.Tests/WpfExtended.SourceGeneration.Tests.csproj
+++ b/WpfExtended.SourceGeneration.Tests/WpfExtended.SourceGeneration.Tests.csproj
@@ -1,7 +1,7 @@
- net7.0-windows
+ net8.0-windows
true
preview
Exe
diff --git a/WpfExtended.SourceGeneration/CodeWriter.cs b/WpfExtended.SourceGeneration/CodeWriter.cs
deleted file mode 100644
index a4d7ad7..0000000
--- a/WpfExtended.SourceGeneration/CodeWriter.cs
+++ /dev/null
@@ -1,143 +0,0 @@
-using System.Extensions.Templates;
-using System.Linq;
-using System.Text;
-
-namespace System.Extensions
-{
- internal class CodeWriter
- {
- private StringBuilder StringBuilder { get; } = new StringBuilder();
- private CodeBlock CurrentBlock { get; set; } = new CodeBlock();
-
- public CodeWriter Append(string value)
- {
- this.CurrentBlock.Append(this.StringBuilder, value);
- return this;
- }
-
- public CodeWriter AppendLine(string value)
- {
- this.CurrentBlock.AppendLine(this.StringBuilder, value);
- return this;
- }
-
- public CodeWriter AppendLine()
- {
- this.CurrentBlock.AppendLine(this.StringBuilder);
- return this;
- }
-
- public CodeWriter Append(char c)
- {
- this.CurrentBlock.Append(this.StringBuilder, c);
- return this;
- }
-
- public CodeWriter AppendLine(char c)
- {
- this.AppendLine(c);
- return this;
- }
-
- public CodeWriter Append(AbstractTemplate abstractTemplate)
- {
- abstractTemplate.Generate(this);
- return this;
- }
-
- public CodeWriter BeginCodeBlock()
- {
- this.CurrentBlock.AppendLine(this.StringBuilder).AppendLine(this.StringBuilder, '{');
- this.CurrentBlock = CodeBlock.GenerateCodeBlock(this.CurrentBlock);
- return this;
- }
-
- public CodeWriter EndCodeBlock()
- {
- this.CurrentBlock = this.CurrentBlock.End(this.StringBuilder).Parent;
- this.CurrentBlock.AppendLine(this.StringBuilder, '}');
- return this;
- }
-
- public override string ToString()
- {
- return this.StringBuilder.ToString();
- }
-
- private class CodeBlock
- {
- private const char PrefixSymbol = '\t';
- private bool NewLine { get; set; } = true;
- private string Prefix { get; }
- public CodeBlock Parent { get; }
- public int Level { get; }
- public CodeBlock()
- {
- this.Level = 0;
- this.Prefix = string.Empty;
- }
- private CodeBlock(int level, CodeBlock parent)
- {
- this.Level = level;
- this.Prefix = new string(Enumerable.Repeat(PrefixSymbol, this.Level).ToArray());
- this.Parent = parent;
- }
-
- public CodeBlock AppendLine(StringBuilder stringBuilder)
- {
- stringBuilder.AppendLine();
- this.NewLine = true;
- return this;
- }
-
- public CodeBlock AppendLine(StringBuilder stringBuilder, string value)
- {
- value = this.NewLine is true ? Prefix + value : value;
- stringBuilder.AppendLine(value);
- this.NewLine = true;
- return this;
- }
-
- public CodeBlock Append(StringBuilder stringBuilder, string value)
- {
- value = this.NewLine is true ? Prefix + value : value;
- stringBuilder.Append(value);
- this.NewLine = false;
- return this;
- }
-
- public CodeBlock Append(StringBuilder stringBuilder, char c)
- {
- var value = this.NewLine is true ? Prefix + c : c.ToString();
- stringBuilder.Append(value);
- this.NewLine = false;
- return this;
- }
-
- public CodeBlock AppendLine(StringBuilder stringBuilder, char c)
- {
- var value = this.NewLine is true ? Prefix + c : c.ToString();
- stringBuilder.AppendLine(value);
- this.NewLine = true;
- return this;
- }
-
- public CodeBlock End(StringBuilder stringBuilder)
- {
- if (this.NewLine is false)
- {
- stringBuilder.AppendLine();
- this.NewLine = true;
- }
-
- return this;
- }
-
- public static CodeBlock GenerateCodeBlock(CodeBlock codeBlock)
- {
- var child = new CodeBlock(codeBlock.Level + 1, codeBlock);
- return child;
- }
- }
- }
-}
diff --git a/WpfExtended.SourceGeneration/DependencyPropertyGenerator.cs b/WpfExtended.SourceGeneration/DependencyPropertyGenerator.cs
index 65bdef9..ec7f98c 100644
--- a/WpfExtended.SourceGeneration/DependencyPropertyGenerator.cs
+++ b/WpfExtended.SourceGeneration/DependencyPropertyGenerator.cs
@@ -2,194 +2,179 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
+using Sybil;
using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
-using System.Extensions.Templates;
-namespace System.Extensions
+namespace System.Extensions;
+#nullable enable
+[Generator(LanguageNames.CSharp)]
+public class DependencyPropertyGenerator : IIncrementalGenerator
{
- [Generator(LanguageNames.CSharp)]
- public class DependencyPropertyGenerator : ISourceGenerator
+ private const string DependencyPropertyNamespace = "System.Windows";
+ private const string AttributeNamespace = "System.Windows.Extensions";
+ private const string AttributeName = "GenerateDependencyPropertyAttribute";
+ private const string AttributeShortName = "GenerateDependencyProperty";
+ private const string DependencyPropertySuffix = "Property";
+ private const string DependencyProperty = "DependencyProperty";
+ private const string Register = "Register";
+ private const string Partial = "partial";
+ private const string Public = "public";
+ private const string Static = "static";
+ private const string Readonly = "readonly";
+ private const string InitialValue = "InitialValue";
+
+ public void Initialize(IncrementalGeneratorInitializationContext context)
{
- private const string AttributeNamespace = "System.Windows.Extensions";
- private const string AttributeName = "GenerateDependencyPropertyAttribute";
- private const string DependencyPropertySuffix = "Property";
- private static readonly ClassTemplate GenerateDependencyPropertyAttribute =
- new ClassTemplate()
- .WithUsings(
- new UsingsTemplate()
- .WithNamespace("System"))
- .WithNamespace(
- new NamespaceTemplate()
- .WithNamespace(AttributeNamespace))
- .WithConstructor(
- new ConstructorTemplate()
- .WithType(AttributeName)
- .WithModifiers(Modifier.Public))
- .WithAttributes(
- new AttributeTemplate()
- .WithAttribute("AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)"))
- .WithModifiers(Modifier.Public, Modifier.Sealed)
- .WithName(AttributeName)
- .WithBase("Attribute")
- .WithProperty(
- new PropertyTemplate()
- .WithModifiers(Modifier.Public)
- .WithName("InitialValue")
- .WithType("object"));
-
- public void Initialize(GeneratorInitializationContext context)
+ context.RegisterPostInitializationOutput(context =>
{
- // Register a syntax receiver that will be created for each generation pass
- context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
+ var attributeNamespaceBuilder = SyntaxBuilder.CreateNamespace(AttributeNamespace)
+ .WithUsing("System.ComponentModel")
+ .WithClass(SyntaxBuilder.CreateClass(AttributeName)
+ .WithModifier(Public)
+ .WithConstructor(SyntaxBuilder.CreateConstructor(AttributeName)
+ .WithModifier(Public))
+ .WithAttribute(SyntaxBuilder.CreateAttribute("AttributeUsage")
+ .WithArgument(AttributeTargets.Field)
+ .WithArgument("Inherited", false)
+ .WithArgument("AllowMultiple", false))
+ .WithBaseClass(nameof(Attribute))
+ .WithProperty(SyntaxBuilder.CreateProperty("object", InitialValue)
+ .WithModifier(Public)
+ .WithAccessor(SyntaxBuilder.CreateGetter())
+ .WithAccessor(SyntaxBuilder.CreateSetter())));
+ var attributeSyntax = attributeNamespaceBuilder.Build();
+ var source = attributeSyntax.ToFullString();
+ context.AddSource($"{AttributeName}.g", source);
+ });
+
+ var classDeclarations = context.SyntaxProvider.CreateSyntaxProvider(
+ predicate: static (s, _) => s is FieldDeclarationSyntax,
+ transform: static (ctx, _) => GetFilteredFieldDeclarationSyntax(ctx)).Where(static c => c is not null);
+
+ var compilationAndClasses = context.CompilationProvider.Combine(classDeclarations.Collect());
+ context.RegisterSourceOutput(compilationAndClasses, (sourceProductionContext, tuple) => Execute(tuple.Left, tuple.Right, sourceProductionContext));
+ }
+
+ private static ClassDeclarationSyntax? GetFilteredFieldDeclarationSyntax(GeneratorSyntaxContext context)
+ {
+ var fieldDeclarationSyntax = (FieldDeclarationSyntax)context.Node;
+ if (fieldDeclarationSyntax.AttributeLists
+ .SelectMany(l => l.Attributes)
+ .OfType()
+ .Any(s => s.Name.ToString() == AttributeName || s.Name.ToString() == AttributeShortName))
+ {
+ return fieldDeclarationSyntax.Parent as ClassDeclarationSyntax;
}
- public void Execute(GeneratorExecutionContext context)
+ return default;
+ }
+
+ private static void Execute(Compilation _, ImmutableArray classes, SourceProductionContext sourceProductionContext)
+ {
+ if (classes.IsDefaultOrEmpty)
{
- var attributeText = GenerateDependencyPropertyAttribute.GenerateString();
- context.AddSource("GenerateDependencyPropertyAttribute.g", attributeText);
+ return;
+ }
- if (context.SyntaxReceiver is not SyntaxReceiver receiver)
- return;
-
- var options = (context.Compilation as CSharpCompilation).SyntaxTrees[0].Options as CSharpParseOptions;
- var compilation = context.Compilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(SourceText.From(attributeText, Encoding.UTF8), options));
-
- var attributeSymbol = compilation.GetTypeByMetadataName($"{AttributeNamespace}.{AttributeName}");
- if (attributeSymbol is null)
+ var distinctClasses = classes.Distinct();
+ foreach (var c in distinctClasses.OfType())
+ {
+ var className = c.Identifier.ToFullString().Trim();
+ if (!c.Modifiers.Any(m => m.ToString() == Partial))
{
- context.ReportDiagnostic(Diagnostics.MissingAttributeDiagnostic(AttributeName, AttributeNamespace));
- return;
+ Diagnostics.ClassNotPartial(className);
+ continue;
}
- var fieldSymbols = new List();
- foreach (var field in receiver.CandidateFields)
+ var originalNamespaceSyntax = GetParentOfType(c);
+ if (originalNamespaceSyntax is null)
{
- var model = compilation.GetSemanticModel(field.SyntaxTree);
- foreach (var variable in field.Declaration.Variables)
+ Diagnostics.ClassNotInNamespace(className);
+ continue;
+ }
+
+ var generatedClassBuilder = SyntaxBuilder.CreateClass(className)
+ .WithModifiers(string.Join(" ", c.Modifiers.Select(m => m.ToFullString())));
+ var generatedNamespaceBuilder = SyntaxBuilder.CreateNamespace(originalNamespaceSyntax.Name.ToFullString())
+ .WithClass(generatedClassBuilder);
+
+ if (originalNamespaceSyntax.Usings.OfType().Any(u => u.Name?.ToString() == DependencyPropertyNamespace) is false &&
+ (originalNamespaceSyntax.Parent as CompilationUnitSyntax)?.Usings.Any(u => u.Name?.ToString() == DependencyPropertyNamespace) is false)
+ {
+ generatedNamespaceBuilder.WithUsing(DependencyPropertyNamespace);
+ }
+
+ foreach (var field in c.DescendantNodes().OfType())
+ {
+ var attributeSyntax = field.AttributeLists
+ .SelectMany(l => l.Attributes)
+ .OfType()
+ .FirstOrDefault(s => s.Name.ToString() == AttributeName ||
+ s.Name.ToString() == AttributeShortName);
+ if (attributeSyntax is null)
{
- var fieldSymbol = model.GetDeclaredSymbol(variable) as IFieldSymbol;
- if (fieldSymbol.GetAttributes().Any(ad => ad.AttributeClass.Equals(attributeSymbol, SymbolEqualityComparer.Default)))
- {
- fieldSymbols.Add(fieldSymbol);
- }
+ continue;
}
+
+ var initialValueArgument = attributeSyntax.ArgumentList?.Arguments
+ .FirstOrDefault(arg => arg.NameEquals != null && arg.NameEquals.Name.Identifier.ValueText == InitialValue);
+
+ var fieldName = field.Declaration.Variables.First().Identifier.ToFullString().Trim();
+ var fieldType = field.Declaration.Type.ToFullString().Trim();
+ var propertyName = fieldName.TrimStart('_');
+ propertyName = $"{char.ToUpper(propertyName[0])}{propertyName.Substring(1)}".Trim();
+ var dependencyPropertyName = $"{propertyName.Trim()}{DependencyPropertySuffix}".Trim();
+ var propertyBuilder = SyntaxBuilder.CreateProperty(fieldType, propertyName)
+ .WithModifier(Public)
+ .WithAccessor(SyntaxBuilder.CreateGetter()
+ .WithBody($"this.{fieldName} = ({fieldType})this.GetValue({dependencyPropertyName});\r\nreturn this.{fieldName};"))
+ .WithAccessor(SyntaxBuilder.CreateSetter()
+ .WithBody($"this.{fieldName} = value;\r\nthis.SetValue({dependencyPropertyName}, value);"));
+ var dependencyPropertyFieldBuilder = SyntaxBuilder.CreateField(DependencyProperty, dependencyPropertyName)
+ .WithModifiers($"{Public} {Static} {Readonly}")
+ .WithInitializer($"{DependencyProperty}.Register(nameof({propertyName}), typeof({fieldType}), typeof({className}), new PropertyMetadata({initialValueArgument?.Expression.ToString()}))");
+
+ generatedClassBuilder
+ .WithProperty(propertyBuilder)
+ .WithField(dependencyPropertyFieldBuilder);
}
- foreach (var group in fieldSymbols.GroupBy(f => f.ContainingType, SymbolEqualityComparer.IncludeNullability))
+ var generatedNamespace = generatedNamespaceBuilder.Build();
+ if (originalNamespaceSyntax.Usings.Count != 0)
{
- var classSource = ProcessClass((INamedTypeSymbol)group.Key, attributeSymbol, group.ToList(), context);
- context.AddSource($"{group.Key.Name}.DependencyPropertyGenerator.g.cs", classSource);
- }
- }
-
- private static string ProcessClass(INamedTypeSymbol classSymbol, INamedTypeSymbol attributeSymbol, List fields, GeneratorExecutionContext generatorExecutionContext)
- {
- if (!classSymbol.ContainingSymbol.Equals(classSymbol.ContainingNamespace, SymbolEqualityComparer.Default))
- {
- var syntaxTree = classSymbol.DeclaringSyntaxReferences.First().SyntaxTree;
- var textSpan = classSymbol.DeclaringSyntaxReferences.First().Span;
- generatorExecutionContext.ReportDiagnostic(
- Diagnostics.ClassNotTopLevelDiagnostic(classSymbol.Name, classSymbol.ContainingSymbol.Name, syntaxTree, textSpan));
- return null;
- }
-
- var namespaceName = classSymbol.ContainingNamespace.ToDisplayString();
-
- var source = new ClassTemplate()
- .WithUsings(
- new UsingsTemplate()
- .WithNamespace("System"),
- new UsingsTemplate()
- .WithNamespace("System.Windows"))
- .WithNamespace(
- new NamespaceTemplate()
- .WithNamespace(namespaceName))
- .WithName(classSymbol.Name);
- AssignModifiers(source, classSymbol);
- foreach (var fieldSymbol in fields)
- {
- ProcessField(source, classSymbol, attributeSymbol, fieldSymbol);
- }
-
- return source.GenerateString();
- }
-
- private static void AssignModifiers(ClassTemplate source, INamedTypeSymbol classSymbol)
- {
- //Find class declaration syntax and parse modifiers.
- if (classSymbol.DeclaringSyntaxReferences.Select(sr => sr.GetSyntax()).OfType().First() is ClassDeclarationSyntax classDeclarationSyntax)
- {
- source.WithModifiers(classDeclarationSyntax.Modifiers.Select(m => Modifier.Parse(m.ValueText)).ToArray());
- }
- }
-
- private static void ProcessField(ClassTemplate source, INamedTypeSymbol classSymbol, INamedTypeSymbol attributeSymbol, IFieldSymbol fieldSymbol)
- {
- var fieldName = fieldSymbol.Name;
- var fieldType = fieldSymbol.Type;
-
- string propertyName = GenerateName(fieldName);
-
- var attributeData = fieldSymbol.GetAttributes().Single(ad => ad.AttributeClass.Equals(attributeSymbol, SymbolEqualityComparer.Default));
- var initialValueOpt = attributeData.NamedArguments.SingleOrDefault(kvp => kvp.Key == "InitialValue").Value;
-
- var dependencyPropertyValue = initialValueOpt.Value is null ?
- @$"DependencyProperty.Register(""{propertyName}"", typeof({fieldType}), typeof({classSymbol.Name}))" :
- @$"DependencyProperty.Register(""{propertyName}"", typeof({fieldType}), typeof({classSymbol.Name}), new PropertyMetadata({initialValueOpt.ToCSharpString()}))";
-
- source
- .WithField(
- new FieldTemplate()
- .WithModifiers(Modifier.Public, Modifier.Static, Modifier.Readonly)
- .WithType("DependencyProperty")
- .WithName(propertyName + DependencyPropertySuffix)
- .WithValue(dependencyPropertyValue))
- .WithProperty(
- new PropertyTemplate()
- .WithType(fieldType.ToString())
- .WithName(propertyName)
- .WithGetter(
- new GetterTemplate()
- .WithCode(
- new CodeBlockTemplate()
- .WithLine($"this.{fieldName} = ({fieldType})this.GetValue({propertyName + DependencyPropertySuffix});")
- .WithLine($"return ({fieldType})this.GetValue({propertyName + DependencyPropertySuffix});")))
- .WithSetter(
- new SetterTemplate()
- .WithCode(
- new CodeBlockTemplate()
- .WithLine($"this.{fieldName} = value;")
- .WithLine($"this.SetValue({propertyName + DependencyPropertySuffix}, value);"))));
- }
-
- private static string GenerateName(string fieldName)
- {
- fieldName = fieldName.TrimStart('_');
- if (char.IsUpper(fieldName.First()))
- {
- return char.ToLower(fieldName.First()) + fieldName.Substring(1);
+ generatedNamespace = generatedNamespace.AddUsings([.. originalNamespaceSyntax.Usings]);
}
else
{
- return char.ToUpper(fieldName.First()) + fieldName.Substring(1);
- }
- }
-
- internal class SyntaxReceiver : ISyntaxReceiver
- {
- public List CandidateFields { get; } = new List();
-
- public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
- {
- if (syntaxNode is FieldDeclarationSyntax fieldDeclarationSyntax &&
- fieldDeclarationSyntax.AttributeLists.Count > 0)
+ if (originalNamespaceSyntax.Parent is CompilationUnitSyntax compilationUnit)
{
- this.CandidateFields.Add(fieldDeclarationSyntax);
+ generatedNamespace = generatedNamespace.AddUsings([.. compilationUnit.Usings]);
}
}
+
+ var fileSource = generatedNamespace.ToFullString();
+ sourceProductionContext.AddSource($"{className}.g", fileSource);
}
}
+
+ private static T? GetParentOfType(SyntaxNode syntaxNode)
+ {
+ if (syntaxNode.Parent is null)
+ {
+ return default;
+ }
+
+ if (syntaxNode.Parent is T parentNode)
+ {
+ return parentNode;
+ }
+
+ return GetParentOfType(syntaxNode.Parent);
+ }
}
+#nullable disable
\ No newline at end of file
diff --git a/WpfExtended.SourceGeneration/Diagnostics.cs b/WpfExtended.SourceGeneration/Diagnostics.cs
index 3df238e..5eb452f 100644
--- a/WpfExtended.SourceGeneration/Diagnostics.cs
+++ b/WpfExtended.SourceGeneration/Diagnostics.cs
@@ -7,6 +7,9 @@ namespace System.Extensions
{
public const string DependencyPropertyGenerator_NoAttributeFound = "DGD 0001";
public const string DependencyPropertyGenerator_ClassMustBeTopLevel = "DGD 1001";
+ public const string DependencyPropertyGenerator_ClassMustBePartial = "DGD 1002";
+ public const string DependencyPropertyGenerator_ClassMustBeInNamespace = "DGD 1003";
+ public const string DependencyPropertyGenerator_ClassMustImplementINotifyPropertyChanged = "DGD 1004";
public static Diagnostic MissingAttributeDiagnostic(string attributeName, string attributeNamespace) => Diagnostic.Create(
new DiagnosticDescriptor(
@@ -33,5 +36,44 @@ namespace System.Extensions
null),
Location.Create(syntaxTree, textSpan),
className, containingSymbol);
+
+ public static Diagnostic ClassNotPartial(string className) => Diagnostic.Create(
+ new DiagnosticDescriptor(
+ DependencyPropertyGenerator_ClassMustBePartial,
+ $"{className} is not partial",
+ "Class {0} must be marked as partial in order for the generator to work",
+ "WpfExtended.SourceGeneration",
+ DiagnosticSeverity.Error,
+ true,
+ null,
+ null),
+ Location.None,
+ className);
+
+ public static Diagnostic ClassNotInNamespace(string className) => Diagnostic.Create(
+ new DiagnosticDescriptor(
+ DependencyPropertyGenerator_ClassMustBeInNamespace,
+ $"{className} is not defined in a namespace",
+ "Class {0} must be defined inside a namespace for the generator to work",
+ "WpfExtended.SourceGeneration",
+ DiagnosticSeverity.Error,
+ true,
+ null,
+ null),
+ Location.None,
+ className);
+
+ public static Diagnostic ClassDoesNotImplementINotifyPropertyChanged(string className) => Diagnostic.Create(
+ new DiagnosticDescriptor(
+ DependencyPropertyGenerator_ClassMustImplementINotifyPropertyChanged,
+ $"{className} must implement INotifyPropertyChanged",
+ "Class {0} must implement INotifyPropertyChanged for the generator to work",
+ "WpfExtended.SourceGeneration",
+ DiagnosticSeverity.Error,
+ true,
+ null,
+ null),
+ Location.None,
+ className);
}
}
diff --git a/WpfExtended.SourceGeneration/NotifyPropertyChangedGenerator.cs b/WpfExtended.SourceGeneration/NotifyPropertyChangedGenerator.cs
new file mode 100644
index 0000000..0f60c23
--- /dev/null
+++ b/WpfExtended.SourceGeneration/NotifyPropertyChangedGenerator.cs
@@ -0,0 +1,164 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Sybil;
+using System.Linq;
+using System.Collections.Immutable;
+
+namespace System.Extensions;
+#nullable enable
+[Generator(LanguageNames.CSharp)]
+public class NotifyPropertyChangedGenerator : IIncrementalGenerator
+{
+ private const string AttributeNamespace = "System.Windows.Extensions";
+ private const string AttributeName = "GenerateNotifyPropertyChangedAttribute";
+ private const string AttributeShortName = "GenerateNotifyPropertyChanged";
+ private const string PropertyChangedEventHandler = "PropertyChangedEventHandler";
+ private const string NullablePropertyChangedEventHandler = "PropertyChangedEventHandler?";
+ private const string PropertyChanged = "PropertyChanged";
+ private const string Partial = "partial";
+ private const string Public = "public";
+
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ context.RegisterPostInitializationOutput(context =>
+ {
+ var attributeNamespaceBuilder = SyntaxBuilder.CreateNamespace(AttributeNamespace)
+ .WithUsing("System.ComponentModel")
+ .WithClass(SyntaxBuilder.CreateClass(AttributeName)
+ .WithModifier(Public)
+ .WithConstructor(SyntaxBuilder.CreateConstructor(AttributeName)
+ .WithModifier(Public))
+ .WithAttribute(SyntaxBuilder.CreateAttribute("AttributeUsage")
+ .WithArgument(AttributeTargets.Field)
+ .WithArgument("Inherited", false)
+ .WithArgument("AllowMultiple", false))
+ .WithBaseClass(nameof(Attribute)));
+ var attributeSyntax = attributeNamespaceBuilder.Build();
+ var source = attributeSyntax.ToFullString();
+ context.AddSource($"{AttributeName}.g", source);
+ });
+
+ var classDeclarations = context.SyntaxProvider.CreateSyntaxProvider(
+ predicate: static (s, _) => s is FieldDeclarationSyntax,
+ transform: static (ctx, _) => GetFilteredFieldDeclarationSyntax(ctx)).Where(static c => c is not null);
+
+ var compilationAndClasses = context.CompilationProvider.Combine(classDeclarations.Collect());
+ context.RegisterSourceOutput(compilationAndClasses, (sourceProductionContext, tuple) => Execute(tuple.Left, tuple.Right, sourceProductionContext));
+ }
+
+ private static ClassDeclarationSyntax? GetFilteredFieldDeclarationSyntax(GeneratorSyntaxContext context)
+ {
+ var fieldDeclarationSyntax = (FieldDeclarationSyntax)context.Node;
+ if (fieldDeclarationSyntax.AttributeLists
+ .SelectMany(l => l.Attributes)
+ .OfType()
+ .Any(s => s.Name.ToString() == AttributeName || s.Name.ToString() == AttributeShortName))
+ {
+ return fieldDeclarationSyntax.Parent as ClassDeclarationSyntax;
+ }
+
+ return default;
+ }
+
+ private static void Execute(Compilation _, ImmutableArray classes, SourceProductionContext sourceProductionContext)
+ {
+ if (classes.IsDefaultOrEmpty)
+ {
+ return;
+ }
+
+ var distinctClasses = classes.Distinct();
+ foreach (var c in distinctClasses.OfType())
+ {
+ var className = c.Identifier.ToFullString();
+ if (!c.Modifiers.Any(m => m.ToString() == Partial))
+ {
+ Diagnostics.ClassNotPartial(className);
+ continue;
+ }
+
+ var originalNamespaceSyntax = GetParentOfType(c);
+ if (originalNamespaceSyntax is null)
+ {
+ Diagnostics.ClassNotInNamespace(className);
+ continue;
+ }
+
+ if (!c.Members
+ .OfType()
+ .Any(eventField =>
+ (eventField.Declaration.Variables.Any(variable => variable.Identifier.Text == PropertyChanged) &&
+ eventField.Modifiers.Any(modifier => modifier.Text == Public) &&
+ eventField.Declaration.Type.ToString() == PropertyChangedEventHandler) ||
+ eventField.Declaration.Type.ToString() == NullablePropertyChangedEventHandler))
+ {
+ Diagnostics.ClassDoesNotImplementINotifyPropertyChanged(className);
+ continue;
+ }
+
+ var generatedClassBuilder = SyntaxBuilder.CreateClass(className)
+ .WithModifiers(string.Join(" ", c.Modifiers.Select(m => m.ToFullString())));
+ var generatedNamespaceBuilder = SyntaxBuilder.CreateNamespace(originalNamespaceSyntax.Name.ToFullString())
+ .WithClass(generatedClassBuilder);
+ if (originalNamespaceSyntax.Usings.Count != 0)
+ {
+ foreach (var u in originalNamespaceSyntax.Usings.OfType())
+ {
+ generatedNamespaceBuilder.WithUsing(u.Name?.ToString());
+ }
+ }
+ else
+ {
+ if (originalNamespaceSyntax.Parent is CompilationUnitSyntax compilationUnit)
+ {
+ foreach(var u in compilationUnit.Usings.OfType())
+ {
+ generatedNamespaceBuilder.WithUsing(u.Name?.ToString());
+ }
+ }
+ }
+
+ foreach (var field in c.DescendantNodes().OfType())
+ {
+ if (field.AttributeLists
+ .SelectMany(l => l.Attributes)
+ .OfType()
+ .Any(s => s.Name.ToString() == AttributeName ||
+ s.Name.ToString() == AttributeShortName) is false)
+ {
+ continue;
+ }
+
+ var fieldName = field.Declaration.Variables.First().Identifier.ToFullString();
+ var propertyName = fieldName.TrimStart('_');
+ propertyName = $"{char.ToUpper(propertyName[0])}{propertyName.Substring(1)}";
+ var propertyBuilder = SyntaxBuilder.CreateProperty(field.Declaration.Type.ToFullString(), propertyName)
+ .WithModifier(Public)
+ .WithAccessor(SyntaxBuilder.CreateGetter()
+ .WithBody($"return this.{fieldName};"))
+ .WithAccessor(SyntaxBuilder.CreateSetter()
+ .WithBody($"this.{fieldName} = value;\r\nthis.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof({propertyName})));"));
+ generatedClassBuilder.WithProperty(propertyBuilder);
+ }
+
+ var fileSource = generatedNamespaceBuilder.Build().ToFullString();
+ sourceProductionContext.AddSource($"{className}.g", fileSource);
+ }
+ }
+
+ private static T? GetParentOfType(SyntaxNode syntaxNode)
+ {
+ if (syntaxNode.Parent is null)
+ {
+ return default;
+ }
+
+ if (syntaxNode.Parent is T parentNode)
+ {
+ return parentNode;
+ }
+
+ return GetParentOfType(syntaxNode.Parent);
+ }
+}
+#nullable disable
diff --git a/WpfExtended.SourceGeneration/Templates/AbstractTemplate.cs b/WpfExtended.SourceGeneration/Templates/AbstractTemplate.cs
deleted file mode 100644
index 81005bf..0000000
--- a/WpfExtended.SourceGeneration/Templates/AbstractTemplate.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace System.Extensions.Templates
-{
- internal abstract class AbstractTemplate
- {
- public abstract void Generate(CodeWriter codeWriter);
- }
-}
diff --git a/WpfExtended.SourceGeneration/Templates/ArgumentTemplate.cs b/WpfExtended.SourceGeneration/Templates/ArgumentTemplate.cs
deleted file mode 100644
index 6c35c60..0000000
--- a/WpfExtended.SourceGeneration/Templates/ArgumentTemplate.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-namespace System.Extensions.Templates
-{
- internal sealed class ArgumentTemplate : AbstractTemplate
- {
- public string Name { get; set; }
- public string Type { get; set; }
-
- public ArgumentTemplate WithName(string name)
- {
- this.Name = name;
- return this;
- }
-
- public ArgumentTemplate WithType(string type)
- {
- this.Type = type;
- return this;
- }
-
- public override void Generate(CodeWriter codeWriter)
- {
- codeWriter
- .Append(this.Type)
- .Append(' ')
- .Append(this.Name);
- }
- }
-}
diff --git a/WpfExtended.SourceGeneration/Templates/AttributeTemplate.cs b/WpfExtended.SourceGeneration/Templates/AttributeTemplate.cs
deleted file mode 100644
index 24df7a8..0000000
--- a/WpfExtended.SourceGeneration/Templates/AttributeTemplate.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-namespace System.Extensions.Templates
-{
- internal sealed class AttributeTemplate : AbstractTemplate
- {
- public string Attribute { get; set; }
-
- public AttributeTemplate WithAttribute(string attribute)
- {
- this.Attribute = attribute;
- return this;
- }
-
- public override void Generate(CodeWriter codeWriter)
- {
- codeWriter
- .Append('[')
- .Append(this.Attribute)
- .Append(']');
- }
- }
-}
diff --git a/WpfExtended.SourceGeneration/Templates/ClassTemplate.cs b/WpfExtended.SourceGeneration/Templates/ClassTemplate.cs
deleted file mode 100644
index 610c26f..0000000
--- a/WpfExtended.SourceGeneration/Templates/ClassTemplate.cs
+++ /dev/null
@@ -1,190 +0,0 @@
-using System.Collections.Generic;
-
-namespace System.Extensions.Templates
-{
- internal sealed class ClassTemplate : AbstractTemplate
- {
- public List Attributes { get; } = new List();
- public List Usings { get; } = new List();
- public List Modifiers { get; } = new List();
- public List Properties { get; } = new List();
- public List Fields { get; } = new List();
- public List Methods { get; } = new List();
- public List Constructors { get; } = new List();
- public NamespaceTemplate Namespace { get; set; }
- public string Name { get; set; }
- public string Base { get; set; }
-
- public ClassTemplate WithAttributes(params AttributeTemplate[] attributeTemplates)
- {
- this.Attributes.Clear();
- this.Attributes.AddRange(attributeTemplates);
- return this;
- }
- public ClassTemplate WithAttribute(AttributeTemplate attributeTemplate)
- {
- this.Attributes.Add(attributeTemplate);
- return this;
- }
- public ClassTemplate WithUsings(params UsingsTemplate[] usingsTemplates)
- {
- this.Usings.Clear();
- this.Usings.AddRange(usingsTemplates);
- return this;
- }
- public ClassTemplate WithUsing(UsingsTemplate usingsTemplate)
- {
- this.Usings.Add(usingsTemplate);
- return this;
- }
- public ClassTemplate WithModifiers(params Modifier[] modifiers)
- {
- this.Modifiers.Clear();
- this.Modifiers.AddRange(modifiers);
- return this;
- }
- public ClassTemplate WithModifier(Modifier modifier)
- {
- this.Modifiers.Add(modifier);
- return this;
- }
- public ClassTemplate WithMethods(params MethodTemplate[] methodTemplates)
- {
- this.Methods.Clear();
- this.Methods.AddRange(methodTemplates);
- return this;
- }
- public ClassTemplate WithMethod(MethodTemplate methodTemplate)
- {
- this.Methods.Add(methodTemplate);
- return this;
- }
- public ClassTemplate WithFields(params FieldTemplate[] fieldTemplates)
- {
- this.Fields.Clear();
- this.Fields.AddRange(fieldTemplates);
- return this;
- }
- public ClassTemplate WithField(FieldTemplate fieldTemplate)
- {
- this.Fields.Add(fieldTemplate);
- return this;
- }
- public ClassTemplate WithProperties(params PropertyTemplate[] propertyTemplates)
- {
- this.Properties.Clear();
- this.Properties.AddRange(propertyTemplates);
- return this;
- }
- public ClassTemplate WithProperty(PropertyTemplate propertyTemplate)
- {
- this.Properties.Add(propertyTemplate);
- return this;
- }
- public ClassTemplate WithConstructors(params ConstructorTemplate[] constructorTemplates)
- {
- this.Constructors.Clear();
- this.Constructors.AddRange(constructorTemplates);
- return this;
- }
- public ClassTemplate WithConstructor(ConstructorTemplate constructorTemplate)
- {
- this.Constructors.Add(constructorTemplate);
- return this;
- }
- public ClassTemplate WithNamespace(NamespaceTemplate namespaceTemplate)
- {
- this.Namespace = namespaceTemplate;
- return this;
- }
- public ClassTemplate WithName(string name)
- {
- this.Name = name;
- return this;
- }
- public ClassTemplate WithBase(string b)
- {
- this.Base = b;
- return this;
- }
-
- public override void Generate(CodeWriter codeWriter)
- {
- foreach(var u in this.Usings)
- {
- codeWriter
- .Append(u)
- .AppendLine();
- }
-
- codeWriter
- .AppendLine()
- .Append(this.Namespace)
- .BeginCodeBlock();
- foreach(var attribute in this.Attributes)
- {
- codeWriter
- .Append(attribute)
- .AppendLine();
- }
-
- foreach(var modifier in this.Modifiers)
- {
- codeWriter
- .Append(modifier)
- .Append(' ');
- }
-
- codeWriter
- .Append("class")
- .Append(' ')
- .Append(this.Name);
- if (!string.IsNullOrEmpty(this.Base))
- {
- codeWriter
- .Append(' ')
- .Append(':')
- .Append(' ')
- .Append(this.Base);
- }
-
- codeWriter.BeginCodeBlock();
- foreach(var field in this.Fields)
- {
- codeWriter
- .Append(field)
- .AppendLine();
- }
-
- foreach(var property in this.Properties)
- {
- codeWriter
- .Append(property)
- .AppendLine();
- }
-
- foreach(var constructor in this.Constructors)
- {
- codeWriter
- .Append(constructor)
- .AppendLine();
- }
-
- foreach(var method in this.Methods)
- {
- codeWriter
- .Append(method)
- .AppendLine();
- }
-
- codeWriter.EndCodeBlock().EndCodeBlock();
- }
-
- public string GenerateString()
- {
- var codeWriter = new CodeWriter();
- this.Generate(codeWriter);
- return codeWriter.ToString();
- }
- }
-}
diff --git a/WpfExtended.SourceGeneration/Templates/CodeBlockTemplate.cs b/WpfExtended.SourceGeneration/Templates/CodeBlockTemplate.cs
deleted file mode 100644
index c36bdcd..0000000
--- a/WpfExtended.SourceGeneration/Templates/CodeBlockTemplate.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using System.Collections.Generic;
-
-namespace System.Extensions.Templates
-{
- internal sealed class CodeBlockTemplate : CodeTemplate
- {
- public List Lines { get; set; } = new List();
-
- public CodeBlockTemplate WithLines(params string[] lines)
- {
- this.Lines.Clear();
- this.Lines.AddRange(lines);
- return this;
- }
-
- public CodeBlockTemplate WithLine(string line)
- {
- this.Lines.Add(line);
- return this;
- }
-
- public override void Generate(CodeWriter codeWriter)
- {
- foreach(var line in this.Lines)
- {
- codeWriter.AppendLine(line);
- }
- }
- }
-}
diff --git a/WpfExtended.SourceGeneration/Templates/CodeTemplate.cs b/WpfExtended.SourceGeneration/Templates/CodeTemplate.cs
deleted file mode 100644
index 2c08335..0000000
--- a/WpfExtended.SourceGeneration/Templates/CodeTemplate.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace System.Extensions.Templates
-{
- internal abstract class CodeTemplate : AbstractTemplate
- {
- }
-}
diff --git a/WpfExtended.SourceGeneration/Templates/ConstructorTemplate.cs b/WpfExtended.SourceGeneration/Templates/ConstructorTemplate.cs
deleted file mode 100644
index a333abc..0000000
--- a/WpfExtended.SourceGeneration/Templates/ConstructorTemplate.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-using System.Collections.Generic;
-
-namespace System.Extensions.Templates
-{
- internal sealed class ConstructorTemplate : AbstractTemplate
- {
- public List Modifiers { get; } = new List();
- public List Arguments { get; } = new List();
- public string Type { get; set; }
- public string Body { get; set; }
- public string Base { get; set; }
-
- public ConstructorTemplate WithType(string type)
- {
- this.Type = type;
- return this;
- }
- public ConstructorTemplate WithBody(string body)
- {
- this.Body = body;
- return this;
- }
- public ConstructorTemplate WithBase(string b)
- {
- this.Base = b;
- return this;
- }
- public ConstructorTemplate WithModifiers(params Modifier[] modifiers)
- {
- this.Modifiers.Clear();
- this.Modifiers.AddRange(modifiers);
- return this;
- }
- public ConstructorTemplate WithArguments(params ArgumentTemplate[] argumentTemplates)
- {
- this.Arguments.Clear();
- this.Arguments.AddRange(argumentTemplates);
- return this;
- }
- public ConstructorTemplate WithArgument(ArgumentTemplate argumentTemplate)
- {
- this.Arguments.Add(argumentTemplate);
- return this;
- }
-
- public override void Generate(CodeWriter codeWriter)
- {
- foreach(var modifier in this.Modifiers)
- {
- codeWriter
- .Append(modifier)
- .Append(' ');
- }
-
- codeWriter
- .Append(this.Type)
- .Append('(');
- for (int i = 0; i < this.Arguments.Count; i++)
- {
- var argument = this.Arguments[i];
- codeWriter.Append(argument);
- if (i < this.Arguments.Count - 1)
- {
- codeWriter.Append(", ");
- }
- }
-
- codeWriter.Append(')');
- if (!string.IsNullOrEmpty(this.Base))
- {
- codeWriter
- .Append(" : base(")
- .Append(this.Base)
- .Append(')');
- }
-
- codeWriter.BeginCodeBlock()
- .Append(this.Body)
- .EndCodeBlock();
- }
- }
-}
diff --git a/WpfExtended.SourceGeneration/Templates/FieldTemplate.cs b/WpfExtended.SourceGeneration/Templates/FieldTemplate.cs
deleted file mode 100644
index 0c5da93..0000000
--- a/WpfExtended.SourceGeneration/Templates/FieldTemplate.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace System.Extensions.Templates
-{
- internal sealed class FieldTemplate : AbstractTemplate
- {
- public List Modifiers { get; } = new List { Modifier.Public };
- public string Name { get; set; }
- public string Value { get; set; }
- public string Type { get; set; }
-
- public FieldTemplate WithName(string name)
- {
- this.Name = name;
- return this;
- }
- public FieldTemplate WithValue(string value)
- {
- this.Value = value;
- return this;
- }
- public FieldTemplate WithModifiers(params Modifier[] modifiers)
- {
- this.Modifiers.Clear();
- this.Modifiers.AddRange(modifiers);
- return this;
- }
- public FieldTemplate WithType(string type)
- {
- this.Type = type;
- return this;
- }
-
- public override void Generate(CodeWriter codeWriter)
- {
- foreach (var modifier in this.Modifiers)
- {
- codeWriter
- .Append(modifier)
- .Append(' ');
- }
-
- codeWriter.Append(this.Type)
- .Append(' ')
- .Append(this.Name)
- .Append(' ')
- .Append('=')
- .Append(' ')
- .Append(this.Value)
- .Append(';');
- }
- }
-}
diff --git a/WpfExtended.SourceGeneration/Templates/GetterTemplate.cs b/WpfExtended.SourceGeneration/Templates/GetterTemplate.cs
deleted file mode 100644
index 78a1bb7..0000000
--- a/WpfExtended.SourceGeneration/Templates/GetterTemplate.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System.Collections.Generic;
-
-namespace System.Extensions.Templates
-{
- internal sealed class GetterTemplate : AbstractTemplate
- {
- public List Modifiers { get; } = new List();
- public CodeTemplate Code { get; set; }
-
- public GetterTemplate WithModifiers(params Modifier[] modifiers)
- {
- this.Modifiers.Clear();
- this.Modifiers.AddRange(modifiers);
- return this;
- }
- public GetterTemplate WithCode(CodeTemplate codeTemplate)
- {
- this.Code = codeTemplate;
- return this;
- }
-
- public override void Generate(CodeWriter codeWriter)
- {
- foreach(var modifier in this.Modifiers)
- {
- codeWriter
- .Append(modifier)
- .Append(' ');
- }
-
- codeWriter.Append("get");
- if (this.Code is SimpleCodeTemplate simpleGetter)
- {
- codeWriter
- .Append(simpleGetter)
- .AppendLine();
- }
- else
- {
- codeWriter
- .BeginCodeBlock()
- .Append(this.Code)
- .EndCodeBlock();
- }
-
- }
- }
-}
diff --git a/WpfExtended.SourceGeneration/Templates/MethodTemplate.cs b/WpfExtended.SourceGeneration/Templates/MethodTemplate.cs
deleted file mode 100644
index 3132947..0000000
--- a/WpfExtended.SourceGeneration/Templates/MethodTemplate.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-using System.Collections.Generic;
-
-namespace System.Extensions.Templates
-{
- internal sealed class MethodTemplate : AbstractTemplate
- {
- public List Modifiers { get; } = new List();
- public List Arguments { get; } = new List();
- public string Name { get; set; }
- public string ReturnType { get; set; }
- public string Body { get; set; }
-
- public MethodTemplate WithModifiers(params Modifier[] modifiers)
- {
- this.Modifiers.Clear();
- this.Modifiers.AddRange(modifiers);
- return this;
- }
- public MethodTemplate WithArguments(params ArgumentTemplate[] argumentTemplates)
- {
- this.Arguments.Clear();
- this.Arguments.AddRange(argumentTemplates);
- return this;
- }
- public MethodTemplate WithName(string name)
- {
- this.Name = name;
- return this;
- }
- public MethodTemplate WithReturnType(string returnType)
- {
- this.ReturnType = returnType;
- return this;
- }
- public MethodTemplate WithBody(string body)
- {
- this.Body = body;
- return this;
- }
-
- public override void Generate(CodeWriter codeWriter)
- {
- foreach(var modifier in this.Modifiers)
- {
- codeWriter
- .Append(modifier)
- .Append(' ');
- }
-
- codeWriter
- .Append(this.ReturnType)
- .Append(' ')
- .Append(this.Name)
- .Append('(');
- for(int i = 0; i < this.Arguments.Count; i++)
- {
- var argument = this.Arguments[i];
- codeWriter.Append(argument);
- if (i < this.Arguments.Count - 1)
- {
- codeWriter.Append(", ");
- }
- }
-
- codeWriter
- .Append(')')
- .BeginCodeBlock()
- .Append(this.Body)
- .EndCodeBlock();
- }
- }
-}
\ No newline at end of file
diff --git a/WpfExtended.SourceGeneration/Templates/Modifiers.cs b/WpfExtended.SourceGeneration/Templates/Modifiers.cs
deleted file mode 100644
index bf1d0e6..0000000
--- a/WpfExtended.SourceGeneration/Templates/Modifiers.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using System.Collections;
-using System.Collections.Generic;
-
-namespace System.Extensions.Templates
-{
- internal sealed class Modifier : AbstractTemplate
- {
- public static Modifier Protected { get; } = new Modifier { Value = "protected" };
- public static Modifier Readonly { get; } = new Modifier { Value = "readonly" };
- public static Modifier Static { get; } = new Modifier { Value = "static" };
- public static Modifier Public { get; } = new Modifier { Value = "public" };
- public static Modifier Internal { get; } = new Modifier { Value = "internal" };
- public static Modifier Private { get; } = new Modifier { Value = "private" };
- public static Modifier Abstract { get; } = new Modifier { Value = "abstract" };
- public static Modifier Virtual { get; } = new Modifier { Value = "virtual" };
- public static Modifier Partial { get; } = new Modifier { Value = "partial" };
- public static Modifier Sealed { get; } = new Modifier { Value = "sealed" };
-
- public string Value { get; private set; }
-
- private Modifier()
- {
- }
-
- public override void Generate(CodeWriter codeWriter)
- {
- codeWriter.Append(this.Value);
- }
-
- public static bool TryParse(string value, out Modifier modifier)
- {
- foreach(var supportedModifier in SupportedModifiers)
- {
- if (supportedModifier.Value.Equals(value, StringComparison.Ordinal))
- {
- modifier = supportedModifier;
- return true;
- }
- }
-
- modifier = null;
- return false;
- }
-
- public static Modifier Parse(string value)
- {
- if (TryParse(value, out var modifier))
- {
- return modifier;
- }
-
- throw new InvalidOperationException($"Could not find a modifier with value {value}");
- }
-
- private static IEnumerable SupportedModifiers { get; } = new List { Protected, Readonly, Static, Internal, Public, Private, Abstract, Virtual, Partial, Sealed };
- }
-}
diff --git a/WpfExtended.SourceGeneration/Templates/NamespaceTemplate.cs b/WpfExtended.SourceGeneration/Templates/NamespaceTemplate.cs
deleted file mode 100644
index b131ea1..0000000
--- a/WpfExtended.SourceGeneration/Templates/NamespaceTemplate.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace System.Extensions.Templates
-{
- internal sealed class NamespaceTemplate : AbstractTemplate
- {
- public string Namespace { get; set; }
- public NamespaceTemplate WithNamespace(string n)
- {
- this.Namespace = n;
- return this;
- }
-
- public override void Generate(CodeWriter codeWriter)
- {
- codeWriter
- .Append("namespace ")
- .Append(this.Namespace);
- }
- }
-}
diff --git a/WpfExtended.SourceGeneration/Templates/PropertyTemplate.cs b/WpfExtended.SourceGeneration/Templates/PropertyTemplate.cs
deleted file mode 100644
index 4639eaf..0000000
--- a/WpfExtended.SourceGeneration/Templates/PropertyTemplate.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-using System.Collections.Generic;
-
-namespace System.Extensions.Templates
-{
- internal sealed class PropertyTemplate : AbstractTemplate
- {
- public static GetterTemplate DefaultGetter = new GetterTemplate()
- .WithCode(
- new SimpleCodeTemplate()
- .WithCode(";"));
- public static SetterTemplate DefaultSetter = new SetterTemplate()
- .WithCode(
- new SimpleCodeTemplate()
- .WithCode(";"));
- public List Modifiers { get; private set; } = new List() { Modifier.Public };
- public GetterTemplate Getter { get; set; } = DefaultGetter;
- public SetterTemplate Setter { get; set; } = DefaultSetter;
- public string Type { get; set; }
- public string Name { get; set; }
-
- public PropertyTemplate WithGetter(GetterTemplate getter)
- {
- this.Getter = getter;
- return this;
- }
- public PropertyTemplate WithSetter(SetterTemplate setter)
- {
- this.Setter = setter;
- return this;
- }
- public PropertyTemplate WithModifiers(params Modifier[] modifiers)
- {
- this.Modifiers.Clear();
- this.Modifiers.AddRange(modifiers);
- return this;
- }
- public PropertyTemplate WithName(string name)
- {
- this.Name = name;
- return this;
- }
- public PropertyTemplate WithType(string type)
- {
- this.Type = type;
- return this;
- }
-
- public override void Generate(CodeWriter codeWriter)
- {
- foreach(var modifier in this.Modifiers)
- {
- codeWriter
- .Append(modifier)
- .Append(' ');
- }
-
- codeWriter
- .Append(this.Type)
- .Append(' ')
- .Append(this.Name)
- .BeginCodeBlock()
- .Append(this.Getter)
- .Append(this.Setter)
- .EndCodeBlock();
- }
- }
-}
diff --git a/WpfExtended.SourceGeneration/Templates/SetterTemplate.cs b/WpfExtended.SourceGeneration/Templates/SetterTemplate.cs
deleted file mode 100644
index 021ef91..0000000
--- a/WpfExtended.SourceGeneration/Templates/SetterTemplate.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System.Collections.Generic;
-
-namespace System.Extensions.Templates
-{
- internal sealed class SetterTemplate : AbstractTemplate
- {
- public List Modifiers { get; } = new List();
- public CodeTemplate Code { get; set; }
-
- public SetterTemplate WithModifiers(params Modifier[] modifiers)
- {
- this.Modifiers.Clear();
- this.Modifiers.AddRange(modifiers);
- return this;
- }
- public SetterTemplate WithCode(CodeTemplate codeTemplate)
- {
- this.Code = codeTemplate;
- return this;
- }
-
- public override void Generate(CodeWriter codeWriter)
- {
- foreach (var modifier in this.Modifiers)
- {
- codeWriter
- .Append(modifier)
- .Append(' ');
- }
-
- codeWriter.Append("set");
- if (this.Code is SimpleCodeTemplate simpleSetter)
- {
- codeWriter
- .Append(simpleSetter)
- .AppendLine();
- }
- else
- {
- codeWriter
- .BeginCodeBlock()
- .Append(this.Code)
- .EndCodeBlock();
- }
-
- }
- }
-}
diff --git a/WpfExtended.SourceGeneration/Templates/SimpleCodeTemplate.cs b/WpfExtended.SourceGeneration/Templates/SimpleCodeTemplate.cs
deleted file mode 100644
index 2aabd4f..0000000
--- a/WpfExtended.SourceGeneration/Templates/SimpleCodeTemplate.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace System.Extensions.Templates
-{
- internal sealed class SimpleCodeTemplate : CodeTemplate
- {
- public string Code { get; set; }
-
- public SimpleCodeTemplate WithCode(string code)
- {
- this.Code = code;
- return this;
- }
-
- public override void Generate(CodeWriter codeWriter)
- {
- codeWriter.Append(this.Code);
- }
- }
-}
diff --git a/WpfExtended.SourceGeneration/Templates/UsingsTemplate.cs b/WpfExtended.SourceGeneration/Templates/UsingsTemplate.cs
deleted file mode 100644
index 425e470..0000000
--- a/WpfExtended.SourceGeneration/Templates/UsingsTemplate.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace System.Extensions.Templates
-{
- internal sealed class UsingsTemplate : AbstractTemplate
- {
- public string Namespace { get; set; }
- public UsingsTemplate WithNamespace(string n)
- {
- this.Namespace = n;
- return this;
- }
-
- public override void Generate(CodeWriter codeWriter)
- {
- codeWriter
- .Append("using ")
- .Append(this.Namespace)
- .Append(';');
- }
- }
-}
diff --git a/WpfExtended.SourceGeneration/WpfExtended.SourceGeneration.csproj b/WpfExtended.SourceGeneration/WpfExtended.SourceGeneration.csproj
index 539fd29..c279ed6 100644
--- a/WpfExtended.SourceGeneration/WpfExtended.SourceGeneration.csproj
+++ b/WpfExtended.SourceGeneration/WpfExtended.SourceGeneration.csproj
@@ -7,16 +7,20 @@
Alexandru Macocian
Source generator library.
LICENSE
- 0.1.2
+ 0.2.0
latest
true
+ true
+ true
+ true
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
+
@@ -29,6 +33,7 @@
+
diff --git a/WpfExtended.Test/WpfExtended.Tests.csproj b/WpfExtended.Test/WpfExtended.Tests.csproj
index 50cd6cf..a580f7d 100644
--- a/WpfExtended.Test/WpfExtended.Tests.csproj
+++ b/WpfExtended.Test/WpfExtended.Tests.csproj
@@ -2,7 +2,7 @@
Library
- net6.0-windows
+ net8.0-windows
true
WpfExtended.Tests.Launcher
diff --git a/WpfExtended/WpfExtended.csproj b/WpfExtended/WpfExtended.csproj
index 4c1f5b1..fe4c231 100644
--- a/WpfExtended/WpfExtended.csproj
+++ b/WpfExtended/WpfExtended.csproj
@@ -1,7 +1,7 @@
- net6.0-windows;netcoreapp3.1
+ net6.0-windows;net7.0-windows;net8.0-windows;netcoreapp3.1
true
true
true