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. 🙂

Running Ruby And Rake (and albacore) Without Installing Them