DCSIMG
CodeRush Plugin - Navigate to Implementation Part 2 - .NET Geek

.NET Geek

"It is upon the Trunk that a gentleman works" - Confucius

CodeRush Plugin - Navigate to Implementation Part 2

In the previous post we just defined what we want the plugin to do. Let's start to walk through the process of creating a plugin with CodeRush.

There's some plumbing that needs to be done every time you create a plugin and that is not directly related to the functionality of the plugin. For this part I'll just point you to a plugin tutorial by Mark Miller. Since our plugin will handle navigation it seems natural to hook into the Navigation provider system provided by CodeRush.

  1. Open the plugin designer
  2. The NavigationProvider does not show up in the toolbox by default so we need to add it.
  3. Place a NavigationProvider on the designer and subscribe to the CheckAvailability and Navigate events.

Here's a dump of all of the code in the code-behind of the plugin.

namespace CR_NavigationContrib

{

    public partial class NavigationContribPlugIn : StandardPlugIn

    {

        private Implementors _implementors;

        // DXCore-generated code...

        #region InitializePlugIn

        public override void InitializePlugIn()

        {

            base.InitializePlugIn();

            _implementors = new Implementors();

            //

            // TODO: Add your initialization code here.

            //

        }

        #endregion

        #region FinalizePlugIn

        public override void FinalizePlugIn()

        {

            //

            // TODO: Add your finalization code here.

            //

            base.FinalizePlugIn();

        }

        #endregion

        private void navigationProvider1_CheckAvailability(object sender, CheckContentAvailabilityEventArgs ea)

        {

            _implementors.Load(ea.Element);

            ea.Available = _implementors.Count > 0;

            if (!ea.Available)

                return;

            foreach (var item in _implementors)

            {

                ea.AddSubMenuItem(item.FullName, item.Name);

            }

        }

        private void navigationProvider1_Navigate(object sender, NavigationEventArgs ea)

        {

            SubMenuItem selectedMenu = ea.SelectedSubMenuItem;

            if (selectedMenu == null)

                return;

            Class implementor = _implementors.Where(c => c.FullName == selectedMenu.Name).FirstOrDefault();

            if (implementor != null)

            {

                Navigator navigator = new Navigator(implementor, ea.Element);

                navigator.Navigate();

            }

        }

    }

}

What we see in the code above, are two methods InitializePlugin() and FinalizePlugin() which are standard boilerplate with every plugin and two event handlers navigationProvider1_CheckAvailability and navigationProvider1_Navigate for the navigation provider. In addition to the plugin class, the plugin has two additional classes that we write. Implementors and Navigator. The Implementors class is responsible for storing which classes implement the Interface we're on if any. The Navigator class will take us to the implementation when the navigation method is called.

A few more issues that are worth to mention.

Here's a screenshot of the event arguments for the CheckAvailability event. As you can see there's quite a lot going on. This turns out to be both a blessing and a curse. On the blessing side you can achieve pretty much everything you want. On the curse side, it is not easy to find what you want.

I wonder why the CodeRush team created such a flat object model. I would much rather prefer a deeper, but more categorized hierarchy. I'm not complaining, don't get me wrong, but the current object model without documentation is not easy to navigate (for me, that is). Maybe if I had a lot of experience with VS extensibility much of what I struggle with would be moot. Time will tell.

image

In the CheckAvailability event handler we get a list of classes that implement the interface we're on. If the Implementors class found at least one class that implements the Interface, we tell CodeRush that our navigation provider should be available to the user. In addition we populate the navigation menu with sub menu items, one for each class. We use the fully qualified name of the class as the key for the menu.

private void navigationProvider1_CheckAvailability(object sender, CheckContentAvailabilityEventArgs ea)

{

    _implementors.Load(ea.Element);

    ea.Available = _implementors.Count > 0;

    if (!ea.Available)

        return;

    foreach (var item in _implementors)

    {

        ea.AddSubMenuItem(item.FullName, item.Name);

    }

}

The Navigate event handler will be called if the user selected one of the sub menu items we created in the CheckAvailability handler. Here we check that we got a menu (maybe not necessary since we couldn't get here without clicking one). Next we query the Implementors class for a class that matches the key we stored as the menu name earlier. If we find a class that implements the interface, we instantiate a new Navigator that will handle the navigation to the location of the implementation. (eg. open and activate files etc.)

private void navigationProvider1_Navigate(object sender, NavigationEventArgs ea)

{

    SubMenuItem selectedMenu = ea.SelectedSubMenuItem;

    if (selectedMenu == null)

        return;

    Class implementor = _implementors.Where(c => c.FullName == selectedMenu.Name).FirstOrDefault();

    if (implementor != null)

    {

        Navigator navigator = new Navigator(implementor, ea.Element);

        navigator.Navigate();

    }

}

In the next part we'll walk through the Implementors and Navigator classes. (At least one of them)

Share this post : del.icio.us it! digg it! dotnetkicks it!

Comments

CodeRush Plugin - Navigate to Implementation Part 1 - .NET Geek said:

Pingback from  CodeRush Plugin - Navigate to Implementation Part 1 - .NET Geek

# October 19, 2008 7:43 AM

Mark Miller said:

With regard to the observation that the DXCore API appears to favor a "flat" approach over a hierarchical one, I would offer that the flat appearance is most distinct when looking at properties on the "ea" argument to an event. With few exceptions, most of the event args do in fact share this flat appearance, and this is intentionally part of the design to improve discoverability. Most of the deeply hierarchical aspects of the API can be accessed through the "CodeRush" object (so, for example, CodeRush.TextViews.Active.Selection.Range.Top.Line gives you the line number of the top of the selected block of code in the active TextView).

# October 19, 2008 4:17 PM

Kim said:

@Mark

I hear you, but I'm not convinced that discoverability is increased by flattening the hierarchy like for example the CheckContentAvailabilityEventArgs. I needed to interact with the menu of the Navigation provider. Realize that at that point I wasn't even sure that I was looking for a menu. I searched the source code for CodeRush for menu and only found something related to the metrics popup menu.

On the CheckContentAvailabilityEventArgs I looked for menu, but didn't find anything. I looked for something starting with Getxxx() since a lot of stuff in CodeRush are Getxxx() methods. There I found GetSubMenus(). At that point maybe I should have realized that if there's a Get there's an Add, but I reasoned that it is a GetAllOfSomething() that has been added somewhere else. My bad. Next time I know it's flat. :-)

Anyhow, The methods that are related to menus on that eventArgs are: AddSubMenuItem, GetSubMenuItems and MenuCaption. I would claim that if you put menu stuff in a Menu namespace that would increase the discoverability. I realize there's a balance between flat and deep, but IMHO 3-4 main classes/namespaces would increase discoverability.

I would prefer something like:

ea.Menu

ea.Menu.Caption // caption of main provider menu

ea.Menu.AddSubMenItem // add an item to the main provider menu

ea.Menu.GetSubMenuItems // get all items you have added to the....

Still pretty flat, but easier to comprehend IMHO.

# October 20, 2008 10:26 AM

.NET Geek said:

This is the last post in series on developing the navigate to an implementing method plugin. Here are

# December 4, 2008 12:45 AM

Mark Miller said:

Hi Kim,

You're totally right on the need to have Menu as an object inside those event args. Thanks for pointing that out. That would indeed improve discoverability.

# December 5, 2008 1:48 PM

doggie said:

Dicten, donde puedo leer sobre esto?  

http://rsfiles.servehttp.com/  

kyla

# August 25, 2011 12:24 PM
Leave a Comment

(required) 

(required) 

(optional)

(required) 


Enter the numbers above: