Dan's Musings

Developing Common Lisp using Vim with tmux or ConEMU

This post is an updated answer to an older post. In that post, I describe using GNU screen and rlwrap. I don't use rlwrap anymore, as it makes the REPL too slow. I also don't use GNU Screen anymore; I've switched to tmux and ConEMU. I've decided I need an up-to-date post showing what I currently use so that I can link to it to show people online what I'm using.

So let's dive in. This is how I Common Lisp in 2024.

The Technique

The idea is that all functionality available in any editor or plugin is also available in the "vanilla" REPL. Debugging, inspecting stack frame variables, compiling files, loading ASDF systems, showing documentation for functions, everything. The developer simply need learn the commands. I took the time to learn the commands, so that's not a problem. All I need now is a way to get code from my editor into the REPL easily. To do this, I employ Jpalardy's vim-slime. I use this plugin to send stuff to the REPL via one of two tools: tmux on *NIX machines, and ConEMU on Windows.

Here is the entire technique:

  1. Open up tmux or ConEMU.
  2. In the first pane, run ros run to spin up a REPL. (I use ros +Q run, actually, since I use OCICL, but that's for another post.)
  3. In another tmux or ConEMU split-pane, either horizontal or vertical, it doesn't matter, open up the lisp file using Vim.
  4. To send code, select it and press the keybinding to send code to the other pane. By default, this is simply C-c C-c, but I've remapped it to <Space>g.

Here is a list of use cases, and how I do them with my set-up (using default key-bindings):

Explanation Key Presses
Start the REPL ros run in the REPL pane
Start up the editor nvim <file>.lisp in the editing pane
Evaluate current form in REPL v a ( C-c C-c
Evaluate current atom in REPL v a w C-c C-c
Indent s-exp v a ( =
View docs of <thing> (describe 'thing) in REPL pane
Search for thing (apropos "thing") in REPL pane
Print source of error in debugger print in REPL pane (SBCL)
Select particular stack frame in debugger down/up in REPL pane (SBCL)
List variables in a stack frame list from REPL pane (SBCL)

The table above is simply to show that this method is fully-featured, but the seasoned vim/terminal expert will note that I'm either entering commands into the REPL or just using "normal" vim commands to select things and send them to the REPL using C-c C-c, the vim-slime keybinding.

The Secret Sauce

To accomplish this, I have to set up vim with vim-slime. Putting this in the .vimrc or init.vim should do the trick (uses vim-plug, built into NeoVim but usable from Vim too):

let b:slime_default_config = {"HWND": "0:T1"}
if has('win32')
  let b:slime_default_config = {"HWND": "O:T1"}
else
  let b:slime_default_config = {"socket_name": get(split($TMUX, ","), 0), "target_pane": ":.0"}
endif

call plug#begin()
Plug 'jpalardy/vim-slime'
Plug 'guns/vim-sexp'
call plug#end()

Comparison to Other Techniques

I have tried a lot of other techniques and set-ups in the non-Emacs space, but none that I have tried are as fully-featured as the set up above described! NOT ONE!

  • Vlime: Pretty good, but you can't edit stuff in the REPL window, everything's read-only. It randomly opens window panes too, which is a royal pain. Still, it's good enough for day-to-day use.
  • Conjure + Swank: Can't interact with the debugger, at least as far as I can tell. I'd actually like to be disproven, someone write-in and tell me! It's otherwise a very nice plugin, might be what I switch to if I can get it to work.
  • VS Code + Alive (my experience report is here): Can't inspect variables for each stack frame, though everything else in this set-up is obviously more ergonomic and easy to use.

This set-up might be simplistic, but it is fully featured in ways other set-ups do not provide. Bonus! I can use basically the same tooling for pretty much anything I can fire up from a terminal window.

For someone that lives from a terminal window, that works for me.

Downsides

The main downside to this technique is ergonomics. The compile errors can be hard to read from the other pane, particularly with SBCL. I wrote a Roswell + SBCL Vim compiler plugin for this case, though. I can just run :make and get stepped through most of the errors. Despite this, I find myself mostly just using the REPL window though. I guess it's not as big of a problem as I thought, looking at my usage patterns. I still use the plugin occasionally though.

Another "downside" is you have to be comfortable in the terminal in the first place. This is a terminal-centric solution. Instead of keybindings, you have to remember commands and be able to quickly type them in. However, being an ops engineer by trade, terminal is life, so it's great for me.

Finally, looking up docs is a little painful. I find myself looking up everything in the CLHS with my browser. I feel like I've always done that though, even when I've tried out other solutions that just "give" me the docs, like VLIME or Alive.

Conclusion

I've never seen anyone else use Jpalardy's vim-slime for actual Common Lisp, except me. I invite others to try it though. Think of it as "camping" away from civilization. There's plenty that can be done from the REPL alone, and it's good to know the commands in case that's all that's available, like perhaps interacting with old production systems. Honestly, I feel like it's the fastest solution available, the one closest to hand.