Eliminating Repetitious Coding With Vim / ViEmu And Macros
Anyone that has been following me on twitter recently is probably aware that I’ve been trying to learn Vim and ViEmu for Visual Studio. It’s been a very slow, somewhat painful learning process, but I think it is finally starting to pay off. I won’t bore you with most of the details as you can find plenty of stories on people’s converting to vim, around the web. What I did want to share was that I just learned how to cut down on serious code repetition using vim’s macros.
The Code That Needs To Change
Here’s the code that I started with. It’s from the code behind of a view implementation in a Model-View-Presenter setup.
1: public void SelectCategory(Lookup category)
2: {
3: cboCategories.SelectedItem = category;
4: }
5:
6: public void SelectGroup(Lookup group)
7: {
8: cboGroups.SelectedItem = group;
9: }
10:
11: public void SelectAssetType(Lookup assetType)
12: {
13: cboTypes.SelectedItem = assetType;
14: }
15:
16: public void SelectProductCode(Lookup productCode)
17: {
18: cboProductCodes.SelectedItem = productCode;
19: }
</div> </div>
Notice that each of these 4 methods is basically the same line of code – just applied to a different combobox, with a differen Lookup object being selected. It’s not exactly rocket science, here… pretty straight forward.
Bugs And Refactoring
There’s a bug in some of the surrounding code caused by the combobox “IndexChanged” events being fired every time I set the selected item with this code. Additionally, there are some visual artifacts that are semi-related to this code (due to this code running on the Compact Framework for Windows Mobile and Windows CE devices) and I want to clean them up.
To fix these issues, I need to call a “TearDownComboBoxEvents()” as the first line of each method, and then call “Application.DoEvents()” and “SetupComboBoxEvents()” as the last two lines of each method. Rather than re-type those three lines of code over and over again, I want to extract them into a method that each of the above methods will call, providing the correct combobox and item to the method. This code provides that functionality:
1: private void SetSelectedItem(ComboBox comboBox, Lookup lookup)
2: {
3: TeardownComboBoxEvents();
4: comboBox.SelectedItem = lookup;
5: Application.DoEvents();
6: SetupComboBoxEvents();
7: }
</div> </div>
Putting It All Together With A ViEmu Macro
Now that I have the method I want to call, I just need to make each of the Select methods from the first code snippet pass in the right combobox and lookup item. A few weeks ago… well, ok… yesterday morning… I would have manually modified each of these methods. This would involve re-typing “SetSelectedItem” 4 times (or at least copy & pasting it 4 times), deleting the “.SelectedItem = “ 4 times, adding a comma between the combobox name and the lookup variable 4 times, and inserting a closing parenthesis just in front of the closing semi-colon 4 times… all while using the arrow keys dozens of times to move my cursor around to make those changes.
That’s a lot of repetition and I really wanted to learn how to avoid it… so… in comes our new hero, Vim/ViEmu and macros! Here’s what I did to solve this with a macro. First, I set my cursor (in “Normal” mode) on the very first character of the “cboCategories” variable in line 3 of the first code snippet. I then recorded this macro:
qaiSetSelectedItem(
2wc3w, $i) q</strong> </p> </blockquote> After recording that macro, I was able to place my cursor (again, in “Normal” mode) on the first character of each of the subsequent combobox variables in the other 3 Select methods and run the macro by typing:
@a
that’s right… 2 characters to replay the macro I just wrote. That’s 30+ keystrokes boiled down into 2 for all of the subsequent method call changes! I save 90+ keystrokes by recording this macro and replaying it against the other 3 lines that needed it. You want to talk about DRY code? Yeah, let’s talk about DRY code writing for a bit… 🙂
The resulting Code
Here’s the results of running this macro on the methods:
1: public void SelectCategory(Lookup category)
2: {
3: SetSelectedItem(cboCategories, category);
4: }
5:
6: public void SelectGroup(Lookup group)
7: {
8: SetSelectedItem(cboGroups, group);
9: }
10:
11: public void SelectAssetType(Lookup assetType)
12: {
13: SetSelectedItem(cboTypes, assetType);
14: }
15:
16: public void SelectProductCode(Lookup productCode)
17: {
18: SetSelectedItem(cboProductCodes, productCode);
19: }
20:
21: private void SetSelectedItem(ComboBox comboBox, Lookup lookup)
22: {
23: TeardownComboBoxEvents();
24: comboBox.SelectedItem = lookup;
25: Application.DoEvents();
26: SetupComboBoxEvents();
27: }
</div> </div>
It’s all correct… each method is calling SetSelectedItem with the right combobox and the right lookup object.
“That Was Easy”
This is quite possibly the most basic of useful macros that I could imagine… yet look how powerful it was. It prevented me from having to copy & paste and then change all the variable names to the correct ones, or re-type the same thing over and over again. I’m not even using anything more than some very basic movements in that macro: move 2 words, change 3 words, move to the end of the line… those are the only movements I’m using… no special cool extra awesome sauce here… just some simple ‘move my cursor a few paces’. Because the code I was working with had a regular pattern to it, I was able to easily craft a macro that accounted for the contextual differences in each of the methods that needed to be changed.
If you’re still not sold on the power of Vim/ViEmu… well… have fun with those 90+ extra keystrokes. I’ll keep my Vim/ViEmu, thankyouverymuch. 🙂