Software localization

How to Write a Vim Plugin with Ruby

Every few months we have two Hackdays that we set aside to work on unrelated projects. For my last project, I built a Vim plugin using Ruby.
Software localization blog category featured image | Phrase

In this article, I'll give you a brief overview and a starting point from which you can develop your own plugins for Vim using Ruby.

Learning VimScript in two days seemed like quite the challenge but I was up for it and after some initial research I decided to use Ruby to develop the plugin.

Basic File Structure of Vim Plugins

First we'll take a look at the file structure that a modern Vim plugin should follow so that it can be installed using tools such as pathogen.vim or Vundle.

There are many directories that you can create in your plugin root path that will contain a special meaning for loading the plugin. Here I've decided to focus on the most important:

  • your_plugin_name/plugin/ This is one of the most important folders that we'll be using. All files contained within will be loaded when we start Vim. The entry point for most plugin functionality will go inside this folder.
  • your_plugin_name/ftdetect/ ft stands for file type. Files in this folder are only used for detecting and setting the filetype. These scripts will also be executed on every newly started session of Vim.
  • your_plugin_name/ftplugin/ Here we find the ft prefix that indicates filetype related items. These files are loaded based on the buffers file type. The naming of files and subdirectories of this directory is of significance. If you have files that should only be loaded if editing python files, you'll have to create a script named py.vim or a subdirectory named ./py and stick your files into that.
  • your_plugin_name/autoload/ All files inside the autoload directory are loaded if needed. This is really useful for larger plugins so as to keep the startup time of Vim short. This will also prevent it from loading the whole script code every time. I'm not going to cover autoloading right now but there is a really nice guide called Learn Vimscript the Hard Way that can point you in the right direction.
  • your_plugin_name/doc/ This contains the documentation for your plugin.

Ruby and VimScript

If using Ruby inside your VimScript, Vim will need to be compiled with +ruby. You can check this for your Vim installation with vim --version. If it is listing +ruby , Vim was compiled with ruby support.

This allows you to do things like use Ruby's powerful Standard Library and RubyGems. You also won't have to dive as deeply into VimScript.

VimScript

As you can probably see, all files in the plugin directory are loaded when you start up Vim.

All that you need to do in order to get started is to create a file with the ".vim" extension in that plugin directory. This file contains the VimScript that defines the functionality that should be provided by your plugin.

Normally it will look something like the following:

function! PhraseAppPush()

   call phraseapp#cliclient#PhraseAppClientExecute("push")

endfunction

command! PhraseAppPush call PhraseAppPush()

function! PhraseAppPull()

   call phraseapp#cliclient#PhraseAppClientExecute("pull")

endfunction

command! PhraseAppPull call PhraseAppPull()

Execute Ruby code inside VimScript

VimScript provides the ruby command that can be used to execute some Ruby code.

Instead of using another VimScript function we can instead refer to it as a Ruby function or executed Ruby code. For a single line of Ruby code we can use the ruby command followed by your Ruby code.

To embed multiple lines of Ruby code there is a syntax that seems quite similar to the syntax Ruby allows for multi-line stings:

function! Name()

  ruby <<EOF

    puts "something"

    ...

    # some more ruby code

    ...

  EOF

endfunction

Separate Ruby Code from the VimScript File

These techniques will allow you to simply separate your Ruby code into different .rb files and list them as ruby require "my_module".

To make this work, the path of the Ruby files must be added to a load path by doing something equal to ruby $LOAD_PATH.unshift File.join(File.dirname(Vim.evaluate('expand("<sfile>")')), 'lib')

If the ruby files are placed under plugin/lib/. expand("<sfile>") they can be used in VimScript to get the directory of the current file (see :help sfile).

By moving the Ruby code into separate files outside of the VimScript it becomes easy to test with test frameworks such as minitest or RSpec.

Getting Access to Vim Functions

If you are using Ruby inside VimScript you will be provided with the Vim Module.

The Vim module | PhraseThis allows you to access buffer settings and other Vim related items needed for the development of your Vim plugin.

For example, in order to get the current buffer you can call Vim::Buffer.current.

Alternatively, another command already placed in the section before is Vim.evaluate()

In order to evaluate a VimScript snippet, we need to get the path of the current file. :help ruby This will provide some  great documentation of the functions provided.

Summary and Further Reading

I hope that this short article helps to give you a starting point for developing your own Vim plugins and a nudge towards publishing them to the community.

While using Ruby for Vim plugin development does add a dependency on Ruby to your plugin, it will still offer you a lot of benefits such as the following.

  • You can use a language you are more familiar with than VimScript
  • Testing your functionality using known test frameworks like Rspec or minitest
  • You can use Ruby's Standard Library

If you're looking to learn more, the following resources are good for digging deeper into the topic: