Click here to Skip to main content
15,886,137 members
Articles / Programming Languages / C#
Article

Building a Refactoring Plugin for VS.NET

Rate me:
Please Sign up or sign in to vote.
4.78/5 (10 votes)
1 Jul 20035 min read 112.1K   1.5K   50   26
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

C#
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

C#
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.

C#
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 

C#
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:

C#
  // 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.

C#
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:

C#
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:

C#
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.

C#
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


Written By
Web Developer
Australia Australia
I am a Software Engineer/Consultant. My work is focussed on helping teams to get more out of their work. So I teach how to do requirements, analysis and design in a format that is easy to understand and apply.
I help with testing too, from starting developers on automated unit testing to running whole testing teams and how they cooperate with development.

For really big projects I provide complete methodologies that support all of the lifecycle.

For relaxation I paddle a sea kayak around Sydney and the Central Coast or write utilities on rainy days to make my life easier.

Comments and Discussions

 
Generalmanipulation of FunctionBody Pin
sebastian_der_kleene17-Aug-06 0:06
sebastian_der_kleene17-Aug-06 0:06 
GeneralRe: manipulation of FunctionBody Pin
Stephan Meyn17-Aug-06 2:09
Stephan Meyn17-Aug-06 2:09 
QuestionHow do we remove from the .NET IDE? Pin
rcm830721-Apr-06 3:53
rcm830721-Apr-06 3:53 
GeneralCan´t see the MakeProperty Add in in the tools menu Pin
Sebastian Streiger11-May-05 3:21
Sebastian Streiger11-May-05 3:21 
GeneralRe: Can´t see the MakeProperty Add in in the tools menu Pin
Stephan Meyn11-May-05 13:07
Stephan Meyn11-May-05 13:07 
GeneralRe: Can´t see the MakeProperty Add in in the tools menu Pin
Sebastian Streiger12-May-05 2:04
Sebastian Streiger12-May-05 2:04 
GeneralIn VB.NET huge problem Pin
rickardr17-Mar-05 1:17
rickardr17-Mar-05 1:17 
GeneralRe: In VB.NET huge problem Pin
rickardr17-Mar-05 1:22
rickardr17-Mar-05 1:22 
GeneralDoesn't work for files in folders Pin
Keeper of the Forest12-May-04 14:32
Keeper of the Forest12-May-04 14:32 
GeneralCodeElement property. Pin
Scott Munro22-Apr-04 1:48
Scott Munro22-Apr-04 1:48 
GeneralDoes not work for codeelements other than variables Pin
angus_grant7-Mar-04 19:03
angus_grant7-Mar-04 19:03 
GeneralRe: Does not work for codeelements other than variables Pin
Anonymous8-Mar-04 23:36
Anonymous8-Mar-04 23:36 
GeneralA small request Pin
Marc Clifton8-Jul-03 2:11
mvaMarc Clifton8-Jul-03 2:11 
AnswerRe: A small request Pin
schwinbp7-Mar-06 11:07
schwinbp7-Mar-06 11:07 
GeneralMakeProperty does not show up Pin
Christian Weyer5-Jul-03 10:52
Christian Weyer5-Jul-03 10:52 
GeneralRe: MakeProperty does not show up Pin
Stephan Meyn6-Jul-03 2:41
Stephan Meyn6-Jul-03 2:41 
GeneralRe: MakeProperty does not show up Pin
Christian Weyer6-Jul-03 7:25
Christian Weyer6-Jul-03 7:25 
GeneralRe: MakeProperty does not show up Pin
tonga10-Jul-03 8:51
tonga10-Jul-03 8:51 
GeneralRe: MakeProperty does not show up Pin
Stephan Meyn14-Jul-03 22:19
Stephan Meyn14-Jul-03 22:19 
GeneralRe: MakeProperty does not show up Pin
Anna-Jayne Metcalfe17-Jul-03 4:02
Anna-Jayne Metcalfe17-Jul-03 4:02 
GeneralRe: MakeProperty does not show up Pin
jtobler15-Jul-03 12:15
jtobler15-Jul-03 12:15 
GeneralRe: MakeProperty does not show up Pin
Henke7221-Jun-04 21:51
Henke7221-Jun-04 21:51 
GeneralNice! Pin
dratti2-Jul-03 2:51
dratti2-Jul-03 2:51 
GeneralSchweet Pin
Paul Watson2-Jul-03 2:07
sitebuilderPaul Watson2-Jul-03 2:07 
GeneralRe: Schweet Pin
Stephan Meyn3-Jul-03 3:09
Stephan Meyn3-Jul-03 3:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.