In Part 1, I showed the hardware I intend to use and gave an preview of functionality we expect to add. I’ve ordered the keyboard and it’s on the way. While we wait for it to arrive, I’m going to create some features that we’ll bind once it gets here. I thought I would start with Paired Delimiter buttons on the left:
Image may be NSFW.
Clik here to view.
This section of buttons will get us quick access to characters that often show up paired in programming. Pressing any of these keys gives you a complete pair, however the keys in the right column place the caret after the closing delimiter.
The keys in the left column place the caret between the two characters (with a field that gets you outside when you press Enter) so you can add the code you need and get out quickly.
Pressing either key when there is a selection embeds the selection in the pair, with the caret placed on the side of the modified text that corresponds to the column of the button that was pressed (left or right).
So the action we’re going to build is going have one of four behaviors, depending upon whether there is a selection or not and which key you press:
Behavior | Left key pressed | Right key pressed |
No Selection | Inserts delimiters, caret between inside field | Inserts delimiters, caret after (no field) |
Selection Exists | Wraps selection in delimiters, caret at start | Wraps selection in delimiters, caret at end |
So to make this functionality work, we will need to create a plug-in for our IDE, and in the plug-in we need to register at least one command (that we can later bind to these shortcuts) that will add paired delimiters as we’ve spec’d out.
I’m using Visual Studio and CodeRush, but if you’re using something else, consult your extensibility documentation for how you can do the equivalent of what we’ve building here.
- From the DevExpress CodeRush menu, select “New Plug-in…”.
- Name the plug-in CR_KeyFeatures and click OK. This plug-in is going to hold all the new features we build.
Image may be NSFW.
Clik here to view. - On the Project Settings dialog, click OK. No changes here.
Image may be NSFW.
Clik here to view.
The wizard will generate the plug-in project and open it. Inside Visual Studio, you’ll see a design surface where you can drop IDE-enhancing controls for your plug-in. - From the DXCore section of the toolbox, drop an Action onto the design surface.
Image may be NSFW.
Clik here to view.
Actions can do anything. You just provide the code. And Actions can be bound to shortcuts. You probably see where this is going. Next, we need to fill out some properties for our Action. - Select the Action control if needed, and set the following properties:
Property Value Name actSmartPairedDelimiters ActionName SmartPairedDelimiters Description Inserts or wraps code inside paired delimiters, passed as parameters to this action. - Next, we need to add parameters to allow us to specify the leading and trailing delimiters, as well as which key was pressed – left or right. Select the Parameters property and click the “…” button.
Image may be NSFW.
Clik here to view. - We’re going to add three parameters. For each parameter, click “Add”, then change the Name and Description properties as follows:
Parameter Name Parameter Description LeadingDelimiter The leading delimiter to insert. TrailingDelimiter The trailing delimiter to insert. CaretPosition The position of the caret after insertion. Can be either “Left” or “Right”.
The Parameter Collection Editor should now look like this:
Image may be NSFW.
Clik here to view.
Click OK. - Nice. OK, our properties are all set. Now let’s click the Events button to see the events for this action.
Image may be NSFW.
Clik here to view. - Double-click the Execute event to attach a handler to it.
Image may be NSFW.
Clik here to view. -
Add the following code inside your event handler:
private void actSmartPairedDelimiters_Execute(ExecuteEventArgs ea) {
string leadingDelimiter = GetParameter("LeadingDelimiter");
string trailingDelimiter = GetParameter("TrailingDelimiter");
CaretPlacement caretPosition = GetCaretPosition();
if (CodeRush.Selection.Exists)
WrapSelectionInDelimiters(leadingDelimiter, trailingDelimiter, caretPosition);
else
InsertDelimiters(leadingDelimiter, trailingDelimiter, caretPosition); } -
Add the following support methods:
static void WrapSelectionInDelimiters(string leadingDelimiter, string trailingDelimiter, CaretPlacement caretPosition)
{
TextDocument activeTextDocument = CodeRush.Documents.ActiveTextDocument;
if (activeTextDocument == null)
return;
SourcePoint savePoint = SourcePoint.Empty;
if (caretPosition == CaretPlacement.Left)
{
TextView activeView = activeTextDocument.ActiveView;
if (activeView != null)
savePoint = activeView.Selection.Range.Top;
}
string undoMessage = String.Format("Embed {0}{1}", leadingDelimiter, trailingDelimiter);
activeTextDocument.EmbedLineFragmentSelection(leadingDelimiter, trailingDelimiter, undoMessage, false);
if (savePoint != SourcePoint.Empty)
CodeRush.Caret.MoveTo(savePoint);
}
static string GetExpansion(string leadingDelimiter, string trailingDelimiter, CaretPlacement caretPosition)
{
string expansion;
if (caretPosition == CaretPlacement.Left)
expansion = String.Format("{0}«Caret»«Field()»{1}«FinalTarget»", leadingDelimiter, trailingDelimiter);
else
expansion = String.Format("{0}{1}«Caret»", leadingDelimiter, trailingDelimiter);
return expansion;
}
static void InsertDelimiters(string leadingDelimiter, string trailingDelimiter, CaretPlacement caretPosition)
{
TextDocument activeTextDocument = CodeRush.Documents.ActiveTextDocument;
if (activeTextDocument == null)
return;
CodeRush.UndoStack.BeginUpdate(String.Format("Smart {0}{1}", leadingDelimiter, trailingDelimiter));
try
{
activeTextDocument.ExpandText(CodeRush.Caret.SourcePoint, GetExpansion(leadingDelimiter, trailingDelimiter, caretPosition));
}
finally
{
CodeRush.UndoStack.EndUpdate();
}
}
string GetParameter(string paramName)
{
string parameterValue = actSmartPairedDelimiters.Parameters.GetString(paramName);
if (parameterValue == "\\\"") // un-escape quotes (convert \" to ").
parameterValue = "\"";
return parameterValue;
}
CaretPlacement GetCaretPosition()
{
if (GetParameter("CaretPosition") == "Right")
return CaretPlacement.Right;
else
return CaretPlacement.Left;
} -
And this enumeration:
public enum CaretPlacement
{
Right,
Left
} -
Now run the project. This will start a second instance of Visual Studio. Since we don’t have the keyboard yet, let’s test by setting up some temporary shortcut bindings to our new action. I used Ctrl+Shift+[ and Ctrl+Shift+]. Both of these shortcuts bind to the SmartPairedDelimiters command. Only the parameters change for the left and right keys.
Image may be NSFW.
Clik here to view.
Here’s a list of the parameters I used to test:To Test: Left Key Parameters Right Key Parameters Smart Braces {,},Left {,},Right Smart Parens (,),Left (,),Right
Smart Brackets [,],Left [,],Right Smart Quotes \",\",Left \",\",Right Smart Angle Brackets <,>,Left <,>,Right
For testing I moved the caret through the editor and pressed the shortcuts (Ctrl+Shift+[ and Ctrl+Shift+]), testing with both selections and no selection. Then I went back into the options dialog and changed the parameters for the two shortcuts and repeated.
Updating and Rebuilding in Future Sessions
As we progress through this series we will add new features and make changes to existing features in this plug-in. When it’s time to compile, you may see errors that the DLL is locked. This can happen when you restart the instance of Visual Studio where you built the plug-in. Restarting causes CodeRush to load all the new plug-ins, which means the plug-in you just built will be loaded into memory (and locked).
Here’s how you can quickly delete the CR_KeyFeatures plug-in (useful whenever you need to recompile and it is locked):
-
From inside Visual Studio’s DevExpress menu, select CodeRush | About.
- Right-click the Orange background and choose “Open Plug-ins folder…”.
Image may be NSFW.
Clik here to view.
Two Explorer windows will appear, each containing plug-ins from two different directories. -
Close the About box and shut down all instances of Visual Studio.
- Inside the Explorer windows, find the CR_KeyFeatures.dll and delete it.
Now you can start Visual Studio and load the CR_KeyFeatures solution to compile, edit, or debug it.
Next Time
OK, we’ve made some good progress. We wrote a useful feature that will be bound to ten of the keys on our keyboard. According to UPS tracking, the keyboard should be here tomorrow. So it won’t be long before we’ll be setting up the keyboard and bind our new features to the new keys. See you tomorrow.
Image may be NSFW.
Clik here to view.
Clik here to view.