I just got into a personal project and I would like to be able to use the Roslyn API to insert instructions / methods between class members (So methods / instructions)
I am currently able to retrieve the different child nodes like here: Namespace> Class> Fields / Methods
But I would like to know how I can insert code between fields / methods and not replace code.
(At the location of the red lines below for example)
EDIT: After some more research I found that I can use InsertTokensBefore
, InsertNodesBefore
or InsertTriviaBefore
but I don't understand how to parse my functions (actually in text/string format) to the needed parameters.
You may use the Microsoft.CodeAnalysis.CSharp.SyntaxFactory to build a Microsoft.CodeAnalysis.SyntaxNode.
To get the syntax factory code that you need in order to create new syntax trees, have a look at the fantastic RoslynQuoter. There, you can put in the C# program that you want to build via the syntax factory API:
public class RoslynClass
{
public static int RoslynMethod(int left, int right)
{
return left + right;
}
}
Check all options to your preference, and Get Roslyn API calls to generate this code!, which you can (partially) use in your production code.
Here I list an example of a source code refactoring, which inserts the method public static int RoslynMethod(int left, int right)
before/after either a field or a method:
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
namespace RoslynTool
{
[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(MethodInserter))]
[Shared]
internal sealed class MethodInserter : CodeRefactoringProvider
{
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
{
SyntaxNode root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
SyntaxNode node = root.FindNode(context.Span);
if (node is FieldDeclarationSyntax or MethodDeclarationSyntax)
{
SyntaxNode newNode = CreateMethodNode();
context.RegisterRefactoring(CodeAction.Create("Insert method before", ct => InsertBeforeAsync(context.Document, node, newNode, ct)));
context.RegisterRefactoring(CodeAction.Create("Insert method after", ct => InsertAfterAsync(context.Document, node, newNode, ct)));
}
}
private static MemberDeclarationSyntax CreateMethodNode()
{
return SyntaxFactory.MethodDeclaration(
SyntaxFactory.PredefinedType(
SyntaxFactory.Token(SyntaxKind.IntKeyword)),
SyntaxFactory.Identifier("RoslynMethod"))
.WithModifiers(
SyntaxFactory.TokenList(new[] {
SyntaxFactory.Token(SyntaxKind.PublicKeyword),
SyntaxFactory.Token(SyntaxKind.StaticKeyword)}))
.WithParameterList(
SyntaxFactory.ParameterList(
SyntaxFactory.SeparatedList<ParameterSyntax>(new SyntaxNodeOrToken[] {
SyntaxFactory.Parameter(
SyntaxFactory.Identifier("left"))
.WithType(
SyntaxFactory.PredefinedType(
SyntaxFactory.Token(SyntaxKind.IntKeyword))),
SyntaxFactory.Token(SyntaxKind.CommaToken),
SyntaxFactory.Parameter(
SyntaxFactory.Identifier("right"))
.WithType(
SyntaxFactory.PredefinedType(
SyntaxFactory.Token(SyntaxKind.IntKeyword)))})))
.WithBody(
SyntaxFactory.Block(
SyntaxFactory.SingletonList<StatementSyntax>(
SyntaxFactory.ReturnStatement(
SyntaxFactory.BinaryExpression(
SyntaxKind.AddExpression,
SyntaxFactory.IdentifierName("left"),
SyntaxFactory.IdentifierName("right"))))));
}
private static async Task<Document> InsertBeforeAsync(Document document, SyntaxNode location, SyntaxNode newNode, CancellationToken cancellationToken)
{
DocumentEditor documentEditor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);
documentEditor.InsertBefore(location, newNode);
return documentEditor.GetChangedDocument();
}
private static async Task<Document> InsertAfterAsync(Document document, SyntaxNode location, SyntaxNode newNode, CancellationToken cancellationToken)
{
DocumentEditor documentEditor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);
documentEditor.InsertAfter(location, newNode);
return documentEditor.GetChangedDocument();
}
}
}
Alternatively, the parse methods of the SyntaxFactory could be used to create a new SyntaxNode, but I had some trouble getting whitespaces/trivia right in that example:
namespace RoslynTool
{
internal sealed class MethodInserter : CodeRefactoringProvider
{
private static MemberDeclarationSyntax CreateMethodNode()
{
return SyntaxFactory.ParseMemberDeclaration(@"
public static int RoslynMethod(int left, int right)
{
return left + right;
}
");
}
}
}
I hope I understood your question correctly.