Using CIDER with clojure-lsp
A lot of Clojure programmers run CIDER alongside clojure-lsp (through lsp-mode or Emacs’s built-in Eglot). The two are complementary rather than competing, but because they both hook into the same Emacs facilities (completion, xref, ElDoc, formatting) it’s worth understanding how to make them coexist cleanly.
This page assumes you already have clojure-lsp and an LSP client set up. It only covers the CIDER side of the relationship.
Why run both?
CIDER and clojure-lsp answer different questions, and the split follows a simple rule of thumb:
-
clojure-lsp is static. It analyzes your project’s source on disk, so it works before you’ve started a REPL and sees code that’s never been loaded. That makes it strong at project-wide navigation and references, rename and other refactorings, completion and signature help without a running process, formatting, and on-the-fly diagnostics from
clj-kondo. -
CIDER is dynamic. Everything it does goes through a running nREPL process, so it reflects the actual state of your program. That’s what powers interactive evaluation, the debugger, the inspector, the test runner, macroexpansion, tracing and profiling, and runtime-accurate documentation and completion.
Used together you get the best of both: clojure-lsp’s whole-project static view for editing and navigation, and CIDER’s live connection for actually running and inspecting your code. The features that only make sense at runtime (evaluation, debugging, inspection, testing) are CIDER’s alone and never overlap with LSP.
Resolving the overlaps
The friction shows up in the handful of areas where both tools plug into the same Emacs feature. In each case you can keep both, or defer to one. There’s no single correct answer - it depends on whether you’d rather have runtime-accurate or static-analysis answers when a REPL is connected.
Definition lookup and references (xref)
Both integrate with Emacs’s built-in xref, so M-. and M-? could be
served by either. CIDER adds its backend with a precedence of -90 (lower means
higher priority), so by default CIDER wins when a REPL is connected. To let
clojure-lsp handle navigation instead, lower CIDER’s precedence:
(setq cider-xref-fn-depth 90)
Or turn CIDER’s xref backend off entirely, so M-. always goes through your LSP client:
(setq cider-use-xref nil)
You’ll have to toggle cider-mode off and on (or restart it) for a change
to cider-use-xref to take effect.
|
See Code Navigation and Browsing for the full picture of CIDER’s own navigation commands.
Documentation in the echo area (ElDoc)
Both can feed the echo area through eldoc-documentation-functions. As of CIDER
2.0, CIDER’s ElDoc function declines its slot when it has nothing to show, so the
two compose cleanly and you can leave both registered. If you’d rather have
clojure-lsp be the sole ElDoc source, remove CIDER’s function:
(remove-hook 'eldoc-documentation-functions #'cider-eldoc)
| Emacs 28+ can merge multiple ElDoc sources, so you can see, say, a linter message from clojure-lsp and CIDER’s runtime arglist at once. See ElDoc for details. |
Completion
CIDER registers a completion-at-point function that completes against the
running REPL, and lsp-mode registers one of its own. They can coexist on
completion-at-point-functions; which one you get depends on their order and on
whether a REPL is connected. If you prefer a single source of completions, keep
whichever you like and disable the other in Clojure buffers (for CIDER, that’s
removing cider-complete-at-point from completion-at-point-functions). See
Code Completion for how CIDER’s completion works.
Formatting
clojure-lsp formats via cljfmt, and so does CIDER
(Formatting Code). Pick one to own formatting rather
than wiring both into your save hook, so a buffer isn’t reformatted twice by two
configurations that might disagree.
Diagnostics and refactoring
These don’t overlap: CIDER does no static linting and only a little refactoring,
so leave diagnostics (clj-kondo) and project-wide refactorings like rename to
clojure-lsp. For AST-powered refactoring on top of a REPL, there’s also the
separate clj-refactor.el.
A note on load order
If you use both, enabling cider-mode and your LSP client in the same Clojure
buffers is enough - the precedence knobs above decide who answers what. Because
CIDER’s backends generally only kick in once a REPL is connected, a common and
comfortable setup is to let clojure-lsp drive navigation, completion and docs
while you’re just editing, and let CIDER take over (per the defaults, or your
tuned precedence) once you’ve jacked in.