예제 #1
0
        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);
        }
예제 #2
0
        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);
        }
예제 #3
0
        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);
        }