首页 文章

Roslyn CodeFixProvider:应用代码修复后移动插入符号

提问于
浏览
0

我已经实现了一个自定义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类 . 此类应该能够将插入符号移动到指定位置 . 但我找不到如何使用这个类的任何说明 . 如果我从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方法中使用此类是否有意义,因为在调用DocumentNavigationOperation时Visual Studio尚未应用此方法中创建的新解决方案 .

在应用代码修复后,有人知道移动插入符号的解决方案吗?

1 回答

  • 0

    好的,与此同时我找到了解决方案 .

    要使其正常工作,需要自定义 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; }
    
    }
    

    新的 CodeAction 可用于 CodeFixProvider 而不是 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);
      }
    
      ...
    }
    

相关问题