(N)vim for Clojure development
Aug 24, 2020Everything you need to write Clojure using (N)vim.
I used different editors and IDEs over the years: Netbeans, Vim, Emacs, and IntelliJ. And on all of them, I always enabled Vim keybindings.
Few months ago, after more than 10 years writing Ruby, I started my first commercial role as a Clojure Software Engineer at Commsor! I’m lazy, so I picked Cursive IDE (which is great BTW!), with IdeaVim plugin enabled. Everything worked out of the box, but after while I, obviously, started to envy my colleagues their custom workflows and setups…
Now that I’m sure I am Vim person to the bone - why not to give it a go? I decided to configure a pretty minimal, as close to the Vim philosophy, setup as possible. Here it is!
Choosing Vim distribution
I’m on MacOS, so I could either use MacVim distribution, console nvim, or some GUI client for it. I decided to try out VimR and I’m not disappointed:
$ brew install vimr
Essential plugins
I decided to manage my plugin dependencies with vim-plug. It keeps the configuration file small and readable, and just gets the job done:
" ~/.config/nvim/init.vim
call plug#begin()
" Plugins to install
Plug 'first/plugin-vim'
Plug 'second/plugin-vim'
call plug#end()
" Rest to the configuation file
" ...
To install them we can reloaded the current file (~/.config/nvim/init.vim
) with :so %
and run :PlugInstall
.
I started by including:
Plug 'tpope/vim-sensible'
Plug 'vim-airline/vim-airline'
which provide universal set of defaults and a lean status line.
For file and project management I picked ctrlp.vim:
Plug 'ctrlpvim/ctrlp.vim'
The only custom configuration I added was setting up additional project root marker and a different user command to list files based on Git index:
let g:ctrlp_root_markers = ['deps.edn']
let g:ctrlp_user_command = ['.git', 'cd %s && git ls-files -co --exclude-standard']
Also, I chose Apprentice as my color scheme:
Plug 'romainl/Apprentice'
colorscheme apprentice
Mandatory screenshot:
The must-have plugin for everyone is:
Plug 'axelf4/vim-strip-trailing-whitespace'
which removes trailing whitespace on modified lines before saving.
Last but not least there is support for searching across the files in the project.
ack.vim supported
by ripgrep works perfectly!
Importantly, it takes .gitignore
settings into account.
$ brew install ripgrep
and
Plug 'mileszs/ack.vim'
Custom configuration sets the ripgrep as a backend, closes the search results popup after choosing the result, and stops the plugin from jumping to the first result automatically.
let g:ackprg = 'rg --vimgrep'
let g:ack_autoclose = 1
cnoreabbrev Ack Ack!
All of that alone would be a potent general-purpose Vim setup.
Clojure support
To efficiently write Clojure code I needed syntax highlighting, structural editing support, REPL management, and context-aware autocomplete.
These three plugins:
Plug 'guns/vim-clojure-highlight'
Plug 'guns/vim-clojure-static'
Plug 'luochen1990/rainbow'
give me syntax highlighting, indentation, and rainbow parentheses to better distinguish forms visually.
Next two plugins:
Plug 'guns/vim-sexp'
Plug 'tpope/vim-sexp-mappings-for-regular-people'
provide structural editing support with vim-like mappings.
These five:
Plug 'clojure-vim/vim-jack-in'
Plug 'radenling/vim-dispatch-neovim'
Plug 'SevereOverfl0w/vim-replant', { 'do': ':UpdateRemotePlugins' }
Plug 'tpope/vim-dispatch'
Plug 'tpope/vim-fireplace'
allow me to start and / or connect to existing nREPL session, with deps.edn
support
by just invoking:
:Clj -A:alias_1:alias_2
vim-replant
adds handy keybindings for working with Clojure REPLs. Just invoke
<LocalLeader>rf
to refresh namespaces by automatically calling your common (stop)
/(start)
functions! Magic!
And you know what? All of this doesn’t need a single line of custom configuration. I love the sane defaults and conventions!
Context-aware autocomplete needs more work, but it’s worth the effort.
Plug 'clojure-vim/async-clj-omni'
Plug 'prabirshrestha/asyncomplete.vim'
These two plugins provide autocomplete which understands your code by using the existing REPL
connection! asyncomplete
doesn’t come with any sources enabled and it needs to be registered:
au User asyncomplete_setup call asyncomplete#register_source({
\ 'name': 'async_clj_omni',
\ 'whitelist': ['clojure'],
\ 'completor': function('async_clj_omni#sources#complete'),
\ })
And that’s all! It’s ready to help you keep all the parentheses balanced :)
The complete configuration file, with few additional plugins, can be found on my tomekw/dontfiles repo. I’m sure the current setup will grow in the future, but you can track the progress by following me on Twitter!