A small, language-agnostic nREPL client for Emacs, in the spirit of monroe.
Where monroe and CIDER target Clojure specifically, neat aims to be the
purest language-agnostic nREPL client in the Emacs world: no Clojure-flavored
helpers, no hardcoded ops, no assumptions about your project's build tool.
It's useful directly (a REPL buffer plus a source-buffer minor mode) and
also as a library that other Emacs packages can build on.
This is an early, experimental project. Expect rough edges.
Pre-alpha. The first cut establishes the project skeleton, a bencode codec, the wire protocol plumbing, and a basic comint-based REPL. No release yet.
neat is part of a broader push to make nREPL a healthy multi-language ecosystem rather than a Clojure-only protocol. That effort has three strands:
- An official nREPL specification. Today the nREPL project is the de facto spec; a formal version is being drafted at nrepl/spec.nrepl.org. neat aims to keep pressure on the spec to stay genuinely language-agnostic by being a client that refuses to silently assume Clojure.
- Reference clients. A spec without independent client implementations is wishful thinking. neat is one such reference client, intentionally built on Emacs builtins and free of Clojure-specific helpers, so it can act as a baseline for what "compliant" should mean on the client side.
- A compatibility test suite. The parameterised integration suite
under
test/neat-integration-test.elalready runs the same assertions against multiple servers (Clojure, Babashka, Basilisp), and divergences between them get surfaced as real findings rather than mysterious bugs. The long-term goal is to grow this into a portable suite any nREPL server can self-check against.
neat isn't on MELPA yet -- that's an item on the road to 0.1. In the
meantime, the easiest path is package-vc-install on Emacs 29+:
(package-vc-install
'(neat :url "https://github.com/nrepl/neat" :branch "main"))On Emacs 30+ with use-package:
(use-package neat
:vc (:url "https://github.com/nrepl/neat" :branch "main")
:commands (neat neat-mode))For a manual checkout (e.g. while contributing):
(add-to-list 'load-path "/path/to/neat")
(require 'neat)neat-mode is a minor mode you turn on per source buffer; hook it onto
whichever languages you actually drive (clojure-mode, fennel-mode,
hy-mode, ...). The mode itself doesn't assume any specific language.
neat is a few small files instead of one big one, so other packages can
pick and choose:
neat-bencode.el- bencode encode/decode, no other dependencies.neat-client.el- connection management, request dispatch, nREPL ops.neat-repl.el- comint-derived REPL buffer.neat.el- entry point, customization group,neat-modeminor mode for source buffers.
Library users typically only need neat-bencode and neat-client.
Start an nREPL server. Anything that speaks the protocol will do; for a Clojure server the easy options are:
bb nrepl-server :port 7888
# or
lein repl :headless :port 7888
# or
clj -M:nrepl
Then in Emacs:
M-x neat RET localhost RET 7888 RET
A *neat: localhost:7888* buffer pops up with a prompt. Type an
expression, hit RET, see the result. Multi-line forms work too --
RET only submits when the input parses as balanced; otherwise it
inserts a newline so you can finish the form. Input history is
persisted between sessions in neat-repl-history-file and the prompt
follows the server's reported namespace (user> , myapp.core> , ...).
To evaluate from a source buffer:
M-x neat-mode
Bindings:
| Key | Command |
|---|---|
C-c C-e |
neat-eval-last-sexp |
C-c C-c |
neat-eval-defun |
C-c C-r |
neat-eval-region |
C-c C-b |
neat-eval-buffer |
C-c C-z |
neat-switch-to-repl |
C-c C-k |
neat-cancel |
For the rationale behind the architecture -- the module split, the
async dispatch model, the comint pipe-process trick, why we target
Emacs 28+, and so on -- see doc/design.md.
The project uses Eldev and Buttercup.
eldev compile # byte-compile
eldev lint # lint
eldev test # run Buttercup suites
The default suite is the fast one. There's also an integration suite that boots real nREPL servers as subprocesses and exercises the full client. It's gated behind an env var since starting a server adds a few seconds:
NEAT_INTEGRATION=1 eldev test
The integration suite walks neat-it--server-impls in
test/neat-integration-test.el and registers a block per implementation
that's installed on PATH. Currently:
| Implementation | Executable | How to install |
|---|---|---|
Clojure (nrepl/nrepl) |
clojure |
clojure.org/guides/install_clojure |
| Babashka | bb |
brew install borkdude/brew/babashka |
| Basilisp (Python) | basilisp |
pipx install basilisp |
Add more entries to the list to teach the suite about your favorite nREPL implementation -- it just needs an executable that prints a port banner on stdout.
Distributed under the GNU General Public License, version 3 or later. See
LICENSE.