How I Tamed VimClojure
NOTE: I really love the setup described below. But I admit it’s a lot to digest. If you just want vimclojure working now, you may be interested in vimclojure-easy
UPDATED October 7th, 2011 for VimClojure 2.3.0
TL;DR VimClojure Download List
Like Windows users at a Ruby conference, vim-using Clojurians can feel a little left out. Everyone uses Emacs. Well, I played a ton of Rogue when I was 11, so I’ve always had a soft spot for vi/vim’s way of doing things, at least the movement keys anyway.
Up until now I’ve been doing Clojure development in a cobbled together mix of screen.vim and VimClojure. I could start a simple repl with screen and rely on VimClojure for basic syntax highlighting, indenting, etc. I’ve been aware that VimClojure had much more to offer, but the setup docs I found were varied, inconsistent and generally confusing. Other blogposts on the subject seemed to either surrender to a half solution like I already had, or were so inconvenient that I wouldn’t use it. So, I finally sucked it up, slogged through the setup, and got everything working to my liking.
Here, I’ll try to clearly document my full setup in hopes that someone else might find it useful. My main goal was a setup where I could make the most of VimClojure directly from vim without a bunch of preparation. If I want to just play around, I don’t want to fiddle around with Maven or Leiningen or anything. I want to start vim, get a repl, and start exploring!
Everything here is in my vimfiles repo on github.com
Note that throughout this document, if you’re on Windows, replace references to the
~/.vim directory with
If you’re not interested in using pathogen, just adjust paths accordingly throughout the rest of this post
To get going, you’ll need to download a few things. First, for vim plugin sanity, I use pathogen.vim. Download it and add
~/.vim/autoload and add the following to the top of your
" Load plugins from .vim/bundles using .vim/autoload/pathogen.vim call pathogen#runtime_append_all_bundles() filetype off " On some Linux systems, this is necessary to make sure pathogen " picks up ftdetect directories in plugins! :( syntax on filetype plugin indent on
Now add a
~/.vim/bundle directory. This is where you’ll expand all your plugins, each in its own directory.
For this setup, I’m still using screen.vim as a simple way of starting up VimClojure’s Nailgun server directly from vim. It makes sure the server is killed when vim exits and is generally useful anyway. Download and install it. Since I’m not a fan of vimball (vba) installers, I just grabbed the source directly from github (hit the “Downloads” button) and dropped it in my
That’s it. Now you can use
:ScreenShell args ... to start a simple screen session.
Note to Windows users: Obviously, Windows doesn’t have GNU screen. The least painful approach seems to be to install Cygwin. I don’t care that much for Cygwin, mostly because it has no uninstaller (wtf !?!?), but a super-minimal Cygwin install seems to be working ok for me. Just be sure to add
c:\cygwin\bin (or whatever) to your system path.
And of course you need VimClojure itself. Everything here assumes VimClojure 2.3.0. Hopefully, it doesn’t change too much. Download VimClojure and extract it to your
~/.vim/bundle folder. For full interactive support, you’ll need two other things:
- The Nailgun client (note this is 2.2.0, but still works fine with 2.3.0) – A tiny executable that allows vim to communicate with a running JVM. (as a side-note, Nailgun is an occasionally useful way to avoid the horrendous startup times of the JVM)
- The VimClojure Nailgun server jar (you can browse here for other versions) – The other half of the Nailgun protocol where the running instance of Clojure will live in a JVM. Note that as documented in the
README.markdownfile in the VimClojure archive, you can also specify this as a dependency in Leiningen, Maven and friends. This isn’t that useful for ad hoc Clojure exploration though.
Assuming you’ve installed VimClojure to
~/.vim/bundle/vimclojure-2.3.0, here’s what I did with these. I created a folder,
~/.vim/bundle/vimclojure-2.3.0/lib and added the following to it:
bundle/vimclojure-2.3.0/lib |-- nailgun | |-- Makefile | |-- ng | |-- ng.exe | |-- ngclient | | `-- ng.c | `-- readme.txt `-- server-2.3.0.jar
Note that the
ng executable on Unix must be built with
Now the only files we still need are the clojure core and contrib jars. I drop these in
~/.vim/lib. Which version you put there is up to you. We’ll set things up in
.vimrc below so that this is the last resort for finding Clojure jars. If a different version is in your project, that should get used instead. If you just start up vim to experiment outside a full project, this is the version you’ll get.
Ok, we have everything we need in our
.vim folder. Now we just need to wire it all up in
.vimrc. First, let’s do some stuff to make things cross-platform:
" Let's remember some things, like where the .vim folder is. if has("win32") || has("win64") let windows=1 let vimfiles=$HOME . "/vimfiles" let sep=";" else let windows=0 let vimfiles=$HOME . "/.vim" let sep=":" endif
Now we know where the
.vim folder is and the correct Java classpath separator (stupid!) to use. So we can build a generic classpath that will cover most project configurations. I tend to always open vim at the root of a project and never change working directory so your mileage may vary with this:
let classpath = join( \[".", \ "src", "src/main/clojure", "src/main/resources", \ "test", "src/test/clojure", "src/test/resources", \ "classes", "target/classes", \ "lib/*", "lib/dev/*", \ "bin", \ vimfiles."/lib/*" \], \ sep)
Basically we’ve just built a big, catchall classpath. Note that the jars in
.vim are at the very end so they’ll be a fallback.
Finally, we’ll pull it all together by configuring VimClojure:
" Settings for VimClojure let vimclojureRoot = vimfiles."/bundle/vimclojure-2.3.0" let vimclojure#HighlightBuiltins=1 let vimclojure#HighlightContrib=1 let vimclojure#DynamicHighlighting=1 let vimclojure#ParenRainbow=1 let vimclojure#WantNailgun = 1 let vimclojure#NailgunClient = vimclojureRoot."/lib/nailgun/ng" if windows " In stupid windows, no forward slashes, and tack on .exe let vimclojure#NailgunClient = substitute(vimclojure#NailgunClient, "/", "\\", "g") . ".exe" endif " Start vimclojure nailgun server (uses screen.vim to manage lifetime) nmap <silent> <Leader>sc :execute "ScreenShell java -cp \"" . classpath . sep . vimclojureRoot . "/lib/*" . "\" vimclojure.nailgun.NGServer 127.0.0.1" <cr> " Start a generic Clojure repl (uses screen.vim) nmap <silent> <Leader>sC :execute "ScreenShell java -cp \"" . classpath . "\" clojure.main" <cr>
Basically we tell VimClojure where our Nailgun client is (and handle Windows silliness) and set up some helpers for starting a Clojure repl (<Leader>sC) or VimClojure Nailgun server (<Leader>sc) with screen.vim. If you don’t feel like using the screen shortcut, you can of course start the server manually as described in the VimClojure docs:
$ java -cp "classpath including clojure jars and server-2.3.0.jar" vimclojure.nailgun.NGServer 127.0.0.1
Kick the Tires
With all that in place, we’re ready to give it a try:
- Start vim
<Leader>sc(or whatever you mapped the server to) to start up the Nailgun server. Note that it must be running before you open a Clojure buffer!
- Open a new Clojure file:
- At this point, there might be a slight, or not so slight, delay (or error message!) as the Nailgun server loads Clojure for the first time. Remember that JVM startup time I mentioned?
<LocalLeader>srto start a repl inside vim (see
:help maplocalleaderif you don’t know what
All the other commands and interactive features of VimClojure are described in the docs (
~/.vim/bundle/vimclojure-2.3.0/doc/clojure.txt). Here’s my current shortlist:
<LocalLeader>sw– Show source for the word under the cursor, including clojure.core! <– this is key if you care about understanding a platform!
<LocalLeader>sr– Start a repl
<LocalLeader>sR– Start a repl in the current namespace
<LocalLeader>eb– Evaluate current visual block. There are several “eX” variations.
<LocalLeader>lw– Lookup docs for word under cursor. There are several “lX” lookup variations
Now we have a Clojure development environment that’s not so embarrassingly inadequate compared to Emacs. This is working well for me so far, but I’m sure there’s room for improvment. Let me know if you see anything really dumb I’m doing. Thanks!