Writing plug-ins for ReSharper: Part 2 of N

[Finally I’ve managed to get the second part of the post on plug-ins. Sorry for the delay to everyone who was waiting. Appreciate your patience.  And now I’ll resume my holidays!]

In the previous part of this series, we saw the basics of how to create a plug-in for ReSharper, install it and run it. We created a context action that would allow us to mark a public method as virtual (where applicable). However, this was done as an explicit action by the user, as such, you didn’t get any kind of hint or suggestion to do this. What we want to do now is make this a hint, so that highlighting appears under methods that could be made virtual. In this part we are going to expand on the same plug-in and convert it into a QuickFix.

What is a QuickFix?

Have you seen the little squiggly lines that appear in Visual Studio?

image

They usually indicate a Suggestion (field can be made read-only), Warning (possible null reference) or Error. ReSharper analyzes and can detect potential issues in the code (similar to what static checker of Code Contracts does). These are known as Highlights and they are related to QuickFixes in that usually a highlight has an QuickFix associated to it, which invokes a context action. This is usually done by placing the cursor on top of the highlighting and press Alt+Enter

image

 

Highlighting Daemons

In the gutter of the Visual Studio editor (right-side), ReSharper displays a series of warnings, errors and hints, which indicate potential issues on a specific file. These issues are detected by background processes known as Daemons. Since what we are looking for is for ReSharper to warn us of existing methods that could be made virtual, what we need to do is somehow hook into these daemons.

image

 

Step by Step Guide

The Daemons in ReSharper use the Visitor pattern to use act on elements, be it code, files, etc. The first step is to implement an IDaemonStage interface, which hold metadata about our daemon stage at at the same time acts as a factory for the actual process we are implementing.

[DaemonStage(StagesBefore = new[]  { typeof(LanguageSpecificDaemonStage) })]
 public class MakeMethodVirtualDaemonStage: IDaemonStage
 {
     public IDaemonStageProcess CreateProcess(IDaemonProcess process, DaemonProcessKind processKind)
     {
         return new MakeMethodVirtualDaemonStageProcess(process);
     }

     public ErrorStripeRequest NeedsErrorStripe(IProjectFile projectFile)
     {
         return ErrorStripeRequest.STRIPE_AND_ERRORS;
     }
 }

There are two main methods to implement. The CreateProcess is what creates the actual process for us and the NeedsErrorStrip which indicates whether this daemon uses the gutter to display strips. The DaemonProcessKind parameter passed into the first method helps us discriminate on when this process should be executed, i.e. only during checking of visible (current) document, during solution wide analysis, etc.

The next step is to implement the process via the IDaemonStageProcess interface:

  public class MakeMethodVirtualDaemonStageProcess : IDaemonStageProcess
  {
      readonly IDaemonProcess _process;


      public MakeMethodVirtualDaemonStageProcess(IDaemonProcess process)
      {
          _process = process;
          
      }

      public void Execute(Action<DaemonStageResult> commiter)
      {
          if (_process.InterruptFlag)
          {
              return;
          }


          var file = _process.ProjectFile.GetPsiFile(CSharpLanguageService.CSHARP) as ICSharpFile;

          if (file != null)
          {
              var highlights = new List<HighlightingInfo>();

              var processor = new RecursiveElementProcessor<IMethodDeclaration>(declaration =>
              {

                  var accessRights = declaration.GetAccessRights();

                  if (accessRights == AccessRights.PUBLIC && !declaration.IsStatic && !declaration.IsVirtual &&
                      !declaration.IsOverride)
                  {
                      var docRange = declaration.GetNameDocumentRange();

                      highlights.Add(new HighlightingInfo(docRange, new MakeMethodVirtualSuggestion(declaration)));
                  }
              });

              file.ProcessDescendants(processor);
              
              commiter(new DaemonStageResult(highlights));
          }

      }

      
  }

