In this screencast I cover how to do basic evaluation and get Clojure documentation from within vim. I’m also including the transcript below.
Vim is a powerful text editor. Clojure is a powerful programming language. While its been possible to edit Clojure code in vim for years, the toolchain has improved greatly over the past year. Today we’re going to see how we can integrate vim with our Clojure REPL environment.
Life without integration Link to heading
In a shell session, let’s fire up a Clojure REPL. I’m going to use lein repl
to do this. In another shell session, let’s start vim and edit a
clojure file.
As I edit my file, I can copy code from the editor, switch to the window with the REPL in it, and paste that code in. This works, but it’s an awkward, slow process. REPLs are supposed to be all about fast feedback. We can do better than copy and paste.
Plugins Link to heading
Before we get started, we should get the some basic plugins for clojure development. Using your preferred vim plugin manager, add these plugins:
guns/vim-clojure-static
tpope/fireplace.vim
Setup Link to heading
After you’ve installed the necessary Vim plugins, enter a project
directory. For example, if you have a leiningen project, cd into the
directory. In one shell session, fire up a REPL with lein repl
. In
another shell session, cd that that folder once again, and then open
vim.
Fireplace is able to detect when you are in the same directory as an active REPL, and will attempt to automatically connect for you. This process is transparent, but should be obvious once we attempt to to send a command to the connected REPL.
Evaluation Link to heading
The most basic fireplace command is :Eval
. :Eval
takes an arbitrary
clojure expression, sends it off to the REPL, and prints the result
for you. For example, we could run :Eval (+ 1 1)
, and we would, as
expected, see 2
printed out. This emulates typing at REPL prompt
directly, but there’s much more we can do with our REPL-connected vim
session.
Let’s stay with :Eval
for a bit longer. :Eval
without any arguments
will send eval and print the outermost form on the current line. For
example, let’s look at a simple expression.
(map inc [1 2 3])
When we have our cursor on this line and type :Eval
with no arguments,
we’ll see (2 3 4)
printed back.
:Eval
, as with many vim commands, can also take a range. So,
:1,3Eval
would evaluate all of lines 1 through 3. All of the normal
special ranges work here, such as %
for the entire file, and '<,'>
for the current selection in visual mode.
:Eval
works well, but there’s a quicker way to get feedback. cp
is
the normal mode mapping for doing a simple eval and print. By default,
cp
expects a motion. The form that I use most though is cpp
, which
will eval and print the innermost form from the cursor’s current
position.
To demonstrate what this means, let’s look at that expression again.
(map inc [1 2 3])
When our cursor is on the m
of map
, and we type cpp
, we’ll see
(2 3 4)
, just as when we did the plain :Eval
. But if we move our
cursor inside the vector and type cpp
again, we’ll see that inner form
evaluated.
Something unique to fireplace is its concept of a quasi-REPL. This is a
cousin of the cp
mappings, but with an intermediate editing window. To
demonstrate this, let’s consider the following example.
(->> [1 2 3]
(map str)
reverse
(mapv dec))
In this trivial example, we want to reverse a sequence and decrement
each number. There’s a bug in here, but it’s in the middle of the
thread-through macro. We could just edit the line directly and
eval/print using cpp
, but there’s another way to do one-off iterative
development like this.
Type cqc
in normal mode. A commandline window will open. This is very
much like a normal vim buffer, with a few notable exceptions:
- It cannot be modified or saved
- Pressing
Enter
in normal mode sends the current line to the REPL for eval-ing. - As you run commands, they are added to this buffer.
tpope calls this the “quasi-repl”, and indeed that is the mnemonic for
the mapping itself: cq
is the “Clojure Quasi-REPL”.
While we’re in this special window, let’s type the following, and hit enter:
(map str [1 2 3])
Immediately, we can see the issue. Converting each number to a string
prevents dec
from working later on.
Having to type the whole line again isn’t always convenient. For those
cases, there’s cqq
, which is like cqc
except that it pre-populates
the command window with the innermost form under the cursor. We can
see this in action by putting our cursor near the beginning of the
thread-through macro, and typing cqq
.
You can think of cqq
as being very similar to cpp
, but with a chance
to edit the line or lines before sending it off to the REPL.
Documentation Link to heading
One of the great things about Clojure is that documentation is a
first-class citizen, and builtin functions have documentation attached
to them. With a standard REPL, we can use the doc
function to get the
signature and documentation for a given function.
With fireplace, we get this with the :Doc
command, and it works just
like doc
. To see the documentation for map
, for example, type :Doc map
. We immediately see the documentation for the map command printed.
There’s an even shorter way to look up documentation for a function.
When your cursor is on a word, you can press K
, that is Shift
and
K
. We can try this again with the map
function by placing our cursor
on the function itself, and pressing K
.
We can also use the :Source
command to show the source for a function.
When we do this with map
, we see the source code for map
from
clojure.core
.