Vim for Ruby and Rails in 2019

September 14, 2019

Many Rails programmers prefer Vim to full-blown bulky IDEs like RubyMine. The are several reasons for that but most important (to me at least) is the ability to customize Vim, make it very powerful and at the same time keep it pretty lightweight.

So let's have a look at what it takes to set up Vim for a comfortable and productive workflow with Ruby And Ruby On Rails framework.

Here's How My Vim Looks Like
Here's How My Vim Looks Like

Disclaimer: The setup described in this articles works for me. But you are free to try different things. There is not a single way to build your configuration for Ruby And Rails. Wherever possible, I will list some alternative plugins so that you could try both.

Essential Vim plugins

Those folks are language-agnostic.

  • NERDTree to navigate the file tree
  • FZF let's you fuzzy search through the files in project (and much more, really)
  • jiangmiao/auto-pairs inserts quotes and parenthesis in pairs as you type
  • tpope/vim-commentary press gcc to comment out a line or gc to comment a selection in visual mode
  • OneDark colorscheme in case you're interesting which colorscheme is used on the screenshot

You can read more about the essential plugins here.

Syntax highlighting

There is a pretty decent syntax highlighting for Ruby file out of the box.

For Rails, there is the VimRails plugin that enhance the highlighting among other things. But we'll talk about it later.

You might also need a custom highlighter for some template formate, for example SLIM.

End-wise

This is kinda minor, but it freaks me out when it's not working as expected. When you type a method name or start an if block and press enter it should automatically add the end keyword for you.

Just install this plugin to make it work — tpope/vim-endwise.

Linting

