A Ruby REPL workflow with Neovim and neoterm

Clojure’s REPL workflow has spoiled me, so I’ve arranged a crude approximation of it for Ruby using Neovim, the neoterm plugin and a tweak to pry.

These things make it possible to very quickly evaluate Ruby code in a console from inside a Neovim session, which is a good thing because

  • it makes it easy to set up state in a REPL
  • while collecting data or building complicated queries you can keep all the steps safe in a buffer, then go back and re-run them without having to sit at the console tapping ‘up’ like a madman
  • everything happens in a Neovim buffer so you can easily search, copy and paste. You can also write anything to a file, including the entire REPL session

Introducing neoterm
Neoterm is a small but complete Neovim plugin. It looks after Neovim’s terminal buffers for you, and provides commands for sending snippets of text from an ordinary buffer to a running terminal.

It installs like any other Neovim plugin; instructions are on the neoterm GitHub repo.

Configuring neoterm
Neoterm needs some configuration to be useful. Here are the settings I arrived at.

let g:neoterm_autoscroll = '1'

^ This means the terminal will always scroll down to reveal the latest output. This isn’t necessary when you’re interacting with the terminal like an ordinary shell, but if you’re sending bits of code to it from another buffer it’s a must-have.

let g:neoterm_size = 16

^ By default neoterm’s buffer occupies 50% of the Neovim window, which is a bit much for me.

command! -nargs=+ TT Topen | T

^ This :TT command was kindly provided to me by kassio, the author of neoterm. It has the effect of opening a new terminal if none is already running, or reusing an existing terminal. Either way, the terminal it summons runs whatever shell command you typed in after :TT — a bit like :!, but smarter.

nnoremap § :TT

^ This is a shortcut. § is the key to the left of 1 on my keyboard. Pressing it in normal mode opens up a :TT on the Neovim command line ready for a shell command.

vnoremap <Leader>2 :TREPLSendSelection
nnoremap <Leader>2 :TREPLSendLine

^ These mappings are for interacting with the REPL. 2 will behave selection- or line-wise in visual or normal mode respectively. The effect is to send all the chosen code to the terminal and hit return at the end of each line.

To be useful, the Neovim terminal should be running a Ruby console when you send it Ruby code. If there’s no pre-existing terminal, one will be started for you and a console booted. Neoterm is smart enough to start an appropriate language REPL for the file you’re currently editing. If you’re in a Rails project, it will start a Rails console for you.

nnoremap <Leader>3 :Tmap
nnoremap <Leader>0 :Ttoggle
let g:neoterm_automap_keys = '<F5>'

^ The neoterm docs are a little bit unclear on what :Tmap does. It lets you define a command which will be repeated when you hit the neoterm_automap_keys key, in my case F5. I’ve found this useful in the shell, where I use it to re-run my script, rake task or whatever over and over again.

Finally, :Ttoggle will hide the current terminal. It can be brought back by sending something to the REPL, or starting afresh with a :TT

With a little practice you will be sending code to the REPL with a flick of 2 and then folding away your console with 0 when you’re done. Lost something in the output? Switch to the terminal buffer and use ordinary Vim movements to swoop up and down through history.

A note about pry
The way the pry console paginates long output disrupts the way neoterm interacts with the REPL; I think Pry expects a q to break out of the pagination and neoterm does not know to send one. I added a line to my ~/.pryrc file to disable the pager when pry was running inside Vim.

if ENV['VIM']
  Pry.config.pager = false
end

My Neovim configuration is on Github with all my other dotfiles.