Posts
-
CIDER 2.0 is Brewing...
Normally I write these posts after a CIDER release is out, with the smug satisfaction of someone who has already tagged the version and updated the changelog. Today I’m flipping the script a little: the release isn’t out yet, but I’m too excited to keep quiet about it.
Here’s the thing. The next CIDER release was supposed to be 1.23. But as I kept piling change upon change, it slowly dawned on me that calling this “1.23” would be doing it a disservice. So I’m now strongly considering shipping it as CIDER 2.0 instead.
A different kind of release
The last few CIDER releases were on the conservative side - lots of important internal work, but relatively little that you’d actually notice day to day. This one is the exact opposite. It’s stuffed with user-visible changes, many of which have been sitting on the issue tracker and in the back of my mind for literally years.
Why now? Two reasons.
First, all that boring groundwork paid off. Some of you noticed I spent a lot of time recently backfilling tests and generally enriching the test suite. That wasn’t busywork - it was me building a safety net, so that finally making sweeping changes would be safe and pleasant instead of terrifying. Mission accomplished: I’ve been refactoring with abandon and sleeping just fine.
Second - and I know this is the controversial part - AI agents have been a genuinely great help. Not for writing CIDER for me (the design taste is still very much mine, thank you), but for quickly prototyping my half-formed ideas in different ways so I can see and feel them before committing to the approach I like best. Turns out “show me, don’t tell me” works wonders when the thing showing you can knock out a prototype in minutes. Boo me if you must, but when you’re the maintainer of so many projects and you have only so little time to spend on all of them that really makes a difference.
There’s a third ingredient worth mentioning: I’d been hammocking on most of this for months, quietly turning the designs over in the back of my mind while I was wrapping up CIDER 1.22. So while 1.22 itself took the better part of four months (and still shipped later than I’d have liked), the release right after it has been snapping together in a couple of weeks - the code was the easy part once the thinking was done. It helps that a lot of these changes close issues that had been gathering dust for years; the ClojureScript macroexpansion bug (#2099) was filed all the way back in 2017. Nothing beats hammock-driven development - even if it’s decidedly not the fastest way to ship anything.
OK, enough preamble. Let’s look at the goodies.
Keymaps you can actually discover
CIDER has a lot of commands, and historically the only way to find them was to memorize cryptic key chords or grep the manual. No more. Every command prefix now pops a transient menu (the same UI magit made famous), and there’s a top-level
cider-menuthat ties them all together:Evaluate Code REPL & session Diagnostics e Eval... t Test... j Insert into REPL... r Trace... m Macroexpand... n Namespace... x Jack-in / connect... p Profile... d Documentation... w References... l Log...Crucially, this isn’t a modal speed bump - your old muscle memory still fires instantly.
C-c C-d C-drunscider-docas fast as ever; the menu only shows up if you pause after the prefix, wondering “wait, what else is in here?”One tree view to rule them all
A bunch of CIDER’s browsers grew up independently and looked it. They now share a single, foldable tree-view widget - the namespace browser, the spec browser, and the new call-graph browsers all behave consistently (
TABto fold,n/pto move around). For example, exploring who calls a function:who-calls clojure.core/reduce ▾ my.app.core/sum ▸ my.app.api/handler ▸ my.app.util/totalExpand a node and CIDER lazily fetches the next level. It’s a much nicer way to spelunk through a codebase than a flat list of strings.
Find usages, batteries included
Speaking of call graphs - CIDER now ships with genuinely capable cross-reference functionality out of the box.
cider-who-calls/cider-who-is-calledfor the call graph,cider-who-implementsfor protocols and multimethods, plusxref-find-references(M-?) that searches your source and therefore finds usages even in code you haven’t loaded into the REPL yet.The practical upshot: if you were reaching for clj-refactor.el or clojure-lsp mostly to find usages, you probably don’t need them anymore. (If you were using them for other things, carry on - we’re friends, not rivals.)
Much of the inspiration here came straight from swank-clojure and SLIME itself, which have offered this kind of cross-referencing for ages. Sometimes nothing beats revisiting the classics.
The debugging toolbox got a serious polish
This is the part I’m most happy about. Pretty much every “what is my code actually doing?” tool got some love.
The macro tooling is the headliner. The macroexpansion buffer finally grew a header line that shows the active expander and options, cycles namespace display and metadata in place, pulses the freshly-expanded form, and - at long last - says something useful when you point it at a special form or an unresolved symbol, instead of silently shrugging. Better still, there’s a brand new inline stepper (
cider-macrostep, a Clojure spin on the venerable macrostep package) that expands macros right where they sit, one step at a time. Expandable sub-forms get underlined so you can hop between them withn/p, and every gensym is painted its own color, so you can finally follow where thatg__1234ends up. I did not expect macro debugging to be fun, and yet here we are.Tracing got the same treatment. Traced calls no longer smear themselves all over your REPL output; they stream into a dedicated, foldable
*cider-trace*buffer instead:*cider-trace* ▾ (my.app/process {:id 7}) ▸ (my.app/validate {:id 7}) => true ▸ (my.app/persist {:id 7}) => {:id 7, :saved? true} => {:id 7, :saved? true}And because “wait, what did I even trace?” is a question I ask myself constantly,
cider-list-tracedandcider-untrace-allare now a keystroke away.Enlighten (you remember it, right?) picked up some manners too. You can light up a single form with
cider-enlighten-defun-at-pointwithout flipping the global mode, andcider-enlighten-stopmakes the whole thing vanish at once, instead of making you re-evaluate everything in penance. And for thetap>crowd,cider-tapopens a buffer that streams whatever you send totap>and lets you crack any value open in the inspector withRET. It’sprintlndebugging, minus the println guilt.ClojureScript gets some love too
ClojureScript is always the trickier sibling, but it got meaningful improvements this round:
- You can now run ClojureScript tests with the regular test commands (including
async
cljs.testtests) instead of CIDER refusing to play along. - Macroexpansion of user-defined cljs macros finally works (it used to silently echo the form back unexpanded - a bug that had been open since forever).
- And when you invoke a Clojure-only command under a cljs REPL, CIDER now tells you so clearly, rather than failing in some confusing way.
This was another area where AI tooling was quite helpful to me, as I’ve rarely used ClojureScript in the past, but I still managed to figure out how to finally solve those problems that have been pain points for CIDER’s users for as long as we’ve had ClojureScript support.
On a related note, I’ve also been chasing down a few small bugs in Piggieback (the middleware that powers most of the cljs REPLs CIDER talks to) - have a look at the recent releases if you’re curious. And I’ve been idly pondering some form of “native” shadow-cljs support down the road. That last one is very much TBD, so don’t hold me to it - but the ClojureScript story keeps inching forward.
A pile of quality-of-life touches
It wouldn’t be a CIDER release without a long tail of small comforts. A few that I keep bumping into and grinning at:
- Eldoc is asynchronous now, so dragging the cursor around no longer blocks Emacs on an nREPL round-trip. Buttery smooth.
- A new
cider-modelighter (with an optional fringe marker) flags when the current buffer’s namespace isn’t loaded into the REPL, or has gone stale because you edited an already-evaluated form. That retires a whole genre of “why isn’t my change taking effect?” head-scratching. cider-doccan pull ClojureDocs examples right into the doc buffer.- Sending a form into your namespace’s
(comment ...)block is a one-keystroke affair now (cider-send-to-comment), andcider-jump-to-commentteleports you back there. - Stuck in
.cljcland? You can pin where a buffer’s evaluations go - clj, cljs, or both - withcider-set-eval-destinationand friends. - The REPL banner is slimmer and far less shouty; the getting-started spiel now
lives in a summonable reference card (
C-c C-h). - The cheatsheet finally learned about all the functions Clojure has grown since 1.11.
I’m probably still forgetting a dozen things - the changelog is genuinely enormous this time around.
Please go play with it!
Unfortunately no one can be told what CIDER 2.0 is – you have to experience it yourselves…
– Clorpheus
Here’s where you come in. All of this is already available in the snapshot release of CIDER on MELPA. I’d love for you to install it, kick the tires, and - especially - tell me how the various UX changes feel. Discoverable? Annoying? Joyful? I genuinely want to know before I carve any of it in stone.
If no serious problems surface, I plan to cut the real release fairly soon - think a week or two. So now’s the perfect time to influence it.
Epilogue
I keep hearing that the Clojure community isn’t innovating much these days. I hope CIDER 2.0 goes a small way towards convincing the doubters that we’re not quite done yet. The best is always yet to come - for both Clojure and Emacs.
Now let’s go forth and brew some (magic) CIDER together! Keep brewing!
- You can now run ClojureScript tests with the regular test commands (including
async
-
copilot.el 0.7
Good news, everyone – copilot.el 0.7 is out! And it’s a big one.
Ever since I took over the maintenance of copilot.el in the spring of 2025, I’ve had one north star: close the gap between Emacs and the editors that enjoy first-party Copilot support. VS Code and (Neo)vim got the rich, official experience – chat, agents, the whole shebang – while we Emacs folks were stuck with humble ghost-text completion. That never sat right with me. Emacs deserves better.
For a bit of perspective on the pace: in its first three years the project never shipped a single tagged release. 0.7 is the seventh I’ve cut since taking over. We’ve been busy.
For a long time that gap felt unbridgeable. We were essentially reverse-engineering an undocumented protocol and replicating it in Elisp, always one step behind. But then Microsoft open-sourced @github/copilot-language-server, and everything changed. Suddenly we had a documented, first-party LSP server to talk to – the very same backend that powers Copilot in VS Code. The migration to it in 0.4 was the turning point. From that moment on, catching up to the other editors stopped being a fantasy and became a (long) TODO list.
We’ve been chipping away at that list ever since. A few months ago, in 0.5, we added basic support for Copilot Chat – the first time the package did anything beyond completion. 0.6 brought Next Edit Suggestions (NES) and the experimental beginnings of agent mode. And now 0.7 fills in pretty much all of the remaining blanks.
So what’s actually in it? Quite a lot. Let me walk you through the highlights.
Agent Mode, For Real
Chat is nice, but talking to an assistant that can’t do anything gets old fast. Enter agent mode:
(setopt copilot-chat-use-agent-mode t)With this on, Copilot can run tools to actually get work done – run shell commands, create and edit files, read your diagnostics, fetch web pages. Ask it to “add a test for
fooand run it” and it’ll write the test, invoke your test runner, read the output, and iterate, all without you leaving the chat buffer.Of course, letting an AI run commands and rewrite your files unsupervised is a recipe for excitement of the wrong kind, so every tool call asks for confirmation first. File edits show you a diff preview before you approve them, and if you trust a particular tool you can pick
alwaysto stop being asked for the rest of the conversation. Long-running terminal commands now run asynchronously too, so Emacs stays responsive while that test suite chugs along – andC-gaborts a command that’s overstaying its welcome.Workspace Awareness
An agent that can only see the current buffer isn’t much of an agent. So 0.7 teaches copilot.el to answer the server’s workspace requests: Copilot can now search your project by file glob or by text/regex (via ripgrep, so your
.gitignoreis respected), read files, and list directories across the whole tree. That’s what makes “where is X handled in this codebase?” actually work.For the truly ambitious there’s also opt-in semantic search:
(setopt copilot-chat-enable-semantic-search t)Flip that on and the server builds an embeddings index of your workspace, so you can ask whole-codebase questions instead of pointing at specific files. It’s off by default, since indexing isn’t free, but it’s there when you want it.
MCP Support
Model Context Protocol is how you give an assistant extra superpowers, and copilot.el now speaks it. Point
copilot-mcp-serversat the servers you want and their tools show up in chat, right alongside the built-in ones:(setopt copilot-mcp-servers '(:fetch (:command "uvx" :args ["mcp-server-fetch"]) :memory (:command "npx" :args ["-y" "@modelcontextprotocol/server-memory"])))M-x copilot-chat-list-mcp-toolsshows you which servers connected and what they offer (and complains loudly when one fails to start, which beats the old silent treatment).A Chat You Can Actually Use
The chat buffer grew up too. A few of the quality-of-life additions:
- Slash commands (
C-c /) –/explain,/fix,/tests,/doc, and friends, fetched live from the server. - Acting on code blocks –
C-c C-iinserts the code block at point into your source buffer,C-c M-wcopies it to the kill ring. No more copy-paste gymnastics out of a read-only buffer. - Attaching context –
copilot-chat-add-file-reference(C-c C-f) andcopilot-chat-add-region-referencelet you hand Copilot specific files or selections along with your question.
Odds and Ends
A grab bag of smaller, but handy, additions:
M-x copilot-quotashows how much of your chat, completion, and premium-request allowance is left – useful now that the fancy models eat into a quota.- Copilot will tell you when a suggestion closely matches public code and
collect those matches, with their licenses and reference URLs, in a buffer you
can open via
M-x copilot-list-code-citations. - A pile of bug fixes, including agent-mode tool confirmations that were flat-out broken against newer servers, and chat finally picking a sensible default model on its own.
As always, see the full changelog for the complete rundown.
Epilogue
Agent mode, MCP, and semantic search are all opt-in, so nothing here changes your setup unless you ask for it. But I’d love for you to ask for it! Turn on agent mode, point it at a real task, and tell me where it shines and where it falls on its face. This kind of feature only gets good with people kicking the tires and filing detailed reports, so please do open issues and let me know how it goes. Your feedback is what shapes the next release.
I’d be remiss not to acknowledge it: there’s a decent chance that by now half of you have wandered off to Claude Code or some other agentic CLI, and that pouring this much time and energy into Copilot support for Emacs is, objectively, a questionable use of a human life. Fair enough.
But here’s the thing – Emacs has always excelled at providing amazing support for niche technologies long after the rest of the world has moved on. We’re the people who’ll lovingly maintain a mode for a language nobody has written any code in since 2003. Keeping Copilot first-class in Emacs is squarely in that grand tradition, and I, for one, intend to see it through. :-)
That’s all I have for you today. Keep hacking!
- Slash commands (
-
MrAnderson 0.6
MrAnderson 0.6 is out. If you’ve never heard of it, that’s perfectly fine - it’s the kind of tool that’s supposed to do its job quietly and stay out of sight. cider-nrepl has been using MrAnderson to inline its dependencies (bundle private copies of them that can’t clash with yours) for about as long as cider-nrepl has existed, and most of the time you’d never know it was there.
The trouble is the “most of the time”. MrAnderson has always had a few rough edges, and every so often one of them would draw blood during a cider-nrepl release and I’d find myself wondering whether we should just rip the whole thing out. I wasn’t even joking about it - at one point I went and opened a PR to remove MrAnderson from cider-nrepl entirely. That’s roughly how fond of it I was feeling.
Read More -
CIDER 1.22 ("São Miguel")
Great news, everyone - CIDER 1.22 (“São Miguel”) is finally out!
And “finally” is the operative word here. This release took me way longer than I wanted it to, but that’s because I decided to stop kicking a few cans down the road and finally tackle some long-standing problems that had been bugging me for years:
- Session and connection management - the logic for figuring out which REPL a buffer is associated with had grown into something I could barely follow myself.
- The decoupling of nREPL from CIDER’s UI layer - a piece of technical debt so old it predates most of you reading this (the tracking issue, #1099, is from 2017).
- A full audit of the codebase and the documentation, hunting for inconsistencies, dead code, broken menu entries, and gaps in the docs.
None of this is the kind of work that makes for a flashy release announcement, but it’s exactly the kind of work that keeps a 14 year old project healthy. I genuinely think this is one of the most important CIDER releases in recent memory, even though most of the changes aren’t really user-visible.
Highlights
Picking a handful of items out of a very long changelog, here’s what I’d call the highlights:
- A huge editor responsiveness fix (#3933) - Clojure buffers no longer lag when there’s no REPL connected. The friendly-session matching used to scan the project classpath on every redisplay; now it does something far cheaper. If you’ve ever felt CIDER make your editing sluggish, this one’s for you.
- Default sessions (#3865) -
cider-set-default-sessionlets you bypass sesman’s project-based dispatch and pin a REPL as the default. Handy for all those workflows that never quite fit the project model. - nREPL is now decoupled from CIDER’s UI (#3892) - the transport layer no longer reaches into CIDER-specific handlers and UI strings. This finally closes #1099 and opens the door for other tools to build on our nREPL client.
- Faster connection completions (#3888) - repeated
cider-connectcompletions no longer re-spawn a round ofps/lsofsubprocesses every single time. - A massive discoverability pass on menus and keybindings - menus across the inspector, REPL history, log, spec browser, and more now actually expose the commands they were missing (often a dozen+ per mode), and
C-h mnow lists the active bindings for several modes. A lot of functionality that was technically there but practically invisible is now front and center. cider-repl-history-doctor(#3921) - a new command that walks your REPL history, finds entries with unbalanced parens, and helps you clean them up. Born out of a real bug report about history rendering breaking.- Support for let-go (#3926) - a Clojure dialect implemented in Go is now recognized as a known nREPL runtime.
- Better remote (TRAMP) jack-in and connect (#3885, #3886, #3887) - endpoint detection, command resolution, and SSH tunneling all behave correctly against remote hosts now.
- Plenty of nREPL robustness fixes - plugged several request-id leaks, bounded the completed-requests table, and made a misbehaving response handler no longer able to drop later responses on the floor.
- Bumped the injected
nREPLto 1.7.0 andcider-nreplto 0.59.0.
The full list is much longer - check out the changelog if you want the gory details.
A fun little detour: Port and Neat
While I was doing the nREPL decoupling work, I got curious and started experimenting with adding support for prepl (Clojure’s built-in socket REPL) as an alternative to nREPL. I even put together a prototype (cider#3899). It sort of worked, but it also reaffirmed my belief that prepl and nREPL are different enough that bolting prepl onto CIDER would mean papering over its limitations in dozens of subtle places. So instead of forcing it, that little experiment grew into two brand new projects of mine:
- Port - a minimalist prepl client for Emacs.
- Neat - a tiny, deliberately language-agnostic nREPL client.
Both have write-ups of their own, so I won’t repeat the details here. I’m pretty excited about where they might lead - a great example of how digging into old technical debt can spark entirely new ideas.
Epilogue
This release is dedicated to São Miguel, the stunningly beautiful main island of the Azores archipelago.1 I got a lot of my recent inspiration for CIDER there, and naming the release after it felt right.
As always, none of this happens in a vacuum. A huge thank you to Alex Yakushev for his continued work on the inspector - it keeps getting better and better. And of course a massive shoutout to Clojurists Together and to all the other contributors and backers of my open-source work. You’re the reason CIDER and friends keep moving forward.
That’s all I have for you today. I hope you’ll enjoy using CIDER 1.22 as much as I enjoyed (eventually) shipping it. Keep hacking!
-
If you ever get the chance to visit, do it. Crater lakes, hot springs, and the greenest hills you’ve ever seen. ↩
-
What's Next for clojure-mode?
Good news, everyone! clojure-mode 5.22 is out with many small improvements and bug fixes!
Read More
Subscribe via RSS | View Older Posts