Computers Log: Clojure

2022-12-03
Reading Time: ~6 minute(s)

I’ve recently become quite interested in the Lisp family of languages and have settled on learning Clojure. Below is a log of some of the setup required to get a NeoVim editor setup with auto-complete and some handy in-editor REPL evaluation. Most of the well-ranked search engine results I found to do this were 5-10 years old so I’ll document this here for myself and posterity.

Options Options Options

I explored a bunch of options here but largely found old articles and dead ends. slimv didn’t work for me as I’d hoped because clojure-swank is deprecated and throws horrible strange traces on my machine.

fireplace.vim seemed to be the best option I found next but it didn’t talk SWANK (some REPL server protocol for editors aka emacs). It speaks nREPL – well that was familiar, lein repl starts that!

I pulled on this nREPL thread a bit and (a) it’s incredibly powerful and terrifying. I may or may not plan to leave an nREPL running in prod code one day for live debugging or hotfixes, but it’s cool I could! (b) Also I rediscovered Conjure (which I’d previously run across and forgotten) and it looks perfect for a newb like me.

Base

It’s another (much longer) log to explain why I’m using Alpine, just go with it. Be sure you have at least Clojure and Leiningen installed before following along below.

apk add clojure leiningen

REPL Setup

How not to start your REPL: clojure instead try lein repl which will get you nREPL which is a network socket REPL and REPL-y which is a command line interface to nREPL.

Back to editing, my cursory understanding is that nREPL largely provides a evaluation but code definitions and auto-completion data are coming from cider-nrepl.

This is middleware that interprets incoming data from the editor to nREPL and either passes it through to nREPL or provides its own response.

I setup cider-nrepl like so – I’m sure there are better ways to do this but I’m just following ‘Quick Start’ guides and trying to make things work. I am a beginner in Clojure who happens to be documenting, not an expert, if you want to tell me how to do better please reach out :)

To make this work on a per user basis – versus per project we can create a profile for lein and provide arbitrary overrides to the project.clj config for all the projects we work in.

I put the following in ~/.lein/profiles.clj:

{:user {:plugins [[cider/cider-nrepl "0.28.7"]]}}

(update the version number to taste, by running lein search cider-nrepl)

For the Pythonistas out there – think of this like an override to every requirements-dev.txt file in all your projects. Or keeping certain tools installed by the package manager outside your venv (like tox).

Finally to ensure lein got it quickly – it’s supposed to do this automatically I’m just paranoid – you can run: lein deps

And to double check you can see the jar files downloaded in the typical Maven location here: ~/.m2/repository/cider/cider-nrepl

Starting nREPL for NeoVim

There are plugins to kick off a nREPL when Vim starts but I haven’t used one yet, I’m doing this so far (but will look at vim-jack-in soon):

lein repl :headless

Which will output a line like this:

nREPL server started on port 34351 on host 127.0.0.1 -
nrepl://127.0.0.1:34351

Take the port and write it into ~/.nrepl-port like so: echo 34351 > ~/.nrepl-port

This is so Conjure can find the nREPL – otherwise you can tell it inside of Vim like so :ConjureConnect 34351.

Conjure

I actually switched to NeoVim to setup Clojure, I’d been meaning to try it anyway and some of the flashy bits of Conjure looked appealing enough to pull the trigger.

apk add neovim neovim-doc

I feel like Vim plugin managers always made a mess of my config backups so I’m not using one. The tldr; is you don’t need one so I pulled up the article where I learned about that[1] and followed along:

TLDR;

  1. Given your Vim directory (~/.config/nvim), create a pack directory
  2. Then create and name a directory whatever you want under pack to organize your plugins
  3. Create start and opt directories inside of that. Plugins in start will be loaded at start-up but you must ask for plugins to be loaded from opt (via :packadd)
  4. Start-up Vim (nvim) and run :helptags ALL to integrate the package docs into :help (for :help conjure later, which you’ll want)

There – less code, fewer dependencies, fewer problems.

mkdir -p ~/.config/nvim/pack/clojure/start
cd ~/.config/nvim/pack/clojure/start
git clone https://github.com/Olical/conjure.git

I’m also not much one for custom config in Vim anymore, so my config is a single line (and you may already have it set):

~/.config/nvim/init.vim (note the difference of location Vim users!)

let maplocalleader = ","

Conjure claimed to make , that the default leader when I opened nvim and ran :ConjureSchool but alas it didn’t work until I put that line in place.

You should now be able to open up a new file (anywhere) with nREPL running (anywhere) but with the port set (~/.nrepl-port or set with :ConjureConnect) and do this:

nvim ~/tmp.clj

(* 4 8)

And while on the same line in command-mode type ,ee and the expression will be evaluated and => 32 will be appended to the line temporarily in a light blue (your colors may differ).

If you’re having trouble with the leader key in Vim (,) then try running the full command :ConjureEval and you should likewise get the pink debug box and light blue.

Auto-complete

I went down lots of rabbit holes to get autocomplete working. It turned out to be easy. Per the Conjure docs (:help conjure but probably on their site too) which lists deoplete up top.

Python3 support is a hard requirement for deoplete but not just local but also Python3 RPC support via msgpack. So I had to install the Python client to NeoVim:

apk add py3-pynvim

Then I could install the plugin:

cd ~/.config/nvim/pack/clojure/start/
git clone https://github.com/Shougo/deoplete.nvim.git

(if you’re reading the deoplete instructions and installing nvim-yarp and vim-hug-neovim-rpc stop, those are only needed for Vim 8 not NeoVim)

And one more line in the config file to enable it:

~/.config/nvim/init.vim

let g:deoplete#enable_at_startup = 1

I found that I had to ’re-seat’ the plugin once installed before it started working by running the following from inside of nvim:

:UpdateRemotePlugins

Probably something about the msgpack API built-in to NeoVim and registering deoplete initially but I didn’t look into it much.

References



./danielwilcox.uk

Opinions expressed on this site are my own.