private static async Task <Document> GenerateToString( Document document, ClassDeclarationSyntax classDeclarationSyntax, IList <PropertyDeclarationSyntax> properties, CancellationToken cancellationToken) { InvocationExpressionSyntax GenerateNameOfCall(SyntaxToken identifier) => ExpressionGenerationHelper.Invocation( "nameof", SyntaxHelpers.ArgumentFromIdentifier(identifier)); var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); if (properties.Count == 0) { return(document); } var firstEl = Enumerable.Repeat( (InterpolatedStringContentSyntax)SyntaxFactory.Interpolation( GenerateNameOfCall(classDeclarationSyntax.Identifier)), 1); var interpolatedStringExpression = InterpolatedStringGenerationHelper.InterpolatedString( firstEl.Concat( properties.SelectMany(p => new InterpolatedStringContentSyntax[] { InterpolatedStringGenerationHelper.Text($" "), SyntaxFactory.Interpolation(GenerateNameOfCall(p.Identifier)), InterpolatedStringGenerationHelper.Text($"="), SyntaxFactory.Interpolation(SyntaxFactory.IdentifierName(p.Identifier.WithoutTrivia())) }))); var methodExpression = MethodGenerationHelper.Builder(ToStringMethodName) .Modifiers(Modifiers.Public, Modifiers.Override) .ReturnType(Types.String) .ArrowBody(ExpressionGenerationHelper.Arrow(interpolatedStringExpression)) .Build(); var previousToString = ClassDeclarationSyntaxAnalysis.GetMembers <MethodDeclarationSyntax>( classDeclarationSyntax) .FirstOrDefault(m => m.Identifier.ValueText.Equals(ToStringMethodName)); var newClassDeclaration = previousToString != null? classDeclarationSyntax.ReplaceNode(previousToString, methodExpression) : classDeclarationSyntax.AddMembers(methodExpression); var root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false); var newRoot = root.ReplaceNode(classDeclarationSyntax, newClassDeclaration); var newDocument = document.WithSyntaxRoot(newRoot); return(newDocument); }
private static async Task <Document> GenerateCreateMethod( Document document, ConstructorDeclarationSyntax constructor, CancellationToken cancellationToken) { var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var classDeclaration = constructor .Parent.FirstAncestorOrSelf <ClassDeclarationSyntax>(); if (classDeclaration == null) { return(document); } var classType = SyntaxFactory.IdentifierName( classDeclaration.Identifier.WithoutTrivia()); var createObjectExpression = ExpressionGenerationHelper.CreateObject( classType, constructor.ParameterList.ToArgumentArray()); var createMethodExpression = MethodGenerationHelper.Builder(CreateMethodName) .Modifiers(Modifiers.Public, Modifiers.Static) .Parameters(constructor.ParameterList.Parameters.ToArray()) .ReturnType(SyntaxFactory.IdentifierName(classDeclaration.Identifier.WithoutTrivia())) .ArrowBody(ExpressionGenerationHelper.Arrow(createObjectExpression)) .Build(); // TODO: validate argument types as well ? var previousCreate = ClassDeclarationSyntaxAnalysis.GetMembers <MethodDeclarationSyntax>(classDeclaration) .FirstOrDefault(m => m.Identifier.ValueText.Equals(CreateMethodName) && m.ParameterList.Parameters.Count == createMethodExpression.ParameterList.Parameters.Count); // TODO: align with "with" method generation var newClassDeclaration = classDeclaration; if (previousCreate == null) { createMethodExpression = createMethodExpression .NormalizeWhitespace(elasticTrivia: false) .WithLeadingTrivia( Settings.EndOfLine, SyntaxFactory.ElasticSpace) .WithTrailingTrivia(Settings.EndOfLine); newClassDeclaration = newClassDeclaration.InsertNodesAfter(constructor, new[] { createMethodExpression }); } else { createMethodExpression = createMethodExpression .NormalizeWhitespace(elasticTrivia: false) .WithTriviaFrom(previousCreate); newClassDeclaration = newClassDeclaration.ReplaceNode(previousCreate, createMethodExpression); } var root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false); var newRoot = root.ReplaceNode(classDeclaration, newClassDeclaration); var newDocument = document.WithSyntaxRoot(newRoot); return(newDocument); }
private static async Task <Document> GenerateWithMethods( Document document, ClassDeclarationSyntax classDeclarationSyntax, IList <PropertyDeclarationSyntax> properties, int[] propertyToParameterIdx, CancellationToken cancellationToken) { int MappedIdx(int i) { int mappedIdx = propertyToParameterIdx[i]; if (mappedIdx == -1) { throw new ArgumentException($"{nameof(propertyToParameterIdx)} contains invalid mapping"); } return(mappedIdx); } ArgumentSyntax[] ReorderArgumentsToMapping(IReadOnlyList <ArgumentSyntax> arguments) { var result = new ArgumentSyntax[arguments.Count]; for (int i = 0; i < arguments.Count; ++i) { result[MappedIdx(i)] = arguments[i]; } return(result); } T ExecuteWithTempArg <T>(ArgumentSyntax[] args, int argIdx, ArgumentSyntax tempArg, Func <ArgumentSyntax[], T> operation) { var oldArg = args[argIdx]; args[argIdx] = tempArg; var result = operation(args); args[argIdx] = oldArg; return(result); } if (properties.Count != propertyToParameterIdx.Length) { throw new ArgumentException("properties.Count != propertyToParameterIdx.Length"); } var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); if (properties.Count == 0) { return(document); } var classType = SyntaxFactory.IdentifierName(classDeclarationSyntax.Identifier.WithoutTrivia()); var newClassDeclaration = classDeclarationSyntax; var argumentsArray = properties.Select(p => p.ToArgument()).ToArray(); argumentsArray = ReorderArgumentsToMapping(argumentsArray); for (int i = 0; i < properties.Count; ++i) { var p = properties[i]; var id = p.Identifier.WithoutTrivia(); var lcId = SyntaxHelpers.LowercaseIdentifierFirstLetter(id); var methodName = WithRefactoringUtils.MethodName(p.Identifier); var arg = SyntaxHelpers.ArgumentFromIdentifier(lcId); var objectCreation = ExecuteWithTempArg(argumentsArray, MappedIdx(i), arg, args => ExpressionGenerationHelper.CreateObject(classType, args)); var withMethodExpression = MethodGenerationHelper.Builder(methodName) .Modifiers(Modifiers.Public) .Parameters(SyntaxHelpers.Parameter(p.Type, lcId)) .ReturnType(SyntaxFactory.IdentifierName(classDeclarationSyntax.Identifier.WithoutTrivia())) .ArrowBody(ExpressionGenerationHelper.Arrow(objectCreation)) .Build(); var previousWith = ClassDeclarationSyntaxAnalysis.GetMembers <MethodDeclarationSyntax>( newClassDeclaration) .FirstOrDefault(m => m.Identifier.ValueText.Equals(methodName)); if (previousWith == null) { // TODO: Group with members next to each other rather than adding at the end withMethodExpression = withMethodExpression .NormalizeWhitespace(elasticTrivia: false) .WithLeadingTrivia( Settings.EndOfLine, SyntaxFactory.ElasticSpace) .WithTrailingTrivia(Settings.EndOfLine); newClassDeclaration = newClassDeclaration.AddMembers(withMethodExpression); } else { withMethodExpression = withMethodExpression .NormalizeWhitespace(elasticTrivia: false) .WithTriviaFrom(previousWith); newClassDeclaration = newClassDeclaration.ReplaceNode(previousWith, withMethodExpression); } } var root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false); var newRoot = root.ReplaceNode(classDeclarationSyntax, newClassDeclaration); var newDocument = document.WithSyntaxRoot(newRoot); return(newDocument); }