Prerequisites: VS2017 and ReSharper 2018.1.0

When I started to work on a compiled ReSharper extension aka plugin, I quickly realized how hard it is to set up an initial infrastructure. Reasons:

  1. Official DevGuide is great, but it is often a step behind.
  2. Existing plugins on GitHub contain too much stuff in them, and it is hard to extract relevant pieces, especially when you are new to it.
  3. Lack of my own experience in building NuGet packages.

I would like to share an absolutely minimal ReSharper plugin template with tests. I created it using dotnet new template engine. Template source code is here.

This post does not pretend to be an ultimate guide. On the contrary, it contains 20% of the knowledge you need to get started, and the remained 80% are links to useful information.

When the plugin is installed into a Visual Studio, it checks if a class name contains ‘Foo’ and displays a warning:

Getting started

dotnet new --install ReSharper.MinimalPluginTemplate

The plugin should appear in the template list:

To generate a project, using the installed template use:

dotnet new ReSharper.MinimalPlugin --name ReSharper.MyPlugin --output <Path_To_Folder>

--name defines an output name.

Important! Name must contain at least one dot. Otherwise, you might experience difficulties when installing the plugin into a Visual Studio. Throughout this guide, I’ll use ReSharper.MyPlugin name.

--output specifies the destination folder. E.g. c:\temp\MyPlugin

Open ReSharper.MyPlugin\src\ReSharper.MyPlugin.sln, compile and run tests in ReSharper:

Yes, that’s it. A fully functional plugin with tests. The plugin is not yet installed into a Visual Studio, but we’ll get there.

Now, when your initial curiosity is satisfied, let me explain how it works and why everything is done that way.

Folders layout

ReSharper plugin should follow folder layout convention described here

The layout looks weird, but I don’t recommend to deviate from it, especially in the beginning. src contains plugin code. test contains test code. Visual Studio solution lives in src/ReSharper.MyPlugin.sln.

src/ReSharper.MyPlugin.csproj

/src/ClassNameAnalyzer.cs and /src/ClassNameWarning.cs

ClassNameAnalyzer checks if a class name contains Foo. It targets a class declaration, as dictated by its base class and ElementProblemAnalyzer attribute. If analysis finds that class name contains ‘Foo’, it’ll assign ClassNameWarning to a corresponding piece of code:

if (element.DeclaredName.Contains("Foo"))
    {
        consumer.AddHighlighting(new ClassNameWarning(element, element.NameIdentifier.GetDocumentRange()));
    }

As a result, you’ll see this:

and a sidebar mark:

Read more: ElementProblemAnalyzer and Highlighting

test/src/ReSharper.MyPlugin.Tests.csproj

ReSharper test SDK allows you to tests real integration with Visual Studio. These tests are slow, but they are much faster than installing a plugin and restarting a Visual Studio.

test/src/TestEnvironment.cs

Contains bootstrap code needed by ReSharper to run tests. It uses NUnit setup test fixture. The bootstrap code must be executed first. It is done by moving TestEnvironment the global namespace. This way NUnit will execute TestEnvironment prior to any other tests.

test/data/nuget.config

ReSharper simply needs it to run test properly. Read here why.

test/src/ClassNameAnalyzerTests.cs

ReSharper test framework runs plugin against input files and compares an output with gold files .

Let’s look at this test:

[Test]
public void TestFoo()
{
    DoNamedTest2();
}

When ReSharper test runner makes a call to DoNamedTest2(), it looks for a test/data/Foo.cs and compares it against test/data/Foo.cs.gold

/src/MinimalReSharperPlugin/Install

src/Install/pack.bat produces NuGet package, which is required to install your plugin into a Visual Studio or upload it to the ReSharper gallery

plugin.nuspec contains NuGet specification for the package.

One tricky thing, you need to know about, is the Wave dependency. plugin.nuspec contains this:

<dependencies>
    <dependency id="Wave" version="[12.0]" />
</dependencies>

“Wave” is a “virtual” package. Your extension will not be visible in ReSharper Extension manager without it. But why “12.0”? You can find the wave version in your SDK’s dependencies:

It worth mentioning, that ReSharper Platform is not backward compatible. It means 2 things:

  1. As a plugin developer, you have an access to all the API, JetBrains devs do.
  2. When a new version of ReSharper comes out, you need to update a plugin SDK, fix all the potential breakages, and put your plugin a new ‘wave’ so it becomes available on the ReSharper Gallery. I saw attempts to automate this. For example here nuke build script uses GetWaveVersion to extract wave version from project dependency.

Installing plugin

Make a NuGet package:

ReSharper plugins are distributed in a form of NuGet packages. To make a NuGet package do this:

  1. Build Release version of the plugin.
  2. Run src\ReSharper.MyPlugin\Install\pack.bat. You’ll see this warning: It is ok: ReSharper expects this NuGet package structure.

Step #2 will produce ReSharper.MinimalPlugin.nupkg inside src\ReSharper.MyPlugin\Install. If you want to change the package name and other parameters, edit src\ReSharper.MyPlugin\Install\plugin.nuspec. Note, that name must be unique within the ReSharper gallery and contain at least one dot.

Install ReSharper into Visual studio hive

When developing plugins it is recommended to use an experimental instance of Visual Studio aka hive. To install ReSharper into a hive version of Visual Studio do this:

Assuming that ReSharer is already installed into non-hive version:

  1. Run ReSharper 2018.1.0 installation.
  2. Click options: .
  3. Click on Install into experimental instance.
  4. Type Plugins.

Install the plugin into a hive version of Visual Studio.

  1. Check if src\ReSharper.MyPlugin\Install\StartHiveVS.bat refers to an existing path and run it.
  2. Click ReSharper->Extension Manager->Options.
  3. Add a new source. It must point to the directory which contains ReSharper.MyPlugin.0.0.0.1.nupkg.
  4. Click Save.
  5. Your plugin should be in Extension Manger’s list: .
  6. Install it.
  7. Create C# console application, add a class named ‘Foo’ and check if the warning appears.

Copy plugin on build (optional)

You don’t need to install your plugin every time you make a change. Once installed you can setup copy-on-build. To do this go to ReSharper.MyPlugin.csproj and uncomment:

<!-- Uncomment it to enable copy-on-build
    <Import Project=".\install\CopyToReSharperInstallationsDirectory.targets" />
-->

Note, if a hive instance is running, you’ll get a copy error. I find this approach annoying, but it can be helpful if your plugin does not have tests.

Getting help

Enjoy!