sponsor Vim development Vim logo Vim Book Ad

intermediate Tip #589: Vim as refactoring tool (with examples in C#)

 tip karma   Rating 259/87, Viewed by 11134 

Read and edit this tip on the Vim tip wiki. The wiki may have a more recent version of this tip.

created:   October 16, 2003 9:52      complexity:   intermediate
author:   Klaus Horsten      as of Vim:   6.0

You can use vim as a refactoring tool.

The advantages are:

1. You automatisate repetitive writing tasks
2. You learn refactoring

You can expect much from a refactoring tool but if you have a look at the
commercial refactoring tools there is much (not all!) vim can do too.

Whatever your opinion is, my experience is that vim helps to refactor.

I give you three examples, all in C#.

Example 1:

Anti-sphagetti code weapon or the "Extract Method" refactoring.

Sphagetti code example:

public string CreateMenu(string startMenu,string file)
{
string strOutput = "";
int i = 0;
ArrayList startArray = new ArrayList();
string strVariable = "";
string strTemp = "";

XmlDocument XMLDoc = new XmlDocument();
try {
XMLDoc.Load(file);
}
catch (Exception e)
{
strOutput = e.GetBaseException().ToString();
return strOutput;
}

XmlNodeList nodeList = XMLDoc.DocumentElement.ChildNodes;

...

Imagine 50 lines of code here.

Use the "extract method refactoring" to make a "composed method".

I use a vim function (see below) to build the exracted method.

I highlight the code part I want to extract and press \em (for e-xtract m-ethod).

A dialog appears and asks me how to name the new method.

I type in "GetXmlDocumentFrom" and do get this:

// = GetXmlDocumentFrom();
private GetXmlDocumentFrom()
{
XmlDocument XMLDoc = new XmlDocument();
try {
XMLDoc.Load(file);
}
catch (Exception e)
{
strOutput = e.GetBaseException().ToString();
return strOutput;
}
// return ;
}

Now I have time to think what parameters the method needs and what to return.

I end up with the following function and  remove it from the original function:

private XmlDocument GetXmlDocumentFrom(string XmlFile)
{
XmlDocument XMLDoc = new XmlDocument();
string strOutput = "";
try
{
XMLDoc.Load(XmlFile);
}
catch (Exception e)
{
strOutput = e.GetBaseException().ToString();
ErrorMessage(strOutput);
}
return XMLDoc;
}

In the original code I put two lines.

XmlDocument XMLDoc = new XmlDocument();
XMLDoc = GetXmlDocumentFrom(XmlFile);

So I reduced the original code for 8 lines and made it clearer what the code does.

I do this with the rest of the code again and again.

Since the class gets bloated because of the many new methods I later will use
the "Extract Class" refactoring to put this method in an own XmlDocument-class.

This has the advantage that our new function is also available for other
similar purposes.

I will create the new class also with the help of vim, the actual extracting of
the method into the new class is just a matter of copy & paste.

Here is the vim-code:

vmap \em :call ExtractMethod()<cr>
function! ExtractMethod() range
let name = inputdialog("Name of new method:")
'<
exe "normal O\<bs>private " . name ."()\<cr>{\<esc>"
'>
exe "normal oreturn ;\<cr>}\<esc>k"
s/return/\/\/ return/ge
normal j%
normal kf(
exe "normal yyPi// = \<esc>wdwA;\<esc>"
normal ==
normal j0w
endfunction


Example 2:

The "Self Encapsulate Field" refactoring.

I have heard a programmer who just uses Visual Studio (nothing against Visual
Studio, it's a great tool!) say: "I do not use properties. It's too much
work." He just uses fields instead.

With vim it is no problem to write a property, id est to use the "Self
Encapsulate Field" refactoring.

I write a name e.g. "Name" press CTRL-C CTRL-P CTRL-S (c-reate p-roperty with
s-tring).  Voila, the new property appears in just a second:

private string m_Name;
public string Name
{
get
{
return m_Name;
}
set
{
m_Name = value;
}
}

Here are the vim mappings and the underlying function:

"Create property
imap <C-c><C-p><C-s> <esc>:call CreateProperty("string")<cr>a
imap <C-c><C-p><C-i> <esc>:call CreateProperty("int")<cr>a

function! CreateProperty(type)
exe "normal bim_\<esc>b\"yywiprivate ".a:type." \<esc>A;\<cr>public ".a:type." \<esc>\"ypb2xea\<cr>{\<esc>oget\<cr>{\<cr>return \<esc>\"ypa;\<cr>}\<cr>set\<cr>{\<cr>\<tab>\<esc>\"yPa = value;\<cr>}\<cr>}\<cr>\<esc>"
normal 12k2wi
endfunction

You can combine Visual Studio and vim. You can work in Visual Studio and load
the file in vim for refactoring. I have made a menu entry in Visual Studio that
loads the actual file I am writing in vim (cf. Tip #580 http://vim.sourceforge.net/tips/tip.php?tip_id=580).


Example 3:

The "Replace Conditional with Polymorphism" refactoring.

Imagine a switch and you want to replace it with an abstract class and some
concrete classes which inherit from this parent class.

You may think "Why should I replace this switch? It's too much work. Writing
all these classes ..."

With vim it's just a question of a few seconds.

To build the abstract class  I type, say  "Fruit".
Then I press CTRL-C CTRL-A CTRL-C (c-reate a-bstract c-lass) and get

public abstract class Fruit
{
public abstract void |();
}

| means the Cursor position.

Now I fill in the methods.

public abstract class Fruit
{
public abstract void Taste();
public abstract void Color();
public abstract string GetSize();
}


Now I go on the first letter of "Fruit" and type CTRL-C CTRL-C CTRL-C (c-reate c-oncrete c-lass).

A dialog appears and asks me for the new name of the concrete class. I type in Apple and get

public class Apple : Fruit
{
public override void Taste()
{
}

public override void Color()
{
}

public override string GetSize()
{
}
}

I continue doing so with all the child classes of the abstract class.
In this way I get code templates that I can implement now.

Here are my mappings and the underlying funtion.

"Create abstract class
imap <C-c><C-a><C-c> <esc>bipublic abstract class <esc>A<cr>{<cr>public abstract void X();<cr>}<esc>:?X<cr>0fXs
"Create concrete class
map <C-c><C-c><C-c> :silent! call ImplementAbstractClass()<cr>

function! ImplementAbstractClass() range
exe "normal \<esc>\"yyw"
/{
normal "xy%
normal %o
exe "normal \<esc>o"
let name = inputdialog("Name of new method:")
exe "normal ipublic class " .name." : \<esc>\"yp\"xp"
exe "normal }O}\<esc>=="
normal %v%
normal gv
'<,'>s/abstract/override/g
normal gv
'<,'>s/;/\r{\r}\r/g
normal ==
normal %kdd%k
endfunction


Happy vimming ... and happy refactoring!


Klaus




 rate this tip  Life Changing Helpful Unfulfilling 

<< How to sort using visual blocks | Using vim to send mail on windows >>

Additional Notes

[email protected], December 2, 2005 2:41
These are amazing!  I never thought of doing this, but I have to say your tips are quite amazing!  I have not begun to use hints 1 and 2 religiously, especially when I have to dig around through code that others have written!  Thank you!!
sakhnik at gmail dot com, January 20, 2006 1:58
The only thing I don't understand in C# programming is the piece of code:
XMLDoc xmldoc = new XMLDoc();
xmldoc = GetXMLDocument();
What purpose stays the first line for???
[email protected], May 10, 2006 8:42
Here is a variation

imap <C-c><C-p> <esc>:call CreateProperty()<cr>a

function! CreateProperty()
exe "normal bim_\<esc>b\"yyybiprivate \<esc>A;\<cr>\<esc>\"ypw\"xyw\<esc>2xbipublic \<esc>$a\<cr>{\<esc>oget\<cr>{\<cr>return \<esc>\"xpa;\<cr>}\<cr>set\<cr>{\<cr>\<tab>\<esc>\"xPa = value;\<cr>}\<cr>}\<cr>\<esc>"
normal 12k2wi
endfunction

This will create a property from a <type> <Field Name>. This alleviates the need for multiple mappings for each data type in the vimrc file

So if you want to create a property from "Rectangle Box" just press "<C-c><C-p>" and you get

private Rectangle m_Box;
public   Rectangle Box
{
get
{
     return m_Box;
}
set
{
     m_Box = value;
}
}

I am still trying to get rid of some extra spaces in property name but I hope this helps
Anonymous, November 12, 2006 8:18
this is great !
I did the extract method in python .
If you have questions or remarks about this site, visit the vimonline development pages. Please use this site responsibly.
Questions about Vim should go to the maillist. Help Bram help Uganda.
   
SourceForge.net Logo