Learning Clojure: Vim REPL integration
In my last post I wrote that I was using the Cursive plugin for IntelliJ. It turned out that it doesn’t play nicely with the Ideavim plugin. I considered mapping keystrokes to Cursive actions, but that’s not an activity I enjoy and I doubted whether my keybindings would be any good.
Emacs is popular in the Clojure community, but I use Vim and I’m not willing to change editor to learn a new language. So I wanted a Vim plugin for REPL-driven development.
The hosts of the Clojure Design Podcast use Tim Pope’s vim-fireplace, so I tried it out. You start a REPL with lein repl
, which writes the port to a .nrepl-port
file. vim-fireplace reads it and connects to the REPL. It worked as the README described but the keybindings didn’t click for me.
After spotting the podcast was from 2019 and searching for something newer, I found Conjure. In fact one of the hosts blogged about switching to Conjure in 2023. Conjure is very interesting. It’s written in Fennel, a Lisp that compiles to Lua (so it’s for Neovim, not Vim). Conjure supports REPL-driven development for a bunch of different Lisps and somehow even Python, which I need to try.
Here I wasted loads of time reluctantly fiddling with Vim configs. I should definitely have tried Conjure out without installing. You should try this first!
Long story short: I didn’t want to move my Python development from PyCharm to Vim, so I settled on three vim config files: separate Ideavim and Neovim configs that both source a common config file of common preferences and keybindings. Ideavim supports a small number of plugins via vim-plug
, so I figured it was easiest to use it in Neovim too.
Luckily, after all the fiddling, the Conjure keybindings made sense to me. Type <localleader>e
to “evaluate” then pick the thing to evaluate with another key:
,eb
– evaluate entire file,ee
– evaluate inner form under cursor,er
– evaluate outer most (root) form under cursor,ew
– evaluate word, e.g. to peek in a var
I like it!
Namespaces
Here’s something I found confusing.
I’ve got a project I started with Leinigen. I start up the REPL with lein repl
and type (doc frequencies)
. It prints out the documentation for frequencies
. Evaluating *ns*
tells me I’m in the noughts-and-crosses.core
namespace, but somehow Clojure still resolves doc
from the clojure.repl
namespace.
Now I open up src/noughts_and_crosses/model.clj
in vim. At the start of the file the namespace is defined with (ns noughts-and-crosses.model)
. I type (doc frequencies)
into the file then evaluate it (,ee
) and Clojure complains it can’t resolve doc
! Evaluating *ns*
tells me I’m in the noughts-and-crosses.model
namespace, so something is switching me into the file’s namespace, but isn’t loading doc
from clojure.repl
.
I couldn’t get to the bottom of why this happens. I suppose the Leinigen REPL loads a bunch of namespaces for convenience.