The main meat of this class is in the Execute method. We first check to make sure that we’ve not received an interruption (Interrupt Flag raised) due to some external action. Next step is to get access to the current file (remember that we are visiting the entire visible document, not just a specific method). Having the file, we can now create a RecusiveElementProcessor* to perform a tree walk of the AST and perform the specific action on each element. The action to perform is declared as the lambda expression. Since we’re interested in the method declaration, the type is IMethodDeclaration (there are many others). If we look at the expression, we can see that it’s pretty much the same as that of Part 1, the only difference is that we add the results to the highlighting variable.

The HighlightingInfo class has a parameter which can be a Suggestion, Warning or Error, as explained previously. Since in our case we need a suggestion, we pass in the MakeMethodVirtualSuggestion:

[StaticSeverityHighlighting(Severity.SUGGESTION)]
 public class MakeMethodVirtualSuggestion : CSharpHighlightingBase, IHighlighting
 {
     public ICSharpTypeMemberDeclaration Declaration { get; private set; }

     public MakeMethodVirtualSuggestion(ICSharpTypeMemberDeclaration memberDeclaration)
     {
         Declaration = memberDeclaration;
     }

     public string ToolTip
     {
         get { return "Method could be marked as virtual"; }
     }

     public string ErrorStripeToolTip
     {
         get { return ToolTip; }

     }

     public override bool IsValid()
     {
         return Declaration.IsValid();
     }

     public int NavigationOffsetPatch
     {
         get { return 0; }
     }
 }

This class is pretty simple. The main property to define is the ToolTip, which is the text that will show when we hover of the highlighting. The ErrorStripeToolTip is what’s displayed in the right-hand side gutter. Finally the Attribute StaticSeverityHighlighting is to indicate what type of tip it is (Warning, Error, etc.).

 

[*Note: In this case, the operation we want to perform is very simple. If we want a more complex scenario where we need to do some processing before and after each element is visited or have a more fine-grained control, we can implement the IRecurisveElementProcessor. I’ll cover this in another post]. 

 

To recap, right now we would have everything place to display highlighting when a method that could be made virtual is encountered. The only remaining part is to now be able to apply a QuickFix. This is in many ways similar to the ContextAction we saw in Part 1:

[QuickFix]
public class MakeMethodVirtualQuickFix : BulbItemImpl, IQuickFix
{
    readonly MakeMethodVirtualSuggestion _highlighter;

    // Takes as parameter the Highlighter the quickfix refers to
    public MakeMethodVirtualQuickFix(MakeMethodVirtualSuggestion highlighter)
    {
        _highlighter = highlighter;
    }

    // In the transaction we make the necessary changes to the code
    protected override Action<ITextControl> ExecuteTransaction(ISolution solution, IProgressIndicator progress)
    {
        _highlighter.Declaration.SetVirtual(true);

        return null;
    }

    // Text that appears in the context menu
    public override string Text
    {
        get { return "Make Method Virtual"; }
    }

    // Indicates when the option is available
    public bool IsAvailable(IUserDataHolder cache)
    {
        return _highlighter.IsValid();
    }
}

The MakeMethodVirtualQuickFix needs to implement the IBulbItem and IQuickFix interfaces. For ease of implementation we can inherit from BulbItemImpl. The constructor should take as parameter always the actual highlighting that has given way to invoking the QuickFix, in our case the MakeMethodVirtualSuggestion. Similar to the ContextAction we implemented in Part 1, the actual fix itself is pretty trivial. All we need to do is make the method virtual. How do we get access to the method? The easiest way is via the Declaration property of the highlighting passed in (this is a property we added before). The only thing left is to call the SetVirtual method on it. Since we are in the ExecuteTransaction method, ReSharper makes sure that any change made is executed as a whole.

The rest of the properties are trivial. Text returns the text of the QuickFix (what appears in the menu), and IsAvailable indicates when the QuickFix is available, which in our case is whenever the highlighting is valid.

 

The End Result

Once we compile the plug-in and place it in the corresponding Plugins folder under ReSharper\Bin, we’re done. Here’s the end result:

image

and invoking Alt+Enter on the highlighting gives us:

 

image

 

Summary

Extending ReSharper to create highlightings and quick fixes is pretty simple once you understand how all the pieces fall into place. Most of the code will usually be the same and what will vary will be the actual element processing to be performed and the corresponding QuickFix. As mentioned previously (in the Note), for complex scenarios, we can have more control over the tree walk and that’s something we’ll examine in a future post.

