Writing plug-ins for ReSharper: Part 1 of Undefined

ReSharper does a lot of things, but as they say, you can’t please all the people all of the time. However, one the great things about ReSharper is that it is quite extensible and there are already quite a number plug-ins available. Some of the better known ones are:

- StyleCop for ReSharper by Howard Van Rooijen

- TDD Productivity Plugin by Eric Hexter

- ReSharper Test Runner for MSpec by Alexander Groß 

and of course there is also the repository of plug-ins available from the JetBrains site, where many of these are located.

Now I’m no expert on writing plug-ins for ReSharper, and the authors of the previous ones leave the bar quite high, but seeing that I actually now have the time to play with them, I’ve decided to do so by sharing my experiences with you in a series of blog posts.

The ReSharper API is pretty extensive and can be overwhelming as I’ve been discovering, so I want to try and take it slowly. This might mean that at times, the code is not always complete and might be missing a few checks for example, but will come in due course.

Everything that I’ll cover applies to version 5.0 which is currently in Beta. There have been some changes from previous versions so if you’re working with 4.5 some of the code might not work.

 

The First Plug-in

I was initially intending to write a useless plug-in for the first demo that didn’t do much, but after talking to Orangy (aslo known as Ilya) who commented on a tweet Jeremy Skinner had posted a few days back:

image_thumb51

we decided to use it as the first sample.

I’m going to build up the sample over the next few series of posts, so the initial solution is not the ideal one or complete, but it will help introduce a few core concepts. Ideally this feature should be implemented as what’s known as a QuickFix but we’re first going to do it as a ContextAction.

 

ContextAction

So what exactly is a Context Action? It’s actions that can be applied based on the context (the name is quite descriptive). They shows up as items in a popup menu which is invoked using Alt+Enter (don’t use the mouse for this…it’s very unproductive).

image_thumb4

What we want to do in this first version of the plug-in is to write a new action that makes methods that are public and not declared virtual, virtual, something NHibernate users would appreciate greatly. As mentioned previously, in this first version we’re going to add a context action.

1. Creating a plug-in assembly

ReSharper plug-ins are assemblies that are located in Plugins folder (by default %programFiles%\JetBrains\ReSharper\v5.0\Bin\Plugins). Each plug-in is in it’s own folder and doesn’t require any further registration. Therefore the first step is to create a class library which will host our context action.

2. Creating the Context Action Skeleton

In order to create a context action, we need to implement the IContextAction interface. This interface has one method IsAvailable which indicates to us if that particular action is available given the context, and a property of type IBulbItem[] which contains a series of bulb items. Each IBulbItem in turn has a property Text which is the text that appears next to the item in the context dropdown, and an Execute action, which is what happens when the item is selected. So as we can see, a context action can consist of more than one bulb item. An added benefit to this decoupling is the re-usability of bulb items. By implementing IBulbItem, we can potentially use it in more than once place (obviously if it makes sense).

Many times however, we only want one bulb item per action, and to somehow simplify the process, we can inherit from the BulbItemImpl class. This class defines an abstract Text property that represents the text of the bulb item, and also has an ExecuteTransaction member which is where our code is executed. We’ll see the difference between this method and Execute shortly.

 

  1. public class MakeMethodVirtualContextActions: BulbItemImpl, IContextAction
  2. {
  3.     protected override Action<ITextControl> ExecuteTransaction(ISolution solution, IProgressIndicator progress)
  4.     {
  5.         throw new NotImplementedException();
  6.     }
  7.  
  8.     public override string Text
  9.     {
  10.         get { throw new NotImplementedException(); }
  11.     }
  12.  
  13.     public bool IsAvailable(IUserDataHolder cache)
  14.     {
  15.         throw new NotImplementedException();
  16.     }
  17. }

 

 

