How to write Resharper plugin.

It is not so easy as one can think. The big trouble it is lack of documentation. So you need to research each step yourself. Thus I will try to help you with some description but I can't promise that all information is absolutely correct and optimal. I hope, you drop me a note if you find something wrong in description or you knew how to do something better.
Note: I'm not native english speaker - you are welcome to correct text too.


Plug-in concept

At this time you have easy plugin which can show about dialog and reverse string under cursor.
But how plug-in works? Each plugin could have have many "entry points" but at least must be one. We called it main entry point. Each entry point marked with special attribute. In most cases attribute placed over worker class and in most cases worker class must be iherited with interface which has relation to the attribute.

One exception is the main entry point where is used assembly attribute with XML file as parameter.
ActionsXml attribute must be placed in AssembyInfo.cs and has one parameter - full qualified path to the special xml file.

assembly: ActionsXml("ReSharperPlugIn_About.Actions.xml")

XML file describe menu binding for our actions. In sample XML file the first line tell ReSharper that we want to insert our menu item to existed menu with id "ReSharper" at the last position. We define new menu group (that means - we want to have nested menu) with id "ReSharperPlugIn-DefSample" and menu text "ReSharperPlugIn-Default Sample". This main menu will have one child with text "About Default Sample plugin" and id "ReSharperPlugIn-DefSample.About". Important that the same Id must have our action handler attribute.
    public class AboutAction : IActionHandler

  <insert group-id="ReSharper" position="last">
    <action-group id="ReSharperPlugIn-DefSample" text="ReSharperPlugIn-Default Sample">
      <action id="ReSharperPlugIn-DefSample.About" text="About Default Sample plugin"/>
      <!-- to break up elements use <separator/> -->

Some usefull menu Ids:
  • "ReSharper" - main ReSharper menu.
  • "ReSharper.Navigate" - main ReSharper menu section Navigate.
  • "ReSharper.Refactor" - main ReSharper menu section Refactor.
  • "VS#Code Window" - Context menu from code source window.
  • "VS#Solution" - solution explorer Solution menu
  • "VS#Item" - solution explorer Item menu
  • "VS#Project" - solution explorer Project menu

As you can seen we have in our first project one action which could be started in any time and marked with ActionHandler attribute. And we have one context action marked with ContextAction attribute.
  [ContextAction(Name = "ReverseString", Description = "Reverses a string", Group = "C#")]
  public class ReverseStringAction : ContextActionBase //IContextAction
Context actions could be started under some conditions from visual studio source editor. Condition could be defined in function:
    public override bool IsAvailable(IUserDataHolder cache)

In our sample you can see the menu item "Reverses a string" in context menu under some string in source window.
We try to summarize some usefull attributes later.

Code entry attributes

I try to look into the samples which attributes these used.

public class ComplexityAnalysisDaemonStage : IDaemonStage

[DaemonStage(StagesBefore = new[] { typeof (LanguageSpecificDaemonStage) })]
public class ErrorElementHighlighting : PsiDaemonStageBase

public abstract class PsiDaemonStageBase : IDaemonStage

[OptionsPage(PID, "Complexity Analysis", typeof(CyclomaticComplexityThemedIcons.ComplexityOptionPage), ParentId = CodeInspectionPage.PID)]
public class ComplexityAnalysisOptionPage : AStackPanelOptionsPage

[SettingsKey(typeof(CodeInspectionSettings), "Complexity Analysis")]
public class ComplexityAnalysisSettings

[StaticSeverityHighlighting(Severity.WARNING, "CSharpInfo")]
public class ComplexityWarning : IHighlighting

[ToolWindowDescriptor(Text = "Type Interface",  ProductNeutralId = "2EBEA6DE-578A-4234-A782-54F4F09B61D5",  VisibilityPersistenceScope = ToolWindowVisibilityPersistenceScope.Solution,   Type = ToolWindowType.MultiInstance)]
public class TypeInterfaceToolWindowDescriptor : ToolWindowDescriptor {

public class TypeInterfaceToolWindowRegistrar

[GeneratorElementProvider("Dispose", typeof(CSharpLanguage))]
internal class CSharpDisposableFieldProvider : GeneratorProviderBase<CSharpGeneratorContext>

[GeneratorBuilder("Dispose", typeof(CSharpLanguage))]
internal class CSharpDisposeBuilder : GeneratorBuilderBase<CSharpGeneratorContext>

public class GenerateDisposeItemProvider : IGenerateActionProvider

[Macro("LiveTemplatesMacro.CurrentFilePath", // macro name should be unique among all other macros, it's recommended to prefix it with your plugin name to achieve that
 ShortDescription = "Current file path", // description of the macro to be shown in the list of macros
 LongDescription="Evaluates current file path" // long description of the macro to be shown in the area below the list
public class CurrentFilePathMacro : IMacro

public class MakeMethodGenericWorkflowProvider : IRefactoringWorkflowProvider

[Language(typeof (CSharpLanguage))]
public class CSharpPowerToyRefactoringsLanguageService : PowerToyRefactoringsLanguageService

[Language(typeof (PsiLanguage))]
internal class PsiVisualElementFactory : IVisualElementFactory

public class PsiFileStructureExplorer : IFileStructureExplorer

internal class PsiCodeCompletionContextProvider : CodeCompletionContextProviderBase

public class PsiGotoFileMemberProvider : IGotoFileMemberProvider

internal class PsiSearcherFactory : IDomainSpecificSearcherFactory

[ProjectFileType(typeof (PsiProjectFileType))]
public class PsiCodeFormatterFactory : IPsiCodeFormatterFactory

public class ReformatCode : ICodeCleanupModule

Additional information you can find here
Copyright disclaimer: Pay attention that project license related to the source code.
Any use of pictures or text from documetation part without the express permission of project moderator is forbidden.

Last edited Oct 25, 2012 at 6:24 PM by AlexNek, version 14


No comments yet.