From c2b9ad174935d0bfdb30b0f5d133b2c0f76d9800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mille=20Bostr=C3=B6m?= Date: Fri, 24 Jan 2020 21:25:20 +0100 Subject: [PATCH] Added constructor initializer and xml documentation for arguments (#58) Fix #57 Fix #55 --- .../Class/ConstructorGeneratorTests.cs | 19 +++ .../Integration/ModelClassTests.cs | 17 +++ src/Testura.Code/Builders/MethodBuilder.cs | 109 ++++++++++++++---- src/Testura.Code/Enums.cs | 13 +++ .../Generators/Class/ConstructorGenerator.cs | 13 ++- .../Models/ConstructorInitializer.cs | 33 ++++++ src/Testura.Code/Models/Parameter.cs | 9 +- src/Testura.Code/Testura.Code.csproj | 10 +- 8 files changed, 195 insertions(+), 28 deletions(-) create mode 100644 src/Testura.Code/Models/ConstructorInitializer.cs diff --git a/src/Testura.Code.Tests/Generators/Class/ConstructorGeneratorTests.cs b/src/Testura.Code.Tests/Generators/Class/ConstructorGeneratorTests.cs index ae806fd..8b3420a 100644 --- a/src/Testura.Code.Tests/Generators/Class/ConstructorGeneratorTests.cs +++ b/src/Testura.Code.Tests/Generators/Class/ConstructorGeneratorTests.cs @@ -2,6 +2,7 @@ using NUnit.Framework; using Testura.Code.Generators.Class; using Testura.Code.Generators.Common; +using Testura.Code.Generators.Common.Arguments.ArgumentTypes; using Testura.Code.Models; using Attribute = Testura.Code.Models.Attribute; @@ -16,6 +17,24 @@ public void Constructor_WhenCreatingConstructor_ShouldGenerateCorrectCode() Assert.AreEqual("MyClass(){}", ConstructorGenerator.Create("MyClass", BodyGenerator.Create()).ToString()); } + [Test] + public void Constructor_WhenCreatingConstructorWithThisInitializer_ShouldGenerateCorrectCode() + { + Assert.AreEqual("MyClass():this(){}", ConstructorGenerator.Create("MyClass", BodyGenerator.Create(), constructorInitializer: new ConstructorInitializer(ConstructorInitializerTypes.This, null)).ToString()); + } + + [Test] + public void Constructor_WhenCreatingConstructorWithBaseInitializer_ShouldGenerateCorrectCode() + { + Assert.AreEqual("MyClass():base(){}", ConstructorGenerator.Create("MyClass", BodyGenerator.Create(), constructorInitializer: new ConstructorInitializer(ConstructorInitializerTypes.Base, null)).ToString()); + } + + [Test] + public void Constructor_WhenCreatingConstructorWithBaseInitializerWithArgument_ShouldGenerateCorrectCode() + { + Assert.AreEqual("MyClass():base(\"myText\"){}", ConstructorGenerator.Create("MyClass", BodyGenerator.Create(), constructorInitializer: new ConstructorInitializer(ConstructorInitializerTypes.Base, new List { new ValueArgument("myText")})).ToString()); + } + [Test] public void Constructor_WhenCreatingConstructorWithParameters_ShouldGenerateCorrectCode() { diff --git a/src/Testura.Code.Tests/Integration/ModelClassTests.cs b/src/Testura.Code.Tests/Integration/ModelClassTests.cs index bfba103..3454d18 100644 --- a/src/Testura.Code.Tests/Integration/ModelClassTests.cs +++ b/src/Testura.Code.Tests/Integration/ModelClassTests.cs @@ -132,5 +132,22 @@ public void Test_CreateClassWithRegionWithMultipleMembers() "usingSystem;namespaceModels{publicclassCat{#region MyRegion \nprivatestring_name;privateint_age;publicstringName{get;set;}publicCat(stringname,intage){Name=name;Age=age;}#endregion}}", @class.ToString()); } + + [Test] + public void Test_CreateClassWithMethodThatHaveXmlDocumentation() + { + var classBuilder = new ClassBuilder("Cat", "Models"); + var @class = classBuilder + .WithUsings("System") + .WithMethods(new MethodBuilder("MyMethod") + .WithParameters(new Parameter("MyParameter", typeof(string), xmlDocumentation: "Some documentation")) + .WithSummary("Some summary") + .Build()) + .Build(); + + Assert.AreEqual( + "usingSystem;namespaceModels{publicclassCat{/// \n/// Some summary\n/// \n/// Some documentation\nvoidMyMethod(stringMyParameter){}}}", + @class.ToString()); + } } } diff --git a/src/Testura.Code/Builders/MethodBuilder.cs b/src/Testura.Code/Builders/MethodBuilder.cs index a16932e..600fa73 100644 --- a/src/Testura.Code/Builders/MethodBuilder.cs +++ b/src/Testura.Code/Builders/MethodBuilder.cs @@ -24,6 +24,7 @@ public class MethodBuilder private string _summary; private SyntaxList _attributes; + private readonly List _parameterXmlDocumentation; /// /// Initializes a new instance of the class. @@ -39,6 +40,7 @@ public MethodBuilder(string name) _name = name.Replace(" ", "_"); _parameters = new List(); _modifiers = new List(); + _parameterXmlDocumentation = new List(); _body = BodyGenerator.Create(); } @@ -50,9 +52,16 @@ public MethodBuilder(string name) public MethodBuilder WithParameters(params Parameter[] parameters) { _parameters.Clear(); + _parameterXmlDocumentation.Clear(); + foreach (var parameter in parameters) { _parameters.Add(ParameterGenerator.Create(parameter)); + + if (parameter.XmlDocumentation != null) + { + _parameterXmlDocumentation.Add(parameter); + } } return this; @@ -183,32 +192,21 @@ private MethodDeclarationSyntax BuildXmlComments(MethodDeclarationSyntax method) return method; } - var summary = new List(); - summary.Add(XmlTextNewLine(TriviaList(), "\n", "\n", TriviaList())); - var commentLines = _summary.Split(new[] { "\n" }, StringSplitOptions.None); - for (int n = 0; n < commentLines.Length; n++) - { - var fixedCommentLine = $" {commentLines[n]}"; - if (n != commentLines.Length - 1) - { - fixedCommentLine += "\n"; - } + var content = List(); - summary.Add(XmlTextLiteral(TriviaList(DocumentationCommentExterior("///")), fixedCommentLine, fixedCommentLine, TriviaList())); + content = CreateSummaryDocumentation(content); + + foreach (var parameter in _parameterXmlDocumentation) + { + content = CreateParameterDocumentation(content, parameter); } - summary.Add(XmlTextNewLine(TriviaList(), "\n", "\n", TriviaList())); - summary.Add(XmlTextLiteral(TriviaList(DocumentationCommentExterior("///")), " ", " ", TriviaList())); + content = content.Add(XmlText().WithTextTokens(TokenList(XmlTextNewLine(TriviaList(), "\n", "\n", TriviaList())))); + var trivia = Trivia( DocumentationCommentTrivia( SyntaxKind.SingleLineDocumentationCommentTrivia, - List(new XmlNodeSyntax[] - { - XmlText().WithTextTokens(TokenList(XmlTextLiteral(TriviaList(DocumentationCommentExterior("///")), " ", " ", TriviaList()))), - XmlElement(XmlElementStartTag(XmlName(Identifier("summary"))), XmlElementEndTag(XmlName(Identifier("summary")))) - .WithContent(SingletonList(XmlText().WithTextTokens(TokenList(summary)))), - XmlText().WithTextTokens(TokenList(XmlTextNewLine(TriviaList(), "\n", "\n", TriviaList()))) - }))); + content)); return method.WithLeadingTrivia(trivia); } @@ -231,5 +229,76 @@ private MethodDeclarationSyntax BuildBody(MethodDeclarationSyntax method) return method.WithBody(_body); } + + private SyntaxList CreateSummaryDocumentation(SyntaxList content) + { + var summary = new List(); + summary.Add(XmlTextNewLine(TriviaList(), "\n", "\n", TriviaList())); + var commentLines = _summary.Split(new[] { "\n" }, StringSplitOptions.None); + for (int n = 0; n < commentLines.Length; n++) + { + var fixedCommentLine = $" {commentLines[n]}"; + if (n != commentLines.Length - 1) + { + fixedCommentLine += "\n"; + } + + summary.Add(XmlTextLiteral(TriviaList(DocumentationCommentExterior("///")), fixedCommentLine, fixedCommentLine, TriviaList())); + } + + summary.Add(XmlTextNewLine(TriviaList(), "\n", "\n", TriviaList())); + summary.Add(XmlTextLiteral(TriviaList(DocumentationCommentExterior("///")), " ", " ", TriviaList())); + + return content.AddRange(new List + { + XmlText().WithTextTokens(TokenList(XmlTextLiteral(TriviaList(DocumentationCommentExterior("///")), " ", " ", TriviaList()))), + XmlElement(XmlElementStartTag(XmlName(Identifier("summary"))), XmlElementEndTag(XmlName(Identifier("summary")))) + .WithContent(SingletonList(XmlText().WithTextTokens(TokenList(summary)))), + }); + } + + private SyntaxList CreateParameterDocumentation(SyntaxList content, Parameter parameter) + { + return content.AddRange(new List { + XmlText().WithTextTokens( + TokenList( + new[] + { + XmlTextNewLine( + TriviaList(), + "\n", + "\n", + TriviaList()), + XmlTextLiteral( + TriviaList( + DocumentationCommentExterior("///")), + " ", + " ", + TriviaList()) + })), + XmlExampleElement(SingletonList( + XmlText().WithTextTokens( + TokenList( + XmlTextLiteral( + TriviaList(), + parameter.XmlDocumentation, + parameter.XmlDocumentation, + TriviaList()))))) + .WithStartTag(XmlElementStartTag( + XmlName( + Identifier("param"))) + .WithAttributes( + SingletonList( + XmlNameAttribute( + XmlName( + Identifier(" name")), + Token(SyntaxKind.DoubleQuoteToken), + IdentifierName(parameter.Name), + Token(SyntaxKind.DoubleQuoteToken))))) + .WithEndTag( + XmlElementEndTag( + XmlName( + Identifier("param")))) }); + } } } diff --git a/src/Testura.Code/Enums.cs b/src/Testura.Code/Enums.cs index 494b2f3..d12a358 100644 --- a/src/Testura.Code/Enums.cs +++ b/src/Testura.Code/Enums.cs @@ -201,4 +201,17 @@ internal enum AssertType AreSame, AreNotSame } + + public enum ConstructorInitializerTypes + { + /// + /// Generate constructor initializer with Base + /// + Base, + + /// + /// Generate constructor initializer with This + /// + This + } } diff --git a/src/Testura.Code/Generators/Class/ConstructorGenerator.cs b/src/Testura.Code/Generators/Class/ConstructorGenerator.cs index 28ec052..886d09e 100644 --- a/src/Testura.Code/Generators/Class/ConstructorGenerator.cs +++ b/src/Testura.Code/Generators/Class/ConstructorGenerator.cs @@ -10,7 +10,7 @@ namespace Testura.Code.Generators.Class { /// - /// Provides functionality to generate class constructors + /// Provides functionality to generate class constructors. /// public static class ConstructorGenerator { @@ -28,7 +28,8 @@ public static ConstructorDeclarationSyntax Create( BlockSyntax body, IEnumerable parameters = null, IEnumerable modifiers = null, - IEnumerable attributes = null) + IEnumerable attributes = null, + ConstructorInitializer constructorInitializer = null) { if (className == null) { @@ -42,6 +43,14 @@ public static ConstructorDeclarationSyntax Create( constructor = constructor.WithParameterList(ParameterGenerator.Create(parameters.ToArray())); } + if (constructorInitializer != null) + { + constructor = constructor.WithInitializer( + SyntaxFactory.ConstructorInitializer( + constructorInitializer.ConstructorInitializerType == ConstructorInitializerTypes.Base ? SyntaxKind.BaseConstructorInitializer : SyntaxKind.ThisConstructorInitializer, + constructorInitializer.Arguments == null ? null : ArgumentGenerator.Create(constructorInitializer.Arguments.ToArray()))); + } + if (attributes != null) { constructor = constructor.WithAttributeLists(AttributeGenerator.Create(attributes.ToArray())); diff --git a/src/Testura.Code/Models/ConstructorInitializer.cs b/src/Testura.Code/Models/ConstructorInitializer.cs new file mode 100644 index 0000000..8f46a62 --- /dev/null +++ b/src/Testura.Code/Models/ConstructorInitializer.cs @@ -0,0 +1,33 @@ +using System.Collections; +using System.Collections.Generic; +using Testura.Code.Generators.Common.Arguments.ArgumentTypes; + +namespace Testura.Code.Models +{ + /// + /// Represent a constructor initializer. + /// + public class ConstructorInitializer + { + /// + /// Initializes a new instance of the class. + /// + /// Type of constructor initializer. + /// Constructor initializer arguments. + public ConstructorInitializer(ConstructorInitializerTypes constructorInitializerType, IEnumerable arguments) + { + ConstructorInitializerType = constructorInitializerType; + Arguments = arguments; + } + + /// + /// Gets the constructor initializer type. + /// + public ConstructorInitializerTypes ConstructorInitializerType { get; } + + /// + /// Gets the constructor initializer arguments. + /// + public IEnumerable Arguments { get; } + } +} diff --git a/src/Testura.Code/Models/Parameter.cs b/src/Testura.Code/Models/Parameter.cs index 2dac655..5f02762 100644 --- a/src/Testura.Code/Models/Parameter.cs +++ b/src/Testura.Code/Models/Parameter.cs @@ -13,7 +13,8 @@ public class Parameter /// Name of the paramter. /// Type of the paramter. /// The paramter modifiers. - public Parameter(string name, Type type, ParameterModifiers modifier = ParameterModifiers.None) + /// The parameters xml documentation. + public Parameter(string name, Type type, ParameterModifiers modifier = ParameterModifiers.None, string xmlDocumentation = null) { if (name == null) { @@ -28,6 +29,7 @@ public Parameter(string name, Type type, ParameterModifiers modifier = Parameter Name = name; Type = type; Modifier = modifier; + XmlDocumentation = xmlDocumentation; } /// @@ -44,5 +46,10 @@ public Parameter(string name, Type type, ParameterModifiers modifier = Parameter /// Gets or sets the parameter modifier. /// public ParameterModifiers Modifier { get; set; } + + /// + /// Gets or sets the xml documentation + /// + public string XmlDocumentation { get; } } } diff --git a/src/Testura.Code/Testura.Code.csproj b/src/Testura.Code/Testura.Code.csproj index 735564e..d83a0fa 100644 --- a/src/Testura.Code/Testura.Code.csproj +++ b/src/Testura.Code/Testura.Code.csproj @@ -1,14 +1,14 @@  - 0.14.0 + 0.15.0 Mille Bostrom Copyright 2016 $(ProjectName) Testura.Code is a wrapper around the Roslyn API and used for generation, saving and and compiling C# code. It provides methods and helpers to generate classes, methods, statements and expressions. - Release 0.14.0: - - Added modifiers for get and set in properties - - Added protected modifier + Release 0.15.0: + - Added support for constructor initializer + - Added xml documentation support for arguments @@ -24,7 +24,7 @@ Settings\Testura.ruleset bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml netstandard2.0 - 0.14.0.0 + 0.15.0.0 true