0

I'm trying to format code automatically to improve readability using Roslyn.

This is what my method looks like:

public SyntaxNode GetFormatedNode(SyntaxNode node)
{
    var workspace = new AdhocWorkspace();
    OptionSet options = workspace.Options;
    options = options
        .WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInMethods, true)
        .WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInProperties, true)
        .WithChangedOption(new OptionKey(FormattingOptions.UseTabs, LanguageNames.CSharp), true)
        .WithChangedOption(new OptionKey(FormattingOptions.IndentationSize, LanguageNames.CSharp), 4);

    return Formatter.Format(node, workspace, options);
}

For example, if I have this code:

class Example
{
    void Method()
    {
        Column col = new Column { ID = "mySuperColumnID", Code = "mySuperColumnCode" };
    }
}

I would like to automatically format it to:

class Example
{
    void Method()
    {
        Column col = new Column
        {
            ID = "mySuperColumnID",
            Code = "mySuperColumnCode"
        };
    }
}

2 Answers 2

1

As far as I know, none of the values from CSharpFormattingOptions will be helpful there. One could expect NewLineForMembersInObjectInit to be suited for your case but it purposely doesn't format when it's on a single line.

However, we can still use a CSharpSyntaxRewriter to put your object initializer on multiple lines first, then let Formatter.Format indent it properly.

Here's an example implementation of such a CSharpSyntaxRewriter :

public class Rewriter : CSharpSyntaxRewriter
{
    public override SyntaxNode VisitObjectCreationExpression(ObjectCreationExpressionSyntax node)
    {
        if (node.Initializer != null)
        {
            node = node.ReplaceNode(node.Initializer, MakeMultiLine(node.Initializer));
        }
        
        return base.VisitObjectCreationExpression(node);
    }


    private InitializerExpressionSyntax MakeMultiLine(InitializerExpressionSyntax initializer)
    {
        var initializerLineSpan = initializer.GetLocation().GetLineSpan();
        
        // Add new lines only if the whole initializer is on a single line
        if (initializerLineSpan.StartLinePosition.Line == initializerLineSpan.EndLinePosition.Line)
        {
            SyntaxTrivia newLineTrivia = SyntaxFactory.EndOfLine(Environment.NewLine);
            initializer = initializer.ReplaceToken(initializer.OpenBraceToken, initializer.OpenBraceToken.WithLeadingTrivia(newLineTrivia));
            initializer = initializer.ReplaceNodes(initializer.Expressions, (x, y) => y.WithLeadingTrivia(newLineTrivia));
            initializer = initializer.ReplaceToken(initializer.CloseBraceToken, initializer.CloseBraceToken.WithLeadingTrivia(newLineTrivia));
        }

        return initializer;
    }
}

To use it before the code gets formatted, we can change your GetFormatedNode to the following :

public static SyntaxNode GetFormatedNode(SyntaxNode node)
{
    node = new Rewriter().Visit(node);
    var workspace = new AdhocWorkspace();
    OptionSet options = workspace.Options;
    // From what I know, object initializers are always indented no matter the options
    return Formatter.Format(node, workspace, options);
}

Note that, while MakeMultiLine could also be used to rewrite collection initializers in the same way, the above implementation only affects object initializers. Here are two variations :

  • If you want to rewrite collection and object initializers, override VisitInitializerExpression instead of VisitObjectCreationExpression in Rewriter as follows :
public override SyntaxNode VisitInitializerExpression(InitializerExpressionSyntax node)
{
    return base.VisitInitializerExpression(MakeMultiLine(node));
}
  • If you want to rewrite collection initializers only, override VisitArrayCreationExpression instead of VisitObjectCreationExpression with this (pretty similar) implementation :
public override SyntaxNode VisitArrayCreationExpression(ArrayCreationExpressionSyntax node)
{
    if (node.Initializer != null)
    {
        node = node.ReplaceNode(node.Initializer, MakeMultiLine(node.Initializer));
    }
    
    return base.VisitArrayCreationExpression(node);
}

Sadly, at the time of writing, unlike for object initializers, Formatter.Format(...) will not indent collection initializers meaning you'd have to do all the work "manually" there (possibly with an other CSharpSyntaxRewriter)

Sign up to request clarification or add additional context in comments.

Comments

-1

Try this,

 options = options
        .WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInObjectCollectionArrayInitializers, true)
        .WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInMethods, true)
        .WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInProperties, true)

1 Comment

The initializer is still in a single line even with this

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.