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