Conjure is my attempt at Clojure and ClojureScript tooling for Neovim without relying on any existing nREPL based dependencies. Instead it relies on the socket prepl which is built into all newer versions of the language.

Here’s a quick demo of Conjure in action for those of you that haven’t seen it before.

I’m going to help you get Clojure code evaluating in a matter of minutes, you’ll be able to try Conjure for yourself and see what it’s all about. All you’re going to need are the following.

  • The Clojure CLI - I installed it through the Arch Linux package manager, you can probably do something similar.

  • Neovim

  • A hot beverage to enjoy as you follow along.

Grab plugin manager

I’m assuming you have nothing set up for Neovim right now, let’s get started with a plugin manager. Skip ahead if you already have a preferred way to install plugins!

I’ve used vim-plug for years and I highly recommend it. Setup is really easy, let’s start out by fetching the script.

curl -fLo ~/.local/share/nvim/site/autoload/plug.vim --create-dirs \
    https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim

Now we need to edit ~/.config/nvim/init.vim to initialise it. I’m going to get you started with vim-better-default, as you’d expect, it’s a set of great default configuration values.

" Specify a directory for plugins.
call plug#begin(stdpath('data') . '/plugged')

" Specify your required plugins here.
Plug 'liuchengxu/vim-better-default'

" Initialize plugin system.
call plug#end()

Now we can open Neovim and execute :PlugInstall. Have a look around the vim-plug documentation to find more commands you can run for things like updating or pruning of unused plugins.

Whenever you make a change to your plugin configuration in init.vim you’ll need to reload the file by restarting Neovim. I think you might be able to :source % too but restarting Neovim is really quick. Once you’ve done that you can install or update whatever was specified.

Optional (amazing) plugins

Although you can get away with just Conjure, I’d highly recommend you also add at least some of these too. They’ll greatly improve your experience!

EasyMotion

The way to move around your buffer with ease, extremely useful when you’re jumping from form to form performing evaluations. The only downside is that editing without it in any other editor now feels far too cumbersome.

Plug 'easymotion/vim-easymotion'

vim-sexp and vim-sexp-mappings-for-regular-people

These will take you a while to get the hang of but they’re so worth it. They allow you to structurally edit any lisp language, this means you can turn (+ 10 20) 30 into (+ 10 20 30) with a key press all while keeping your cursor in the same place.

The learning curve is steep but optional, you can always edit the text manually. The more you learn the faster and more fluid you’ll get, it’s well worth investing some time into.

Plug 'guns/vim-sexp'
Plug 'tpope/vim-sexp-mappings-for-regular-people'

Deoplete and float-preview

Make sure you have Python 3.6.1 or greater installed before attempting to use Deoplete!

Conjure has completion built in via Neovim’s native Omnicompletion but I’d recommend you add an asynchronous autocompleter for maximum convinience.

Plug 'Shougo/deoplete.nvim'
Plug 'ncm2/float-preview.nvim'

" Place configuration AFTER `call plug#end()`!
let g:deoplete#enable_at_startup = 1
call deoplete#custom#option('keyword_patterns', {'clojure': '[\w!$%&*+/:<=>[email protected]\^_~\-\.#]*'})
set completeopt-=preview

let g:float_preview#docked = 0
let g:float_preview#max_width = 80
let g:float_preview#max_height = 40

Conjure comes bundled with the Python code required to hook itself into Deoplete automatically, combine that with float-preview and suddenly you have automatic completions popping up with their documentation alongside it as you type.

If Deoplete isn’t your thing you might want to try Conquer of Completion (Coc for short!). coc-conjure will provide Conjure support, I think Coc works with language servers out of the box too, it’s definitely worth considering.

I’ve just always been a Deoplete fan and don’t find myself using language servers. The plugins being JavaScript dependencies always felt a little odd to me too although that’s a minor silly point.

auto-pairs

Simply a really handy way to keep your pairs of characters in check, not quite essential but nice to have.

Plug 'jiangmiao/auto-pairs', { 'tag': 'v2.0.0' }

ALE

The Asynchronous Lint Engine, when combined with clj-kondo and joker, is an indispensable tool. It’s caught countless typos and brain farts for me before I’ve had a chance to evaluate the code.

Plug 'w0rp/ale'

" Place configuration AFTER `call plug#end()`!
let g:ale_linters = {
      \ 'clojure': ['clj-kondo', 'joker']
      \}

vim-clap

If you want to get into Neovim properly you’re probably going to want a way to find things in your project, that’s where vim-clap comes in.

It allows you to hook into various searching tools such ripgrep in a pretty floating window. You’ll have to spend a little while setting up your mappings to each command but it’s well worth the investment. You can find my configuration in my dotfiles.

Plug 'liuchengxu/vim-clap'

" Configuration from my dotfiles.
let g:clap_provider_grep_delay = 50
let g:clap_provider_grep_opts = '-H --no-heading --vimgrep --smart-case --hidden -g "!.git/"'

