dave yarwood
Conjuring Clojure in Vim

date:

November 22, 2019

Clojure tooling for Vim has been getting more and more interesting over the past few years, especially in the last year or so.

When I first came to Clojure, Fireplace was the standard Vim plugin for Clojure development, providing Clojure developers with a great in-editor REPL experience. I think it’s still the case that the majority of Clojurian Vimmers use Fireplace, but in recent years, a number of viable alternatives have begun to appear, including the plugins Acid, Iced, and Conjure.

I talked about these plugins a little bit several months ago, and I mentioned that Fireplace is what I would recommend for beginners. After six months of using Conjure heavily and watching it improve and grow more stable, I think I can now recommend Conjure as a way for Clojure beginners to get their development environment up and running quickly and start writing Clojure code.

What is this sorcery?

The author of Conjure, Oliver Caldwell, has written a bunch of blog posts that help to make Conjure, as well as Clojure’s prepl, approachable for newcomers:

Since I last wrote about Conjure, Oliver has also made a number of improvements that make the out-of-the-box experience with the plugin quite nice. At this point, if you’re a Neovim user and you know how to use a plugin manager like vim-plug, all you have to do is:

  1. Install the Conjure plugin for Neovim
  2. Open a Clojure source file (i.e. vim /tmp/foo.clj)
  3. Wait a few seconds for Conjure to connect to its own prepl server.

Then you can start writing forms like (+ 1 2) and evaluating them by pressing <localleader>ee, and you’ll see the results appear right next to the form you evaluated.

BYOP (bring your own prepl)

This is great for one-off REPL experiments involving just the Clojure standard library, but the built-in REPL that Conjure connects to out of the box doesn’t include your project’s source code or the libraries it depends on. To complete your Clojure development setup, you’ll need to be able to start a prepl server within the context of your project.

For an in-depth discussion of various ways to do this, you can refer to Oliver’s socket prepl cookbook.

The majority of Clojure projects that I interact with use either Boot or the Clojure CLI as a build tool.

For Boot projects, I have a custom prepl-server task that I defined in my profile.boot:

(deftask prepl-server
  "Start a prepl server."
  [p port PORT int "The port on which to start the prepl server (optional)."]
  (comp
    (socket-server
      :accept 'clojure.core.server/io-prepl
      :port   port)
    (wait)))

Worth noting: I contributed this task to Boot, and it got merged into the master branch, so in some future release of Boot, prepl-server will be available out of the box as a built-in task!

My Clojure CLI setup is a little bit more complicated, but the result is that I can start a prepl server in any Clojure project directory with a deps.edn by running clj -Aprepl-server.

Both my Boot task and my Clojure CLI alias spit out a .socket-port file in the current directory containing the port number on which the prepl server is running. Configuring Conjure to automatically connect to the prepl server on the correct port is super easy:

;; ~/.config/conjure/conjure.edn
{:conns
 {:cwd {:port #slurp-edn ".socket-port"}}}

With this setup in place, I can start a prepl server via boot or clj and start editing Clojure source files, and Conjure will automatically connect to my prepl server. Then I can evaluate code within the context of my project, and I can use any dependencies that I’ve included in my build.boot or deps.edn.

A refreshing experience

Conjure provides a convenient way to reload code that changed in your prepl via clojure.tools.namespace/refresh. Ordinarily, you would need to include clojure.tools.namespace as a dependency in your project in order to do that, but through clever use of mranderson, Conjure automatically injects the dependency into your prepl connection. That means refreshing your REPL is always just a few keystrokes away!

I haven’t tended to use clojure.tools.namespace refreshing much in the past, but since the feature was added to Conjure, I’ve found myself using it more and more because it’s right there under my fingertips and it requires no setup. It’s really handy for those times when I’ve changed a bunch of code and I don’t remember exactly what I changed; I can simply reload everything by pressing <localleader>rr.

You can even configure Conjure at the project level to run hooks before and after refresh, which can be handy when you’re developing something that you might want to restart every time you make changes, like a web server.

Casting spells

One day, in the #conjure channel on Clojurians slack, an off-the-cuff discussion about re-evaluating the same form over and over again during development led to an intriguing new Conjure feature called “eval at mark.”

This feature is also affectionately known as the “spellbook” feature because it lets you evaluate any number of predefined Clojure forms, on demand, just by pressing a few keys. I’ve been using this feature a lot since it was introduced, and I love it!

In a typical workflow, I might set the mark F at a function call in a scratch namespace, and then I can call that function from anywhere, e.g. while I’m editing code in another namespace, by pressing <localleader>emF.

This sort of workflow helps a lot in common scenarios where I’m testing the behavior of a function that I’m writing, and that function itself calls a number of other functions that are defined in other namespaces. Sometimes, in the heat of development, I end up jumping around through a bunch of different files as I’m chasing a bug or implementing a complex feature.

Previously, I had to jump back into my scratch namespace everytime I wanted to re-evaluate a form that calls the function I’m testing. Now, I can stay where I am in the implementation code and just press:

  • <localleader>rr to reload all of the code that changed, then

  • <localleader>emF to re-eval the form at mark F

Try it!

If you’re a Vim-using Clojurist or a Clojure-using Vimmer, hopefully I’ve inspired you to give Conjure a try. Go ahead, it’s fun!

Comments?

Reply to this tweet with any comments, questions, etc.!