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