Before implementing any of the methods, there’s one thing we need to do. ReSharper requires that context action have a constructor with a parameter that implements a IContextActionDataProvider. This parameter is actually useful to use to provide information about the context as we’ll see shortly. Therefore, we need to add a constructor to the previous code (lines 6-9). Since our action is only for C#, we will use a ICSharpContextActionDataProvider. We then save this provider for later use (line 3). What we’re effectively doing here of course is nothing more than dependency injection. The plumbing (passing in the correct provider) is taken care of for us by ReSharper.

 

  1. public class MakeMethodVirtualContextAction : BulbItemImpl, IContextAction
  2. {
  3.     readonly ICSharpContextActionDataProvider _provider;
  4.  
  5.  
  6.     public MakeMethodVirtualContextAction(ICSharpContextActionDataProvider provider)
  7.     {
  8.         _provider = provider;
  9.     }
  10.     protected override Action<ITextControl> ExecuteTransaction(ISolution solution, IProgressIndicator progress)
  11.     {
  12.         throw new NotImplementedException();
  13.     }
  14.  
  15.     public override string Text
  16.     {
  17.         get { throw new NotImplementedException(); }
  18.     }
  19.  
  20.     public bool IsAvailable(IUserDataHolder cache)
  21.     {
  22.         throw new NotImplementedException();
  23.     }
  24. }

 

3. Defining Availability

The next step is to define when our action is available. In order to do so, we need to implement the IsAvailable method. In our case, we want to convert methods that are public and not virtual, to virtual. We therefore need to identify methods that are public and not virtual. We also need to filter out static methods. In order to get access to the current context we’re in, we can use the provider we injected in via the constructor. The provider has a GetSelectedElement method which corresponds to the element the caret is on. Since we’re interested in a method, we can invoke this function, requesting back a method declaration. If the caret is in the context of a method (i.e. header, body), it will return this information. If it’s not, it will return null.

If we have a valid method declaration, the next step is to find out if it is public. We do that by invoking GetAccessRights [In later series we’ll see that further checks are necessary here]. If it is public, we then need to check whether it is a static method, an override or already virtual. Based on that, we return true or false, indicating whether the action is available in the current context.

 

  1. public bool IsAvailable(IUserDataHolder cache)
  2. {
  3.     var item = _provider.GetSelectedElement<IMethodDeclaration>(false, true);
  4.  
  5.     if (item != null)
  6.     {
  7.         var accessRights = item.GetAccessRights();
  8.  
  9.         if (accessRights == AccessRights.PUBLIC && !item.IsStatic && !item.IsVirtual && !item.IsOverride)
  10.         {
  11.             return true;
  12.         }
  13.     }
  14.     return false;
  15. }

4. Performing the action

When the action is available and the user selects it, what we need to do is make the method virtual. This is done in the ExecuteTransaction method. Since we are going to modify the code, we need to make sure that the file is writable. Normally this would be done by invoking a call to a method named EnsureWritable. However, inside the method ExecuteTransaction, this is done for us, so we don’t have to worry about it. This is one of the differences between ExecuteTransaction and IBulbItem.Execute.

So how do we go about modifying code in ReSharper? Well remember that refactoring code is ReSharper’s daily bread. It’s what it does. As such, there’s a ton of infrastructure in place to allow us to do modify code easily. In fact, making a method virtual is as easy as calling a method, as shown below.

  1. protected override Action<ITextControl> ExecuteTransaction(ISolution solution, IProgressIndicator progress)
  2. {
  3.     var method = _provider.GetSelectedElement<IMethodDeclaration>(false, true);
  4.  
  5.     if (method != null)
  6.     {
  7.         method.SetVirtual(true);
  8.     }
  9.  
  10.     return null;
  11. }

5. Testing the plug-in

The only thing left to do is test that it works (writing unit tests for plug-ins is something we’ll cover in the future). One option is to copy the assembly to the plug-in folder for ReSharper and restart Visual Studio. However, in order to be able to debug it, what we can do is set the project properties to start an external program, which is non other than devenv.exe, and pass as parameter to it /ReSharper.Plugin <Path_to_Plugin_Assembly>, instructing ReSharper to load a specific plug-in.

