Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Building a Refactoring Plugin for VS.NET

0.00/5 (No votes)
1 Jul 2003 1  
This article describes how to develop a refactoring addin for Visual Studio.NET

Introduction

Whenever I code I regularly find myself in a situation where I need to turn a variable into a property. I know I should write all class attributes that are publicly visible as properties but it is more hassle than just declaring a variable public. To make my life easier I turned to Visual Studio's Extensibility framework and built an add-in that automates this exercise for me. Every VBA programmer knows that you can create routines within its office suite to automate everyday tasks. The same holds for Visual Studio. You can write macros using VBA and interact with the projects and files in your project. But you do not have to use VBA - you can write add-ins in your .NET language of choice. Being a right snob I write mine in C#.

Let's assume in my source file in visual studio I have a variable called Balance

public Currency Balance; 

To convert it into a property I place the cursor on the variable and select in the tool menu my menu item called 'Make Property'.

The result then should look as follows

public Currency Balance
{
  get { return _Balance;}
  set { _Balance = value;} 
}
private Currency _Balance;

Creating the Add-in Project in VS.NET

In the New Project Dialog  choose Other Project > Extensibility Projects > Visual Studio.Net Add-In and name it MkProperty.

The New Project Wizard then leads you through a series of questions such as what the add-in should be called in the tools menu. Make sure you check the 'create a tools menu item' option.

Once finished the wizard will create the appropriate class and set up the OnConnect routine which will ensure the add-in will be accessible through the tools menu.

Using the code

Now comes the interesting part. When the menu Make Property is called the Exec method is invoked. In the Exec method I call makeProperty()  From now on I will discuss what happens in bool makeProperty():

The first thing the method needs to do is to find where the cursor is and determine that it is located on a variable.

private bool makeProperty()
{
  // identify where the cursor is

  TextSelection selection = 
    (TextSelection) applicationObject.ActiveWindow.Selection;
  // get the starting point

  EditPoint Start = selection.TopPoint.CreateEditPoint();
  // get the element under the cursor

  CodeElement element = applicationObject.ActiveDocument.ProjectItem.
     FileCodeModel.CodeElementFromPoint(Start, 
     vsCMElement.vsCMElementVariable);

....

This identifies a CodeElement that represents whatever is under the cursor. From there I  establish that this is a variable declaration and cast it to a CodeVariable 

  if (element.Kind == vsCMElement.vsCMElementVariable)
  {
     CodeVariable theVar = element as CodeVariable;

The variable named 'theVar' is a CodeVariable which is one of the automation objects that makes life so easy. At this point the method needs to do two things:

  1. Find out the name of the variable (which will become the name of the new property)
  2. rename the variable (by adding an underscore in front of it)

Look at how easy the Extensibility framework makes this:

  // get the variables name

  string propertyName = theVar.Name;
  // rename it 

  theVar.Name = "_" + propertyName;

Wasn't that easy??

The next step is to retrieve the accessibility (the new property needs to have the same accessibility) and to change it to a private variable.

  vsCMAccess propertyAccess = theVar.Access;
  // make the variable private

  theVar.Access = vsCMAccess.vsCMAccessPrivate;

Now the method has to create the property. In preparation it has to get the type of the variable and to determine the CodeElement that represents the owning class:

  string varTypeName = theVar.Type.AsString;
  CodeClass classElement = theVar.Parent as EnvDTE.CodeClass;

As you might expect, the CodeElement of the owning class is the parent of the CodeElement representing the variable.

 Now to create the property:

  CodeProperty newProperty = classElement.AddProperty(
    "A", "A", varTypeName,element,propertyAccess,element);
  newProperty.Name = propertyName;

If you are starting to get confused by the parameters in this call, don't worry. Here we are getting to the rougher edges of the Automation framework.

The first two parameters "A" and "A" are the getter and setter names for the property. As they are anonymous functions in C# we don't have to worry about them. The third parameter is the type which is the same type as the original variable. The fourth parameter gives the position. As we want to insert the property right next to the variable it is sufficient to simply provide the CodeElement representing the variable.

The last step is to actually specify the bodies of the get and set methods. Unfortunately I have not been able to find a similarly elegant approach that actually worked. Instead the following code simply pastes in the raw code that is specified here.

  string getExpression = "get {  return " +  var.Name + "; }";
  EditPoint ep = newProperty.Getter.StartPoint.CreateEditPoint();
  ep.ReplaceText(newProperty.Getter.EndPoint, 
       getExpression, vsEPReplaceTextOptionsAutoFormat);

While this is ugly compared to the previous bits it does redeem itself by providing the option to nicely format the code upon inserting it.

What is left now is to compile and deploy. VS.NET helps a lot, but sometimes it gets itself into a knot. Make sure you read the instructions in the generated code to help you out in case things go all out of shape.

When you build and run the project VS.NET will start another copy of itself. You should find in the Tools menu an entry called MakeProperty. Open a project (not just a file, it has to be a project otherwise the CodeElements won't be accessible) and place the cursor over a class variable. Select Tools->Make Property and voila! 

Points of Interest

When you create the project, the Wizard also creates an installation project. If you get in a knot with VS.NET and it starts misbehaving then select the install project, right click and select Uninstall in the popup menu.

Likewise, before completing the project, fill in the properties in the install project with a project name and your company name. This will affect the behaviour of the created MSI file as it takes these two parameters to determine the install path.

At the bottom of the source file you will find a routine called message(string msg). This is a simple utility that outputs a message into the VS.NET window called Output.

There is a lot of information in the Visual Studio.NET HELP. You will find it in:

Visual Studio.NET
  Developing with Visual Studio.NET
    Manipulating the Development Environment
and
    Reference

Next time I'll be writing an article on generating typed collections in VS.NET. Cheers

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here