├── .gitignore ├── .rvmrc ├── .travis.yml ├── Gemfile ├── Gemfile.lock ├── README.md ├── Rakefile ├── autoload └── common.vim ├── cucumber.yml ├── doc └── rubyrefactoring.txt ├── features ├── README.markdown ├── add_parameter.feature ├── add_parameter_without_using_brackets.feature ├── extract_constant.feature ├── extract_let.feature ├── extract_local_variable.feature ├── extract_method.feature ├── inline_temp.feature ├── introduce_variable.feature ├── post_conditional.feature ├── rename_instance_variable.feature ├── rename_local_variable.feature ├── step_definitions │ ├── add_parameter_steps.rb │ ├── extract_constant_steps.rb │ ├── extract_let_steps.rb │ ├── extract_local_variable_steps.rb │ ├── extract_method_steps.rb │ ├── introduce_variable_steps.rb │ ├── rename_instance_variable_steps.rb │ ├── rename_local_variable_steps.rb │ ├── robot_vim.rb │ └── shared_steps.rb └── support │ └── shared_methods.rb ├── plugin ├── refactorings │ └── general │ │ ├── addparameter.vim │ │ ├── addparameternb.vim │ │ ├── extractconstant.vim │ │ ├── extractmethod.vim │ │ ├── extractvariable.vim │ │ ├── inlinetemp.vim │ │ ├── introducevariable.vim │ │ ├── postconditional.vim │ │ ├── renameinstancevariable.vim │ │ ├── renamelocalvariable.vim │ │ └── rspec_extractlet.vim └── ruby-refactoring.vim └── test └── testcases.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.un~ 2 | *.swp 3 | doc/tags 4 | -------------------------------------------------------------------------------- /.rvmrc: -------------------------------------------------------------------------------- 1 | rvm use 1.9.2@vim-ruby-refactoring --create 2 | 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | rvm: 2 | - 1.9.2 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source :rubygems 2 | 3 | group :development do 4 | gem 'cucumber' 5 | gem 'rake' 6 | gem 'relish', git: 'https://github.com/mattwynne/relish.git' 7 | gem 'robot-vim' 8 | gem 'rspec' 9 | end 10 | 11 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GIT 2 | remote: https://github.com/mattwynne/relish.git 3 | revision: 311c11d7931e6056d3cfbbe638a3aa694f14ca56 4 | specs: 5 | relish (0.4.0) 6 | archive-tar-minitar (~> 0.5.2) 7 | json (~> 1.4.6) 8 | rest-client (~> 1.6.1) 9 | 10 | GEM 11 | remote: http://rubygems.org/ 12 | specs: 13 | archive-tar-minitar (0.5.2) 14 | builder (3.0.0) 15 | cucumber (1.0.2) 16 | builder (>= 2.1.2) 17 | diff-lcs (>= 1.1.2) 18 | gherkin (~> 2.4.5) 19 | json (>= 1.4.6) 20 | term-ansicolor (>= 1.0.5) 21 | diff-lcs (1.1.3) 22 | gherkin (2.4.16) 23 | json (>= 1.4.6) 24 | json (1.4.6) 25 | macaddr (1.4.0) 26 | systemu (~> 2.2.0) 27 | mime-types (1.16) 28 | rest-client (1.6.7) 29 | mime-types (>= 1.16) 30 | robot-vim (1.1.0) 31 | uuid (~> 2.3.1) 32 | rspec (2.6.0) 33 | rspec-core (~> 2.6.0) 34 | rspec-expectations (~> 2.6.0) 35 | rspec-mocks (~> 2.6.0) 36 | rspec-core (2.6.4) 37 | rspec-expectations (2.6.0) 38 | diff-lcs (~> 1.1.2) 39 | rspec-mocks (2.6.0) 40 | systemu (2.2.0) 41 | term-ansicolor (1.0.6) 42 | uuid (2.3.3) 43 | macaddr (~> 1.0) 44 | 45 | PLATFORMS 46 | ruby 47 | 48 | DEPENDENCIES 49 | cucumber 50 | relish! 51 | robot-vim 52 | rspec 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ruby Refactoring Tool for Vim 2 | 3 | I love vim! It's my editor of choice when I am developing software. 4 | 5 | Currently (for the last 4 years at least) I have been working mainly 6 | with the Ruby Programming Language. 7 | 8 | I have been missing a refactoring tool for a while (like the ones you 9 | can find in IDE's) while I am developing, but I never had the nerve 10 | to dwell into vim script to actually code my own. 11 | 12 | Recently (a couple of weeks ago) Gary Bernhardt presented his vim 13 | configuration in the Software Craftsmanship User Group UK and he 14 | showed us the two refactoring patterns he has written in vim script. 15 | 16 | Initially I just thought "cool", but it didn't really sink in until 17 | a couple of weeks later. 18 | 19 | So now I have decided to code this in vim script, but I am not sure how far 20 | I will go with it (clone at your own risk). 21 | 22 | N.B. 'Rename Local Variable', 'Rename Instance Variable' and 'ExtractMethod' require matchit.vim: 23 | 24 | [http://www.vim.org/scripts/script.php?script_id=39](http://relishapp.com/despo/vim-ruby-refactoring) 25 | 26 | ## Implemented commands/patterns: 27 | 28 | :RAddParameter - Add Parameter 29 | :RInlineTemp - Inline Temp 30 | :RConvertPostConditional - Convert Post Conditional 31 | :RExtractConstant - Extract Constant (visual selection) 32 | :RExtractLet - Extract to Let (Rspec) 33 | :RExtractLocalVariable - Extract Local Variable (visual selection) 34 | :RRenameLocalVariable - Rename Local Variable (visual selection/variable under the cursor, *REQUIRES matchit.vim*) 35 | :RRenameInstanceVariable - Rename Instance Variable (visual selection, *REQUIRES matchit.vim*) 36 | :RExtractMethod - Extract Method (visual selection, *REQUIRES matchit.vim*) 37 | 38 | ## Default bindings: 39 | 40 | :nnoremap rap :RAddParameter 41 | :nnoremap rcpc :RConvertPostConditional 42 | :nnoremap rel :RExtractLet 43 | :vnoremap rec :RExtractConstant 44 | :vnoremap relv :RExtractLocalVariable 45 | :nnoremap rit :RInlineTemp 46 | :vnoremap rrlv :RRenameLocalVariable 47 | :vnoremap rriv :RRenameInstanceVariable 48 | :vnoremap rem :RExtractMethod 49 | 50 | Additional usage examples (thanks Justin!): 51 | [http://justinram.wordpress.com/2010/12/30/vim-ruby-refactoring-series/](http://justinram.wordpress.com/2010/12/30/vim-ruby-refactoring-series/) 52 | 53 | ## Documentation 54 | [http://relishapp.com/despo/vim-ruby-refactoring](http://relishapp.com/despo/vim-ruby-refactoring) 55 | 56 | Enrique Comba Riepenhausen & Paul King 57 | 58 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'cucumber/rake/task' 2 | 3 | Cucumber::Rake::Task.new(:cucumber) 4 | namespace :cucumber do 5 | 6 | Cucumber::Rake::Task.new(:wip) do |cukes| 7 | cukes.cucumber_opts = "-p wip" 8 | end 9 | 10 | Cucumber::Rake::Task.new(:issues) do |issue| 11 | issue.cucumber_opts = "-p issues" 12 | end 13 | end 14 | 15 | task :default => :cucumber 16 | 17 | namespace :relish do 18 | task :push do 19 | `bundle exec relish push vim-ruby-refactoring` 20 | end 21 | end 22 | 23 | -------------------------------------------------------------------------------- /autoload/common.vim: -------------------------------------------------------------------------------- 1 | " Support functions 2 | 3 | " Synopsis: 4 | " Returns the input given by the user 5 | function! common#get_input(message, error_message) 6 | let name = input(a:message) 7 | if name == '' 8 | throw a:error_message 9 | endif 10 | return name 11 | endfunction 12 | 13 | " Synopsis: 14 | " Param: Optional parameter of '1' dictates cut, rather than copy 15 | " Returns the text that was selected when the function was invoked 16 | " without clobbering any registers 17 | function! common#get_visual_selection(...) 18 | try 19 | let a_save = @a 20 | if a:0 >= 1 && a:1 == 1 21 | normal! gv"ad 22 | else 23 | normal! gv"ay 24 | endif 25 | return @a 26 | finally 27 | let @a = a_save 28 | endtry 29 | endfunction 30 | 31 | " Synopsis: 32 | " Find pattern to matching end, flags as per :h search() 33 | function! common#get_range_for_block(pattern_start, flags) 34 | " matchit.vim required 35 | if !exists("g:loaded_matchit") 36 | throw("matchit.vim (http://www.vim.org/scripts/script.php?script_id=39) required") 37 | endif 38 | 39 | let cursor_position = getpos(".") 40 | 41 | let block_start = search(a:pattern_start, a:flags) 42 | 43 | if (match(getline("."), "^\\s*it\\s\\+") == 0) 44 | normal $ 45 | endif 46 | 47 | normal % 48 | let block_end = line(".") 49 | 50 | " Restore the cursor 51 | call setpos(".",cursor_position) 52 | 53 | return [block_start, block_end] 54 | endfunction 55 | 56 | " Synopsis: 57 | " Loop over the line range given, global replace pattern with replace 58 | function! common#gsub_all_in_range(start_line, end_line, pattern, replace) 59 | let lnum = a:start_line 60 | while lnum <= a:end_line 61 | let oldline = getline(lnum) 62 | let newline = substitute(oldline,a:pattern,a:replace,'g') 63 | call setline(lnum, newline) 64 | let lnum = lnum + 1 65 | endwhile 66 | endfunction! 67 | 68 | " Synopsis: 69 | " Removed duplicates from a target list 70 | function! common#dedupe_list(target) 71 | call filter(a:target, 'count(a:target,v:val) > 1 ? 0 : 1') 72 | endfunction 73 | 74 | " Synopsis: 75 | " Copies, removes, then returns the text that was selected when 76 | " the function was invoked without clobbering any registers 77 | function! common#cut_visual_selection() 78 | return common#get_visual_selection(1) 79 | endfunction 80 | -------------------------------------------------------------------------------- /cucumber.yml: -------------------------------------------------------------------------------- 1 | default: --format progress --tags ~@wip --tags ~@issues features 2 | wip: --tags @wip --wip features 3 | issues: --tags @issues --wip features 4 | -------------------------------------------------------------------------------- /doc/rubyrefactoring.txt: -------------------------------------------------------------------------------- 1 | *rubyrefactoring.txt* Ruby Refactoring Tool for Vim 2 | 3 | Author: Enrique Comba Riepenhausen *rubyrefactoring-author* 4 | 5 | INTRODUCTION *rubyrefactoring* 6 | 7 | I love vim! It's my editor of choice when I am developing software. 8 | 9 | Currently (for the last 4 years at least) I have been working mainly 10 | with the Ruby Programming Language. 11 | 12 | I have been missing a refactoring tool for a while (like the ones you 13 | can find in IDE's) while I am developing, but I never had the nerve 14 | to dwell into vim script to actually code my own. 15 | 16 | Recently (a couple of weeks ago) Gary Bernhardt presented his vim 17 | configuration in the Software Craftsmanship User Group UK and he 18 | showed us the two refactoring patterns he has written in vim script. 19 | 20 | Initially I just thought "cool", but it didn't really sink in until 21 | a couple of weeks later. 22 | 23 | So now I have decided to code this in vim script, but I am not sure how far 24 | I will go with it (clone at your own risk). 25 | 26 | Enrique Comba Riepenhausen 27 | 28 | COMMANDS *rubyrefactoring-commands* 29 | 30 | Implemented commands/patterns: 31 | 32 | Command Refactoring Notes ~ 33 | :RAddParameter Add Parameter 34 | :RInlineTemp Inline Temp 35 | :RConvertPostConditional Convert Post Conditional 36 | :RExtractConstant Extract Constant visual selection 37 | :RExtractLet Extract to Let (Rspec) 38 | :RExtractLocalVariable Extract Local Variable visual selection 39 | :RRenameLocalVariable Rename Local Variable visual selection or 40 | variable under cursor 41 | :RRenameInstanceVariable Rename Instance Variable visual selection 42 | :RExtractMethod Extract Method visual selection 43 | 44 | NOTE: Rename Instance Variable and Rename Local Variable require matchit.vim 45 | 46 | MAPPINGS *rubyrefactoring-mappings* 47 | 48 | Default bindings: 49 | :nnoremap rap :RAddParameter 50 | :nnoremap rcpc :RConvertPostConditional 51 | :nnoremap rel :RExtractLet 52 | :vnoremap rec :RExtractConstant 53 | :vnoremap relv :RExtractLocalVariable 54 | :nnoremap rit :RInlineTemp 55 | :vnoremap rrlv :RRenameLocalVariable 56 | :vnoremap rriv :RRenameInstanceVariable 57 | :vnoremap rem :RExtractMethod 58 | 59 | Disable default bindings with: 60 | > 61 | let g:ruby_refactoring_map_keys=0 62 | < 63 | 64 | ADDITIONAL USAGE EXAMPLES *rubyrefactoring-usageexamples* 65 | 66 | http://justinram.wordpress.com/2010/12/30/vim-ruby-refactoring-series/ 67 | 68 | 69 | -------------------------------------------------------------------------------- /features/README.markdown: -------------------------------------------------------------------------------- 1 | # Ruby Refactoring Tool for Vim 2 | 3 | To develop with vim refactoring create a symlink between the folder you have cloned this plugin and your .vim/bundle folder 4 | 5 | ## Issues 6 | 7 | If you identify any issues or specific funtionality you would like to see added, write the Cucumber feature and submit an [issue](https://github.com/ecomba/vim-ruby-refactoring/issues) or a [pull](https://github.com/ecomba/vim-ruby-refactoring/pulls) request: 8 | 9 | @issue 10 | Scenario: Add a parameter to a method defined with no parameters or parentheses 11 | Given I have the following code: 12 | """ 13 | def set_name 14 | end 15 | """ 16 | When I select the method and execute: 17 | """ 18 | :RAddParameter 19 | """ 20 | And I fill in the parameter "name" 21 | Then I should see: 22 | """ 23 | def set_name(name) 24 | end 25 | 26 | """ 27 | -------------------------------------------------------------------------------- /features/add_parameter.feature: -------------------------------------------------------------------------------- 1 | Feature: Add Parameter :RAddParameter 2 | This refactoring should add a new parameter to a method definition, regardless of how many the method already has. 3 | 4 | Shortcuts: 5 | :RAddParameter 6 | rap 7 | 8 | Scenario: Add a parameter to a method defined with no parameters or parentheses 9 | Given I have the following code: 10 | """ 11 | def set_name 12 | end 13 | """ 14 | When I select the method and execute: 15 | """ 16 | :RAddParameter 17 | """ 18 | And I fill in the parameter "name" 19 | Then I should see: 20 | """ 21 | def set_name(name) 22 | end 23 | 24 | """ 25 | 26 | Scenario: Add a parameter to a method defined with no parameters 27 | Given I have the following code: 28 | """ 29 | def set_name() 30 | end 31 | """ 32 | When I select the method and execute: 33 | """ 34 | :RAddParameter 35 | """ 36 | And I fill in the parameter "name" 37 | Then I should see: 38 | """ 39 | def set_name(name) 40 | end 41 | 42 | """ 43 | 44 | Scenario: Add a parameter to a method with an existing parameter 45 | Given I have the following code: 46 | """ 47 | def set_details(name) 48 | end 49 | """ 50 | When I select the method and execute: 51 | """ 52 | :RAddParameter 53 | """ 54 | And I fill in the parameter "dob" 55 | Then I should see: 56 | """ 57 | def set_details(name, dob) 58 | end 59 | 60 | """ 61 | 62 | @issues 63 | Scenario: Add a parameter to a method with an existing parameter but not brackets 64 | Given I have the following code: 65 | """ 66 | def set_details name 67 | end 68 | """ 69 | When I select the method and execute: 70 | """ 71 | :RAddParameter 72 | """ 73 | And I fill in the parameter "dob" 74 | Then I should see: 75 | """ 76 | def set_details name, dob 77 | end 78 | 79 | """ 80 | -------------------------------------------------------------------------------- /features/add_parameter_without_using_brackets.feature: -------------------------------------------------------------------------------- 1 | Feature: Add Parameter No Brackets :RAddParameterNB 2 | This refactoring should add a new parameter to a method definition, regardless of how many the method already has. 3 | 4 | Shortcuts: 5 | :RAddParameterNB 6 | rapn 7 | 8 | Scenario: Add a parameter to a method defined with no parameters or parentheses 9 | Given I have the following code: 10 | """ 11 | def set_name 12 | end 13 | """ 14 | When I select the method and execute: 15 | """ 16 | :RAddParameterNB 17 | """ 18 | And I fill in the parameter "name" 19 | Then I should see: 20 | """ 21 | def set_name name 22 | end 23 | 24 | """ 25 | 26 | @issues 27 | Scenario: Add a parameter to a method with an existing parameter 28 | Given I have the following code: 29 | """ 30 | def set_details name 31 | end 32 | """ 33 | When I select the method and execute: 34 | """ 35 | :RAddParameterNB 36 | """ 37 | And I fill in the parameter "dob" 38 | Then I should see: 39 | """ 40 | def set_details name, dob 41 | end 42 | 43 | """ 44 | -------------------------------------------------------------------------------- /features/extract_constant.feature: -------------------------------------------------------------------------------- 1 | Feature: Extract Constant :RExtractConstant 2 | Extracts the selected range into a constant at the top of the current module/class 3 | 4 | Shortcuts: 5 | :RExtractConstant 6 | rec 7 | 8 | Scenario: Extract a constant 9 | Given I have the following code: 10 | """ 11 | class Foo 12 | def bar 13 | "some magic number" 14 | end 15 | end 16 | 17 | """ 18 | When I select "some magic number" and execute: 19 | """ 20 | :RExtractConstant 21 | """ 22 | And I fill in the parameter "magic_string" 23 | Then I should see: 24 | """ 25 | class Foo 26 | MAGIC_STRING = "some magic number" 27 | def bar 28 | MAGIC_STRING 29 | end 30 | end 31 | 32 | """ 33 | -------------------------------------------------------------------------------- /features/extract_let.feature: -------------------------------------------------------------------------------- 1 | Feature: Extract RSpec Let :RExtractLet 2 | Take an assignment in an rspec specfication and convert it into a let declaration 3 | 4 | Shortcuts: 5 | :RExtractLet 6 | rel 7 | 8 | Scenario: Extract to rspec let declaration 9 | Given I have the following code: 10 | """ 11 | describe "something" do 12 | it "does stuff" do 13 | bar = 10 14 | bar.should == 10 15 | end 16 | end 17 | """ 18 | When I select the variable assignment and execute: 19 | """ 20 | :RExtractLet 21 | """ 22 | Then I should see: 23 | """ 24 | describe "something" do 25 | let(:bar) { 10 } 26 | it "does stuff" do 27 | bar.should == 10 28 | end 29 | end 30 | 31 | """ 32 | 33 | Scenario: Nothing to extract 34 | Given I have the following code: 35 | """ 36 | describe "something" do 37 | let (:x) { 10 } 38 | it "does stuff" do 39 | bar.should == 10 40 | end 41 | """ 42 | When I select the let and execute: 43 | """ 44 | :RextractLet 45 | """ 46 | Then I see no errors 47 | -------------------------------------------------------------------------------- /features/extract_local_variable.feature: -------------------------------------------------------------------------------- 1 | Feature: Extract Local Variable :RExtractLocalVariable 2 | Takes an expression that is being used directly and assigns it to a local variable first 3 | 4 | Shortcuts: 5 | :RExtractLocalVariable 6 | relv 7 | 8 | Scenario: Extract a local variable from a magic number 9 | Given I have the following code: 10 | """ 11 | class Foo 12 | def bar 13 | "some magic number" 14 | end 15 | end 16 | """ 17 | When I select "some magic number" and execute: 18 | """ 19 | :RExtractLocalVariable" 20 | """ 21 | And I fill in the parameter "local_variable" 22 | Then I should see: 23 | """ 24 | class Foo 25 | def bar 26 | local_variable = "some magic number" 27 | local_variable 28 | end 29 | end 30 | 31 | """ 32 | 33 | @issue 34 | Scenario: Extract a single word without selecting it 35 | Given I have the following code: 36 | """ 37 | class Foo 38 | def bar 39 | 20 40 | end 41 | end 42 | """ 43 | When I place my cursor on the 2 in 20 and execute: 44 | """ 45 | :RExtractLocalVariable" 46 | """ 47 | And I fill in the parameter "local_variable" 48 | Then I should see: 49 | """ 50 | class Foo 51 | def bar 52 | local_variable = 20 53 | local_variable 54 | end 55 | end 56 | 57 | """ 58 | -------------------------------------------------------------------------------- /features/extract_method.feature: -------------------------------------------------------------------------------- 1 | Feature: Extract Method :RExtractMethod 2 | Extracts the selected code into a new method of its own. 3 | 4 | Shortcuts: 5 | :RExtractMethod 6 | rem 7 | 8 | Scenario: Extract one line assignment into a new method 9 | Given I have the following code: 10 | """ 11 | class Foo 12 | def method_one 13 | @bar = foo 14 | end 15 | 16 | def method_two 17 | one = 1 18 | two = 2 19 | three = 3 20 | four = two + two 21 | five = two + three 22 | six = five + one 23 | end 24 | end 25 | """ 26 | When I select "two + three" and execute: 27 | """ 28 | :RExtractMethod 29 | """ 30 | And I fill in the parameter "add" 31 | Then I should see: 32 | """ 33 | class Foo 34 | def method_one 35 | @bar = foo 36 | end 37 | 38 | def method_two 39 | one = 1 40 | two = 2 41 | three = 3 42 | four = two + two 43 | five = add(two, three) 44 | six = five + one 45 | end 46 | 47 | def add(two, three) 48 | two + three 49 | end 50 | end 51 | 52 | """ 53 | 54 | @issues 55 | Scenario: Extract selected line to method with no parameter 56 | Given I have the following code: 57 | """ 58 | def return_key 59 | ' ' 60 | end 61 | 62 | def select_method 63 | @commands = ':normal gg' 64 | @commands << return_key 65 | end 66 | 67 | """ 68 | When I select "@commands << return_key" and execute: 69 | """ 70 | :RExtractMethod 71 | """ 72 | And I fill in the parameter "add_return_key" 73 | Then I should see: 74 | """ 75 | def return_key 76 | ' ' 77 | end 78 | 79 | def select_method 80 | @commands = ':normal gg' 81 | add_return_key 82 | end 83 | 84 | def add_return_key 85 | @commands << return_key 86 | end 87 | 88 | """ 89 | 90 | @issue 91 | Scenario: Extract in an rspec file does not add lets as parameters 92 | Given I have the following code: 93 | """ 94 | require 'bowling' 95 | 96 | describe Bowling,"score" do 97 | let(:bowling) { Bowling.new } 98 | 99 | it "should return 0 when rolling all gutter balls" do 100 | 20.times do 101 | bowling.roll 0 102 | end 103 | bowling.score.should == 0 104 | end 105 | end 106 | 107 | """ 108 | When I select the "20.times do" block and execute: 109 | """ 110 | :RExtractMethod 111 | """ 112 | And I fill in the parameter "roll_many" 113 | Then I should see: 114 | """ 115 | require 'bowling' 116 | 117 | describe Bowling,"score" do 118 | let(:bowling) { Bowling.new } 119 | 120 | it "should return 0 when rolling all gutter balls" do 121 | roll_many 122 | bowling.score.should == 0 123 | end 124 | 125 | def roll_many 126 | 20.times do 127 | bowling.roll 0 128 | end 129 | end 130 | end 131 | 132 | """ 133 | 134 | @issue 135 | Scenario: Parameters to extracted method should be in the order they are declared in original method when declared on separate lines. 136 | Given I have the following code: 137 | """ 138 | def originalMethod 139 | x = 1 140 | y = 2 141 | z = x + y 142 | end 143 | 144 | """ 145 | When I select "x + y" and execute: 146 | """ 147 | :RExtractMethod 148 | """ 149 | And I fill in the parameter "add" 150 | Then I should see: 151 | """ 152 | def originalMethod 153 | x = 1 154 | y = 2 155 | z = add(x, y) 156 | end 157 | 158 | def add(x, y) 159 | x + y 160 | end 161 | 162 | """ 163 | 164 | @issue 165 | Scenario: Parameters to extracted method should be in the order they are declared in original method when declared on same lines. 166 | Given I have the following code: 167 | """ 168 | def originalMethod(b, a) 169 | c = a + b 170 | end 171 | 172 | """ 173 | When I select "a + b" and execute: 174 | """ 175 | :RExtractMethod 176 | """ 177 | And I fill in the parameter "add" 178 | Then I should see: 179 | """ 180 | def originalMethod(b, a) 181 | c = add(b, a) 182 | end 183 | 184 | def add(b, a) 185 | a + b 186 | end 187 | 188 | """ 189 | -------------------------------------------------------------------------------- /features/inline_temp.feature: -------------------------------------------------------------------------------- 1 | Feature: Inline Temp :RInlineTemp 2 | This refactoring takes a temporary variable and inlines the use of it. 3 | 4 | Shortcuts: 5 | :RInlineTemp 6 | rit 7 | 8 | Scenario: Inline a temporary variable 9 | Given I have the following code: 10 | """ 11 | foo = 10 12 | puts foo 13 | """ 14 | When I go to the line and execute: 15 | """ 16 | :RInlineTemp 17 | """ 18 | Then I should see: 19 | """ 20 | puts 10 21 | 22 | """ 23 | 24 | Scenario: Inline a temporary variable to two variables on the same line 25 | Given I have the following code: 26 | """ 27 | x = 5 28 | y = x and z = x 29 | """ 30 | When I go to the line and execute: 31 | """ 32 | :RInlineTemp 33 | """ 34 | Then I should see: 35 | """ 36 | y = 5 and z = 5 37 | 38 | """ 39 | 40 | Scenario: Inline a temporary variable to all variables within the context of a method 41 | Given I have the following code: 42 | """ 43 | def some_method 44 | x = 5 45 | y = x and z = x 46 | r = x + 2 47 | end 48 | 49 | def some_other_method 50 | x = 2 51 | y = x + 1 52 | end 53 | """ 54 | When I go to line "2" and execute: 55 | """ 56 | :RInlineTemp 57 | """ 58 | Then I should see: 59 | """ 60 | def some_method 61 | y = 5 and z = 5 62 | r = 5 + 2 63 | end 64 | 65 | def some_other_method 66 | x = 2 67 | y = x + 1 68 | end 69 | 70 | """ 71 | 72 | Scenario: Inline a temporary variable to all variables within the context of a method 73 | Given I have the following code: 74 | """ 75 | x = 5 76 | foo = x + 10 77 | x = 10 78 | bar = x + 10 79 | """ 80 | When I go to the line and execute: 81 | """ 82 | :RInlineTemp 83 | """ 84 | Then I should see: 85 | """ 86 | foo = 5 + 10 87 | x = 10 88 | bar = x + 10 89 | 90 | """ 91 | 92 | Scenario: Inline a temporary variable to all variables within the context of a method 93 | Given I have the following code: 94 | """ 95 | a = 1 96 | b = a + 1 97 | a = 2 98 | c = a + 1 99 | """ 100 | When I go to line "3" and execute: 101 | """ 102 | :RInlineTemp 103 | """ 104 | Then I should see: 105 | """ 106 | a = 1 107 | b = a + 1 108 | c = 2 + 1 109 | 110 | """ 111 | 112 | @issues 113 | Scenario: Inline a temporary variable to a value within a string 114 | Given I have the following code: 115 | """ 116 | then = 2006 117 | word = "#{then}-01-01" 118 | """ 119 | When I go to the line and execute: 120 | """ 121 | :RInlineTemp 122 | """ 123 | Then I should see: 124 | """ 125 | word = "2006-01-01" 126 | """ 127 | 128 | @issue 129 | Scenario: Inline temp in an rspec it when an arbitrary helper method exists 130 | Given I have the following code: 131 | """ 132 | describe "foo" do 133 | def arbitrary 134 | 0 135 | end 136 | 137 | it "should allow puts" do 138 | foo = 10 139 | puts foo 140 | end 141 | end 142 | 143 | """ 144 | When I go to line "7" and execute: 145 | """ 146 | :RInlineTemp 147 | """ 148 | Then I should see: 149 | """ 150 | describe "foo" do 151 | def arbitrary 152 | 0 153 | end 154 | 155 | it "should allow puts" do 156 | puts 10 157 | end 158 | end 159 | 160 | """ 161 | -------------------------------------------------------------------------------- /features/introduce_variable.feature: -------------------------------------------------------------------------------- 1 | Feature: Introduce variable :RIntroduceVariable 2 | Take a method of instancated class and introduce variable 3 | 4 | Shortcuts: 5 | :RIntroduceVariable 6 | riv 7 | 8 | Scenario: Introduce variable from class 9 | Given I have the following code: 10 | """ 11 | HelloWorld.new 12 | """ 13 | When I select the class and execute: 14 | """ 15 | :RIntroduceVariable 16 | """ 17 | Then I should see: 18 | """ 19 | hello_world = HelloWorld.new 20 | """ 21 | 22 | Scenario: Introduce variable from method name 23 | Given I have the following code: 24 | """ 25 | hello_world.person 26 | """ 27 | When I select the class and execute: 28 | """ 29 | :RIntroduceVariable 30 | """ 31 | Then I should see: 32 | """ 33 | person = hello_world.person 34 | """ 35 | 36 | Scenario: Introduce variable when method has parameters 37 | Given I have the following code: 38 | """ 39 | hello_world.person("Foo", "Bar") 40 | """ 41 | When I select the class and execute: 42 | """ 43 | :RIntroduceVariable 44 | """ 45 | Then I should see: 46 | """ 47 | person = hello_world.person("Foo", "Bar") 48 | """ 49 | 50 | Scenario: Introduce variable when method has parameters that include method call 51 | Given I have the following code: 52 | """ 53 | hello_world.person("Foo", Bar.new) 54 | """ 55 | When I select the class and execute: 56 | """ 57 | :RIntroduceVariable 58 | """ 59 | Then I should see: 60 | """ 61 | person = hello_world.person("Foo", Bar.new) 62 | """ 63 | 64 | Scenario: Introduce variable when method has parameters that starts with a hash 65 | Given I have the following code: 66 | """ 67 | hello_world.person {:foo => "bar} 68 | """ 69 | When I select the class and execute: 70 | """ 71 | :RIntroduceVariable 72 | """ 73 | Then I should see: 74 | """ 75 | person = hello_world.person {:foo => "bar} 76 | """ 77 | 78 | Scenario: Introduce variable when method has parameters on the second method name 79 | Given I have the following code: 80 | """ 81 | hello_world.person.name("Foo", Bar.new) 82 | """ 83 | When I select the class and execute: 84 | """ 85 | :RIntroduceVariable 86 | """ 87 | Then I should see: 88 | """ 89 | name = hello_world.person.name("Foo", Bar.new) 90 | """ 91 | -------------------------------------------------------------------------------- /features/post_conditional.feature: -------------------------------------------------------------------------------- 1 | Feature: Convert Post Conditional :RConvertPostConditional 2 | Takes a post-conditional expression and converts it into a regular conditional statement (and vice versa) 3 | 4 | Shortcuts: 5 | :RConvertPostConditional 6 | rcpc 7 | 8 | @issue 9 | Scenario: Convert a simple if post-conditional expression 10 | Given I have the following code: 11 | """ 12 | do_something if condition 13 | """ 14 | When I go to the line and execute: 15 | """ 16 | :RConvertPostConditional 17 | """ 18 | Then I should see: 19 | """ 20 | if condition 21 | do_something 22 | end 23 | """ 24 | 25 | Scenario: Convert a simple if pre-conditional expression 26 | Given I have the following code: 27 | """ 28 | if condition 29 | do_something 30 | end 31 | """ 32 | When I go to the line and execute: 33 | """ 34 | :RConvertPostConditional 35 | """ 36 | Then I should see: 37 | """ 38 | do_something if condition 39 | """ 40 | -------------------------------------------------------------------------------- /features/rename_instance_variable.feature: -------------------------------------------------------------------------------- 1 | Feature: Renaming instance variable :RRenameInstanceVariable 2 | 3 | Shortcuts: 4 | :RRenameInstanceVariable 5 | rriv 6 | 7 | Scenario: Renaming a single occurence of an instance variable 8 | Given I have the following code: 9 | """ 10 | def method 11 | @instance_variable 12 | end 13 | """ 14 | When I select the instance variable and execute: 15 | """ 16 | :RRenameInstanceVariable 17 | """ 18 | And I fill in the parameter "foo" 19 | Then I should see: 20 | """ 21 | def method 22 | @foo 23 | end 24 | 25 | """ 26 | -------------------------------------------------------------------------------- /features/rename_local_variable.feature: -------------------------------------------------------------------------------- 1 | Feature: Rename Local Variable :RRenameLocalVariable 2 | Renames a local variable to be something more meaningful and intention revealing 3 | 4 | Shortcuts: 5 | :RRenameLocalVariable 6 | rrlv 7 | 8 | Scenario: Rename single occurance of a local variable 9 | Given I have the following code: 10 | """ 11 | def method 12 | foo = 7 13 | end 14 | """ 15 | When I select the local variable assignment and execute: 16 | """ 17 | :RRenameLocalVariable 18 | """ 19 | And I fill in the parameter "days_in_week" 20 | Then I should see: 21 | """ 22 | def method 23 | days_in_week = 7 24 | end 25 | 26 | """ 27 | -------------------------------------------------------------------------------- /features/step_definitions/add_parameter_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I select the method and execute:$/ do |command| 2 | select_method 3 | add_to_commands(command) 4 | end 5 | -------------------------------------------------------------------------------- /features/step_definitions/extract_constant_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I select \"some magic number\" and execute:$/ do |command| 2 | select_magic_number 3 | add_to_commands command 4 | end 5 | 6 | def select_magic_number 7 | @commands = ':normal 3Gvg_' 8 | add_return_key 9 | end 10 | 11 | -------------------------------------------------------------------------------- /features/step_definitions/extract_let_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I select the variable assignment and execute:$/ do |command| 2 | select_variable_assignment 3 | add_to_commands command 4 | end 5 | 6 | Then /^I see no errors$/ do 7 | @commands << ':normal G"zp' 8 | add_return_key 9 | result_of_executing_the_commands.should_not include("Error") 10 | end 11 | 12 | When /^I select the let and execute:$/ do |command| 13 | select_rspec_let 14 | add_to_commands command 15 | end 16 | 17 | def select_variable_assignment 18 | @commands = ':normal 3G' 19 | add_return_key 20 | end 21 | 22 | def select_rspec_let 23 | @commands = "redir @z>>" 24 | @commands << ":normal 2G" 25 | end 26 | 27 | -------------------------------------------------------------------------------- /features/step_definitions/extract_local_variable_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I place my cursor on the 2 in 20 and execute:$/ do |command| 2 | @commands = ':normal 3Gf2' 3 | add_return_key 4 | add_to_commands command 5 | end 6 | -------------------------------------------------------------------------------- /features/step_definitions/extract_method_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I select \"@commands << return_key\" and execute:$/ do |command| 2 | select_line 3 | add_to_commands command 4 | end 5 | 6 | def select_line 7 | @commands = ":normal 7Gvg_" 8 | add_return_key 9 | end 10 | 11 | When /^I select \"two \+ three\" and execute:$/ do |command| 12 | select_lines 13 | add_to_commands command 14 | end 15 | 16 | def select_lines 17 | @commands = ":normal 11G2wv$" 18 | add_return_key 19 | end 20 | 21 | When /^I select the \"20\.times do\" block and execute:$/ do |command| 22 | @commands = ":normal 7G^v2j$" 23 | add_return_key 24 | add_to_commands command 25 | end 26 | 27 | When /^I select \"x \+ y\" and execute:$/ do |command| 28 | @commands = ":normal 4Gfxv$" 29 | add_return_key 30 | add_to_commands command 31 | end 32 | 33 | When /^I select \"a \+ b\" and execute:$/ do |command| 34 | @commands = ":normal 2Gfav$" 35 | add_return_key 36 | add_to_commands command 37 | end 38 | -------------------------------------------------------------------------------- /features/step_definitions/introduce_variable_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I select the class and execute:$/ do |command| 2 | @commands = ":normal gg$" 3 | add_return_key 4 | add_to_commands command 5 | add_return_key 6 | end 7 | 8 | -------------------------------------------------------------------------------- /features/step_definitions/rename_instance_variable_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I select the instance variable and execute:$/ do |command| 2 | select_instance_variable 3 | add_to_commands command 4 | end 5 | 6 | def select_instance_variable 7 | @commands = ":normal 2Glve" 8 | add_return_key 9 | end 10 | -------------------------------------------------------------------------------- /features/step_definitions/rename_local_variable_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I select the local variable assignment and execute:$/ do |command| 2 | select_local_variable 3 | add_to_commands command 4 | end 5 | 6 | def select_local_variable 7 | @commands = ":normal 2Gve" 8 | add_return_key 9 | end 10 | -------------------------------------------------------------------------------- /features/step_definitions/robot_vim.rb: -------------------------------------------------------------------------------- 1 | require 'robot-vim' 2 | -------------------------------------------------------------------------------- /features/step_definitions/shared_steps.rb: -------------------------------------------------------------------------------- 1 | Given /^I have the following code:$/ do |code| 2 | @input = code 3 | end 4 | 5 | When /^I fill in the parameter "([^"]*)"$/ do |parameter| 6 | add_to_commands(parameter) 7 | end 8 | 9 | Then /^I should see:$/ do |result| 10 | result_of_executing_the_commands.strip.should == result.strip 11 | end 12 | 13 | When /^I go to line "([^"]*)" and execute:$/ do |line, command| 14 | go_to line 15 | add_to_commands command 16 | end 17 | 18 | When /^I go to the line and execute:$/ do |command| 19 | select_method 20 | add_to_commands(command) 21 | end 22 | -------------------------------------------------------------------------------- /features/support/shared_methods.rb: -------------------------------------------------------------------------------- 1 | # This file will allow us to edit the vim runner 2 | # configuration without having to edit every feature file 3 | def result_of_executing_the_commands 4 | RobotVim::Runner.new.run(:input_file => @input, :commands => commands) 5 | end 6 | 7 | def return_key 8 | ' ' 9 | end 10 | 11 | def select_method 12 | first_line = "1" 13 | go_to first_line 14 | end 15 | 16 | def add_to_commands command 17 | @commands << command 18 | add_return_key 19 | end 20 | 21 | def set_filetype 22 | ":set ft=ruby" + return_key 23 | end 24 | 25 | def commands 26 | set_filetype + @commands 27 | end 28 | 29 | def add_return_key 30 | @commands << return_key 31 | end 32 | 33 | def go_to line 34 | @commands = ":normal #{line}G" 35 | add_return_key 36 | end 37 | -------------------------------------------------------------------------------- /plugin/refactorings/general/addparameter.vim: -------------------------------------------------------------------------------- 1 | 2 | " Synopsis: 3 | " Adds a parameter (or many separated with commas) to a method 4 | function! AddParameter() 5 | try 6 | let name = common#get_input("Parameter name: ", "No parameter name given!") 7 | catch 8 | echo v:exception 9 | return 10 | endtry 11 | 12 | " Save current position 13 | let cursor_position = getpos(".") 14 | 15 | " Move backwards to the method definiton if you are not already on the 16 | " correct line 17 | if empty(matchstr(getline("."), '\')) 18 | exec "?\\" 19 | endif 20 | 21 | let closing_bracket_index = stridx(getline("."), ")") 22 | let opening_bracket_index = stridx(getline("."), "(") 23 | 24 | if closing_bracket_index == -1 25 | execute "normal A(" . name . ")\" 26 | " there is an open & close paren but no parameters 27 | elseif opening_bracket_index != -1 && opening_bracket_index == closing_bracket_index - 1 28 | exec ':s/)/' . name . ')/' 29 | else 30 | exec ':.s/)/, ' . name . ')/' 31 | endif 32 | 33 | " Restore caret position 34 | call setpos(".", cursor_position) 35 | endfunction 36 | 37 | -------------------------------------------------------------------------------- /plugin/refactorings/general/addparameternb.vim: -------------------------------------------------------------------------------- 1 | 2 | " Synopsis: 3 | " Adds a parameter (or many separated with commas) to a method with no 4 | " brackets 5 | function! AddParameterNB() 6 | try 7 | let name = common#get_input("Parameter name: ", "No parameter name given!") 8 | catch 9 | echo v:exception 10 | return 11 | endtry 12 | 13 | " Save current position 14 | let cursor_position = getpos(".") 15 | 16 | " Move backwards to the method definiton if you are not already on the 17 | " correct line 18 | if empty(matchstr(getline("."), '\')) 19 | exec "?\\" 20 | endif 21 | 22 | execute "normal A " . name . "\" 23 | 24 | " Restore caret position 25 | call setpos(".", cursor_position) 26 | endfunction 27 | 28 | -------------------------------------------------------------------------------- /plugin/refactorings/general/extractconstant.vim: -------------------------------------------------------------------------------- 1 | " Synopsis: 2 | " Extracts the selected scope into a constant at the top of the current 3 | " module or class 4 | function! ExtractConstant() 5 | try 6 | let name = toupper(common#get_input("Constant name: ", "No constant name given!")) 7 | catch 8 | echo v:exception 9 | return 10 | endtry 11 | 12 | " Save the scope to register a and then reselect the scope in visual mode 13 | normal! gv 14 | " Replace selected text with the constant's name 15 | exec "normal c" . name 16 | " Find the enclosing class or module 17 | exec "?^\\" 18 | " Define the constant inside the class or module 19 | exec "normal! o" . name . " = " 20 | normal! $p 21 | endfunction 22 | -------------------------------------------------------------------------------- /plugin/refactorings/general/extractmethod.vim: -------------------------------------------------------------------------------- 1 | " Synopsis: 2 | " Extracts the selected scope into a method above the scope of the 3 | " current method 4 | function! ExtractMethod() range 5 | try 6 | let name = common#get_input("Method name: ", "No method name given!") 7 | catch 8 | echo v:exception 9 | return 10 | endtry 11 | 12 | let [block_start, block_end] = common#get_range_for_block('\','Wb') 13 | 14 | let pre_selection = join( getline(block_start+1,a:firstline-1), "\n" ) 15 | let pre_selection_variables = s:ruby_determine_variables(pre_selection) 16 | 17 | let post_selection = join( getline(a:lastline+1,block_end), "\n" ) 18 | let post_selection_variables = s:ruby_determine_variables(post_selection) 19 | 20 | let selection = common#cut_visual_selection() 21 | let selection_variables = s:ruby_determine_variables(selection) 22 | 23 | let parameters = [] 24 | let retvals = [] 25 | 26 | " determine parameters 27 | for var in selection_variables[1] 28 | call insert(parameters,var) 29 | endfor 30 | 31 | let parameters = s:sort_parameters_by_declaration(parameters) 32 | 33 | for var in selection_variables[0] 34 | if index(post_selection_variables[1], var) != -1 35 | call insert(retvals, var) 36 | endif 37 | endfor 38 | 39 | call s:em_insert_new_method(name, selection, parameters, retvals, block_end) 40 | endfunction 41 | 42 | function! s:sort_parameters_by_declaration(parameters) 43 | if (len(a:parameters) <= 1) 44 | return a:parameters 45 | endif 46 | let pairs = s:build_parameter_declaration_position_pairs(a:parameters) 47 | call sort(pairs, "s:sort_parameter_declaration_position_pairs") 48 | return s:parameter_names_of(pairs) 49 | endfunction 50 | 51 | function! s:build_parameter_declaration_position_pairs(parameters) 52 | let cursor_position = getpos(".") 53 | let pairs = [] 54 | 55 | for parm in a:parameters 56 | if (searchdecl(parm) == 0) " could find and position cursor at parameter declaration 57 | call insert(pairs, [parm, getpos(".")]) 58 | else 59 | call insert(pairs, [parm, getpos("$")]) " use end of file to sink to bottom 60 | endif 61 | call setpos(".",cursor_position) 62 | endfor 63 | 64 | return pairs 65 | endfunction 66 | 67 | function! s:sort_parameter_declaration_position_pairs(pair1, pair2) 68 | let lineIndex = 1 69 | let colIndex = 2 70 | if (a:pair1[1][lineIndex] == a:pair2[1][lineIndex]) 71 | return a:pair1[1][colIndex] - a:pair2[1][colIndex] 72 | else 73 | return a:pair1[1][lineIndex] - a:pair2[1][lineIndex] 74 | endif 75 | endfunction 76 | 77 | function! s:parameter_names_of(pairs) 78 | let sorted_parameters = [] 79 | for pair in a:pairs 80 | call extend(sorted_parameters, [pair[0]]) 81 | endfor 82 | return sorted_parameters 83 | endfunction 84 | 85 | function! s:ruby_determine_variables(block) 86 | let tokens = s:ruby_tokenize(a:block) 87 | let statements = s:ruby_identify_tokens(tokens) 88 | 89 | let assigned = [] 90 | let referenced = [] 91 | 92 | for statement in statements 93 | call s:ruby_identify_methods( statement ) 94 | let results = s:ruby_identify_variables( statement ) 95 | call extend(assigned,results[0]) 96 | call extend(referenced,results[1]) 97 | endfor 98 | 99 | call common#dedupe_list(assigned) 100 | call common#dedupe_list(referenced) 101 | 102 | return [assigned,referenced] 103 | endfunction 104 | 105 | " Synopsis: 106 | " Splits a block of code into individual statements, then splits said 107 | " statements into individual tokens (variables, operators, symbols, etc) 108 | function! s:ruby_tokenize( block ) 109 | let stripped_block = tr( a:block, "\n\r\t", "; " ) 110 | let tokens = [] 111 | 112 | let ofs = 0 113 | while 1 114 | let a = matchstr( stripped_block, '\v^(#|;|,|\(|\)|\d+\.\d+|(\:|\@)?\w+|\s+|\''[^\'']*\''|\"[^\"]*\"|\=|\S+)', ofs ) 115 | if a == "" 116 | break 117 | endif 118 | let ofs = ofs + len(a) 119 | call add(tokens,a) 120 | endwhile 121 | 122 | return tokens 123 | endfunction 124 | 125 | " Synopsis: 126 | " Determines what each of a list of strings is with respect to the ruby 127 | " language. E.g. keywords, operators, variables, methods 128 | " 129 | " TODO: Improve this with ref to http://www.zenspider.com/Languages/Ruby/QuickRef.html#4 130 | function! s:ruby_identify_tokens( tokenlist ) 131 | let symbols = [] 132 | let statements = [] 133 | let reserved = [ "alias", "and", "BEGIN", "begin", "break", "case", "class", "def", "defined?", "do", "else", "elsif", "END", "end", "ensure", "false", "for", "if", "in", "module", "next", "nil", "not", "or", "redo", "rescue", "retry", "return", "self", "super", "then", "true", "undef", "unless", "until", "when", "while", "yield" ] 134 | 135 | let ignore_to_eos = 0 136 | 137 | for token in a:tokenlist 138 | if index(reserved,token) != -1 139 | let sym = "KEYWORD" 140 | elseif match(token, '\v^\s+$') != -1 141 | let sym = "WS" 142 | elseif match(token, '\v^\:\w+$') != -1 143 | let sym = "SYMBOL" 144 | elseif match(token, '\v^\I\i*$') != -1 145 | let sym = "VAR" 146 | elseif match(token, '\v^\@\I\i*$') != -1 147 | let sym = "IVAR" 148 | elseif match(token, '\v^\d+(\.\d+)?$') != -1 149 | let sym = "CONST" 150 | elseif token[0] == "'" || token[0] == '"' 151 | let sym = "STR" 152 | elseif token == '#' 153 | let ignore_to_eos = 1 154 | elseif token == '=' 155 | let sym = 'ASSIGN' 156 | elseif token == ',' 157 | let sym = 'COMMA' 158 | elseif token == '"' 159 | let sym = 'DQUOTE' 160 | elseif token == "'" 161 | let sym = 'SQUOTE' 162 | elseif token == '(' 163 | let sym = 'LPAREN' 164 | elseif token == ')' 165 | let sym = 'RPAREN' 166 | elseif token == ';' 167 | let sym = "EOS" 168 | if len(symbols) > 0 169 | call add(statements, symbols) 170 | let symbols = [] 171 | let ignore_to_eos = 0 172 | continue 173 | endif 174 | else 175 | let sym = "OPER" 176 | endif 177 | 178 | if ignore_to_eos == 1 179 | let sym = "COMMENT" 180 | endif 181 | 182 | if sym != "WS" 183 | call add(symbols,[sym,token]) 184 | endif 185 | endfor 186 | 187 | if len(symbols) > 1 188 | call add(statements,symbols) 189 | endif 190 | 191 | return statements 192 | endfunction 193 | 194 | " Synopsis: 195 | " Has a stab (badly) at working out if a string is a method or a variable 196 | " TODO: Improve this by searching through the current file for the name 197 | " preceded by 'def' 198 | function! s:ruby_identify_methods( tuples ) 199 | let lasttuple = [] 200 | for tuple in a:tuples 201 | let lastsym = get(lasttuple,0,"") 202 | let sym = tuple[0] 203 | if ((sym == "LPAREN") && (lastsym == "VAR")) || ((sym == "VAR") && (lastsym == "VAR")) || ((sym == "STR" && lastsym == "VAR")) 204 | let lasttuple[0] = "METHOD" 205 | endif 206 | let lasttuple = tuple 207 | endfor 208 | endfunction 209 | 210 | " Synopsis: 211 | " Takes a list of variables and tries to work out if the variable has been 212 | " assigned to OR used in a comparison. This determines which variables need 213 | " to be passed into the new method, and which need to be returned. 214 | " TODO: Change the name of this, it's misleading 215 | function! s:ruby_identify_variables( tuples ) 216 | let assigned = [] 217 | let referenced = [] 218 | 219 | for tuple in a:tuples 220 | if tuple[0] == "ASSIGN" 221 | let assigned = deepcopy(referenced) 222 | let referenced = [] 223 | elseif tuple[0] == "VAR" && !s:is_target_of_rspec_let(tuple[1]) 224 | call add(referenced,tuple[1]) 225 | endif 226 | endfor 227 | 228 | return [assigned, referenced] 229 | endfunction 230 | 231 | function! s:is_target_of_rspec_let(name) 232 | return search('^\s*let\s*[(]\s*[:]' . a:name . '\s*[)]', 'Wbn') > 0 233 | endfunction 234 | 235 | " Synopsis: 236 | " Do the vim bit of creating the new method, and the call to it. 237 | function! s:em_insert_new_method(name, selection, parameters, retvals, block_end) 238 | " Remove last \n if it exists, as we're adding one on prior to the 'end' 239 | let has_trailing_newline = strridx(a:selection,"\n") == (strlen(a:selection) - 1) ? 1 : 0 240 | 241 | " Build new method text, split into a list for easy insertion 242 | let method_params = "" 243 | if len(a:parameters) > 0 244 | let method_params = "(" . join(a:parameters, ", ") . ")" 245 | endif 246 | 247 | let method_retvals = "" 248 | if len(a:retvals) > 0 249 | let method_retvals = join(a:retvals,", ") 250 | endif 251 | 252 | let method_lines = split( "\ndef " . a:name . method_params . "\n" . a:selection . (has_trailing_newline ? "" : "\n") . (len(a:retvals) > 0 ? "return " . method_retvals . "\n" : "") . "end", "\n", 1) 253 | 254 | let start_line_number = a:block_end - len(split(a:selection, "\n", 1)) + 1 255 | 256 | " Insert new method 257 | call append(start_line_number, method_lines) 258 | 259 | " Insert call to new method, and fix up the source so it makes sense 260 | if has_trailing_newline 261 | exec "normal i" . (len(a:retvals) > 0 ? method_retvals . " = " : "") . a:name . method_params . "\n" 262 | normal k 263 | else 264 | exec "normal i" . a:name 265 | end 266 | 267 | " Reset cursor position 268 | let cursor_position = getpos(".") 269 | 270 | " Fix indent on call to method in case we corrupted it 271 | normal V= 272 | 273 | " Indent new codeblock 274 | exec "normal " . (start_line_number+1) . "GV" . len(method_lines) . "j=" 275 | 276 | " Jump back again, 277 | call setpos(".", cursor_position) 278 | 279 | " Visual mode normally moves the caret, go back 280 | if has_trailing_newline 281 | normal $ 282 | endif 283 | endfunction 284 | -------------------------------------------------------------------------------- /plugin/refactorings/general/extractvariable.vim: -------------------------------------------------------------------------------- 1 | " Synopsis: 2 | " Extracts the selected scope to a variable 3 | function! ExtractLocalVariable() 4 | try 5 | let name = common#get_input("Variable name: ", "No variable name given!") 6 | catch 7 | echo v:exception 8 | return 9 | endtry 10 | 11 | call s:select_variable_contents() 12 | 13 | " Replace selected text with the variable name 14 | exec "normal c" . name 15 | 16 | " Define the variable on the line above 17 | exec "normal! O" . name . " = " 18 | 19 | " Paste the original selected text to be the variable value 20 | normal! $p 21 | endfunction 22 | 23 | function! s:select_variable_contents() 24 | " select current word or re-establish selection 25 | " (not sure why we need to re-select) 26 | if (visualmode() == "") 27 | normal! viw 28 | else 29 | normal! gv 30 | endif 31 | endfunction 32 | 33 | -------------------------------------------------------------------------------- /plugin/refactorings/general/inlinetemp.vim: -------------------------------------------------------------------------------- 1 | " Synopsis: 2 | " Inlines a variable 3 | function! InlineTemp() 4 | " Copy the variable under the cursor into the 'a' register 5 | " XXX: How do I copy into a variable so I don't pollute the registers? 6 | let original_a = @a 7 | normal "ayiw 8 | 9 | " It takes 4 diws to get the variable, equal sign, and surrounding 10 | " whitespace. I'm not sure why. diw is different from dw in this 11 | " respect. 12 | normal 4diw 13 | " Delete the expression into the 'b' register 14 | let original_b = @b 15 | normal "bd$ 16 | 17 | " Delete the remnants of the line 18 | normal dd 19 | 20 | " Store current line, that's where we will start searching from 21 | let current_line = line(".") 22 | 23 | " Find the start and end of the current block 24 | " TODO: tidy up if no matching 'def' found (start would be 0 atm) 25 | let [block_start, block_end] = common#get_range_for_block('\','Wb') 26 | 27 | " Rename the variable within the range of the block 28 | call common#gsub_all_in_range(current_line, block_end, '\<' . @a . '\>', @b) 29 | 30 | " Put back original register contents 31 | let @a = original_a 32 | let @b = original_b 33 | endfunction 34 | -------------------------------------------------------------------------------- /plugin/refactorings/general/introducevariable.vim: -------------------------------------------------------------------------------- 1 | "Synopsis: 2 | " Introduce variable from class or method name 3 | function! IntroduceVariable() 4 | let original_a = @a 5 | 6 | normal ^ 7 | 8 | call search('\.*(\|{\|\n', 'p') 9 | normal hh"ayiw 10 | 11 | let line = @a 12 | 13 | if line == "new" 14 | normal ^"ayiw 15 | let line = @a 16 | endif 17 | 18 | let @a = original_a 19 | let var = s:snakecase(line) 20 | exec "normal I" . var . " = " 21 | 22 | endfunction 23 | 24 | function! s:snakecase(word) 25 | let word = substitute(a:word,'::','/','g') 26 | let word = substitute(word,'\(\u\+\)\(\u\l\)','\1_\2','g') 27 | let word = substitute(word,'\(\l\|\d\)\(\u\)','\1_\2','g') 28 | let word = substitute(word,'-','_','g') 29 | let word = tolower(word) 30 | return word 31 | endfunction 32 | -------------------------------------------------------------------------------- /plugin/refactorings/general/postconditional.vim: -------------------------------------------------------------------------------- 1 | " Synopsis: 2 | " converts a post-conditional expression to a conditional expression 3 | " note: will convert both types of conditional expression 4 | function! ConvertPostConditional() 5 | " pattern to match 6 | let conditional_operators = '\' 7 | " save the current line 8 | let current_line = line('.') 9 | " go to end of current line 10 | normal $ 11 | " find the first match for a conditional operator 12 | let first_match = search(conditional_operators, 'bnW') 13 | 14 | " if the first match isn't on the current line, exit. 15 | if current_line != first_match 16 | "echo "no match" 17 | return 18 | endif 19 | 20 | " move the cursor *backward* to the first found conditional operator 21 | call search(conditional_operators, 'bW') 22 | let conditional_pos = col(".") 23 | 24 | " move the cursor to the first word char on the line 25 | normal ^ 26 | let line_start_pos = col(".") 27 | 28 | " move the cursor *forward* to the first found conditional operator 29 | call search(conditional_operators, 'cW') 30 | 31 | " if conditional starts line (pre-conditional), convert *to* post-conditional 32 | let is_pre_conditional = line_start_pos == conditional_pos 33 | if is_pre_conditional 34 | " convert to post-conditional (e.g. do_stuff() if condition) 35 | 36 | " assert conditional statement takes exactly three lines 37 | let first_line = line('.') 38 | let last_line = search('^\s*end\s*$', 'nW') 39 | let is_three_lines = (last_line - first_line) == 2 40 | if is_three_lines 41 | " delete third line, cut first, paste after second, join, indent properly 42 | normal jjddkkddpkJ== 43 | else 44 | "echo "multi-line conditional contains 2+ statements, aborting" 45 | endif 46 | else 47 | " convert to pre-conditional (e.g. if condition \n do_stuff() \n end) 48 | 49 | " save original value of buffer a into temp variable 50 | let original_value = @a 51 | " delete to the end of the line into buffer a 52 | normal "ad$ 53 | " insert new line above 54 | normal O 55 | " and paste buffer a 56 | normal "ap 57 | " indent conditional properly 58 | normal == 59 | " restore original value back to register a 60 | let @a = original_value 61 | " move one line down and add 'end' 62 | exec "normal jo" . "end" 63 | " move back to the line that you started at 64 | normal k 65 | " indent the conditional body 66 | normal >> 67 | " remove trailing whitespace from paste operation 68 | s/\s*$//g 69 | endif 70 | endfunction 71 | -------------------------------------------------------------------------------- /plugin/refactorings/general/renameinstancevariable.vim: -------------------------------------------------------------------------------- 1 | 2 | " Synopsis: 3 | " Rename the selected instance variable 4 | function! RenameInstanceVariable() 5 | try 6 | let selection = common#get_visual_selection() 7 | 8 | " If no @ at the start of selection, then abort 9 | if match( selection, "^@" ) == -1 10 | let left_of_selection = getline(".")[col(".")-2] 11 | if left_of_selection == "@" 12 | let selection = "@".selection 13 | else 14 | throw "Selection '" . selection . "' is not an instance variable" 15 | end 16 | endif 17 | 18 | let name = common#get_input("Rename to: @", "No variable name given!" ) 19 | catch 20 | echo v:exception 21 | return 22 | endtry 23 | 24 | " Assume no prefix given 25 | let name_no_prefix = name 26 | 27 | " Add leading @ if none provided 28 | if( match( name, "^@" ) == -1 ) 29 | let name = "@" . name 30 | else 31 | " Remove the @ from the no_prefix version 32 | let name_no_prefix = matchstr(name,'^@\zs.*') 33 | endif 34 | 35 | " Find the start and end of the current block 36 | " TODO: tidy up if no matching 'def' found (start would be 0 atm) 37 | let [block_start, block_end] = common#get_range_for_block('\','Wb') 38 | 39 | " Rename the variable within the range of the block 40 | call common#gsub_all_in_range(block_start, block_end, selection.'\>\ze\([^\(]\|$\)', name) 41 | 42 | " copy with no prefix for the attr_* match 43 | let selection_no_prefix = matchstr( selection, '^@\zs.*' ) 44 | 45 | " Rename attr_* symbols 46 | call common#gsub_all_in_range(block_start, block_end, '^\s*attr_\(reader\|writer\|accessor\).*\:\zs'.selection_no_prefix, name_no_prefix) 47 | endfunction 48 | -------------------------------------------------------------------------------- /plugin/refactorings/general/renamelocalvariable.vim: -------------------------------------------------------------------------------- 1 | " Synopsis: 2 | " Rename the selected local variable 3 | function! RenameLocalVariable() 4 | try 5 | let selection = common#get_visual_selection() 6 | 7 | " If @ at the start of selection, then abort 8 | if match( selection, "@" ) != -1 9 | throw "Selection '" . selection . "' is not a local variable" 10 | endif 11 | 12 | let name = common#get_input("Rename to: ", "No variable name given!" ) 13 | catch 14 | echo v:exception 15 | return 16 | endtry 17 | 18 | " Find the start and end of the current block 19 | " TODO: tidy up if no matching 'def' found (start would be 0 atm) 20 | let [block_start, block_end] = common#get_range_for_block('\','Wb') 21 | 22 | " Rename the variable within the range of the block 23 | call common#gsub_all_in_range(block_start, block_end, '[^@]\<\zs'.selection.'\>\ze\([^\(]\|$\)', name) 24 | endfunction 25 | 26 | -------------------------------------------------------------------------------- /plugin/refactorings/general/rspec_extractlet.vim: -------------------------------------------------------------------------------- 1 | " Synopsis: 2 | " Extracts into an Rspec let declaration 3 | " Special thanks to ReinH (#vim room at irc.freenode.net) 4 | function! ExtractIntoRspecLet() 5 | normal 0 6 | if empty(matchstr(getline("."), "=")) == 1 7 | echo "Can't find an assignment" 8 | return 9 | end 10 | normal! dd 11 | exec "?^\\s*\\<\\(describe\\|context\\)\\>" 12 | normal! $p 13 | exec 's/\v([a-z_][a-zA-Z0-9_]*) \= (.+)/let(:\1) { \2 }' 14 | normal V= 15 | 16 | endfunction 17 | -------------------------------------------------------------------------------- /plugin/ruby-refactoring.vim: -------------------------------------------------------------------------------- 1 | " 2 | " Author: Enrique Comba Riepenhausen (@ecomba) & Paul King (@nrocy) 3 | " Email: enrique@edendevelopment.co.uk 4 | " Email: somecrocodile@gmail.com 5 | " 6 | " Acknowledgements: 7 | " Thanks to Gary Bernhardt for the inspiration for this tool and the original 8 | " ExtractVariable() and InlineTemp() functions. 9 | " 10 | " Contributions from Stuart Gale (@bishboria) 11 | " 12 | " Some support functions borrowed from Luc Hermitte's lh-vim library 13 | " Also borrowed snake case function from tim popes vim-abloish plugin 14 | 15 | " Load all refactoring recipes 16 | exec 'runtime ' . expand(':p:h') . '/refactorings/general/*.vim' 17 | 18 | " Commands: 19 | " 20 | " Using a simple 'R' prefix for now 21 | " TODO: Do we even need this prefix? How likely is it that we'll conflict? 22 | 23 | command! RAddParameter call AddParameter() 24 | command! RAddParameterNB call AddParameterNB() 25 | command! RInlineTemp call InlineTemp() 26 | command! RExtractLet call ExtractIntoRspecLet() 27 | command! RConvertPostConditional call ConvertPostConditional() 28 | command! RIntroduceVariable call IntroduceVariable() 29 | 30 | command! -range RExtractConstant call ExtractConstant() 31 | command! -range RExtractLocalVariable call ExtractLocalVariable() 32 | command! -range RRenameLocalVariable call RenameLocalVariable() 33 | command! -range RRenameInstanceVariable call RenameInstanceVariable() 34 | command! -range RExtractMethod call ExtractMethod() 35 | 36 | " Mappings: 37 | " 38 | " Default mappings are r followed by an acronym of the pattern's name 39 | " E.g. Extract Method is mapped to rem 40 | 41 | if !exists('g:ruby_refactoring_map_keys') 42 | let g:ruby_refactoring_map_keys = 1 43 | endif 44 | 45 | if g:ruby_refactoring_map_keys 46 | nnoremap rap :RAddParameter 47 | nnoremap rapn :RAddParameterNB 48 | nnoremap rit :RInlineTemp 49 | nnoremap rel :RExtractLet 50 | nnoremap rcpc :RConvertPostConditional 51 | nnoremap riv :RIntroduceVariable 52 | 53 | vnoremap rec :RExtractConstant 54 | vnoremap relv :RExtractLocalVariable 55 | vnoremap rrlv :RRenameLocalVariable 56 | vnoremap rriv :RRenameInstanceVariable 57 | vnoremap rem :RExtractMethod 58 | endif 59 | -------------------------------------------------------------------------------- /test/testcases.rb: -------------------------------------------------------------------------------- 1 | # rename local variable rrlv 2 | def method 3 | asdf = 10 4 | end 5 | 6 | # rename instance variable rriv 7 | class Foo 8 | def method_one 9 | @bar = foo 10 | end 11 | 12 | def method_two 13 | @bar = bar 14 | end 15 | end 16 | --------------------------------------------------------------------------------