Roslyn Code Action:如何检查预览或实际执行?

我目前正在尝试使用Roslyn和Code Actions,更具体的Code Refactorings。 这感觉很简单,但我有一个难以理解的问题。

代码操作作为“预览”选项针对虚拟工作区执行一次,以便您可以在单击操作并在实际工作区上执行操作之前查看实际更改。

现在我正在处理Roslyn不能真正做的一些事情,所以我正在通过EnvDTE做一些改变。 我知道,这很糟糕,但我找不到另一种方式。

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

我用我的代码的一个小例子创建了一个要点 。 它没有多大意义,但应该展示我想要实现的目标。 通过roslyn做一些修改,然后通过EnvDTE做一些事情,比如改变光标位置。 但当然只有真正的执行。

那些无法点击要点的人的相关部分:

 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 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; } 

您可以选择覆盖ComputePreviewOperationsAsync以使常规代码的预览具有不同的行为。

在Keven Pilch回答之后,我通过深入挖掘和反复试验找到了解决问题的方法。 他让我朝着正确的方向前进。

解决方案是覆盖我自己的CodeAction中的ComputePreviewOperationsAsyncGetChangedSolutionAsync方法。

这里是我的CustomCodeAction的相关部分,或者全部要点 。

 private readonly Func> _createChangedSolution; protected override async Task> ComputePreviewOperationsAsync(CancellationToken cancellationToken) { const bool isPreview = true; // Content copied from http://source.roslyn.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 GetChangedSolutionAsync(CancellationToken cancellationToken) { const bool isPreview = false; return GetChangedSolutionWithPreviewAsync(cancellationToken, isPreview); } protected virtual Task GetChangedSolutionWithPreviewAsync(CancellationToken cancellationToken, bool isPreview) { return _createChangedSolution(cancellationToken, isPreview); } 

创建动作的代码保持非常相似,除了添加bool ,我可以检查它:

 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 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代替。
写起来相当复杂,但我想上面的代码应该很好地解释它。

希望这有助于其他人。