1

我想大致了解如何为 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;
        }
    }
}
4

0 回答 0