π£ I'm happy to introduce neat - a tiny language-agnostic nREPL client for Emacs https://batsov.com/articles/2026/05/20/neat-a-language-agnostic-nrepl-client-for-emacs/ (it doesn't target Clojure specifically, but it works just fine with Clojure and related languages)
Heavily inspired by my recent work on Port and listening way too many times to "Too Sweet" by Hozier! π I don't expect many people to need or use it, but you might very well surprise me. π Feedback and suggestions are always welcome!
I've mentioned neat yesterday on Apropos (https://www.youtube.com/watch?v=sZpV5IIKHvs) and I thought it was probably a good time to announce it officially now that cat's out of the bag. cider π₯
I've been toying around with let-go recently. Got CIDER working with it but with some hiccups (pun not intended) β it sounds like a neat usecase for neat. Will try it out, thank you!
let-go should probably work reasonably well with CIDER as well (eventually), but it will definitely work better with neat. π
(depending on the nREPL server for let-go of course)
https://github.com/seancorfield/rephrase https://github.com/seancorfield/rephrase/releases/tag/v1.0.0 -- Rephrase exceptions to be more beginner-friendly
Initial Public Release!
β’ Provides nREPL middleware org.corfield.rephrase.nrepl/wrap-rephrase to rephrase common confusing exceptions to be more intuitive
β’ Provides org.corfield.rephrase/repl-caught as a replacement for clojure.main/repl-caught that can be used, via the :caught option, when you start a basic REPL via clojure.main/repl
β’ You can add your own rephrasings via an additional EDN file on the classpath
Example, assuming :rephrase alias specifies this dependency:
> clj -A:rephrase
Clojure 1.12.5
user=> (* 1 2 :three)
Execution error (ClassCastException) at user/eval1 (REPL:1).
class clojure.lang.Keyword cannot be cast to class java.lang.Number (clojure.lang.Keyword is in unnamed module of loader 'app'; java.lang.Number is in module java.base of loader 'bootstrap')
user=> (require '[org.corfield.rephrase :refer [repl-caught]])
nil
user=> (clojure.main/repl :caught repl-caught)
user=> (* 1 2 :three)
Expected a number, but was given a keyword, at user/eval273 (REPL:1) - runtime error (unexpected type).
user=>
Follow-up in #rephrasePretty cool! π
My first ever nREPL middleware, BTW.
@seancorfield I hope this was a straightforward experience. π Feedback welcome!
I may try to write up my thoughts and post to #nrepl but off the top of my head:
β’ took me a while to understand the difference between requires and expects
β’ testing is... interesting π
β’ the docs cover middleware that handles ops in messages but not post-processing of responses which is what I initially thought I needed (until I discovered wrap-caught)
Would be interesting to have something like this come standard with core. Might help deflect the common βClojure error messages are crapβ complaint.
@droberts3 This deliberately doesn't touch the original, internal error reporting so *e and various stacktrace stuff is not affected. This is intended to be a dev-only cleanup and mostly focused on nREPL usage.
In particular, testing libraries would have to add rephrase as a dependency and invoke the rephrasing fn on exceptions as part of reporting them (which each testing library already handles slightly differently).
Ah, gotcha. Thanks.
Very cool. Nice touch with the nREPL middleware also.