Как реализовать массовые действия в ReSharper 10?
Я пытаюсь получить общий обзор о том, как реализовать плагины для ReSharper. Я смог разработать очень простой плагин, развернуть его и использовать для выполнения желаемых действий.
Однако я хотел изменить свой плагин для поддержки режима массовых действий. Поэтому я сначала следовал официальным рекомендациям ReSharper, которые на данный момент устарели и действительны только для v8.0. Затем я обнаружил эту проблему, и стало ясно, что массовые действия еще не задокументированы должным образом. Тем не менее, я смог собрать "что-то" вместе, основываясь на сообщении групп Google, на которое ссылается проблема github.
Я хотел реализовать что-то действительно простое и глупое, просто подтверждение концепции, поэтому я декомпилировал некоторые сборки ReSharper и скопировал реализацию ToggleVarFix
в мое решение. Я изменил его в соответствии с публикацией в группах Google и произвел функциональное изменение - оригинальная версия заменяет явный тип на 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;
}
}
}