How does ReSharper know which classes in the assembly are context actions? The easiest way is to decorate these with the ContextAction attribute

 

  1. [ContextAction(Group="C#", Name = "MakeMethodVirtual", Description = "Adds context action to make methods virtual")]
  2.     public class MakeMethodVirtualContextAction : BulbItemImpl, IContextAction
  3.     {
  4.   

 

This information is also what appears under ReSharper –> C# –> Context Actions

 

image_thumb1

Now all that’s left is to try it out. Write a new class, add a few methods and check to see it’s all working correctly.

 

image_thumb41

Public static method (not available)

 

image_thumb5

Private method (not available)

 

image_thumb7

Public method (option available)

 

 

 

 

 

 

 

What’s next?

In this first part we’ve seen the basics of writing plug-ins for ReSharper. In the next blog post, we’ll extend the example to also include properties and we’ll change it to be a QuickFix as opposed to a context action. This means that we’ll get some nice highlighting by ReSharper telling us something can be changed (similar to when you have something named incorrectly for instance), thus being more *in your face*.

There’s so much more to see than what’s here, and gradually we’ll drill more into each of the different areas, examining parameters, return values, etc. Until then, Happy ReSharping (did I just coin that? Too lame?)


Posted 01-12-2010 2:27 PM by Hadi Hariri
Filed under: ,

[Advertisement]

Comments

sergiopereira wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 01-12-2010 2:05 PM

What assemblies do I need to reference in my plugin project (i.e. where are the interfaces you mentioned)? Also, is there a link to documentation for writing plugins on the site?

Hadi Hariri wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 01-12-2010 3:23 PM

@Sergio,

Unforunately Live Writer didn't play well with the Attachment plugin. The source code had all the correct references. I've uploaded the project to hadihariri.com/.../MakeVirtualPlugin.zip. Let me know if you have any issues.

Kenny wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 01-13-2010 3:00 AM

Cool stuff! :)

web design wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 01-13-2010 5:33 AM

what a nice blog dear. I never seen such an attractive and useful blog after seeing it. I will pray for your success and also appreciate you at your great step that you have taken toward your bright future..

Thanks a lot again.

Gamlor wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 01-13-2010 7:29 AM

Hey, a cool blog post. I'll certainly follow the next post on this topic.

And adding a context-action is easier than I imagined.

Is there a good documentation of the Resharper-API somewhere. I haven't found anything on Resharpers website.

Andrew Kazyrevich wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 01-15-2010 12:08 PM

Hadi, great start.

R# OpenAPI definitely lacks documentation - hopefully your blog posts would shed some light!

--Andrew

Pascal wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 01-16-2010 11:08 AM

Great post. For a while now I've been wanting to create a R# plugin but it always seemed too complicated and undocumented. I think your posts on the topic will help a lot. Please keep up with the series. Thanks.

Hadi Hariri's Blog wrote Writing plug-ins for ReSharper: Part 2 of N
on 07-20-2010 6:11 AM

Writing plug-ins for ReSharper: Part 2 of N

claysoniso wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 09-28-2010 12:53 AM

permafrost store different 2008

devlicio.us wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 05-23-2011 6:10 AM

Writing plug ins for resharper part 1 of undefined.. OMG! :)

devlicio.us wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 06-03-2011 7:55 AM

Writing plug ins for resharper part 1 of undefined.. Huh, really? :)

devlicio.us wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 06-05-2011 8:12 AM

Writing plug ins for resharper part 1 of undefined.. Retweeted it :)

byri porn video qop1 wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 07-02-2011 2:03 PM

Writing plug ins for resharper part 1 of undefined.. OMG! :)

EB wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 05-15-2012 10:12 AM

I'm having trouble getting this to work properly.

I dutifully copied and pasted the code provided here, compiled it, and moved the output to the plugin folder. The plugin shows up in the appropriate Visual Studio options screens, and I can see the context action under ReSharper Code Editing.

The problem is that the context action menu doesn't appear for public methods in the text editor at all. Not with the mouse, not with Alt-Enter. I get no options whatsoever. The private methods give me a list as always, of course without the new "To virtual" option.

