Contents
Introduction
This document exists in order to help anyone who wants to extend ReSharper by writing their own plug-ins. Since there’s very little documentation regarding ReSharper extensibility, I though I’d create a page that I can update with my own findings.
Disclaimer: I am not in any way affiliated with JetBrains, so my knowledge of ReSharper is very much second-hand. As a result, things I show may not always be optimal. Also note that ReSharper API fluctuates between versions, so the examples I show might not compile at all!
Please note: I use the terms ‘add-in’ and ‘plug-in’ interchangeably here, and they both refer to plug-ins for ReSharper, not Visual Studio.
Also note: if you happen to work with/on ReSharper, you are doubly urged to contact me with any inaccuracies you see. After all, I could be getting things fundamentally wrong, and spreading confusion to other people. Not a good thing, as I’m sure you will agree.
Where to Look for Help
TODO
Tools of the Trade
Before we dive into the API, let’s briefly discuss the tools you need to actually developer R# plug-ins. Basically, you need the following:
-
Visual Studio — this one’s pretty obvious, really. You need VS to debug in, but what you debug with is your decision. Let em emphasize: SharpDevelop is fine for plug-in development, and offers certain advantages, such as support for Boo.
-
ReSharper — if you are writing add-ins for youself, get the latest version. If you’re making something commercial, you’ll need to fire up your test rig and set up remote debugging for the machines you want to try it on. If you don’t have that many machines, VMWare is great.
-
Your favourite programming language — all the examples shown here are for the C# part of ReSharper, and are correspondingly written in C#. There’s nothing preventing you from writing ReSharper plug-ins using F#, Boo or C++. In fact, F# might be a really good choice because manipulation of the syntax tree becomes a very monadic affair, and the succinctness of F# lends itself nicely to the task.
Project Set-Up (Important!)
Let me explain a few things regarding the way you set up your development. First, there are two flags that can be passed to devenv.exe that you need to be aware of:
-
/ReSharper.Internal — this flag turns on ReSharper’s internal menu, which is useful for all sorts of debugging. Its main feature, and one you’ll use most, is the ‘Go To Psi Tree’ menu item, which shows you the tree representing the currently edited code. It’s a great way to learn about what code elements are called. So go on – change your VS shortcut to include this flag. Do it now.
-
/ReSharper.Plugin <path to your plugin DLL> — this is what you append to devenv.exe when debugging your plug-in. Yep – your plugin is a regular .Net DLL whose debug target is, guess what, Visual Studio. Using this flag (and I’d also throw in /ReSharer.Internal in there, if I were you) lets you load up and debug your add-in.
What Can You Do?
ReSharper offers many avenues of extensibility that are worth knowing even if, like me, you only choose one particular aspect to extend. Here are some concepts to get your head around.
-
Refactoring — a refactoring is actually a general-purpose term that has nothing to do with ReSharper per se (though maybe the Re does stand for ‘refactor’). Anyways, refactoring is all about changing the program in some useful way, and ReSharper supports changing the source code by turning it into a tree that you can edit and rearrange.
-
Context action — when you move your mouse over a class name, ReSharper offers an option to create a derived type. This option is shown as a lightbulb in the small drop-down list on the left of the editor.
-
Quick fix — this is like a context action, but is shown in response to certain consitions in your code, such as blatant errors, API misuse, and so on. Quick fixes are typically associated with the horizontal bars right next to the vertical scrollbar.
-
Bulb item — a bulb item is an item that gets shown next to the lightbulb. Bulb items can be yellow or red, depending on the severity (i.e., seriousness) of the issue. Bulb items are shown for both context actions and quick fixes.
-
Daemon — a code analyser that runs in the background. Basically, a daemon is a Visitor that visits your codebase periodically. It is useful for highlighting errors or just caching useful information for later use.
-
Highlighting — this is one of the things that, e.g., a daemon can do to indicate an erroneous condition.
-
Live template — a live template is a snippet on steroids. Just like a Visual Studio snippet, a live template can generate code for quick insertion into your app. Unlike a snippet, you can write live template macros that let you use executable code when expanding the templates. When used correctly, this is a very powerful ability that can let you auto-generate complex bits of code.
Tree Manipulation
This section describes various odds and ends related to the manipulation of the code tree. When extending R#, it’s important to be able to traverse the tree correctly, and it’s equally important to know how to manipulate it. What follows below is essentially a set of code snippets with explanations as to what they do.
How Code Elements Are Made
If you’re intent on creating new code elements, you need to use the C# element factory, which is created as follows:
var factory = CSharpElementFactory.GetInstance(provider.PsiModule, true);
To create types, use either TypeFactory or CSharpTypeFactory.
The $ (dollar) Syntax
Quite often, you’ll see that the R# API has methods which take a string followed by a params array. This is similar to the String.Format binding, but is actually a lot smarter, and is the method you should use in order to insert composite statements. Why? Let’s suppose that you’re thinking of creating a variable, and you give the variable name string. Pretty bad, right? Well, if you use String.Format() you’ll probably get a decaration that does call a variable string, which is pretty bad. If you use the dollar syntax, the variable will have the name @string, which is legal.
The dollar syntax is used as follows:
factory.CreateSomething("const string $0;", identifier);
Determine Whether You’re on a Particular Element
Quite often, refactorings (e.g., via a context action) are only applicable if the caret is in a particular position. For example, say your refactoring only works when the caret is on a method name. To check for this condition, use the CSharpContextActionDataProvider to find the caret, and then check that it intersects the range of a particular element. For example,
var meth = GetSelectedElement<IMethodDeclaration>(true);
var cls = GetSelectedElement<IClassDeclaration>(true);
// make sure we're on a method
if (meth != null && cls != null &&
provider.DocumentCaret.Intersects(meth.GetNameDocumentRange()))
{
⋮
}
Creating Constant Declarations
A constant field in C# has a different representation from a non-constant field when parsed by R#. In R# 4.5, the C# element factory does not have a CreateConstDeclaration statement, but you can emulate it with the following method:
private static IConstantDeclaration CreateConstDeclaration(
CSharpElementFactory factory, IDeclaredType type, string name, string value)
{
return (IConstantDeclaration)factory.CreateTypeMemberDeclaration(
"const $0 $1 = $2;", type, name, value);
}
Context Actions
This section covers context actions. Context actions are code manipulation options that are shown or hidden depending where your cursor is. Each context action actually shows one or several bulb items, and the new R# API explicitly supports this idea.
Tutorial: Your First R# Context Action
We are now going to write a R# context action from scratch. You can start by making a DLL to contain your add-in code. Note that the DLL doesn’t require any special tagging or App.config modification – it’s just a DLL that ReSharper locates and then looks at the class attributes. There are no naming conventions here.
So once you’ve got your DLL project, you need to add references to the R# assemblies. Beware: there will be a lot of references. I bet you’re wondering which references to add, but my advice if you are just getting started is to add all references from the ReSharper folder (on my machine, it’s C:\Program Files\JetBrains\ReSharper\v4.5\Bin) that have the word ReSharper in it. This way, you’ve got support for all of the API. It won’t be quick, mind you, but you can optimize later.
Note: in the code examples, I will often omit namespace imports for brevity.
Context Action Class
Once you have all references, you can make your first context action. A context action is a class, though each type of bulb item, as we’ll see in a bit, is also a class.
Now, for this example, let’s make a context action that can HTML-encode and HTML-decode strings. I’ve chosen this example because it has more than one bulb item, and because the implementation is simple. We start by stubbing out our context action class.
[ContextAction(Group = "C#")]
public class StringHtmlEncodeCA : CSharpContextActionBase
{
}
As you can see, we use the ContextAction attribute to decorate the class. This attribute has several parameters, but it is okay (I think) even if you ignore them all. The second thing you should note is that we subclass CSharpContextActionBase which is the preferred (but not the only) way of making a context action in C#. An alternative is to subclass from OneItemContextActionBase, but that class is obsolete, so we won’t discuss it.
Now, one thing you need to do straight away is make a constructor. The constructor takes a parameter called provider of type CSharpContextActionDataProvider which contains a lot of very useful context information given to us by R#. Not only are we going to use the provider in the context action class, but we will also send it to our bulb items to make use of. It really is that useful.
[ContextAction(Group = "C#")]
public class StringHtmlEncodeCA : CSharpContextActionBase
{
private readonly ICSharpContextActionDataProvider provider;
public StringHtmlEncodeCA(ICSharpContextActionDataProvider provider) : base(provider)
{
this.provider = provider;
}
Of course, we also need to implement the necessary interface features from the base class. There are two mehods and a property here.
-
bool IsAvailableInternal() — this method is your chance to check whether the context action is actually applicable at the location where your end user has their caret. You can browser the syntax tree here, navigating up and down, but actually changing the tree here is probably not a good idea.
-
void ExecuteInternal(params object[] param) — though this is a method used to apply the context action, I typically leave the implementation blank. The reason is that there’s typically different behavior for each bulb item that I want to show, and the bulb items have their own handlers.
-
IBulbItem[] Items — you return all applicable bulb items through this property. That’s right – each bulb item has to implement an IBulbItem interface, which we’ll meet in a moment.
Let us now add a bit more to the context action.
[ContextAction(Group = "C#")]
public class StringHtmlEncodeCA : CSharpContextActionBase
{
private readonly ICSharpContextActionDataProvider provider;
private readonly IList<IBulbItem> items = new List<IBulbItem>();
public StringHtmlEncodeCA(ICSharpContextActionDataProvider provider) : base(provider)
{
this.provider = provider;
}
protected override bool IsAvailableInternal()
{
items.Clear();
// your code here
return items.Count > 0;
}
protected override void ExecuteInternal(params object[] param)
{
}
public override IBulbItem[] Items
{
get { return items.ToArray(); }
}
}
As you can see, I’m being sly by keeping a List<> of IBulbItem objects that are applicable at this particular instance. All that’s necessary is to fill the list in IsAvailableInternal(), and the bulb items will be shown. Let’s do that now.
IsAvailableInternal()
Okay, so for this little exercise, we’re going to add options to HTML-endocode and decode a string literal. This would let the user refactor, say, ">" to ">" or, conversely, refactory "<" to "<".
As you can imagine, this kind of refactoring works only on string literals, not class declaration or integer literals, or anything else. So to determine whether the context action is applicable at all, we need to figure out whether the caret is on a string literal.
The simplest way of determine whether the caret is on a string literal is using a GetSelectedElement<>() method. This inherited method takes a type parameter for the type of element you are on and if you are not on this parameter, the method will return null. It’s important to bear in mind that elements are nested, so you might be on a variable declaration inside a method declaration inside a class declaration, and calls for all three of these types return non-null values.
In our instance we want a string literal. The simplest way of checking it is as follows:
var e = GetSelectedElement<ILiteralExpression>(true);
if (e != null && e.IsConstantValue())
{
if (e.ConstantValue.IsString())
{
// it's a string!
}
}
So given that we do in fact have a string, we want to offer an option to encode or decode a string only if it’s actually useful, i.e., if it changes a string. There’s no point in HTML-encoding a string “abc”, right? This part is easy. Below I show the complete IsAvailableInternal() implementation.
protected override bool IsAvailableInternal()
{
items.Clear();
var e = GetSelectedElement<ILiteralExpression>(true);
if (e != null && e.IsConstantValue())
{
if (e.ConstantValue.IsString())
{
string s = (string)e.ConstantValue.Value;
// it's only worth encoding something which changes when decoding-encoding
if (s != HttpUtility.HtmlEncode(s))
items.Add(new StringHtmlEncodeImpl(provider, e));
// same goes for decoding
if (s != HttpUtility.HtmlDecode(s))
items.Add(new StringHtmlDecodeImpl(provider, e));
}
}
return items.Count > 0;
}
The above code uses two classes – StringHtmlEncodeImpl and StringHtmlDecodeImpl which both happen to implement the IBulbItem interface. Depending on conditions, none, one or all of these classes are applicable to a particular string.
That’s all for the context action class. Next, we’ll take a look at the bulb item classes.
Bulb Item Class
The bulb item class typically implements the IBulbItem interface automatically by inheriting from the BulbItemImpl class. Just as with the context action, the bulb item has an interface contract it has to satisfy. Unlike the context action, it does not require any attributes to function.
-
string Text — this property is the text that gets shown on the bulb item.
-
Action<ITextControl> ExecuteTransaction(...) — this method determines whet the add-in itself does, and also determines what post-execution actions happen afterwards.
Before we go on implementing those, let’s take a look at the core of one of our two bulb items (and they are both more or less identical).
private class StringHtmlEncodeImpl : BulbItemImpl
{
private readonly ICSharpContextActionDataProvider provider;
private ILiteralExpression literal;
public StringHtmlEncodeImpl(ICSharpContextActionDataProvider provider, ILiteralExpression literal)
{
this.provider = provider;
this.literal = literal;
}
}
As you can see, I pass in both the context action data provider, and the literal expression I want to modify. This makes things easier because I don’t have to dig things out again.
Now, the implementation of the Text property is very simple:
public override string Text
{
get { return "HTML-encode".ƒ(); }
}
I deliberatly left the curly-f syntax in place. Basically, the ƒ() method is a fluent equivalent of String.Format that is auto-generated by my template (yes, a ReSharper live template) for the bulb item.
Of course, the really important bit is what is happening in ExecuteTransaction() – that’s where we replace the string literal by its modified version. Essentially, we’re replacing one tree element by another. Let me show you the full code:
protected override Action<ITextControl> ExecuteTransaction(
ISolution solution, IProgressIndicator progress)
{
var factory = CSharpElementFactory.GetInstance(provider.PsiModule, true);
string value = literal.ConstantValue.Value as string;
if (value != null)
{
string encoded = HttpUtility.HtmlEncode(value).Quoted();
var ex = factory.CreateExpressionAsIs(encoded);
literal.ReplaceBy(ex);
}
return null;
}
Let’s discuss what’s happening up above.
-
I get a CSharpElementFactory, which is a class that knows how to create C# code elements such as parameters, expressions, all sorts of things.
-
I grab the string value I’m about to transform. The null check isn’t necessary, but as you’ll learn, extra safety never hurts when extending R#.
-
I HTML-encode the string. The Quoted() extension method simply puts quotes around the string.
-
I turn my new string into an expression. The factory’s CreateExpressionAsIs() method lets you pass in a CLR string to create an expression from. Very convenient.
-
Finally, I replace the literal by the new literal I just made.
As you can see, the method returns null. Often you want to return something meaningful here. For example, if you’ve generated a variable and you want to let the user rename it via Refactor→Rename, you would do it by returning a corresponding Action.
Debugging and Deployment
By now, you should have set up your debugging string to fire up Visual Studio with the /ReSharper.Plugin switch. If you haven’t, see the ‘Project Set-Up’ section earlier. Pressing F5 will let you run your add-in. You can set breakpoints and investigate the various R# structures. Hours of fun.
When you’re ready to deploy, simply copy the DLL to the ReSharper bin\Plugins\Whatever folder, and R# will pick it up the next time you fire up Visual Studio.
Congratulations – you just wrote your first context action.
Bulb Items
A bulb item is a general-purpose object which represents an item next to the lightbulb that pops up on the left as a context action/quick fix.
Post-Transaction Processing
After you have manipulated the tree, you can specify further actions, such as moving the caret or calling a ReSharper refactoring. Here’s an example: suppose you’ve just added a field to a class and want to call the “Rename” refactoring on it. To do this, the return result of your transaction method (it has to be an Action<>, remember?) would look as follows:
return tc =>
{
tc.SelectionModel.SetRange(inserted.GetNameDocumentRange().TextRange);
var rename = ActionManager.Instance.GetAction("Rename");
ActionManager.Instance.ExecuteActionIfAvailable((IExecutableAction)rename);
};
Daemons
Just like a Unix deamon, the R# variety is basically a background process that acts as a Visitor (as per the GoF pattern) for various code elements. A Daemon is really a general-purpose element, whose goal is to look at a particular code element and then take whatever action you wish on your code.
What type of action? Well, it’s really up to you. One possible action is to highlight a particular item in code.
Highlighting
Highlighting is one of the simple things. Basically, when you want a piece of code underlined and an indicator on the right-hand side (you know, one of those colourful bars), you make a class which implements the IHighlighting interface and decorate it with the StaticSeverityHighlighting attribute.
The attribute is fairly obvious in terms of use – you have an option to choose severity (suggestion, error, warning) with which the item gets shown. You can also specify your own bar color.
Let’s briefly discuss the IHighlighting interface though, admittedly, its use is very simple.
-
bool IsValid() — this method is supposed to return true if data associated with highlighting is valid. I’ve never returned anything but true from it.
-
string ToolTip — this is the tooltip that appears in code when you hover the mouse over the code element that has been highlighted. You can make the highlighting pretty big, just make sure that you break the lines with \n so that they fit the screen. (R# won’t do it for you.)
-
string ErrorStripeToolTip — this is the tooltip on the error stripe (you know… the vertical bar on the right with little horizontal lines). A shorter piece of text should go here because, chances are, nobody will spend too much time reading it.
-
int NavigationOffsetPatch — don’t know exactly what it’s for, but apparently you can move the caret when navigating to this highlighting. The fact it’s there means it’s useful for something, but I recommend you return 0 (zero).
To apply a highlighting, you feed it to a DeamonStageResult. Typically, you would use some additional scaffolding so that you can add highlightings to a collection inside your deamon infrastructure.
Deamon Tooltip Provider
On the subject of highlighting, it’s also possible to define your own tooltip provider. This functionality is quite powerful – we’re talking about custom-rendering a tooltip here. If you are really sure you want to do this, add a reference to System.Drawing and System.Windows.Forms and let’s begin.
First, you need a class that implements ITooltipProvider, a deceptively simple interface that requires only one method: ITooltipInfo GetTooltip(Point pt). This method is called by the tooltip manager when it needs to determine whether to render the tooltip, and how. The method returns an ITooltipInfo, which is a
Live Templates
Live templates are equivalents of VS snippets. Visual Studio authors did have ideas to allow executable code or extensibility of snippets, but they never got it implemented for 2008, whereas R# has. If you open a typical template in R#, you’ll see a part of the template marked with dollar signs, like so: $CLASS$. This is a variable name that can be associated with a macro which is, as you may have guessed, a piece of C# executable code.
Since it’s now possible to execute code inside live templates, you can define your own code to execute by writing a Live Template macro. It can be tough to think of a use case for an LT macro, so this functionality is not for everyone. However, I shall present LT macro programming here just in case anyone is interested.
Note: live templates only ‘merge’ with ordinary snippets if you use ReSharper’s brand of IntelliSense. Since most people I know turn it off as soon as they install ReSharper, the typical use case is somewhat inconvenient: you need to call up a separate menu item or wire up a keyboard shortcut for a live template. This problem is likely resolved for VS2010 due to its IntelliSense extensibility model.
Tutorial: Live Template Macro
Let’s write a simple LT macro.
Fun Stuff
This section is all about making R# programming easier and more fun. Hey, life would be pretty boring without a Fun section, right? So here it is.
Monadic Syntax
Most operations that try to determine, say, the appicability of context actions exemplify the ‘maybe’ monad. I mean, sure, Nullable<T> is supposed to do that, but in R# we deal with reference types, so it doesn’t really matter. What do I mean? Okay, look at the following pseudocode:
var a = GetSelectedElement<IX>(true);
if (a != null)
{
var b = a.Something;
if (b != null)
{
var c = b.DoY() as Z;
if (c != null)
{
⋮ and so on
}
}
}
In C#, you can instead use the monadic syntax as follows:
this.With(x => GetSelectedElement<IX>(true))
.With(x => x.Something)
.With(x => x.DoY() as Z)
.Do(x => { /* something */ });
Of course, you need to implement the monadic API yourself. The example below shows a few rudimentary statements one can extend the object class with.
public static TResult With<TInput, TResult>(this TInput o, Func<TInput, TResult> evaluator)
where TResult : class where TInput : class
{
if (o == null) return null;
return evaluator(o);
}
public static TInput If<TInput>(this TInput o, Func<TInput, bool> evaluator)
where TInput : class
{
if (o == null) return null;
return evaluator(o) ? o : null;
}
public static TInput Unless<TInput>(this TInput o, Func<TInput, bool> evaluator)
where TInput : class
{
if (o == null) return null;
return evaluator(o) ? null : o;
}
public static TResult Return<TInput,TResult>(this TInput o,
Func<TInput, TResult> evaluator, TResult failureValue) where TInput: class
{
if (o == null) return failureValue;
return evaluator(o);
}
public static TInput Do<TInput>(this TInput o, Action<TInput> action)
where TInput: class
{
if (o == null) return null;
action(o);
return o;
}