我想大致了解如何为 ReSharper 实现插件。我能够开发一个非常简单的插件,部署它,并使用它来执行我想要的操作。
但是,我想更改我的插件以支持批量操作模式。因此,我首先遵循了官方的 ReSharper 指南,这些指南已经过时,目前仅对 v8.0 有效。然后我发现了这个github 问题,很明显批量操作尚未正确记录。但是,我能够根据github 问题中引用的google 群组帖子将“某些东西”放在一起。
我想实现一些非常简单和愚蠢的东西,只是一个概念证明,因此我反编译了一些 ReSharper 程序集并将实现复制ToggleVarFix
到我的解决方案中。我根据 google groups post 对其进行了修改并进行了功能更改-原始版本将显式类型替换为var
,我的自定义版本将其替换为abstract
. 这当然没有意义,我只是想看看批量操作的实际效果。
令我惊讶的是,我的插件仍然只能在单一修复模式下工作,即我能够将显式类型更改为abstract
关键字以进行单个声明。但是,一旦我尝试以批量模式(对于文件或项目)调用我的操作,显式类型就会更改为var
,我真的不知道为什么会发生这种情况以及为什么不会发生这种情况abstract
。下图更好地解释了我对插件当前工作方式的不了解:
有人可以审查我的插件的实现,并可能提出我做错了什么吗?下面是我的代码,与原始版本相比,唯一的“功能”更改ToggleVarFix
应该被注释掉declaration.SetVar();
,取而代之的是一个稍微修改过的版本,abstract
用于替换显式类型而不是var
原始版本。
using System;
using JetBrains.Annotations;
using JetBrains.Application.Progress;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Daemon.CSharp.CodeCleanup.CodeStyles;
using JetBrains.ReSharper.Daemon.CSharp.Errors;
using JetBrains.ReSharper.Feature.Services.Bulk;
using JetBrains.ReSharper.Feature.Services.Bulk.Actions;
using JetBrains.ReSharper.Feature.Services.CodeCleanup;
using JetBrains.ReSharper.Feature.Services.QuickFixes;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.CSharp;
using JetBrains.ReSharper.Psi.CSharp.CodeStyle;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using JetBrains.ReSharper.Psi.ExtensionsAPI.Tree;
using JetBrains.ReSharper.Psi.Tree;
using JetBrains.ReSharper.Resources.Shell;
using JetBrains.TextControl;
using JetBrains.Util;
namespace RSharpExtensionSample
{
[QuickFix]
public class CustomToggleVarFix : QuickFixBase, ICodeCleanupAction
{
private static readonly Key InstanceKey = new Key("CustomToggleVarFix");
private readonly ITreeNode m_myDeclaration;
private readonly CodeCleanupProfile m_myCodeCleanupProfile;
private readonly IProjectFile m_myProjectFile;
public override string Text
{
get { return IsConvertingToVar() ? "[Custom] Use 'var'" : "[Custom] Use explicit type"; }
}
public string BulkText
{
get { return Text + " everywhere"; }
}
public FileCollectorInfo FileCollectorInfo
{
get { return new FileCollectorInfo(m_myProjectFile, CSharpLanguage.Instance); }
}
public bool Single
{
get { return false; }
}
public CustomToggleVarFix([NotNull] UseVarOrTypeForBuiltInTypesWarning warning)
: this(warning.Declaration)
{
}
public CustomToggleVarFix([NotNull] UseVarOrTypeElsewhereWarning warning)
: this(warning.Declaration)
{
}
public CustomToggleVarFix([NotNull] UseVarOrTypeForSimpleTypesWarning warning)
: this(warning.Declaration)
{
}
private CustomToggleVarFix([NotNull] ITreeNode node)
{
m_myDeclaration = node;
m_myProjectFile = m_myDeclaration.GetSourceFile().ToProjectFile();
var value = IsConvertingToVar() ? VariableStyle.UseVar : VariableStyle.UseExplicitType;
var component = Shell.Instance.GetComponent<CodeCleanupSettingsComponent>();
m_myCodeCleanupProfile = component.CreateEmptyProfile("Test");
m_myCodeCleanupProfile.SetSetting(ReplaceByVarCodeCleanupModule.DESCRIPTOR, true);
m_myCodeCleanupProfile.SetSetting(ReplaceByVarCodeCleanupModule.OPTIONS, new ReplaceByVarCodeCleanupModule.Options {
ForBuiltInTypes = value,
ForSimpleTypes = value,
ForOtherTypes = value
});
}
protected override Action<ITextControl> ExecutePsiTransaction([NotNull] ISolution solution, [NotNull] IProgressIndicator progress)
{
TryExecuteFor(m_myDeclaration as IMultipleLocalVariableDeclaration);
TryExecuteFor(m_myDeclaration as IForeachVariableDeclaration);
return null;
}
private void TryExecuteFor(IForeachVariableDeclaration declaration)
{
if (declaration == null)
{
return;
}
if (declaration.VarKeyword == null)
{
//declaration.SetVar();
ChangeExplicitTypeToAbstract(declaration);
return;
}
declaration.SetType(declaration.DeclaredElement.Type);
}
private void TryExecuteFor(IMultipleLocalVariableDeclaration declaration)
{
if (declaration == null)
{
return;
}
if (declaration.VarKeyword == null)
{
//declaration.SetVar();
ChangeExplicitTypeToAbstract(declaration);
return;
}
var localVariableDeclaration = declaration.Declarators[0] as ILocalVariableDeclaration;
if (localVariableDeclaration == null)
{
return;
}
var declaredElement = localVariableDeclaration.DeclaredElement;
localVariableDeclaration.SetType(declaredElement.Type);
}
private static void ChangeExplicitTypeToAbstract(IMultipleLocalVariableDeclaration declaration)
{
using (WriteLockCookie.Create(declaration.IsPhysical()))
{
ModificationUtil.ReplaceChild(
declaration.TypeDesignator,
JetBrains.ReSharper.Psi.CSharp.Parsing.CSharpTokenType.ABSTRACT_KEYWORD.CreateLeafElement());
}
}
private static void ChangeExplicitTypeToAbstract(IForeachVariableDeclaration declaration)
{
if (declaration.TypeUsage == null) return;
using (WriteLockCookie.Create(declaration.IsPhysical()))
{
ModificationUtil.ReplaceChild(
declaration.TypeUsage,
JetBrains.ReSharper.Psi.CSharp.Parsing.CSharpTokenType.ABSTRACT_KEYWORD.CreateLeafElement());
}
}
public override bool IsAvailable(IUserDataHolder cache)
{
if (!IsAvailableEx()) return false;
cache.PutData(InstanceKey, this);
return true;
}
private bool IsAvailableEx()
{
return CSharpExtensionMethods.IsCSharp3Supported(m_myDeclaration)
&& (IsAvailableFor(m_myDeclaration as IMultipleLocalVariableDeclaration)
|| IsAvailableFor(m_myDeclaration as IForeachVariableDeclaration));
}
private bool IsAvailableFor(IForeachVariableDeclaration declaration)
{
return declaration != null;
}
private bool IsAvailableFor(IMultipleLocalVariableDeclaration declaration)
{
return declaration != null && declaration.Declarators.Count == 1;
}
private bool IsConvertingToVar()
{
var multipleLocalVariableDeclaration = m_myDeclaration as IMultipleLocalVariableDeclaration;
if (multipleLocalVariableDeclaration != null)
{
return multipleLocalVariableDeclaration.VarKeyword == null;
}
var foreachVariableDeclaration = m_myDeclaration as IForeachVariableDeclaration;
if (foreachVariableDeclaration != null)
{
return foreachVariableDeclaration.VarKeyword == null;
}
throw new InvalidOperationException();
}
public CodeCleanupProfile GetCodeCleanupProfile()
{
return m_myCodeCleanupProfile;
}
}
}