我试图概述如何为ReSharper实现插件。我能够开发一个非常简单的插件,部署它,然后用它来执行我想要的动作。
但是,我想更改我的插件以支持批量操作模式。因此,我首先关注过时的official ReSharper guidelines,目前仅对v8.0有效。然后我发现了这个github issue,很明显,批量操作尚未正确记录。但是,我能够把#34;"一起基于github问题中引用的google groups post。
我想实现一些非常简单和愚蠢的东西,只是一个概念证明,因此我反编译了一些ReSharper程序集并将ToggleVarFix
的实现复制到我的解决方案中。我根据google groups post对其进行了修改并进行了功能更改 - 原始版本替换了var
的显式类型,我的自定义版本将其替换为abstract
。这当然没有意义,我只想看到批量行动的实际效果。
令我惊讶的是,我的插件仍然只能在单一修复模式下工作,即我可以将单一声明的显式类型更改为abstract
关键字。但是,一旦我尝试以批量模式(对于文件或项目)调用我的操作,显式类型将更改为var
,我真的不知道为什么会发生这种情况以及为什么它不是abstract
1}}而不是。下面的图片更好地解释了我的插件目前的工作原理:
有人可以查看我的插件的实现,也许可以提出我做错了吗?以下是我的代码,唯一的"功能"更改与原始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;
}
}
}