I’ve placed the code up on my github account so feel free to download it, play with it and ping me if you have any comments or questions. The code is updated to work with ReSharper 5.1


Posted 07-20-2010 11:02 PM by Hadi Hariri
Filed under: ,

[Advertisement]

Comments

gOODiDEA.NET wrote Cheatsheet: 2010 07.09 ~ 07.21
on 07-21-2010 8:58 PM

Web Replacing text in the DOM… it’s not that simple! Object-Oriented PHP for Beginners -

lanniepepe wrote re: Writing plug-ins for ReSharper: Part 2 of N
on 09-26-2010 6:31 PM

depend methane solutions century

devlicio.us wrote re: Writing plug-ins for ReSharper: Part 2 of N
on 04-04-2011 2:32 AM

Writing plug ins for resharper part 2 of n.. Awesome :)

devlicio.us wrote re: Writing plug-ins for ReSharper: Part 2 of N
on 04-22-2011 9:06 AM

Writing plug ins for resharper part 2 of n.. Nifty :)

devlicio.us wrote re: Writing plug-ins for ReSharper: Part 2 of N
on 06-02-2011 7:57 PM

Writing plug ins for resharper part 2 of n.. Bang-up :)

devlicio.us wrote re: Writing plug-ins for ReSharper: Part 2 of N
on 06-04-2011 8:18 PM

Writing plug ins for resharper part 2 of n.. Great! :)

cxtn free porn 390u wrote re: Writing plug-ins for ReSharper: Part 2 of N
on 07-02-2011 2:02 AM

Writing plug ins for resharper part 2 of n.. Huh, really? :)

social bookmarking submissions wrote re: Writing plug-ins for ReSharper: Part 2 of N
on 01-17-2013 6:45 PM

CYf9Mo Looking forward to reading more. Great article.Really looking forward to read more. Fantastic.

buy imitrex online wrote re: Writing plug-ins for ReSharper: Part 2 of N
on 02-15-2013 4:29 PM

l70vnV I loved your blog post.Really looking forward to read more. Great.

buy clomid wrote re: Writing plug-ins for ReSharper: Part 2 of N
on 02-28-2013 2:30 PM

oAl1kT Fantastic article. Really Great.

bookmaring service wrote re: Writing plug-ins for ReSharper: Part 2 of N
on 03-13-2013 11:50 PM

whEiEe Thanks so much for the blog article.Really thank you! Much obliged.

bookmaring service wrote re: Writing plug-ins for ReSharper: Part 2 of N
on 03-14-2013 6:19 PM

BHxrIu Im obliged for the article.Really looking forward to read more. Great.

social bookmarking service wrote re: Writing plug-ins for ReSharper: Part 2 of N
on 03-24-2013 9:50 PM

RgaAce wow, awesome blog article. Much obliged.

digital slr lenses wrote re: Writing plug-ins for ReSharper: Part 2 of N
on 05-14-2013 11:38 AM

2m4kb7 Thanks again for the post.Thanks Again. Will read on...

best news of them all wrote re: Writing plug-ins for ReSharper: Part 2 of N
on 08-05-2013 1:19 AM

Bj4P66 I think this is a real great blog post.Much thanks again. Keep writing.

link building team wrote re: Writing plug-ins for ReSharper: Part 2 of N
on 09-30-2013 9:58 PM

q4g6TU Very neat blog.Really looking forward to read more. Want more.

link building team wrote re: Writing plug-ins for ReSharper: Part 2 of N
on 10-15-2013 9:10 PM

z0e4Ov Great, thanks for sharing this post.Thanks Again. Want more.

check this out wrote re: Writing plug-ins for ReSharper: Part 2 of N
on 11-20-2013 5:37 AM

AwCYMe Very informative post. Much obliged.

stunning seo guys wrote re: Writing plug-ins for ReSharper: Part 2 of N
on 01-21-2014 1:29 AM

m5tFK6 I appreciate you sharing this blog.Much thanks again. Great.

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)