Posts

  • Making CIDER More Discoverable

    This series about the notable changes in CIDER 2.0 continues with the change you’ll bump into first, whatever your workflow: the transient menus, and the broader push to make CIDER’s functionality discoverable.

    The problem: CIDER is huge

    CIDER has well over 300 interactive commands. I’ll admit something I’ve said before: there are features in CIDER that even I forget exist, and I wrote half of them. For users, historically, the options for finding functionality were:

    • memorize cryptic key chords (C-c C-w i, anyone?)
    • grep the (very long) manual
    • read the source
    • stumble on a feature by accident three years in and feel robbed

    That’s not great for a tool whose whole pitch is making you more productive. The Emacs answer to this problem was demonstrated years ago by Magit: transient menus, which turn every prefix into a self-documenting popup. It took us embarrassingly long to follow suit, but CIDER 2.0 finally does.

    Transient everywhere

    Every command group in CIDER now opens a transient menu: cider-eval-menu at C-c C-v, cider-doc-menu at C-c C-d, and likewise for test, namespace, macroexpand, profile, trace and references. A top-level cider-menu ties them all together, and even the debugger (? mid-session) and the inspector (m) got menus of their own. Jack-in and connect live in cider-start-menu at C-c C-x.

    One design constraint was non-negotiable: your muscle memory is safe. These menus replace bare prefix keymaps, so every existing keybinding works exactly as before, at full speed - C-c C-v e still evaluates instantly, menu or no menu. And if you’d rather not see the menus at all unless you actually hesitate mid-chord, set transient-show-popup to a short delay and they’ll appear only in that moment of doubt - which is precisely when you need them.

    Transient also gave us something the old keymaps never could: arguments. Menus now carry flags for the things that vary per invocation - pick a pretty-printer with --print-fn=, set test selectors with --include=/--exclude= and reuse them across runs, toggle cider-ns-refresh’s modes explicitly, pass Clojure CLI aliases at jack-in time. All those “this command behaves differently with a prefix argument” paragraphs in the manual are becoming visible checkboxes instead.

    Discovery beyond menus

    The menus are the headline, but the discoverability push in the 2.0 cycle went wider:

    • A new keybindings reference page collects every binding in one place, and the printable refcard was brought back up to date.
    • The REPL’s shouty welcome banner is gone, replaced by a one-line hint; the getting-started material now lives in a summonable reference card (C-c C-h, or the ,refcard REPL shortcut) - available when you want it, invisible when you don’t.
    • CIDER now warns (once per session) when you use a deprecated keybinding, so bindings can actually be retired someday without silently breaking people. M-x cider-list-deprecated-keybindings shows what’s on the way out.
    • The 1.22 cycle’s big audit already made the mode menus expose dozens of commands that were technically present but practically invisible; 2.0 builds on that foundation.
    • Even cider-doctor is discoverability of a sort - it surfaces the problems in your setup that you’d otherwise discover one confusing bug report at a time.

    The philosophy

    If I had to compress the 2.0 discoverability work into one sentence: the features were always there; now the tool tells you about them. Documentation is where knowledge goes to be forgotten - the only reliable place to teach a user about a feature is inside the workflow itself, at the moment of hesitation. Transient menus are exactly that, and Magit proved the pattern scales to enormous command sets.

    If some menu feels wrong - a missing command, a flag that should exist, a grouping that doesn’t match how you think - please file an issue. This part of CIDER is young and very much open to feedback.

    The keybindings docs have the full picture. Keep hacking!

  • clj-suitable 0.8.0: Closing the Gap with Compliment

    You had me at js/.

    – Jerry Maguire, on ClojureScript interop completion

    clj-suitable 0.8.0 is out! If the name doesn’t ring a bell, that’s rather the point - clj-suitable is the small library that quietly powers ClojureScript code completion in CIDER, Calva, and pretty much anything else that talks to a cljs REPL over nREPL. Think of it as the ClojureScript counterpart to what compliment1 does for Clojure. For years it lagged well behind its Clojure sibling, and this release is my attempt to finally close that gap - to make cljs completion, if you’ll forgive me, a touch more suitable.

    This is also a direct follow-up to the Piggieback work I wrote about a couple of weeks ago. Once I had the cljs REPL plumbing back in decent shape, fixing up the completion story sitting on top of it was the obvious next move.2

    1. The completion library, spelled with an i. Not the nice thing 

    2. One thing invariably leads to another with this stuff. You set out to fix 

    Read More
  • Sayid 0.8

    Sayid 0.8 is out! It’s the third release since I brought Sayid back from the dead a couple of weeks ago, and it has a clear theme: making the tool easy to pick up. The revival releases were mostly about the engine - bounding the recording, consolidating the API, getting the data out. This one is about the experience. If you’ve ever bounced off Sayid because you couldn’t figure out what to press, or what it was trying to tell you, 0.8 is for you.

    Read More
  • Projectile 3.2

    Projectile 3.2 is out!1 That’s the third Projectile release this month, and by now you probably see the pattern - a whole lot of nothing for a couple of years, then everything at once. Where 3.0 was the big cleanup and 3.1 the pile of long-standing feature requests, 3.2 is a focused release with one clear theme: search and replace. Plus one bonus feature I’ve wanted for ages, but more on that below.

    1. Technically it’s been out since July 12th, and there’s already a 3.2.1 

    Read More
  • Stepping Through Macros in CIDER

    This is another installment in the series of articles about the notable changes in CIDER 2.0. Today’s topic is one of those “ambitious ideas that lay dormant for ages” I keep mentioning: proper interactive macro stepping.

    The Dream

    CIDER has had macroexpansion support practically forever - C-c C-m expands the form before point into a dedicated buffer, a feature we inherited spiritually from SLIME. It works, but it has always felt a bit… detached. The expansion lives in another buffer, divorced from the code you’re reading, and for deeply nested macros you end up bouncing between buffers trying to keep your bearings.

    Emacs Lisp hackers have long had something nicer: macrostep, a brilliant little package that expands macros in place - right where they sit in your code - one step at a time, and collapses them back when you’re done. I’ve wanted a Clojure version of this for years. CIDER 2.0 finally ships one.

    Standing on shoulders, not on top of them

    I’m hardly the first person to want this. The idea of bridging CIDER and macrostep goes back to at least 2016, where a proof of concept wired up macrostep’s extension hooks to CIDER by injecting a couple of helper functions into your REPL and shuttling forms back and forth as strings. Later, macrostep-geiser - a Scheme-oriented macrostep backend - grew CIDER support as well, and SLIME itself ships a slime-macrostep contrib built on the same extension API. So the hooks were there, the hacks existed, and I could have blessed one of them and called it a day.

    I opted for a clean, from-scratch implementation in CIDER instead - mostly to provide the best possible experience for Clojure programmers, without the compromises the bridges had to make. macrostep’s extension API was designed around Emacs Lisp’s happy circumstances, and Clojure violates most of them:

    • macrostep-expand-1-function must be synchronous. That’s fine when expansion is an elisp function call; it’s less fine when it’s a network round-trip to an nREPL server. (CIDER has been busy removing exactly this kind of blocking call lately - asynchronous eldoc being the poster child.)
    • The API traffics in forms - elisp data structures. But Clojure code isn’t elisp data: keywords, reader tags, #() lambdas and namespaced maps have no faithful elisp representation, so every bridge ends up round-tripping code through strings and hoping the quoting survives. The 2016 proof of concept literally did (format "(expand-once '%s)" form), which should make anyone a little nervous.
    • macrostep-print-function expects the client to print and fontify the expansion. In CIDER the server does the printing - that’s how we get namespace tidying (when instead of clojure.core/when), print-option handling and metadata display for free. A client-side printer would have to reimplement all of that, badly.

    There’s also a less technical reason: lately I’ve grown rather averse to adding third-party dependencies to CIDER. A dependency is a bet on someone else’s continued enthusiasm, and such bets sometimes go bad - tellingly, macrostep itself spent a stretch unmaintained and now lives in a GitHub org literally named “emacsorphanage” (it has since found a new maintainer, but the point stands). CIDER is 14 years old and intends to stick around; over that kind of horizon, owning ~600 lines of overlay code is cheaper than adopting someone else’s semi-abandoned package. So we kept macrostep’s brilliant UX ideas and its familiar keybindings, and left the coupling behind.

    cider-macrostep

    The entry point is cider-macrostep-expand (C-c M-m e). Put point after a macro form, invoke it, and the form is replaced inline with its one-step expansion, highlighted so you can tell what’s expansion and what’s your code:

    (when-let [x (fetch-thing)]
      (process x))
    
    ;; C-c M-m e =>
    
    (let [temp__5825__auto__ (fetch-thing)]
      (when temp__5825__auto__
        (let [x temp__5825__auto__]
          (process x))))
    

    From there you’re in cider-macrostep-mode, where the further-expandable sub-forms are underlined, n/p hop between them, e (or RET) steps into one, and c/q collapse one level or everything back to the original code. Your buffer is never actually modified in a way that sticks - collapse everything and it’s exactly as it was.

    Here’s the whole flow in motion - expand, step into a nested macro, collapse back to the original code:

    cider-macrostep stepping through when-let and collapsing back

    A couple of touches I’m particularly fond of:

    • Every distinct gensym in the expansion gets its own color, so you can finally track where that temp__5825__auto__ flows through the expanded code. Once you’ve seen a for expansion with colorized gensyms, you won’t want to go back:

    cider-macrostep expanding for, with colorized gensyms

    • E (cider-macrostep-expand-all) fully expands the form in one step, for when you don’t care about the journey.
    • b (cider-macrostep-expand-in-buffer) runs the same stepping session in a dedicated popup, leaving the source buffer untouched - handy when you’re in someone else’s code and feel uneasy about inline rewrites, however temporary.

    The classic buffer got some love too

    The traditional macroexpansion buffer wasn’t neglected either: it grew a header line showing the active expander and display options, n and t cycle namespace display and metadata in place, g re-expands with the latest macro definition, and freshly expanded forms pulse briefly so your eye lands in the right place.

    The expansion commands also got more talkative: pointing them at an unresolved symbol now tells you whether the namespace simply isn’t loaded yet (evaluate the buffer!) or you’ve got a typo, instead of silently doing nothing.1

    Why bother?

    Macros are the part of Clojure people are most likely to describe as “magic”, and the standard advice - “just macroexpand it” - has always carried a hidden tax: the expansion of any non-trivial macro is a wall of gensyms and nested let*s that’s genuinely hard to read cold. Stepping through the expansion one level at a time, in place, with the gensyms color-coded, turns that wall into something you can actually follow. I did not expect macro debugging to become fun, and yet here we are.

    All the details are in the macroexpansion docs. Give it a try the next time a macro surprises you - and keep hacking!

    1. Amusingly, we initially overdid these diagnostics - the guard refused to expand let and fn (which are macros wearing special-form badges) and broke a beloved trick of using macroexpansion to normalize reader syntax like ::auto/keywords. See #4111 - fixed right after 2.0. Even diagnostics need diagnostics. 

Subscribe via RSS | View Older Posts