Roslyn CodeFixProvider:应用代码修复后移动插入符号
Roslyn CodeFixProvider: Move caret after applying code fix
我已经实现了一个自定义 CodeFixProvider,它为成员添加了一些 XML 文档。
示例:
public void MyMethod() { }
将转换为
/// <summary></summary>
public void MyMethod() { }
CodeFixProvider是这样实现的:
public class MyCodeFixProvider : CodeFixProvider
{
...
public async override Task RegisterCodeFixesAsync(CodeFixContext context)
{
await Task.Run(() =>
{
Diagnostics diagnostics = context.Diagnostics.First();
CodeAction codeFix = CodeAction.Create("Title", c => CreateXmlDocs(...));
context.RegisterCodeFix(codeFix, diagnostics);
}
).ConfigureAwait(false);
}
...
}
一切正常。
现在我想添加一些额外的功能:应用代码修复后,脱字符号应该移到空的摘要标签内。
我发现 Microsoft.CodeAnalysis.Features NuGet 包中包含 DocumentNavigationOperation class。这 class 应该能够将插入符号移动到指定位置。但是我找不到任何关于如何使用它的说明 class。如果我从我的 CreateXmlDocs 方法内部调用它,则会抛出异常:
Navigation must be performed on the foreground thread.
代码:
private static async Task<Solution> CreateXmlDocs()
{
...
new DocumentNavigationOperation(newDocument.Id, 42)
.Apply(newDocument.Project.Solution.Workspace, cancellationToken);
...
}
我不确定在我的 CreateXmlDocs 方法中使用此 class 是否有意义,因为在调用 DocumentNavigationOperation 时 Visual Studio 尚未应用此方法中创建的新解决方案.
有人知道应用代码修复后移动插入符号的解决方案吗?
好的,与此同时我找到了解决方案。
需要自定义 CodeAction
才能正常工作:
internal class NavigateAfterCodeChangeAction : CodeAction
{
private readonly Func<CancellationToken, Task<Solution>> codeChangeOperation;
private readonly Func<Solution, CancellationToken, Task<NavigationTarget>> navigationTargetCalculation;
public NavigateAfterCodeChangeAction(
string title,
Func<CancellationToken, Task<Solution>> codeChangeOperation,
Func<Solution, CancellationToken, Task<NavigationTarget>> navigationTargetCalculation)
{
this.Title = title;
this.codeChangeOperation = codeChangeOperation;
this.navigationTargetCalculation = navigationTargetCalculation;
}
public override string Title { get; }
protected override async Task<IEnumerable<CodeActionOperation>> ComputeOperationsAsync(CancellationToken cancellationToken)
{
var operations = new List<CodeActionOperation>();
Solution changedSolution = await this.codeChangeOperation(cancellationToken);
NavigationTarget navigationTarget = await this.navigationTargetCalculation(changedSolution, cancellationToken);
operations.Add(new ApplyChangesOperation(changedSolution));
if (navigationTarget != null)
{
operations.Add(new DocumentNavigationOperation(navigationTarget.DocumentId, navigationTarget.Position));
}
return operations;
}
}
internal class NavigationTarget
{
public NavigationTarget(DocumentId documentId, int position)
{
this.DocumentId = documentId;
this.Position = position;
}
public DocumentId DocumentId { get; }
public int Position { get; }
}
可以在CodeFixProvider
中使用新的CodeAction
代替CodeAction.Create()
:
public class MyCodeFixProvider : CodeFixProvider
{
...
public async override Task RegisterCodeFixesAsync(CodeFixContext context)
{
await Task.Run(() =>
{
Diagnostics diagnostics = context.Diagnostics.First();
CodeAction codeFix = new NavigateAfterCodeChangeAction(
"Title",
c => CreateXmlDocs(...)
(s, c) => CalculateNavigationTarget(context.Document));
context.RegisterCodeFix(codeFix, diagnostics);
}
).ConfigureAwait(false);
}
private static NavigationTarget CalculateNavigationTarget(Document doc)
{
// Calculate the navigation target here...
// Example: Navigate to position 42 of the document
return new NavigationTarget(doc.Id, 42);
}
...
}
我已经实现了一个自定义 CodeFixProvider,它为成员添加了一些 XML 文档。
示例:
public void MyMethod() { }
将转换为
/// <summary></summary>
public void MyMethod() { }
CodeFixProvider是这样实现的:
public class MyCodeFixProvider : CodeFixProvider
{
...
public async override Task RegisterCodeFixesAsync(CodeFixContext context)
{
await Task.Run(() =>
{
Diagnostics diagnostics = context.Diagnostics.First();
CodeAction codeFix = CodeAction.Create("Title", c => CreateXmlDocs(...));
context.RegisterCodeFix(codeFix, diagnostics);
}
).ConfigureAwait(false);
}
...
}
一切正常。
现在我想添加一些额外的功能:应用代码修复后,脱字符号应该移到空的摘要标签内。
我发现 Microsoft.CodeAnalysis.Features NuGet 包中包含 DocumentNavigationOperation class。这 class 应该能够将插入符号移动到指定位置。但是我找不到任何关于如何使用它的说明 class。如果我从我的 CreateXmlDocs 方法内部调用它,则会抛出异常:
Navigation must be performed on the foreground thread.
代码:
private static async Task<Solution> CreateXmlDocs()
{
...
new DocumentNavigationOperation(newDocument.Id, 42)
.Apply(newDocument.Project.Solution.Workspace, cancellationToken);
...
}
我不确定在我的 CreateXmlDocs 方法中使用此 class 是否有意义,因为在调用 DocumentNavigationOperation 时 Visual Studio 尚未应用此方法中创建的新解决方案.
有人知道应用代码修复后移动插入符号的解决方案吗?
好的,与此同时我找到了解决方案。
需要自定义 CodeAction
才能正常工作:
internal class NavigateAfterCodeChangeAction : CodeAction
{
private readonly Func<CancellationToken, Task<Solution>> codeChangeOperation;
private readonly Func<Solution, CancellationToken, Task<NavigationTarget>> navigationTargetCalculation;
public NavigateAfterCodeChangeAction(
string title,
Func<CancellationToken, Task<Solution>> codeChangeOperation,
Func<Solution, CancellationToken, Task<NavigationTarget>> navigationTargetCalculation)
{
this.Title = title;
this.codeChangeOperation = codeChangeOperation;
this.navigationTargetCalculation = navigationTargetCalculation;
}
public override string Title { get; }
protected override async Task<IEnumerable<CodeActionOperation>> ComputeOperationsAsync(CancellationToken cancellationToken)
{
var operations = new List<CodeActionOperation>();
Solution changedSolution = await this.codeChangeOperation(cancellationToken);
NavigationTarget navigationTarget = await this.navigationTargetCalculation(changedSolution, cancellationToken);
operations.Add(new ApplyChangesOperation(changedSolution));
if (navigationTarget != null)
{
operations.Add(new DocumentNavigationOperation(navigationTarget.DocumentId, navigationTarget.Position));
}
return operations;
}
}
internal class NavigationTarget
{
public NavigationTarget(DocumentId documentId, int position)
{
this.DocumentId = documentId;
this.Position = position;
}
public DocumentId DocumentId { get; }
public int Position { get; }
}
可以在CodeFixProvider
中使用新的CodeAction
代替CodeAction.Create()
:
public class MyCodeFixProvider : CodeFixProvider
{
...
public async override Task RegisterCodeFixesAsync(CodeFixContext context)
{
await Task.Run(() =>
{
Diagnostics diagnostics = context.Diagnostics.First();
CodeAction codeFix = new NavigateAfterCodeChangeAction(
"Title",
c => CreateXmlDocs(...)
(s, c) => CalculateNavigationTarget(context.Document));
context.RegisterCodeFix(codeFix, diagnostics);
}
).ConfigureAwait(false);
}
private static NavigationTarget CalculateNavigationTarget(Document doc)
{
// Calculate the navigation target here...
// Example: Navigate to position 42 of the document
return new NavigationTarget(doc.Id, 42);
}
...
}