Ruby On Vim June 6th, 2007

Vim is my religion. Vim is true programmers editor. It can be extended. It has many features. Using vim make you looks like a true hacker. If vim is a weapon, it could be used for short distance fighting (like knife or sword). But you can use it to shoot somebody (as pistol). Don’t underestimate it. Snipper can use it to shoot the president of a country in distance of 400m. You can destroy the buildings full of enemy soldiers with vim (think bazooka). With hard works of a reasonable amount of specific people, you can use vim to destroy a planet (okay, I am drunk now). But you get the idea. You can find many scripts to extend vim. But someday you need to extend vim your self.

Ruby is my favorite programming language. Ruby is fun. Ruby makes programmers happy. So why not combine them? Extend vim with ruby. I will guide you to write simple vim plugin using ruby. But you need to be familiar with vim and ruby (just be familiar is enough, no need to be a ninja).

You need vim compiled with ruby support. If you get vim from default place (from Linux distro maybe), most likely you have ruby support in vim. But I think you need to install vim-ruby package or something like that to enable ruby support for default vim. If you want to compile vim from source, make sure you have ruby development header. Don’t forget to use —enable-rubyinterp option in configure step. You can check if your vim has ruby support or not with this vim command.

:echo has("ruby")
If you get 1, you are all set. If you get 0, you have to set up ruby support first for your vim.

Okay, just like traditional programming language tutorial tradition, we start with printing “Hello World” message. Use this command.

:ruby print "Hello World"
Wow, we did it. Now what? We can use ruby to find out our vim status. Try to make another window (use :split). Then if you want to know how many windows do you have right now, use this command.
:ruby print VIM::Window.count
Yes, you manipulate vim with ruby using VIM module.

But typing command one by one is tiresome. That’s why when you want to extend vim with ruby, you write the script in another file. Imagine you have a ruby script named hello.rb containing print “Hello World”. You can call it using this vim command.

:rubyf hello.rb
Simple, huh?!!!

Okay, right now we will write simple vim plugin using ruby. This is what our plugin suppose to do.

# filename: blabla.rb
require 'date'
require 'abbrev'

print 'blabla'

Imagine you are editing that $1 million ruby script. Then you want to read the required file, that is date.rb or abbrev.rb. So you put cursor in one of the lines containing require word. Then call our ruby function. Voila, you are opening the required file.

We named our ruby vim plugin open_require.rb.
def open_require(open_type)
end
That is our ruby function. open_type parameter indicates whether we want to open the required file in a new window (split), or a new tab (tabnew), or the same window (edit). We filter the parameter now.
  case open_type
  when 'e' then open_command = 'edit'
  when 's' then open_command = 'split'
  else open_command = 'tabnew'
  end
Remember that we want to open the required file when the cursor are in the line containing the require word. So we must get current line. Here’s how.
  cb = VIM::Buffer.current
  cur_line = cb.line

Right now the cur_line variable containing this string: require ‘date’. We want to extract the date word. Dirty but does the job.

file = (cur_line.split('require ')[1].chomp)[1..-2] + '.rb'
file variable will hold date.rb string value. Okay, we got the filename. But where is the file located. $LOAD_PATH special variable contains our ruby library path.
$LOAD_PATH.each {|path|
    if File.exist?(path + '/' + file) then
      VIM::command("#{open_command} #{path + '/' + file}")
    end
}
Our ruby function is complete but we need to register the ruby function as vim function. Sure we can call our ruby function directly but I like to make it more abstract. Remember, abstraction is important concept in software development. :) Put this below our ruby function.
VIM::command("function! OpenRequire(open)
                           ruby open_require(VIM::evaluate('a:open'))
                          endfunction")

Okay, our vim plugin is complete. You can not just pass vim variable to ruby. So you need VIM::evaluate to send the vim variable to ruby. Open our $1 million ruby script. Put the cursor in the line: require ‘date’. Then…. but before that, we need to execute our vim plugin. Run this first. :rubyf open_require.rb Then we execute this vim command. :call OpenRequire('t') Voila, new tab opened for date.rb file.

By the way, here is the final version of open_require.rb.
def open_require(open_type)
  case open_type
  when 'e' then open_command = 'edit'
  when 's' then open_command = 'split'
  else open_command = 'tabnew'
  end
  cb = VIM::Buffer.current
  cur_line = cb.line
  file = (cur_line.split('require ')[1].chomp)[1..-2] + '.rb'
  $LOAD_PATH.each {|path|
    if File.exist?(path + '/' + file) then
      VIM::command("#{open_command} #{path + '/' + file}")
    end
  }
end

VIM::command("function! OpenRequire(open)
                ruby open_require(VIM::evaluate('a:open'))
              endfunction")
Now is the time to spread our vim plugin so humanity will benefit from it. You need to know how ftplugin works in vim. To enable our vim plugin work every time you open or make new ruby script, you must put the vim plugin in special directory. This will be ~/ftplugin/ruby directory. This is not the only way to achieve auto load vim plugin. Read vim manual for that. But Vim only load the files with vim extension. Our ruby file will be ignored. So you need to make helper file. Named it require.vim.
if exists("*OpenRequire")
  finish
else
  rubyf open_require.rb
endif

First we need to check whether the OpenRequire vim function has been loaded or not. You do not want to redefine the OpenRequire vim function every time you open the ruby file. It will give you warning. If not, execute our vim plugin. After that you can call OpenRequire function in your ruby file editing session. If typing call OpenRequire(‘t’) is tiresome, you can map it. Read vim manual for that.

So now you have the basic skill to destroy Darth Vader ship with vim using ruby.
if exists("*DestroyVaderShip")
  finish
else
  rubyf destroy_vader_ship_ruby_script.rb
endif

Then in vim session, you type :call DestroyVaderShip() to save the universe from Sith.

1 Response to “Ruby On Vim”

  1. Jason Says:
    Thanks for the tutorial. I like working in ruby far more than trying to learn the syntax for vim.

Leave a Reply