Creating semantic code with roslyn

We are trying to figure out how to generate code with Roslyn. I'm not talking about something like CSharpSyntaxTree.ParseText , which will take several lines and convert them to AST. Instead, I would like to somehow build my model (pseudocode):

  • Create file as compilation unit
  • Add MyClass class to file
  • Add DoSomething Method to MyClass
  • Install the DoSomething body in the same way as System.Linq.Expressions

We recently discovered Microsoft.CodeAnalysis.CSharp.SyntaxFactory , and that seemed promising. However, obviously, we must add the little things ourselves.

After creating a tree with SyntaxFactory.CompilationUnit() and adding some elements back and forth, the output of ToFullString() is just a bunch of text that is neither readable nor compiled (like no braces). Did we miss something while creating text from the model?

EDIT:

When using workspaces, you can set parameters that affect the behavior in the form of spaces:

 public string Generate (CompilationNode rootNode) { var cw = new CustomWorkspace(); cw.Options.WithChangedOption (CSharpFormattingOptions.IndentBraces, true); var formattedCode = Formatter.Format (CreateFile(rootNode), cw); return formattedCode.ToFullString(); } 

This already gives the best result. Can someone confirm this as a good solution or is it more likely a hack?

One problem remains. We want to create an auto-property, we are currently using SF.AccessorDeclaration , but it skips the semicolon when converting to a full string.

+8
c # roslyn
source share
2 answers

Basically you have to add block definitions, then Roslyn processes the little things for you while you use Formatter (as you already wrote)

Here is an example of a simple class that is generated correctly, without having to specify any little things

 var consoleWriteLine = Syntax.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, Syntax.IdentifierName("Console"), name: Syntax.IdentifierName("WriteLine")); var arguments = Syntax.ArgumentList ( Syntax.SeparatedList ( new[] { Syntax.Argument ( Syntax.LiteralExpression ( SyntaxKind.StringLiteralExpression, Syntax.Literal (@"""Goodbye everyone!""", "Goodbye everyone!"))) })); var consoleWriteLineStatement = Syntax.ExpressionStatement (Syntax.InvocationExpression (consoleWriteLine, arguments)); var voidType = Syntax.ParseTypeName ("void"); var method = Syntax.MethodDeclaration (voidType, "Method").WithBody (Syntax.Block(consoleWriteLineStatement)); var intType = Syntax.ParseTypeName ("int"); var getterBody = Syntax.ReturnStatement (Syntax.DefaultExpression (intType)); var getter = Syntax.AccessorDeclaration (SyntaxKind.GetAccessorDeclaration, Syntax.Block (getterBody)); var property = Syntax.PropertyDeclaration (intType, "Property").WithAccessorList (Syntax.AccessorList (Syntax.SingletonList (getter))); var @class = Syntax.ClassDeclaration ("MyClass").WithMembers (Syntax.List (new MemberDeclarationSyntax[] { method, property })); var cw = new CustomWorkspace(); cw.Options.WithChangedOption (CSharpFormattingOptions.IndentBraces, true); var formattedCode = Formatter.Format (@class, cw); Console.WriteLine (formattedCode.ToFullString()); 

Note. Syntax = Microsoft.CodeAnalysis.CSharp.SyntaxFactory

This generates the following class definition:

 class MyClass { void Method() { Console.WriteLine("Goodbye everyone!"); } int Property { get { return default(int); } } } 

Seems beautiful.

+10
source share

I had the same problem and found that CustomWorkspace is now called AdhocWorkspace .

 var cw = new AdhocWorkspace(); cw.Options.WithChangedOption(CSharpFormattingOptions.IndentBraces, true); var formatter = Formatter.Format(cu, cw); StringBuilder sb = new StringBuilder(); using (StringWriter writer = new StringWriter(sb)) { formatter.WriteTo(writer); } var code = sb.ToString(); 
0
source share

All Articles