Vim with Solargraph: autocompletion heaven.

I’ve long been in search of good Ruby auto-completion framework for vim, without luck. As soon as a new option comes about, I’ll jump on it, only to find that it either doesn’t work at all, or sometimes does its job at best.

Fortunately, we now have Solargraph. Solargraph understands Ruby code and does a great job of extracting pertinent information for auto-completion, documentation, and refactoring purposes.

Just want to get it working? Skip to the TLDR.

While Solargraph does a great job of handling a codebase, we still need a way to integrate with it in vim. Solargraph’s readme points vim users to vim-solargraph, but this project’s readme doesn’t look very optimistic:

Please consider this as a first prototype pre ALPHA version, just to prove that it can be done.

Props to the author for making this, but unfortunately, this is one of those options which I’ve tried and failed to get working.

On a more positive note, Solargraph can operate as a Language Server; This means we can use Solargraph with nothing more than a generic Language Server client, such as LanguageClient-Neovim.

First, we need the Solargraph gem. Nothing special here:

gem install solargraph

Now we’ll need to install a language client plugin. I’ve chosen LanguageClient-Neovim simply because I use neovim and it’s pretty popular. I’m going to go ahead and assume the use of the vim-plug plugin manager, but use what you will. vimrc:

call plug#begin('~/.local/share/nvim/plugged')

" This language client actually makes use of a binary, hence the `install.sh`.
" We also need the `next` branch in order to specify
" a language server's TCP port at the time of writing
Plug 'autozimu/LanguageClient-neovim', {
    \ 'branch': 'next',
    \ 'do': 'bash install.sh',
    \ }

call plug#end()

Now let’s ensure that’s installed and remote plugins are updated:

vim +PlugInstall +UpdateRemotePlugins +qa

Season vimrc:

" Tell the language client to use the default IP and port
" that Solargraph runs on
let g:LanguageClient_serverCommands = {
    \ 'ruby': ['tcp://localhost:7658']
    \ }

" Don't send a stop signal to the server when exiting vim.
" This is optional, but I don't like having to restart Solargraph
" every time I restart vim.
let g:LanguageClient_autoStop = 0

" Configure ruby omni-completion to use the language client:
autocmd FileType ruby setlocal omnifunc=LanguageClient#complete

That’s it! Now let’s start up the Solargraph language server:

solargraph socket

Vim’s omnicompletion should now be hooked up and calling out to Solargraph. This can, as always, be triggered with <C-x><C-o>.

Basic omnicompletion

Solargraph also understands YARD documentation comments.

Omnicompletion with YARD documentation

What about documentation for gems? Just generate YARD documentation for them:

yard gems

Now, given a valid require statement in the code, we should see completion results for gems too.

Omnicompletion for gems

Alternatively, we can require the gems we want documented in a special .solargraph.yml file:

---
include:
- "**/*.rb"
exclude:
- spec/**/*
- test/**/*
- vendor/**/*
require:
- rails
reporters: []
domains: []
plugins: []

Omnicompletion for gems without an explicit require

To save us running yard gems regularly, we can also ensure that any newly-installed gems automatically generate YARD documentation:

yard config --gem-install-yri

Finally, language servers can provide us a lot more than just auto-completion. Take a look at the LanguageClient documentation (:help LanguageClient) to see what it can do for you. To get started, the author suggests some mappings:

nnoremap <F5> :call LanguageClient_contextMenu()<CR>
nnoremap <silent> K :call LanguageClient#textDocument_hover()<CR>
nnoremap <silent> gd :call LanguageClient#textDocument_definition()<CR>
nnoremap <silent> <F2> :call LanguageClient#textDocument_rename()<CR>

TLDR

Assuming neovim and vim-plug:

Install Solargraph

gem install solargraph

Modify vimrc

call plug#begin('~/.local/share/nvim/plugged')

Plug 'autozimu/LanguageClient-neovim', {
    \ 'branch': 'next',
    \ 'do': 'bash install.sh',
    \ }

call plug#end()

let g:LanguageClient_autoStop = 0
let g:LanguageClient_serverCommands = {
    \ 'ruby': ['tcp://localhost:7658']
    \ }

nnoremap <F5> :call LanguageClient_contextMenu()<CR>
nnoremap <silent> K :call LanguageClient#textDocument_hover()<CR>
nnoremap <silent> gd :call LanguageClient#textDocument_definition()<CR>
nnoremap <silent> <F2> :call LanguageClient#textDocument_rename()<CR>

autocmd FileType ruby setlocal omnifunc=LanguageClient#complete

Install plugin

vim +PlugInstall +UpdateRemotePlugins +qa

Start solargraph

solargraph socket