Hashrocket.com / blog

Large 11327967516 c4d4211aa8 b

Screencast: Clojure + vim basics

posted on and written by in

Image 100x100 josh davey

I've released a screencast detailing getting vim setup for Clojure development. This screencast covers how to do basic evaluation and get Clojure documentation from within vim.

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

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

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

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

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:

  1. It cannot be modified or saved
  2. Pressing Enter in normal mode sends the current line to the REPL for eval-ing.
  3. 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

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.

Posted in Vim and tagged with Vim, Clojure