I have been using Vim for a decade and a half. It has become ingrained in how
I do things. I use vi bindings in my browser (via Vimperator), my command
line, my e-mail client, my newsreader, etc. Whenever I am editing a document
in some strange environment, like Word, I inevitably end up inserting a load of
aitches, jays, kays, and ells—much to my infuriation.
Unsuprisingly, when I started working with Clojure, I immediately sought a
plug-in to provide the sorts of niceties we all expect in a development tool,
such as syntax highlighting, completion, etc. Of course, with Clojure, I also
wanted a REPL. However, getting a satisfactory REPL from Vim is easier said
than done.
A word about SLIME
SLIME, the Superior Lisp Interaction Mode for Emacs, is an Emacs mode
for working with Common Lisp. Even if you are not familiar with Emacs, it does
not take long to realise how nice and convenient it is. Unsurprisingly,
Clojure programmers have written the necessary plug-ins for a Clojure mode in
Emacs with SLIME support. Often, Emacs is considered the preferred choice for
working with Clojure.
Unfortunately for those of us who are hard-wired for vi, it is currently very
difficult to implement a “Superior Lisp Interaction Mode for Vim”. This is
because Vim lacks support for asynchronous subprocesses. Nevertheless, there
are a number of Clojure options for Vim users. Most prominent among these is
VimClojure.
VimClojure
VimClojure is a Vim plug-in developed by Meikel Brandmeyer. It provides
support for Clojure syntax highlighting (including rainbow parentheses),
indentation, and completion.
In addition, it supports a number of SLIME-type features, such as:
- Evaluate and see the results of the current:
- top-level s-expression
- file
- line
- visual block
- paragraph
- Tell Clojure to require the current namespace with either a
:reload or :reload-all
- See the macro expansion of the current innermost s-expression with either
macroexpand or macroexpand-1
- Interactively execute
(find-doc …)
- For the current word or an interactively given word:
- Lookup documentation using
(doc …)
- Lookup JavaDoc using
(clojure.contrib.repl-utils/javadoc …)
- See the source using
(clojure.contrib.repl-utils/source …)
- Open the source in Vim
- Show the word’s metadaka, like
(meta #'…)
- Start a REPL within Vim
This is a pretty nice set of features that can make developing Clojure within
Vim a much more delightful experience. For example, say one of your functions
is failing a test. From Vim, you can fix the function and type ,et to send
the entire (defn …) form to Clojure (assuming , is your LocalLeader).
Now you can re-run your tests and see whether or not your fix worked.
Unfortunately, VimClojure is not without its idiosyncrasies.
Caveat lector
No tool is perfect, and think VimClojure does a very good job. Nonetheless,
there a few things about which you should be aware:
- For most of the above functionality to work, The gorilla (nailgun) server
for VimClojure must include your Clojure source directories in its class
path.
- You can't mix VimClojure 2.1.2 with Clojure 1.2. The current mercurial
version of VimClojure is no longer tied to a particular Clojure version.
- The REPL stinks. Admittedly, because of the above-mentioned Vim
shortcoming, it's hard to get a REPL that is quite as nice as the one for
SLIME. However, things that I normally expect to have, such as the ability
to arrow keys to navigate the REPL history, are not supported.
Alternative Clojure plug-ins for Vim
VimClojure is not the only way to go with Clojure and Vim. Some of the
alternatives include using VimClojure in off-line mode, using VimClojure in
off-line mode with an alternative REPL, and Slimv, another Vim plug-in.
VimClojure (off-line)
It may be a stretch to call this a ‘VimClojure alternative’, but it is possible
to use Vim without any of the SLIME-like features. In this mode, you keep the
indentation, syntax highlighting, and completion. It may not be as powerful as
the full VimClojure plug-in, but it saves you the trouble having to start a
nailgun server and worry about class paths.
VimClojure (off-line) and slime.vim
This method is described by Lee Hinman in his article ‘How I develop Clojure
with Vim’. This method uses a supplemental Vim script called slime.vim
that works around Vim’s asynchronous shortcomings through the use of GNU
Screen.
In this arrangement, you can run your REPL in a separate Screen and have
Vim send commands to it using screen’s command line interface. I first
encountered this plug-in when I tried some Lisp development in Vim. I do not
use it because I think that VimClojure’s capabilities are much better.
Slimv
Slimv is a relatively unknown plug-in in the Clojure community. It is
written by Tamas Kovacs, and originated as plug-in for Lisp programmers.
However, in the past few versions, it has begun to support Clojure.
Slimv uses a Python script to launch a REPL, to which it sends commands in a
way, in principle, not terribly unlike slime.vim. Instead of running a REPL
using Screen, it launches the REPL in a new terminal.
Unlike slime.vim, it seems like it supports a much greater amount of
functionality, much like VimClojure. In fact, it supports having a REPL inside
of Vim that has the potential to be vastly superior to the one provided by
VimClojure. In particular, it allows using the arrow keys in insert mode to
navigate the REPL history.
Unfortunately, the current version is a bit buggy. For example, the REPL
works, but after every command I get series of Vim errors that, although
apparently harmless, make the experience intolerable.
The latest version also includes a paredit plug-in, that promises some of the
features of Emacs’ paredit minor mode. Unfortunately, I found it fairly
buggy. For example, in one case where I wanted to delete to the end of a line
using D, it deleted my entire line. In another case when I wanted to move a
one-line def using dd and p, I found that the p pasted nothing at all.
Yanking and putting still worked, though.
I think Slimv has some great features, but until some of these bugs are worked
out, it is not going to be my plug-in of choice. Unfortunately, there does not
seem to be a project page or repository available anywhere for Slimv.
How I develop Clojure with Vim
My ideal Vim plug-in would combine all of the best features of both VimClojure
and Slimv, eliminate all of the bugs, and make it simple to install and use.
Unfortunately, no such plug-in exists, nor will I be taking the time to write
it any time soon. So, I do the best I can with the tools that are available.
For a given Clojure project, my development environment consists of three things:
- A Clojure script that I use to launch nailgun, set up utility functions, and
run my REPL.
- A shell script to set up my class path and run my script.
- Vim with VimClojure
The development script
This development script does a few things that make development easier. I will
load clojure.contrib.repl-utils, start nailgun, and often times write a
function that runs all of my unit tests. Here is an example of such a script:
(ns #^{:doc "A sample development.environment Clojure script"
:author "Daniel Solano Gómez"}
example-repl
(:import com.martiansoftware.nailgun.NGServer
java.net.InetAddress)
(:use clojure.contrib.repl-utils
clojure.test))
(defn- dotests []
(require :reload-all :verbose 'example.test)
(run-tests 'example.test))
(def ng-host "127.0.0.1")
(def ng-port 2113)
(def ng-server (NGServer. (InetAddress/getByName ng-host) ng-port))
(.start (Thread. ng-server))
(println "Welcome to your development REPL")
(clojure.main/repl)
(.shutdown ng-server false)
Without going into a lot of detail, this script accomplishes the following:
- Set up a namespace, making the necessary imports to get nailgun to run and
including
repl-utils so can (source …) from the REPL.
- Set up a convenience function that will reload all my namespaces and execute
my tests. From the REPL, I only have to
(dotests).
- Instantiate and start a nailgun server.
- Start the REPL with a friendly welcome message.
- When the REPL ends, shut down the nailgun server gracefully.
You can do other things in this script, such as set up a server for Compojure
development or set values for vars like *warn-on-reflection*.
The shell script
The job of the shell script is to set up my class path and run the development
script. I also use rlwrap to give me readline support with vi key bindings
within the REPL.
A minimal example of such a script:
#!/bin/sh
CLOJURE_CP=/usr/share/clojure-1.1/lib/clojure.jar
CLOJURE_CONTRIB_CP=/usr/share/clojure-contrib-1.1/lib/clojure-contrib.jar
VIMCLOJURE_CP=/tmp/vimclojure-2.1.2/build/vimclojure.jar
PROJECT_CP=src
CLASSPATH="${CLOJURE_CP}:${CLOJURE_CONTRIB_CP}:${VIMCLOJURE_CP}:${PROJECT_CP}"
BREAK_CHARS="(){}[],^%$#@\"\";:''|\\"
rlwrap --remember -c -b "${BREAKCHARS}" -f "${HOME}/.clj_completions" java -cp ${CLASSPATH} clojure.main devrepl.clj
VimClojure
I usually develop within GNU Screen. In one screen, I launch my REPL, via
./devrepl, and in another I launch Vim. With this configuration, I have all
of the goodies from VimClojure along with a REPL with readline support and Vim
bindings.
On occassion when I have just launched my REPL, VimClojure will complain that
it can not connect to the nailgun server. If I do a netstat -lt, I will in
fact see that the REPL is not listening on the nailgun port. I have usually
found that running (.isRunning ng-server) within the REPL remedies this
situation. I suppose this is a bug in nailgun.
Concluding remarks
Is this a perfect set-up? No, not entirely. Am I missing out on some SLIMEy
goodness? Probably. However, it gives me most of what I need to be able to
develop Clojure productively.
If you have any good Vim and Clojure hints or tips, feel free to share them by
posting a comment.