Alpha implementation of dependency property source generator.

Testing project for source generator
This commit is contained in:
Alexandru Macocian
2021-04-06 10:40:06 +02:00
parent 127cfd749f
commit 7d3c2fbb9b
26 changed files with 1163 additions and 3 deletions
@@ -0,0 +1,11 @@
using System.Windows.Controls;
using System.Windows.Extensions;
namespace WpfExtended.SourceGeneration.Tests
{
public partial class Class1 : UserControl
{
[GenerateDependencyProperty]
public int someF;
}
}
@@ -0,0 +1,10 @@
namespace WpfExtended.SourceGeneration.Tests
{
public class Launcher
{
public static int Main()
{
return 0;
}
}
}
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<LangVersion>preview</LangVersion>
<OutputType>Exe</OutputType>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>obj</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\WpfExtended.SourceGeneration\WpfExtended.SourceGeneration.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
</Project>
+143
View File
@@ -0,0 +1,143 @@
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;
}
}
}
}
@@ -0,0 +1,186 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Extensions.Templates;
namespace System.Extensions
{
[Generator]
public class DependencyPropertyGenerator : ISourceGenerator
{
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)
{
// Register a syntax receiver that will be created for each generation pass
context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
}
public void Execute(GeneratorExecutionContext context)
{
var attributeText = GenerateDependencyPropertyAttribute.GenerateString();
context.AddSource("GenerateDependencyPropertyAttribute.g", attributeText);
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)
{
return;
}
var fieldSymbols = new List<IFieldSymbol>();
foreach (var field in receiver.CandidateFields)
{
var model = compilation.GetSemanticModel(field.SyntaxTree);
foreach (var variable in field.Declaration.Variables)
{
var fieldSymbol = model.GetDeclaredSymbol(variable) as IFieldSymbol;
if (fieldSymbol.GetAttributes().Any(ad => ad.AttributeClass.Equals(attributeSymbol, SymbolEqualityComparer.Default)))
{
fieldSymbols.Add(fieldSymbol);
}
}
}
foreach (var group in fieldSymbols.GroupBy(f => f.ContainingType, SymbolEqualityComparer.IncludeNullability))
{
var classSource = ProcessClass((INamedTypeSymbol)group.Key, attributeSymbol, group.ToList());
context.AddSource($"{group.Key.Name}.DependencyPropertyGenerator.g.cs", classSource);
}
}
private static string ProcessClass(INamedTypeSymbol classSymbol, INamedTypeSymbol attributeSymbol, List<IFieldSymbol> fields)
{
if (!classSymbol.ContainingSymbol.Equals(classSymbol.ContainingNamespace, SymbolEqualityComparer.Default))
{
return null; //TODO: issue a diagnostic that it must be top level
}
var namespaceName = classSymbol.ContainingNamespace.ToDisplayString();
var source = new ClassTemplate()
.WithUsings(
new UsingsTemplate()
.WithNamespace("System"),
new UsingsTemplate()
.WithNamespace("System.Windows"))
.WithNamespace(
new NamespaceTemplate()
.WithNamespace(namespaceName))
.WithModifiers(Modifier.Public, Modifier.Partial)
.WithName(classSymbol.Name);
foreach (var fieldSymbol in fields)
{
ProcessField(source, classSymbol, attributeSymbol, fieldSymbol);
}
return source.GenerateString();
}
private static void ProcessField(ClassTemplate source, INamedTypeSymbol classSymbol, INamedTypeSymbol attributeSymbol, IFieldSymbol fieldSymbol)
{
var fieldName = fieldSymbol.Name;
var fieldType = fieldSymbol.Type;
string propertyName = GenerateName(fieldName);
if (propertyName.Length == 0 || propertyName == fieldName)
{
//TODO: issue a diagnostic that we can't process this field
return;
}
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);
}
else
{
return char.ToUpper(fieldName.First()) + fieldName.Substring(1);
}
}
internal class SyntaxReceiver : ISyntaxReceiver
{
public List<FieldDeclarationSyntax> CandidateFields { get; } = new List<FieldDeclarationSyntax>();
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
if (syntaxNode is FieldDeclarationSyntax fieldDeclarationSyntax &&
fieldDeclarationSyntax.AttributeLists.Count > 0)
{
this.CandidateFields.Add(fieldDeclarationSyntax);
}
}
}
}
}
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Macocian Alexandru Victor
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,7 @@
namespace System.Extensions.Templates
{
internal abstract class AbstractTemplate
{
public abstract void Generate(CodeWriter codeWriter);
}
}
@@ -0,0 +1,28 @@
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);
}
}
}
@@ -0,0 +1,21 @@
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(']');
}
}
}
@@ -0,0 +1,185 @@
using System.Collections.Generic;
namespace System.Extensions.Templates
{
internal sealed class ClassTemplate : AbstractTemplate
{
public List<AttributeTemplate> Attributes { get; } = new List<AttributeTemplate>();
public List<UsingsTemplate> Usings { get; } = new List<UsingsTemplate>();
public List<Modifier> Modifiers { get; } = new List<Modifier>();
public List<PropertyTemplate> Properties { get; } = new List<PropertyTemplate>();
public List<FieldTemplate> Fields { get; } = new List<FieldTemplate>();
public List<MethodTemplate> Methods { get; } = new List<MethodTemplate>();
public List<ConstructorTemplate> Constructors { get; } = new List<ConstructorTemplate>();
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 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();
}
}
}
@@ -0,0 +1,30 @@
using System.Collections.Generic;
namespace System.Extensions.Templates
{
internal sealed class CodeBlockTemplate : CodeTemplate
{
public List<string> Lines { get; set; } = new List<string>();
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);
}
}
}
}
@@ -0,0 +1,6 @@
namespace System.Extensions.Templates
{
internal abstract class CodeTemplate : AbstractTemplate
{
}
}
@@ -0,0 +1,82 @@
using System.Collections.Generic;
namespace System.Extensions.Templates
{
internal sealed class ConstructorTemplate : AbstractTemplate
{
public List<Modifier> Modifiers { get; } = new List<Modifier>();
public List<ArgumentTemplate> Arguments { get; } = new List<ArgumentTemplate>();
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();
}
}
}
@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace System.Extensions.Templates
{
internal sealed class FieldTemplate : AbstractTemplate
{
public List<Modifier> Modifiers { get; } = new List<Modifier> { 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(';');
}
}
}
@@ -0,0 +1,48 @@
using System.Collections.Generic;
namespace System.Extensions.Templates
{
internal sealed class GetterTemplate : AbstractTemplate
{
public List<Modifier> Modifiers { get; } = new List<Modifier>();
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();
}
}
}
}
@@ -0,0 +1,72 @@
using System.Collections.Generic;
namespace System.Extensions.Templates
{
internal sealed class MethodTemplate : AbstractTemplate
{
public List<Modifier> Modifiers { get; } = new List<Modifier>();
public List<ArgumentTemplate> Arguments { get; } = new List<ArgumentTemplate>();
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();
}
}
}
@@ -0,0 +1,26 @@
namespace System.Extensions.Templates
{
internal sealed class Modifier : AbstractTemplate
{
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);
}
}
}
@@ -0,0 +1,19 @@
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);
}
}
}
@@ -0,0 +1,67 @@
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<Modifier> Modifiers { get; private set; } = new List<Modifier>() { 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();
}
}
}
@@ -0,0 +1,48 @@
using System.Collections.Generic;
namespace System.Extensions.Templates
{
internal sealed class SetterTemplate : AbstractTemplate
{
public List<Modifier> Modifiers { get; } = new List<Modifier>();
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();
}
}
}
}
@@ -0,0 +1,18 @@
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);
}
}
}
@@ -0,0 +1,20 @@
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(';');
}
}
}
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<Authors>Alexandru Macocian</Authors>
<Description>Source generator library.</Description>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<Version>0.1</Version>
<LangVersion>latest</LangVersion>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2" PrivateAssets="all">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.9.0" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<None Include="LICENSE">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>
</Project>
+1 -1
View File
@@ -11,7 +11,7 @@ namespace WpfExtended.Tests
[STAThread]
public static int Main()
{
{
return Instance.Run();
}
+13 -1
View File
@@ -5,7 +5,11 @@ VisualStudioVersion = 16.0.30804.86
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WpfExtended", "WpfExtended\WpfExtended.csproj", "{E822AE0D-5EA1-4C61-A1DC-76CF73C8A524}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfExtended.Tests", "WpfExtended.Test\WpfExtended.Tests.csproj", "{48D2B8E6-F906-40D8-952E-B7B567D76A9B}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WpfExtended.Tests", "WpfExtended.Test\WpfExtended.Tests.csproj", "{48D2B8E6-F906-40D8-952E-B7B567D76A9B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfExtended.SourceGeneration", "WpfExtended.SourceGeneration\WpfExtended.SourceGeneration.csproj", "{BB890187-DF49-4E58-AE41-09C40F541292}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfExtended.SourceGeneration.Tests", "WpfExtended.SourceGeneration.Tests\WpfExtended.SourceGeneration.Tests.csproj", "{BDD41E4C-3F5D-4734-8BD2-62B444E4845D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -21,6 +25,14 @@ Global
{48D2B8E6-F906-40D8-952E-B7B567D76A9B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{48D2B8E6-F906-40D8-952E-B7B567D76A9B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{48D2B8E6-F906-40D8-952E-B7B567D76A9B}.Release|Any CPU.Build.0 = Release|Any CPU
{BB890187-DF49-4E58-AE41-09C40F541292}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BB890187-DF49-4E58-AE41-09C40F541292}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BB890187-DF49-4E58-AE41-09C40F541292}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BB890187-DF49-4E58-AE41-09C40F541292}.Release|Any CPU.Build.0 = Release|Any CPU
{BDD41E4C-3F5D-4734-8BD2-62B444E4845D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BDD41E4C-3F5D-4734-8BD2-62B444E4845D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BDD41E4C-3F5D-4734-8BD2-62B444E4845D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BDD41E4C-3F5D-4734-8BD2-62B444E4845D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+1 -1
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0-windows</TargetFramework>
<TargetFrameworks>net5.0-windows;netcoreapp3.1</TargetFrameworks>
<UseWPF>true</UseWPF>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>