nnoremap <leader>* :Clap grep ++query=<cword><cr>
nnoremap <leader>fg :Clap grep<cr>
nnoremap <leader>ff :Clap files --hidden<cr>
nnoremap <leader>fb :Clap buffers<cr>
nnoremap <leader>fw :Clap windows<cr>
nnoremap <leader>fr :Clap history<cr>
nnoremap <leader>fh :Clap command_history<cr>
nnoremap <leader>fj :Clap jumps<cr>
nnoremap <leader>fl :Clap blines<cr>
nnoremap <leader>fL :Clap lines<cr>
nnoremap <leader>ft :Clap filetypes<cr>
nnoremap <leader>fm :Clap marks<cr>

Adding Conjure

Installing the plugin is the same as any other apart from one minor detail: We have to specify a "compile" script that gets executed upon update to ensure Conjure starts quickly. Luckily, this is easy to do with vim-plug!

I’m specifying the latest tagged version at the time of writing this post, have a look at the Conjure repository to find the latest version and decide if you would like to use that instead.

I’d highly recommend you subscribe to new releases through GitHub’s UI, that way you can be notified automatically and update to newer versions when it suits you.

Plug 'Olical/conjure', { 'tag': 'v2.0.0', 'do': 'bin/compile' }

We’ve essentially asked vim-plug to install v2.0.0 and then execute bin/compile whenever changes occur (it won’t re-compile if the code hasn’t changed, don’t worry).

If you haven’t already, be sure to execute :PlugInstall within Neovim to ensure all of your plugins are installed and set up correctly.

Using Conjure to evaluate Clojure

Conjure is actually written in Clojure which means it starts a small Clojure instance when you open a Clojure file. The cool thing about this is that Conjure will automatically connect to this JVM when you haven’t configured any projects to connect to, this means we can already open any Clojure file and start evaluating things!

Try it for yourself, open up a new Clojure file with nvim foo.clj, write some Clojure (such as (+ 10 20)) and evaluate it with <localleader>ee. By default your <localleader> will be set to the \ key, I set mine to the , key but this is entirely up to you.

If everything’s set up correctly you’ll hopefully see something like this.

Bear in mind I’ve added all of the optional plugins I mentioned previously to get things like autocompletion working.

My full ~/.config/nvim/init.vim looks like this.

" Specify a directory for plugins.
call plug#begin(stdpath('data') . '/plugged')

" Specify your required plugins here.
Plug 'liuchengxu/vim-better-default'

" Optional useful plugins I highly recommend.
Plug 'easymotion/vim-easymotion'
Plug 'guns/vim-sexp'
Plug 'tpope/vim-sexp-mappings-for-regular-people'
Plug 'Shougo/deoplete.nvim'
Plug 'ncm2/float-preview.nvim'
Plug 'jiangmiao/auto-pairs', { 'tag': 'v2.0.0' }
Plug 'w0rp/ale'

" I skipped vim-clap but feel free to add it!

" Conjure! :D
Plug 'Olical/conjure', { 'tag': 'v2.0.0', 'do': 'bin/compile' }

" Initialize plugin system.
call plug#end()

" Configuration for various plugins.
let g:deoplete#enable_at_startup = 1
call deoplete#custom#option('keyword_patterns', {'clojure': '[\w!$%&*+/:<=>[email protected]\^_~\-\.#]*'})
set completeopt-=preview

let g:float_preview#docked = 0
let g:float_preview#max_width = 80
let g:float_preview#max_height = 40

let g:ale_linters = {
      \ 'clojure': ['clj-kondo', 'joker']
      \}

There’s not much to it but we’ve already got a fully Clojure integrated editor! Head over to the Conjure repo and wiki to find out more about configuration, mappings and features.

Connecting to a real project

This "self prepl" is very handy for trying things out quickly in any directory as well as following along with books and tutorials but at some point you’re probably going to want your own deps.edn (or project.clj) file in your own project.

There’s already a lot of resources around the internet to help you set up your projects. Clojure projects from scratch will help you structure your repository and deps.edn file, for example. Some of the versions may be a little out of date but the content should still be completely valid.

Once you have a project you’ll want to start your own socket prepl instead of relying on Conjure’s internal one. You can do that through some simple JVM args, a small amount of Clojure or with my own tool, Propel. You can read about starting prepls without Propel in the Clojure socket prepl cookbook or with Propel in REPLing into projects with prepl and Propel.

Once your socket prepl is up and running you can configure Conjure to connect to it automatically by writing your own .conjure.edn file.

;; This will connect to a prepl on port 5656 when you open a Clojure file.
;; It'll also connect to a ClojureScript prepl on 8899 when you open a ClojureScript file!
{:conns {:dev {:port 5656}
         :ui {:port 8899, :lang :cljs}}}

You can also configure Conjure to connect to the port specified in .prepl-port (when it exists) automatically. Propel will write this file for you when you provide the -w argument. I specify this in my global ~/.config/conjure/conjure.edn file so Conjure will connect to any project that spits out a .prepl-port file automatically.

{:conns {:local {:port #slurp-edn ".prepl-port"}}}

I hope this post as well as the others I’ve linked to are enough to get you started and hooked on Conjure. Please do get in touch with any questions, thoughts or feelings on the project. You can find me on twitter (link in the footer) as well as #conjure in the Clojurians Slack.