Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get autoproperties on one line when generating code with Roslyn?

I have the following partial code which I use to generate datacontracts based on an excel-file we use for customer facing workshops and such.

    private PropertyDeclarationSyntax[] GenerateProperties()
    {
        var props = new List<PropertyDeclarationSyntax>();
        props.Add(SF.PropertyDeclaration(SF.ParseTypeName("IMigrationInformation"), "MigrationInformation")
            .AddModifiers(SF.Token(SyntaxKind.PublicKeyword), SF.Token(SyntaxKind.OverrideKeyword))
            .AddAccessorListAccessors(
                SF.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
                    .WithBody(SF.Block(SF.ReturnStatement(SF.ObjectCreationExpression(SF.ParseTypeName($"{Form.RegistryName}MigrationInformation")))))
            ));
        foreach (var field in Form.AllDataFields().Where(f => f.FieldTypeInfo != null))
        {
            props.Add(SF.PropertyDeclaration(SF.ParseTypeName(field.FieldTypeInfo.BackingType.Name), field.SafeName)
                .AddModifiers(SF.Token(SyntaxKind.PublicKeyword))
                .AddAccessorListAccessors(
                    SF.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).WithSemicolonToken(SF.Token(SyntaxKind.SemicolonToken)),
                    SF.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration).WithSemicolonToken(SF.Token(SyntaxKind.SemicolonToken))
                ));
        }

        return props.ToArray();
    }

The code works surprisingly well with one small snag. The code generated looks like this:

public string VariableName
{
    get;
    set;
}

And I really want it to look like this:

public string VariableName { get; set; }

Does anyone know how to do this, if possible?

like image 850
Christian Wattengård Avatar asked Jun 28 '18 12:06

Christian Wattengård


3 Answers

I got the exact same problem. In my case, I was saving the code to file after converting it to a string something like this:

private void WriteCodeToFile(NamespaceDeclarationSyntax ns)
{
    var codeAsString = ns.NormalizeWhitespace()
                         .ToFullString();

    File.WriteAllText(destFileName, codeAsString);
}

So, in that case, I just solved it byrunning a regex replace on the string, in an extension method:

private static readonly Regex AutoPropRegex = new Regex(@"\s*\{\s*get;\s*set;\s*}\s");

public static string FormatAutoPropertiesOnOneLine(this string str)
{
    return AutoPropRegex.Replace(str, " { get; set; }");
}

And then call it after converting it to string:

        var codeAsString = ns.NormalizeWhitespace()
                         .ToFullString()
                         .FormatAutoPropertiesOnOneLine();

You are not speficying how you do your file-writing step (if any), so if this is not relevant at all in your case, I apologize for not targeting your question 100%. In any case, someone else might benefit from it.

like image 110
audunsol Avatar answered Nov 11 '22 17:11

audunsol


You could implement a CSharpSyntaxRewriter and than apply it to a parent SyntaxNode:

public static class WhitespaceFormatter
{
    public static SyntaxNode NormalizeWhitespacesSingleLineProperties(this SyntaxNode node) => 
        node.NormalizeWhitespace().SingleLineProperties();

    public static SyntaxNode SingleLineProperties(this SyntaxNode node) => new SingleLinePropertyRewriter().Visit(node);

    class SingleLinePropertyRewriter : CSharpSyntaxRewriter
    {
        public override SyntaxNode VisitPropertyDeclaration(PropertyDeclarationSyntax node) =>
            node.NormalizeWhitespace(indentation: "", eol: " ")
                .WithLeadingTrivia(node.GetLeadingTrivia())
                .WithTrailingTrivia(node.GetTrailingTrivia());
    }
}

Use NormalizeWhitespacesSingleLineProperties to apply default whitespaces to everything but property declarations which will be written to a single line. Make sure not to call Format or NormalizeWhitespace() on your node afterwards, because it will again clutter your property declarations.

like image 2
Alex Avatar answered Nov 11 '22 16:11

Alex


As one of simple solution (another way create a nodes and tokens with correct trivias) just use SyntaxNodeExtensions.NormalizeWhitespace(...) for nodes that you want to represent at the one line:

...
foreach (var field in Form.AllDataFields().Where(f => f.FieldTypeInfo != null))
{
    props.Add(SF.PropertyDeclaration(SF.ParseTypeName(field.FieldTypeInfo.BackingType.Name), field.SafeName)
        .AddModifiers(SF.Token(SyntaxKind.PublicKeyword))
        .AddAccessorListAccessors(
            SF.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).WithSemicolonToken(SF.Token(SyntaxKind.SemicolonToken)),
            SF.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration).WithSemicolonToken(SF.Token(SyntaxKind.SemicolonToken))
        ).NormalizeWhitespace(indentation: "", eol: " "));
}
like image 1
George Alexandria Avatar answered Nov 11 '22 16:11

George Alexandria