Roslyn Code Action:如何检查预览还是实际执行? [英] Roslyn Code Action: How to check if preview or real execution?

查看:74
本文介绍了Roslyn Code Action:如何检查预览还是实际执行?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在尝试使用Roslyn和Code Action,更具体的代码重构. 感觉很简单,但是我有一个我无法解决的困难.

I am currently experimenting with Roslyn and Code Actions, more specific Code Refactorings. It feels kind of easy, but I have a difficulty I cannot solve.

对代码工作空间执行一次代码操作(作为预览"选项),这样您就可以在单击操作并针对实际工作空间执行操作之前查看实际的更改.

Code actions are executed once against a dummy workspace as a "preview" option, so that you can see the actual changes before you click the action and execute it against the real workspace.

现在我正在处理Roslyn尚未真正完成的某些事情,所以我正在通过EnvDTE进行一些更改.我知道这很糟糕,但是我找不到其他方法.

Now I am dealing with some things Roslyn can't really do (yet), so I am doing some changes via EnvDTE. I know, it's bad, but I couldn't find another way.

所以这里的问题是: 当我将鼠标悬停在代码操作上时,代码将作为预览执行,并且不应更改EnvDTE.只有在真正的执行发生时才能完成这些操作.

So the issue here is: When I hover over my code action, the code gets executed as preview, and it should NOT do the EnvDTE changes. Those should only be done when the real execute happens.

我创建了一个要点,并提供了我的代码的一个小示例.这实际上没有任何意义,但应该显示出我想要实现的目标.通过roslyn进行一些修改,然后通过EnvDTE进行一些更改,例如更改Cursor的位置.但是,当然只有真正的执行.

I have created a gist with a small example of my code. It doesn't really makes sense, but should show what I want to achieve. Do some modifications via roslyn, then do something via EnvDTE, like changing Cursor position. But of course only on the real execution.

无法点击要点的相关部分:

The relevant part for those who can't click the gist:

public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
{
    var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(continueOnCapturedContext: false);
    var node = root.FindNode(context.Span);

    var dec = node as MethodDeclarationSyntax;
    if (dec == null)
        return;

    context.RegisterRefactoring(CodeAction.Create("MyAction", c => DoMyAction(context.Document, dec, c)));
}

private static async Task<Solution> DoMyAction(Document document, MethodDeclarationSyntax method, CancellationToken cancellationToken)
{
    var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken);
    var root = await syntaxTree.GetRootAsync(cancellationToken);

    // some - for the question irrelevant - roslyn changes, like:
    document = document.WithSyntaxRoot(root.ReplaceNode(method, method.WithIdentifier(SyntaxFactory.ParseToken(method.Identifier.Text + "Suffix"))));

    // now the DTE magic
    var preview = false; // <--- TODO: How to check if I am in preview here?
    if (!preview)
    {
        var requestedItem = DTE.Solution.FindProjectItem(document.FilePath);
        var window = requestedItem.Open(Constants.vsViewKindCode);
        window.Activate();

        var position = method.Identifier.GetLocation().GetLineSpan().EndLinePosition;
        var textSelection = (TextSelection) window.Document.Selection;
        textSelection.MoveTo(position.Line, position.Character);
    }

    return document.Project.Solution;
}

推荐答案

我通过在

I've found the solution to my problem by digging deeper and trial and error after Keven Pilch's answer. He bumped me in the right direction.

解决方案是覆盖 ComputePreviewOperationsAsync 和我自己的 GetChangedSolutionAsync 方法CodeAction.

The solution was to override both the ComputePreviewOperationsAsync and the GetChangedSolutionAsync methods in my own CodeAction.

这是我的CustomCodeAction的相关部分,或此处的要点.

Here the relevant part of my CustomCodeAction, or full gist here.

private readonly Func<CancellationToken, bool, Task<Solution>> _createChangedSolution;

protected override async Task<IEnumerable<CodeActionOperation>> ComputePreviewOperationsAsync(CancellationToken cancellationToken)
{
    const bool isPreview = true;
    // Content copied from http://sourceroslyn.io/#Microsoft.CodeAnalysis.Workspaces/CodeActions/CodeAction.cs,81b0a0866b894b0e,references
    var changedSolution = await GetChangedSolutionWithPreviewAsync(cancellationToken, isPreview).ConfigureAwait(false);
    if (changedSolution == null)
        return null;

    return new CodeActionOperation[] { new ApplyChangesOperation(changedSolution) };
}

protected override Task<Solution> GetChangedSolutionAsync(CancellationToken cancellationToken)
{
    const bool isPreview = false;
    return GetChangedSolutionWithPreviewAsync(cancellationToken, isPreview);
}

protected virtual Task<Solution> GetChangedSolutionWithPreviewAsync(CancellationToken cancellationToken, bool isPreview)
{
    return _createChangedSolution(cancellationToken, isPreview);
}

创建动作的代码与之相似,只是添加了bool,然后我可以对其进行检查:

The code to create the action stays quite similar, except the bool is added and I can check against it then:

public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
{
    // [...]

    context.RegisterRefactoring(CustomCodeAction.Create("MyAction",
        (c, isPreview) => DoMyAction(context.Document, dec, c, isPreview)));
}

private static async Task<Solution> DoMyAction(Document document, MethodDeclarationSyntax method, CancellationToken cancellationToken, bool isPreview)
{
    // some - for the question irrelevant - roslyn changes, like:
    // [...]

    // now the DTE magic
    if (!isPreview)
    {
        // [...]
    }

    return document.Project.Solution;
}

为什么要选择这两个? ComputePreviewOperationsAsync调用普通的ComputeOperationsAsync,内部调用ComputeOperationsAsync.此计算执行GetChangedSolutionAsync.因此,两种方式-预览与否-都以GetChangedSolutionAsync结尾.这就是我真正想要的,调用相同的代码,得到非常相似的解决方案,但是给出bool标志(无论它是否为预览).
因此,我编写了自己的GetChangedSolutionWithPreviewAsync,以代替使用.我使用自定义的Get函数覆盖了默认的GetChangedSolutionAsync,然后使用完全自定义的主体覆盖了ComputePreviewOperationsAsync.我没有调用默认的ComputeOperationsAsync,而是复制了该函数的代码,并将其修改为使用我的GetChangedSolutionWithPreviewAsync.
听起来很复杂,但是我想上面的代码应该能很好地解释它.

Why those two?
The ComputePreviewOperationsAsync calls the the normal ComputeOperationsAsync, which internally calls ComputeOperationsAsync. This computation executes GetChangedSolutionAsync. So both ways - preview and not - end up at GetChangedSolutionAsync. That's what I actually want, calling the same code, getting a very similar solution, but giving a bool flag if it is preview or not too.
So I've written my own GetChangedSolutionWithPreviewAsync which I use instead. I have overriden the default GetChangedSolutionAsync using my custom Get function, and then ComputePreviewOperationsAsync with a fully customized body. Instead of calling ComputeOperationsAsync, which the default one does, I've copied the code of that function, and modified it to use my GetChangedSolutionWithPreviewAsync instead.
Sounds rather complicated in written from, but I guess the code above should explain it quite well.

希望这对其他人有帮助.

Hope this helps other people.

这篇关于Roslyn Code Action:如何检查预览还是实际执行?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