When I uncheck the plugin in the ReSharper options menu, I get the context menus back. I also tried reducing the constraints in the IsAvailable method, e.g., by removing the check on public access rights. This allows the "To virtual" action to show up on private methods (as expected), but still doesn't fix the context menus on public methods, which is obviously what we want.

I can successfully make private methods virtual (ha!), so I know the Execute portion is capable of working, it just doesn't apply to public methods.

For reference, I'm using VS10 with R# 6.1.1. Any help would be greatly appreciated.

high pr backlinks wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 01-17-2013 7:19 PM

KLvZ3V Thanks for sharing, this is a fantastic article post.Really thank you! Really Great.

buy imitrex online wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 02-15-2013 12:47 PM

puTZtG I really liked your blog article. Fantastic.

buy social bookmarks wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 03-24-2013 5:17 PM

WpPP8r Very informative blog.Really looking forward to read more. Fantastic.

bears wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 04-06-2013 12:35 PM

Hey, thanks for the blog.Thanks Again. Really Cool.

Social bookmarks wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 04-12-2013 7:44 AM

HFhbpw Really informative post.Much thanks again. Great.

buy social bookmarks wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 04-13-2013 10:04 AM

ml2y0R I loved your article post.Really looking forward to read more. Keep writing.

Social bookmarks wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 04-15-2013 9:19 PM

BuZzeO I really like and appreciate your article post.Thanks Again. Want more.

Social bookmarks wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 04-19-2013 11:16 AM

F0H2uP Great, thanks for sharing this post. Fantastic.

social bookmarking service wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 04-24-2013 3:24 AM

ClmMJB Thanks-a-mundo for the post.Thanks Again.

Social bookmarks wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 04-28-2013 5:41 AM

gklbC9 I really like and appreciate your blog.Really thank you! Awesome.

digital camera guide wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 05-14-2013 3:18 PM

H512Io Thanks a lot for the blog post.Really thank you! Really Cool.

seo service wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 05-29-2013 12:19 PM

lEz1Em I really liked your blog post.Really thank you! Much obliged.

green modular homes wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 06-05-2013 1:24 AM

apbvQ4 Appreciate you sharing, great blog article.

cheap social bookmarks wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 06-19-2013 6:31 PM

SYiUwd Thanks a lot for the article. Really Cool.

great service wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 07-04-2013 1:27 PM

Am589j Wow, great post.Much thanks again.

news wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 07-26-2013 8:59 AM

b2cFEI I loved your blog article.Thanks Again. Really Great.

news wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 08-02-2013 1:04 PM

s5LDkW Muchos Gracias for your article post.Really thank you! Awesome.

awesome links for you wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 08-19-2013 9:44 AM

B8dy04 Really informative blog.Really thank you! Fantastic.

great link buildng wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 08-19-2013 8:58 PM

g7qDw9 Fantastic blog post. Much obliged.

great seo service wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 09-04-2013 12:50 AM

CNb13q Very neat article.Thanks Again. Keep writing.

make money online wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 09-11-2013 5:43 PM

5rpgb2 Thanks so much for the blog.Really looking forward to read more. Really Cool.

awesome link building wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 09-24-2013 11:54 AM

yKZPdp Thank you ever so for you post. Cool.

best link build wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 10-16-2013 12:20 PM

pkO4J1 Really appreciate you sharing this post.Much thanks again. Really Cool.

take a look at it! wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 10-24-2013 4:44 PM

j6l5Kt Very neat article post.Really looking forward to read more. Really Cool.

john wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 07-17-2014 7:23 PM
link building wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 07-18-2014 6:38 PM

Y27DXD I think this is a real great article.Really looking forward to read more. Much obliged.

crorkz wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 08-06-2014 7:30 AM

LPJqJs Thanks so much for the blog post.Much thanks again. Fantastic.

john wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 10-15-2014 11:54 AM
john wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 10-15-2014 12:10 PM
john wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 10-17-2014 1:52 AM
john wrote re: Writing plug-ins for ReSharper: Part 1 of Undefined
on 10-18-2014 5:10 PM

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)