小猪退后Add a parameter to a method with a Roslyn CodeFixProvider,
我正在努力创建一个CodeFixProvider
,以确保所有异步方法都采用CancellationToken
:
//Before Code Fix:
public async Task Example(){}
//After Code Fix
public async Task Example(CancellationToken token){}
我可以将参数添加到方法中,但我必须使用Type.FullName
。相反,我想将System.Threading
的using语句添加到类文件的顶部,这样该方法就不需要使用完整的命名空间。换句话说:
// What I have thus far:
public class AClass{
public async Task Example(System.Threading.CancellationToken token){}
}
// What I want:
using System.Threading;
public class AClass{
public async Task Example(CancellationToken token){}
}
如何将使用语句添加到Document
?
我已经尝试了几种方法,但是当我替换SyntaxTree
中的多个节点时它的接缝正在丢失(因为树在每次更改时都是不可变的并且重建)。
我能够让它部分地使用下面的代码,但这只有在填充CompilationUnitSyntax.Using
属性时才有效,而在命名空间之后使用语句时则不然。这也依赖于文件中至少有一个using
语句。
有更好的方法吗?
private async Task<Document> HaveMethodTakeACancellationTokenParameter(
Document document, SyntaxNode syntaxNode, CancellationToken cancellationToken)
{
var syntaxTree =
(await document.GetSyntaxTreeAsync(cancellationToken))
.GetRoot(cancellationToken);
var method = syntaxNode as MethodDeclarationSyntax;
#region Add Parameter
var newParameter =
SyntaxFactory.Parameter(
SyntaxFactory.Identifier("cancellationToken")
)
.WithType(
SyntaxFactory.ParseTypeName(
typeof(CancellationToken).FullName));
var updatedMethod = method.AddParameterListParameters(newParameter);
syntaxTree = syntaxTree.ReplaceNode(method, updatedMethod);
#endregion
#region Add Using Statements
var compilation =
syntaxTree as CompilationUnitSyntax;
var systemThreadingUsingName =
SyntaxFactory.QualifiedName(
SyntaxFactory.IdentifierName("System"),
SyntaxFactory.IdentifierName("Threading"));
if (compilation.Usings.All(u => u.Name.GetText().ToString() != typeof(CancellationToken).Namespace))
{
syntaxTree = syntaxTree.InsertNodesAfter(compilation.Usings.Last(), new[]
{
SyntaxFactory.UsingDirective(
systemThreadingUsingName)
});
}
#endregion
return document.WithSyntaxRoot(syntaxTree);
}
答案 0 :(得分:1)
选项是使用注释标记所有方法,添加using语句,使用注释查找方法,更改所有方法并删除注释。
如你所说,树是不可变的,但修改期间注释不会丢失。所以你需要以下内容:
var annotation = new SyntaxAnnotation();
var newRoot = root.ReplaceNode(
method,
method.WithAdditionalAnnotations(annotation));
newRoot = AddUsing(newRoot);
method = newRoot.GetAnnotatedNodes(annotation).First();
var newMethod = ChangeParameters(method);
newRoot = root.ReplaceNode(method, newMethod.WithoutAnnotations(annotation));
全面实施:
private async Task<Document> HaveMethodTakeACancellationTokenParameter(
Document document, SyntaxNode syntaxNode, CancellationToken cancellationToken)
{
var method = syntaxNode as MethodDeclarationSyntax;
var cancellationTokenParameter =
SyntaxFactory.Parameter(
SyntaxFactory.Identifier("cancellationToken")
)
.WithType(
SyntaxFactory.ParseTypeName(
typeof(CancellationToken).Name));
var root =
(await document.GetSyntaxTreeAsync(cancellationToken))
.GetRoot(cancellationToken);
var annotation = new SyntaxAnnotation();
var newRoot = root.ReplaceNode(
method,
method.WithAdditionalAnnotations(annotation));
#region Add Using Statements
var systemThreadingUsingStatement =
SyntaxFactory.UsingDirective(
SyntaxFactory.QualifiedName(
SyntaxFactory.IdentifierName("System"),
SyntaxFactory.IdentifierName("Threading")));
var compilation =
newRoot as CompilationUnitSyntax;
if (null == compilation)
{
newRoot =
newRoot.InsertNodesBefore(
newRoot.ChildNodes().First(),
new[] {systemThreadingUsingStatement});
}
else if (compilation.Usings.All(u => u.Name.GetText().ToString() != typeof(CancellationToken).Namespace))
{
newRoot =
newRoot.InsertNodesAfter(compilation.Usings.Last(),
new[]{ systemThreadingUsingStatement });
}
#endregion
method = (MethodDeclarationSyntax)newRoot.GetAnnotatedNodes(annotation).First();
var updatedMethod = method.AddParameterListParameters(cancellationTokenParameter);
newRoot = newRoot.ReplaceNode(method, updatedMethod.WithoutAnnotations(annotation));
return document.WithSyntaxRoot(newRoot);
}