Here's another article on writing plug-ins based on the DXCore. This plug-in is based on a request from the Refactor! Pro newsgroups, which goes like this:
Whenever I choose to automatically implement an interface, a property that throws NotImplementedException in the getter and setter appears. For example:
public int Foo
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
It would be great if there was a refactoring that let me turn this into an auto property, or a backing field property. I know this technically isn't a "refactoring" since it's actually causing the functionality of the code to change, but I find myself running into this quite a bit.
This is a great request, in fact, so good, we implemented a feature similar to this for CodeRush 9.1. So for those of you who already have CodeRush 9.1 or above, this tutorial will serve as a how-to for creating tools that generate code intelligently. For the rest of you, this tutorial will have the added benefit of producing a useful feature you can add to your Visual Studio toolset.
As the author mentions, this feature technically isn't a refactoring. That's okay, because although Refactor! Pro is all about refactoring, CodeRush includes a sweet mechanism for changing code (in ways that might result in a change to program behavior), called CodeProviders. CodeProviders are very similar to RefactoringProviders, the primary distinction is how they are presented to developers in the CodeRush/Refactor menu. For example:
In the screen shot above the CodeRush/Refactor! menu is divided into two sections. The top section in red lists refactorings, while the lower section in blue lists code providers. This visual distinction alerts developers that menu items in blue may change program behavior.
So while it might be considered bad form to create a refactoring that converts an unimplemented property into an implemented one, adding this feature as a CodeProvider makes all the sense in the world. So let's do that.
Getting Started
From the DevExpress menu, select New Plug-in....
I'm calling this plug-in project CR_ImplementProperty. I like to start all my CodeRush plug-ins with the CR_ prefix, and I should point out that because this plug-in is based on the CodeProvider technology, developers will need CodeRush to try it out.
Click OK. The DXCore Plug-in Project Settings dialog will appear.
We can simply accept the default options on this dialog and click OK.
The project will load and the plug-in design surface will be activated.
Dropping the CodeProvider
From the Toolbox, navigate to the DXCore tab and select the CodeProvider control.
Note: If you don't see this control you can add it by right-clicking the Toolbox, selecting "Choose Items...", and then placing a check next to the CodeProvider control in the DevExpress.CodeRush.Core namespace.
Double-click the CodeProvider control in the Toolbox to drop it on the plug-in design surface.
Now let's change the following properties of this CodeProvider:
Property | Value |
(Name) | cpImplementUnimplementedProperty |
AutoUndo | True |
Description | Implements an unimplemented property. |
DisplayName | Implement Property |
ProviderName | ImplementUnimplementedProperty |
Switch to the Events page of the Properties grid.
First we need to tell the system that our CodeProvider is available. We can add that logic in a handler for the CheckAvailability event.
I want this CodeProvider to be available when the caret is inside a property declared like this:
public int Foo
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
or this:
public int Foo
{
get
{
throw new NotImplementedException();
}
}
or this:
public int Foo
{
set
{
throw new NotImplementedException();
}
}
Normally when write plug-ins that work with source code, we break out the Expressions Lab. The Expressions Lab reveals the structure of the language-independent tree that represents the source code inside Visual Studio.
However, instead of starting with the Expression Lab immediately, it might be interesting to see how far we can get using only Visual Studio's Intellisense and a bit of knowledge learned from previous ventures with the Expressions Lab.
From past lessons we know that every element in the parse tree (e.g., for-loops, expressions, primitives, variables, methods, classes, etc.) either descends from LanguageElement or implements IElement. LanguageElement and many of its descendants are located in the DevExpress.CodeRush.StructuralParser namespace. For example, inside this namespace, there is a class called Property which represents property declarations in source code.
The ea argument to the CheckAvailability event includes a property called "Element", and this generally refers to the active element at the caret. By the way, this Element can also be set programmatically by plug-ins wishing to remotely invoke a refactoring or a code provider, so it is possible that this Element may actually be different from the element at the caret. So we need to resist the urge to use CodeRush.Source.ActiveProperty (which we've seen in other plug-ins in the past) because while that may work when the caret is actually in the property, checking ActiveProperty is likely to produce unexpected results if this CodeProvider is invoked remotely for a different property somewhere else in the code.
So, if Element is a Property, we expect to eventually drill down into its accessors to see if they contain a Throw, passing a NotImplementedException.
However if Element refers to a starting code element inside the property (such as the get accessor, for example), then it makes sense to find the parenting Property so we have a consistent starting point. This allows the CodeProvider to be available even when the caret is inside the unimplemented property declaration. Having a wider range of where the feature is available makes it easier to discover.
So for the purposes of identifying unimplemented properties, it is important to get a consistent starting point, regardless of whether ea.Element refers to the property or a child element of the property.
Here's the code I use to get to that Property starting point:
Property property = ea.Element as Property;
if (property == null)
property = ea.Element.GetParentProperty();
We're likely to need this later when we apply this code provider to make changes to the code, so let's place this code in its own method, like this:
private static Property GetProperty(LanguageElement element)
{
Property property = element as Property;
if (property == null && element != null)
property = element.GetParentProperty();
return property;
}
With that in place, my event handler now looks like this, containing two calls to a method named IsUnimplemented, which we'll create in a moment:
private void cpImplementUnimplementedProperty_CheckAvailability(object sender, CheckContentAvailabilityEventArgs ea)
{
Property property = GetProperty(ea.Element);
if (property == null)
return;
if (property.HasGetter && !IsUnimplemented(property.Getter))
return;
if (property.HasSetter && !IsUnimplemented(property.Setter))
return;
ea.Available = true;
}
The IsUnimplemented method is going to take the Getter or the Setter accessor, and find the first child statement and see if it's a Throw statement. IsUnimplemented looks like this:
private static bool IsUnimplemented(PropertyAccessor accessor)
{
LanguageElement firstChild = accessor.GetFirstCodeChild();
if (firstChild == null)
return true;
if (firstChild.ElementType != LanguageElementType.Throw)
return false;
return Throws(firstChild as Throw, "NotImplementedException");
}
The call to the GetFirstCodeChild() method returns the first LanguageElement that is a child of the accessor that is not a comment. Note that right after this call there's a check to see if that firstChild is null, and if so, IsUnimplemented returns true. That means our CodeProvider will also be available in these declaration scenarios (not all of which compile):
public int Foo
{
get
{
}
set
{
}
}
And this:
public int Foo
{
get
{
}
}
And this:
public int Foo
{
set
{
}
}
After that, the IsUnimplemented method compares the ElementType of the firstChild against the enum LanguageElementType.Throw. This is done for efficiency -- it's faster than comparing against the Throw class itself, and a quick way to get out of the IsUnimplemented method for the majority of Property scenarios that are in fact implemented. Performance in availability checks is something we want to maximize for CodeProviders and RefactoringProviders.
The last call in IsUnimplemented is to a method called Throws, which needs to return true if the name of the exception thrown actually matches the one passed in.
And now let's bring in the Expressions Lab. When using the Expressions Lab to write code that can find structures, I usually place samples of those structures right inside the plug-in code I'm working on, usually inside a method that is never called. This makes exploring the structure and writing code to detect that structure fast and easy. So for this example, I'll add this line to the code:
throw
new NotImplementedException();
And then check the result inside the Expressions Lab:
This three-level structure starts with a Throw object, which parents a DetailNode (in blue). That DetailNode is an ObjectCreationExpression, which in turn has a regular child node (in black) that is a TypeReferenceExpression. That TypeReferenceExpression has a Name property set to "NotImplementedException". If you click on each of these three nodes inside the Expressions Lab, you can see interesting values for the properties these nodes declare. For example, if you click on the Throw node, you can see that it has an Expression property described as "new NotImplementedException()", which happens to be the first detail node that you see in blue, above. Often you will find properties like this Expression property, which are there simply to make it easier to access the blue detail nodes of the element.
So based on this exploration, I can create a Throws method that looks like this:
private static bool Throws(Throw throwStatement, string exceptionName)
{
if (throwStatement == null)
return false;
ObjectCreationExpression expression = throwStatement.Expression as ObjectCreationExpression;
if (expression == null)
return false;
TypeReferenceExpression typeRef = expression.ObjectType;
if (typeRef == null)
return false;
return typeRef.Name == exceptionName;
}
Now it's time to test. We haven't written any code yet to actually fill in the missing implementation for the property. We've only written code to see if our CodeProvider should actually be available. So when we test, we're really only testing the availability check.
Compile and Run. A second instance of Visual Studio will start. In that instance, open a source code file that contains an unimplemented property (you might want to create a dedicated test file or project for this), and move the caret so it rests inside that property, and then press the CodeRush/Refactor! key (e.g., Ctrl+`). The Refactor/Code menu will appear, and it should look something like this:
And so here we can see our Implement Property CodeProvider is now available in the Code menu. Notice the description added previously now appears inside the light yellow hint to the right.
Now move the caret to a fully-implemented property (e.g., something with backing store), and press the CodeRush/Refactor! key to verify that in this location the Implement Property CodeProvider does not appear on the Refactor/Code menu.
Excellent!
We're getting closer. The next step is to generate the source code for a new replacement property along with appropriate backing store. This is where things get interesting.
Generating the Implemented Property
We've already seen that when you're creating plug-ins that work with source code, it is possible to deal with that code in a language-independent manner. This means that with the code we've written so far, we would expect to see our Implement Property CodeProvider appear in any other language that supports properties, such as Visual Basic.
It makes sense to have the replacement property generated in a similar language-independent way, so this works regardless of what programming language is active inside of Visual Studio.
If the second instance of Visual Studio is still running, you can close that down.
Let's handle the Apply event. This event will be fired when you select "Implement Property" from the Code menu. To handle this event, back in the original instance of Visual Studio, activate the PlugIn1.cs [Design] surface, click the cpImplementUnimplementedProperty control, and then on the Events page of the Properties grid, double-click on the Apply event to create a new handler.
The DXCore supports language-independent code generation through a number of API access points, the most robust of which is an ElementBuilder. One of the cool things about the DXCore, is that many of the events pass specialized EventArg descendants filled with useful methods and properties. One of the methods passed through the ApplyContentEventArgs is the NewElementBuilder method. This method creates a new instance of an ElementBuilder, one dedicated to building parse trees for the active language. You can also get an ElementBuilder from the CodeRush.Language service.
Inside the Apply event handler, type in "ea.", and then select the "NewElementBuilder" method.
Add the missing parens and the semi-colon (tip: just press the open paren key and then the semi-colon -- CodeRush will add the closing paren and place the semi-colon outside the parens).
Next, press the CodeRush/Refactor! key and select Declare Local.
And you'll get a line of code looking like this:
ElementBuilder newElementBuilder = ea.NewElementBuilder();
By the way, Declare Local, a feature that you've likely seen in CodeRush already, is a CodeProvider, just like the feature we're building now.
Now that we have our ElementBuilder, let's add some elements. But to do this we need more information about the property we'll be creating. For example, we need to know its type, its name, and whether it has a getter and/or a setter. We can get all that information from the property we're replacing. How can we get to the property structure so we can query it? We can simply call that GetProperty method we added before, passing in the ea.Element:
Property oldProperty = GetProperty(ea.Element);
The ea argument for the Apply event also includes an Element property. Cool.
OK, so now we have a reference to the oldProperty (the one we'll be replacing), and we have an ElementBuilder. Based on these two, I now have an Apply event handler that looks like this:
private void cpImplementUnimplementedProperty_Apply(object sender, ApplyContentEventArgs ea)
{
Property oldProperty = GetProperty(ea.Element);
if (oldProperty == null)
return;
ElementBuilder elementBuilder = ea.NewElementBuilder();
string newCode = GetNewPropertyDeclaration(elementBuilder, oldProperty);
ea.TextDocument.Replace(oldProperty.Range, newCode, "Implement Property", true);
}
We'll discuss the last line of code in this method a little later. For now we'll focus on the call to GetNewPropertyDeclaration.
The GetNewPropertyDeclaration method needs to generate a new property declaration based on the oldProperty. This method needs to generate a new property based on the existing one, replacing the calls to throw exceptions with real code that accesses the backing store of a field variable. Let's take this method a piece at a time, and then at the end we'll see the whole thing as a single method.
First, we need to get the name and type of the property to declare, as well as an appropriate name for the field variable. That code looks like this:
private static string GetNewPropertyDeclaration(ElementBuilder elementBuilder, Property oldProperty)
{
string propName = oldProperty.Name;
string typeName = oldProperty.GetTypeName();
string fieldVariableName = CodeRush.Strings.Get("FormatFieldName", propName);
...
}
The most interesting line of code here is the the call into CodeRush.Strings.Get. This is turn makes a call into a StringProvider named "FormatFieldName". This StringProvider formats the passed-in parameter (propName) so it matches your style for field declarations. You can change the style for identifiers in the Editor\Code Style\Identifiers options page.
By the way, this StringProvider is used in a number of templates, including templates that generate properties with backing store. You can find documentation for this and other StringProviders in the CodeRush User Guide, in the Reference\String Providers section.
Here's what you will see when you click the FormatFieldName link:
Next, we need to add the backing store declaration to the elementBuilder, like this:
Variable fieldVar = elementBuilder.AddVariable(null, typeName, fieldVariableName);
fieldVar.IsStatic = oldProperty.IsStatic;
So the AddVariable method on ElementBuilder generates a tree structure that looks like this:
It's a Variable named "_Foo" (or whatever value you pass in through fieldVariableName) with a DetailNode that is a TypeReferenceExpression named "int" (or whatever value you pass in through typeName). When generated, this tree structure will become this in C#:
int _Foo;
Next we need to add the property declaration to the elementBuilder, like this:
Property newProperty = elementBuilder.AddProperty(null, typeName, propName);
newProperty.Visibility = oldProperty.Visibility;
newProperty.IsStatic = oldProperty.IsStatic;
newProperty.IsVirtual = oldProperty.IsVirtual;
newProperty.IsOverride = oldProperty.IsOverride;
newProperty.IsExplicitInterfaceMember = oldProperty.IsExplicitInterfaceMember;
ElementBuilder's Add methods create the specified element and then add that element to the specified parent (the first parameter to the AddXxxxxxx methods). In both of these cases so far, the first parameter passed in was null, which means these elements will be added to ElementBuilder's TopLevelElements collection. Later, when we call ElementBuilder's GenerateCode method, code will be generated for all the LanguageElements contained in the TopLevelElements collection.
Notice also in the code above how we can modify the references returned by ElementBuilder's AddXxxxxx calls. ElementBuilder's AddProperty method generates a tree structure that looks like this:
It's a Property named "Foo" (or whatever value you pass in through propName) with a DetailNode that is a TypeReferenceExpression named "int" (or whatever value you pass in through typeName). This Property element does not yet have a getter or a setter, so let's add those code blocks to the newProperty reference (the local variable that gets the result of the AddProperty call).
Here's code to add the getter:
if (oldProperty.HasGetter)
{
Get getter = elementBuilder.AddGetter(newProperty);
elementBuilder.AddReturn(getter, fieldVariableName);
}
By storing the result of an ElementBuilder.AddXxxxxx call, and then later passing that LanguageElement back in as the parent (first argument) in subsequent ElementBuilder.AddXxxxxx calls, we are able to generate hierarchical structures to match the code we want. The call to AddReturn, above, generates a return statement returning the passed-in fieldVariableName. The tree for the getter looks like this:
And corresponds to code like this in C#:
get
{
return _Foo;
}
Next we'll add the setter, if necessary:
if (oldProperty.HasSetter)
{
Set setter = elementBuilder.AddSetter(newProperty);
elementBuilder.AddAssignment(setter, fieldVariableName, "value");
}
The call to AddAssignment, above, creates a statement that assigns "value" to the fieldVariableName. The tree for this segment looks like this:
So far, the tree we've built using the ElementBuilder looks like this (with _Foo, Foo, and int being replaced with the values you've passed in for field variable name, property name, and type name respectively):
Finally we can get all of the generated code as a string so we can insert it into the document, by calling GenerateCode:
return elementBuilder.GenerateCode();
The entire method looks like this:
private static string GetNewPropertyDeclaration(ElementBuilder elementBuilder, Property oldProperty)
{
string propName = oldProperty.Name;
string typeName = oldProperty.GetTypeName();
string fieldVariableName = CodeRush.Strings.Get("FormatFieldName", propName);
Variable fieldVar = elementBuilder.AddVariable(null, typeName, fieldVariableName);
fieldVar.IsStatic = oldProperty.IsStatic;
Property newProperty = elementBuilder.AddProperty(null, typeName, propName);
newProperty.Visibility = oldProperty.Visibility;
newProperty.IsStatic = oldProperty.IsStatic;
newProperty.IsVirtual = oldProperty.IsVirtual;
newProperty.IsOverride = oldProperty.IsOverride;
newProperty.IsExplicitInterfaceMember = oldProperty.IsExplicitInterfaceMember;
if (oldProperty.HasGetter)
{
Get getter = elementBuilder.AddGetter(newProperty);
elementBuilder.AddReturn(getter, fieldVariableName);
}
if (oldProperty.HasSetter)
{
Set setter = elementBuilder.AddSetter(newProperty);
elementBuilder.AddAssignment(setter, fieldVariableName, "value");
}
return elementBuilder.GenerateCode();
}
Before we test this, let's look at that last line of code in our Apply event handler:
ea.TextDocument.Replace(oldProperty.Range, newCode, "Implement Property", true);
This line calls Replace on the active TextDocument. Replace accepts a SourceRange of code to replace (in this case, the oldProperty), the newly-generated code, a display string to describe the operation (for the undo/redo stack), and an optional parameter to specify whether the newCode we pass in should be formatted or not. Visual Studio's active language service (e.g., C#, Visual Basic, etc) performs the formatting based on formatting options you've defined for that language in the Visual Studio options dialog. In most cases you will want to pass in true for this last parameter, as that will ensure the generated code is properly indented and matches the coding styles defined in Visual Studio.
And that wraps up the explanation of the code needed to generate the implemented property. It's time to test once again.
Testing Code Generation
Click Run. In the second instance of Visual Studio, find an unimplemented property and press the CodeRush/Refactor key to apply this CodeProvider.
Starting with this sample code in C#...
public int Foo
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
After applying Implement Property I get this:
int _Foo;
public int Foo
{
get
{
return _Foo;
}
set
{
_Foo = value;
}
}
Nice!
Undo and Redo
Notice also right after applying this CodeProvider, the top entry in the undo stack reads "Implement Property":
The undo entry has been added for us automatically through the call to TextDocument.Replace.
Adding a Preview Hint
The only thing missing to make this a professional-grade CodeProvider is a preview hint. Preview hints show what will happen before the menu item is selected. For example, here's the preview hint for Extract Method for a selection inside our GetNewPropertyDeclaration method:
Let's add that now. Close down the second instance of Visual Studio if it's still running. Activate PlugIn1.cs [Design] and select the cpImplementUnimplementedProperty CodeProvider.
Double-click the PreparePreview event to create a handler....
The PreparePreview event is fired whenever the mouse hovers over an item in the CodeRush/Refactor menu, or when the keyboard is used to highlight entries and an entry has been highlighted for more than a few moments. So when this event fires your CodeProvider has already determined that it is available. So the sequence of events usually looks like this:
CheckAvailability -> PreparePreview -> Apply
Note that it can also be like this if the menu item is selected very quickly or if only one refactoring or code provider is available:
CheckAvailability -> Apply
And it can also be like this if a refactoring or code provider is invoked programmatically by another plug-in:
Apply
The code for our PreparePreview event handler looks like this:
private void cpImplementUnimplementedProperty_PreparePreview(object sender, PrepareContentPreviewEventArgs ea)
{
Property oldProperty = GetProperty(ea.Element);
if (oldProperty == null)
return;
ElementBuilder elementBuilder = ea.NewElementBuilder();
string newCode = GetNewPropertyDeclaration(elementBuilder, oldProperty);
ea.AddCodePreview(oldProperty.Range.Start, newCode.Trim());
ea.AddStrikethrough(oldProperty.Range);
}
Notice starts off similarly to the code in our Apply event handler. We get the Property to refactor, and get the source code that will replace it. The most interesting part of this handler are the last two lines.
The first, a call to AddCodePreview, creates a floating window containing the specified source code with a small arrow which points to the specified SourcePoint (in this case the start of the oldProperty).
The last line calls AddStrikethrough, which crosses out the code within the specified range.
Run and test. You should get something like this:
Language Independence
In the CodeProvider we just built you'll notice that we didn't write any code specific to any particular programming language. We just built the tree we wanted to generate using the ElementBuilder. As a result, this CodeProvider will work in any language supported by the DXCore (any language that also supports properties, classes, and field variables). To prove this, open up a Visual Basic file and try it out. You should see something like this:
That's it! I hope this helps make it easier for you to add your own refactorings and code providers to the DXCore! Let me know if you have any questions or would like to see more tutorials like this in the future.
Free DevExpress Products - Get Your Copy Today
The following free DevExpress product offers remain available. Should you have any questions about the free offers below, please submit a ticket via the
DevExpress Support Center at your convenience. We'll be happy to follow-up.