For linting (whether it's ruby, typescript, python, or CSS) I'm using a plugin called ALE. Basically, it runs an external linter for you asynchronously (so it doesn't block the UI).

ALE supports plenty of languages and linters. So if you have rubocop installed in the system it will run it for you on the current file, so you don't have to set up anything.

Although if you need to specify which linters you want to run, you can do this in the settings file by providing a special variable:

let g:ale_linters = {
      \   'ruby': ['standardrb', 'rubocop'],
      \   'python': ['flake8', 'pylint'],
      \   'javascript': ['eslint'],
      \}

To see the list of available linters for the current file run :ALEInfo<Enter> in the command line.

Some of the linters can also fix your code. For example, testdouble/standard will both fix the errors and format the file. In order to set up a "fixer" for your file there's again a variable you need to set:

let g:ale_fixers = {
      \    'ruby': ['standardrb'],
      \}
let g:ale_fix_on_save = 1

The last option is a huge time saver — it will automatically fix (and thus format) your file on save.

I also have a little piece of configuration that shows the total number of warnings and errors in the status line. Very convenient.

function! LinterStatus() abort
  let l:counts = ale#statusline#Count(bufnr(''))

  let l:all_errors = l:counts.error + l:counts.style_error
  let l:all_non_errors = l:counts.total - l:all_errors

  return l:counts.total == 0 ? '✨ all good ✨' : printf(
        \   '😞 %dW %dE',
        \   all_non_errors,
        \   all_errors
        \)
endfunction

set statusline=
set statusline+=%m
set statusline+=\ %f
set statusline+=%=
set statusline+=\ %{LinterStatus()}

And here are a couple of alternatives to ALE:

  • vim-syntastic/syntastic very popular one but synchronous which can cause significant lags in UI
  • neomake/neomake asynchronous linting and make framework for Neovim/Vim (didn't try that one)
  • Vanilla way an attempt to use makeprg for linting instead of plugins

Navigation between files

Sometimes you'd want to quickly jump from one file to another (for example, from a model to controller, from controller to test, etc.). This is what Vim Rails let's you do.

This plugin provides a lots of niceties and enhancements around Rails.

You can do things like:

  • it enhances gf so you can jump to partials for example, or to model files
  • Emodel, :Eview, :Econtroller to easily jump to corresponding model, view and controller files
  • :Rails runner to execute with the rails runner
  • :Rails without arguments to run the current test, spec or feature

There are many other small and not so small niceties so make sure to read the README file.

Autocompletion

Autocompletion in dynamic languages like Ruby is a bit of a challenge. But modern IDEs can be very helpful, by indexing and analyzing a large portion of files. How to achieve something similar to Vim?

First and foremost, I recommend installing the deoplete plugin (though have a look at the alternatives at the end of this section). It is an asynchronous completion framework that will suggest a completions for you as you type.

To enable it on startup,

let g:deoplete#enable_at_startup = 1

Here's a little tweak for it work with Tab:

inoremap <silent><expr> <TAB>
      \ pumvisible() ? "\<C-n>" :
      \ <SID>check_back_space() ? "\<TAB>" :
      \ deoplete#mappings#manual_complete()
function! s:check_back_space() abort "{{{
  let col = col('.') - 1
  return !col || getline('.')[col - 1]  =~ '\s'
endfunction"}}}

Have you heard about the "Language Servers"? It's a concept pioneered by Microsoft with TypeScript which denotes to a separate process running in a background and analysing your code in real time. Editors and IDEs can communicate with this process and ask for some specific information (syntax errors, autocomplete suggestions, etc.). Of cause there is one for Ruby as well.

You can install it and run like this

gem install solargraph
solargraph socket

Then you will also need a Language Server plugin for Vim. If you're using Neovim, you can try this one.

In order for it to work you need to tell it where it should find the language server for a particular language. Put this into your vim settings:

let g:LanguageClient_serverCommands = {
    \ 'ruby': ['~/.rbenv/shims/solargraph', 'stdio'],
    \ }

You might want to change the configuration depending on where your gem is installed (I use rbenv). Check which solargraph.

Restart Vim, and you should be able to use it. I have to warn you that solargraph is not too smart but I guess this is how far you can get with a dynamic language like ruby.

For better rails support though you will also want to run

  solargraph bundle

It will run a bunch of magical stuff in the background (it will need to install the documentation first), and after that you should be able to autocomplete things like belongs_to, before_action and other Rails-specific method.

For more insides on Rails support, you can follow this thread.

Now here are the promised Deoplete alternatives:

  • YouCompleteMe An older code-completion engine with whooping 19K+ stars on GitHub
  • SuperTab is a vim plugin which allows you to use Tab for all your insert completion needs

Snippets

Expanding a snippet with Neosnippet
Expanding a snippet with Neosnippet

Snippets is ability to type something short like meth then press some magical button and see it expanded into a full method where you can type method name right away and the jump to body.

As usual there are several engines that can do the trick but a small challenge there is how to actually make it compatible with other plugins you already have like completion plugins for example.

If you are using the deoplete for completion, then I recommend trying out the https://github.com/Shougo/neosnippet.vim plugin.

if has('nvim')
  Plug 'Shougo/deoplete.nvim', { 'do': ':UpdateRemotePlugins' }
else
  Plug 'Shougo/deoplete.nvim'
  Plug 'roxma/nvim-yarp'
  Plug 'roxma/vim-hug-neovim-rpc'
endif

Plug 'Shougo/neosnippet.vim'
Plug 'Shougo/neosnippet-snippets'

Some mappings:

imap <C-k>     <Plug>(neosnippet_expand_or_jump)
smap <C-k>     <Plug>(neosnippet_expand_or_jump)
xmap <C-k>     <Plug>(neosnippet_expand_target)

With it you can select a snippet through the familiar deoplete interface, and then you can press <C-k> multiple times to expand the snippet and move cursor to the next editable part.

Where To Go Next?

Okay, I think that's it. Please let me know if I forgot anything or if something doesn't work for you.

Here are some more things to look at:

Vim For Developers

Vim For Developers

❄️ 35% off this Christmas season! ❄️

Learn Vim and upgrade your productivity to the next level by building the IDE of your dreams.

LEARN MORE