No edit summary |
(indent fix) |
||
(14 intermediate revisions by 8 users not shown) | |||
Line 1: | Line 1: | ||
{{review}} |
{{review}} |
||
+ | {{TipImported |
||
− | {{Tip |
||
|id=589 |
|id=589 |
||
+ | |previous=588 |
||
− | |title=Vim as refactoring tool (with examples in C__HASH__) |
||
+ | |next=590 |
||
− | |created=October 16, 2003 9:52 |
||
+ | |created=2003 |
||
|complexity=intermediate |
|complexity=intermediate |
||
|author=Klaus Horsten |
|author=Klaus Horsten |
||
|version=6.0 |
|version=6.0 |
||
|rating=225/77 |
|rating=225/77 |
||
+ | |category1=Automated Text Insertion |
||
− | |text= |
||
+ | |category2=C sharp |
||
− | You can use vim as a refactoring tool. |
||
+ | |category3=LanguageSpecific |
||
+ | }} |
||
+ | You can use Vim as a refactoring tool. The advantages are: |
||
+ | #You automate repetitive writing tasks. |
||
+ | #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. |
||
+ | I give you three examples, all in C#. |
||
+ | ===Example 1: The ''Extract Method'' refactoring=== |
||
− | The advantages are: |
||
+ | Sphagetti code example: |
||
+ | <pre> |
||
+ | 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; |
||
+ | ... |
||
+ | </pre> |
||
+ | Imagine 50 lines of code here. |
||
− | 1. You automatisate repetitive writing tasks |
||
− | 2. You learn refactoring |
||
− | + | Use the ''extract method refactoring'' to make a ''composed method''. |
|
− | commercial refactoring tools there is much (not all!) vim can do too. |
||
+ | I use a Vim function (see below) to build the exracted method. |
||
− | Whatever your opinion is, my experience is that vim helps to refactor. |
||
+ | I highlight the code part I want to extract and press <code>\em</code> (for '''e'''xtract '''m'''ethod). |
||
− | I give you three examples, all in C#. |
||
+ | A dialog appears and asks me how to name the new method. |
||
− | * Example 1: |
||
+ | I type in "GetXmlDocumentFrom" and do get this: |
||
− | Anti-sphagetti code weapon or the "Extract Method" refactoring. |
||
+ | <pre> |
||
− | |||
+ | // = GetXmlDocumentFrom(); |
||
− | Sphagetti code example: |
||
+ | private GetXmlDocumentFrom() |
||
− | |||
+ | { |
||
− | public string CreateMenu(string startMenu,string file) |
||
+ | XmlDocument XMLDoc = new XmlDocument(); |
||
− | { |
||
+ | try { |
||
− | string strOutput = ""; |
||
− | + | XMLDoc.Load(file); |
|
+ | } |
||
− | ArrayList startArray = new ArrayList(); |
||
+ | catch (Exception e) |
||
− | string strVariable = ""; |
||
+ | { |
||
− | string strTemp = ""; |
||
− | XmlDocument XMLDoc = new XmlDocument(); |
||
− | try { |
||
− | XMLDoc.Load(file); |
||
− | } |
||
− | catch (Exception e) { |
||
strOutput = e.GetBaseException().ToString(); |
strOutput = e.GetBaseException().ToString(); |
||
return strOutput; |
return strOutput; |
||
} |
} |
||
+ | // return ; |
||
− | XmlNodeList nodeList = XMLDoc.DocumentElement.ChildNodes; |
||
+ | } |
||
− | ... |
||
+ | </pre> |
||
+ | 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: |
||
+ | <pre> |
||
+ | 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; |
||
+ | } |
||
+ | </pre> |
||
+ | In the original code I put two lines. |
||
− | Imagine 50 lines of code here. |
||
+ | <pre> |
||
+ | XmlDocument XMLDoc = new XmlDocument(); |
||
+ | XMLDoc = GetXmlDocumentFrom(XmlFile); |
||
+ | </pre> |
||
+ | 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. |
||
− | Use the "extract method refactoring" to make a "composed method". |
||
+ | 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: |
||
− | I use a vim function (see below) to build the exracted method. |
||
+ | <pre> |
||
+ | 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 |
||
+ | </pre> |
||
+ | ===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, that is, to use the ''Self Encapsulate Field'' refactoring. |
||
+ | I write a name e.g. <code>Name</code> press <code>CTRL-C CTRL-P CTRL-S</code> ('''c'''reate '''p'''roperty with '''s'''tring). Voila, the new property appears in just a second: |
||
− | I highlight the code part I want to extract and press \em (for e-xtract m-ethod). |
||
+ | <pre> |
||
+ | private string m_Name; |
||
+ | public string Name |
||
+ | { |
||
+ | get |
||
+ | { |
||
+ | return m_Name; |
||
+ | } |
||
+ | set |
||
+ | { |
||
+ | m_Name = value; |
||
+ | } |
||
+ | } |
||
+ | </pre> |
||
+ | Here are the Vim mappings and the underlying function: |
||
+ | <pre> |
||
+ | "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 |
||
+ | </pre> |
||
+ | 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. [[Integrate gvim with Visual Studio#Vim as an External Tool|Vim as an External Tool]]). |
||
+ | ===Example 3: The ''Replace Conditional with Polymorphism'' refactoring=== |
||
− | A dialog appears and asks me how to name the new method. |
||
+ | 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 <code>Fruit</code>. |
||
− | I type in "GetXmlDocumentFrom" and do get this: |
||
+ | Then I press <code>CTRL-C CTRL-A CTRL-C</code> ('''c'''reate '''a'''bstract '''c'''lass) and get |
||
+ | <pre> |
||
+ | public abstract class Fruit |
||
+ | { |
||
+ | public abstract void |(); |
||
+ | } |
||
+ | | is the cursor position |
||
+ | </pre> |
||
+ | Now I fill in the methods. |
||
+ | <pre> |
||
+ | public abstract class Fruit |
||
+ | { |
||
+ | public abstract void Taste(); |
||
+ | public abstract void Color(); |
||
+ | public abstract string GetSize(); |
||
+ | } |
||
+ | </pre> |
||
+ | Now I go on the first letter of <code>Fruit</code> and type <code>CTRL-C CTRL-C CTRL-C</code> ('''c'''reate '''c'''oncrete '''c'''lass). |
||
− | // = GetXmlDocumentFrom(); |
||
+ | A dialog appears and asks me for the new name of the concrete class. I type in <code>Apple</code> and get |
||
− | private GetXmlDocumentFrom() |
||
+ | <pre> |
||
+ | public class Apple : Fruit |
||
+ | { |
||
+ | public override void Taste() |
||
+ | { |
||
+ | } |
||
+ | public override void Color() |
||
+ | { |
||
+ | } |
||
+ | public override string GetSize() |
||
+ | { |
||
+ | } |
||
+ | } |
||
+ | </pre> |
||
+ | 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. |
||
− | XmlDocument XMLDoc = new XmlDocument(); |
||
+ | Here are my mappings and the underlying funtion. |
||
− | try { |
||
+ | <pre> |
||
+ | "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 |
||
+ | </pre> |
||
+ | ==Comments== |
||
− | XMLDoc.Load(file); |
||
+ | These are amazing! I never thought of doing this, but I have to say your tips are quite amazing! I have begun to use hints 1 and 2 religiously, especially when I have to dig around through code that others have written! Thank you!! |
||
− | } |
||
− | |||
− | 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 [[VimTip580]]). |
||
− | |||
− | |||
− | |||
− | |||
− | |||
− | 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 |
||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | }} |
||
− | |||
− | == Comments == |
||
− | 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!! |
||
− | |||
− | phaitour--AT--gmail.com |
||
− | , December 2, 2005 2:41 |
||
---- |
---- |
||
+ | Here is a variation |
||
− | The only thing I don't understand in C# programming is the piece of code: |
||
+ | <pre> |
||
− | XMLDoc xmldoc = new XMLDoc(); |
||
+ | imap <C-c><C-p> <Esc>:call CreateProperty()<CR>a |
||
− | xmldoc = GetXMLDocument(); |
||
+ | function! CreateProperty() |
||
− | What purpose stays the first line for??? |
||
+ | 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 |
||
+ | </pre> |
||
+ | 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 |
||
− | sakhnik at gmail dot com |
||
− | , January 20, 2006 1:58 |
||
− | ---- |
||
− | Here is a variation |
||
+ | So if you want to create a property from <code>Rectangle Box</code> just press <code><C-c><C-p></code> and you get |
||
− | imap <C-c><C-p> <esc>:call CreateProperty()<cr>a |
||
+ | <pre> |
||
− | |||
+ | private Rectangle m_Box; |
||
− | function! CreateProperty() |
||
+ | public Rectangle Box |
||
− | 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 |
||
+ | get |
||
− | endfunction |
||
+ | { |
||
− | |||
+ | return m_Box; |
||
− | 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 |
||
+ | } |
||
− | |||
+ | set |
||
− | So if you want to create a property from "Rectangle Box" just press "<C-c><C-p>" and you get |
||
+ | { |
||
− | |||
− | + | m_Box = value; |
|
+ | } |
||
− | public Rectangle Box |
||
+ | } |
||
− | { |
||
+ | </pre> |
||
− | 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 |
||
− | |||
− | |||
− | jendave--AT--yahoo.com |
||
− | , May 10, 2006 8:42 |
||
− | ---- |
||
− | this is great ! |
||
− | I did the extract method in python . |
||
+ | 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 |
||
---- |
---- |
||
− | <!-- parsed by vimtips.py in 0.571460 seconds--> |
Revision as of 22:09, 28 July 2013
created 2003 · complexity intermediate · author Klaus Horsten · version 6.0
You can use Vim as a refactoring tool. The advantages are:
- You automate repetitive writing tasks.
- 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.
I give you three examples, all in C#.
Example 1: 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 extract method).
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, that is, to use the Self Encapsulate Field refactoring.
I write a name e.g. Name
press CTRL-C CTRL-P CTRL-S
(create property with string). 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. Vim as an External Tool).
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
(create abstract class) and get
public abstract class Fruit { public abstract void |(); } | is 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
(create concrete class).
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
Comments
These are amazing! I never thought of doing this, but I have to say your tips are quite amazing! I have begun to use hints 1 and 2 religiously, especially when I have to dig around through code that others have written! Thank you!!
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.