Code Completion

CIDER provides intelligent code completion for both source buffers (powered by cider-mode) and REPL buffers.

On the Emacs side the completion is exposed through the standard completion-at-point mechanism, so it requires no setup and works with every completion frontend - the built-in completion UI, as well as packages like corfu and company.

Internally CIDER leverages compliment for Clojure and clj-suitable for ClojureScript.

Improvements to the two libraries automatically translate to improvements in CIDER.

Standard completion

Out-of-the-box CIDER uses the standard Emacs tooling for code completion. When you press TAB or M-TAB you’ll get completion candidates in a dedicated buffer.

Code Completion

There are two things to keep in mind about the standard completion:

  1. The default keybinding M-TAB is not usable in desktop environments that use it for switching between open applications.

  2. You have to configure TAB to do completion manually by adding this snippet your Emacs config:

(setq tab-always-indent 'complete)

Normally TAB only indents, but now it will also do completion if the code is already properly indented.

On Emacs 30+ you can also enable the built-in completion-preview-mode, which shows the top completion candidate inline as you type (accept it with TAB). It’s a lightweight alternative to the popup UIs described below and works out-of-the-box with CIDER’s completion.

Completion in minibuffer prompts

Some commands ask you for a Clojure symbol in the minibuffer (for example cider-doc or cider-find-var when there’s no symbol at point, or when cider-prompt-for-symbol is t). By default these prompts use a plain minibuffer read where TAB completes the symbol against the running REPL.

If you drive completion with a completing-read UI such as Vertico, Ivy or Helm, set cider-use-completing-read-for-symbol to t. The symbol prompts then go through completing-read, so your usual narrowing UI, sorting and annotations apply, and candidates are labelled with their type and namespace:

(setq cider-use-completing-read-for-symbol t)

Candidates are fetched from the runtime lazily as you type, so nothing is transferred up front. Querying only starts once your input reaches cider-completion-symbol-prompt-min-length characters (2 by default), which keeps an empty prompt from asking the runtime for every symbol.

The prompt also feeds eldoc, so the arglist of the symbol you’re typing shows in the echo area. With a vertical completion UI the eldoc line and the minibuffer can compete for the echo area; if you rarely see the arglist, the per-candidate annotations (type and namespace) still give you the essentials, and you can widen the echo area with max-mini-window-height.

Auto-completion

While the standard Emacs tooling works just fine, we suggest that CIDER users consider using corfu or company-mode instead. These display the completion candidates in a popup as you type, in both source code and REPL buffers, with the following advantages:

  • A nicer UI.

  • Integration with Clojure docstrings and Java doc comments.

Both are frontends to the same completion-at-point machinery, so they receive identical candidates from CIDER; pick whichever UI you prefer. Corfu is the more modern of the two - it’s a thin layer over the standard completion API and composes well with the rest of the modern completion ecosystem (vertico, orderless, cape, etc.).

corfu installation

To install corfu:

M-x package-install RET corfu RET

After installation, you can turn it on globally:

(global-corfu-mode)

or through mode-specific hooks:

(add-hook 'cider-repl-mode-hook #'corfu-mode)
(add-hook 'cider-mode-hook #'corfu-mode)

Out of the box corfu only shows its popup when you invoke completion explicitly (e.g. via TAB with tab-always-indent set to complete). For the typical auto-completion experience, where candidates pop up as you type, enable corfu-auto:

(setq corfu-auto t)

To see the documentation of the candidates while completing, enable the corfu-popupinfo extension that ships with corfu:

(corfu-popupinfo-mode)

or press M-h (corfu-info-documentation) on a candidate to display its documentation in a help buffer.

To show a type icon next to each candidate, install the kind-icon package and add its formatter to corfu:

(with-eval-after-load 'corfu
  (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter))

CIDER tags every candidate with a kind (the :company-kind completion property), which kind-icon maps to an icon.

company-mode installation

To install company-mode:

M-x package-install RET company RET

After installation, you can turn on company-mode globally:

(global-company-mode)

or through mode-specific hooks:

(add-hook 'cider-repl-mode-hook #'company-mode)
(add-hook 'cider-mode-hook #'company-mode)

When company-mode is enabled, it will receive completion information from cider-complete-at-point and requires no additional setup or plugins.

If you’d prefer to trigger completions manually you can add this to your config:

(setq company-idle-delay nil) ; never start completions automatically
(global-set-key (kbd "M-TAB") #'company-complete) ; use M-TAB, a.k.a. C-M-i, as manual trigger

To make TAB complete, without losing the ability to manually indent, you can add this to your config:

(global-set-key (kbd "TAB") #'company-indent-or-complete-common)

Company’s documentation mechanism and CIDER’s documentation facilities are integrated.

While a completion is being offered to you, you can hit (F1) (the default company-show-doc-buffer key binding) for displaying documentation and arglists under a temporary cider-doc buffer.

In order for Company to always show docstrings and other metadata under a temporary cider-doc buffer, without needing to hit an extra key, please customize:

;; (You may want to do this as a setq-local within a clojure-mode-hook instead)
(custom-set-variables '(company-auto-update-doc t))

Documentation display configuration

Regardless of the frontend you use, the following CIDER configuration options affect the documentation shown while completing:

  • cider-docstring-max-lines (default 20) controls how many lines, at most, of this docstring will be included (in a popup or the echo area, depending on your setup) while offering completions. It’s worth noting, for Java documentation, CIDER doesn’t simply trim lines, but it looks at the structure and tries to find the largest combination of these that fits into cider-docstring-max-lines:

    • The entire comment body, followed by its "block tags" (Returns/Throws/Params information)

    • The first sentence of the comment, followed by the block tags

    • The block tags

    • The first sentence of the comment.

Rich candidate matching

Starting with version 1.18, CIDER by default enables a custom completion style (named cider) that defers matching to the backend (compliment). The backend matches candidates server-side and returns richer, more useful results than a plain prefix match, for example:

  • Long vars that contain dashes by first characters of individual parts, e.g. mi or mai complete to map-indexed.

  • Namespaces by first characters of parts, e.g. cji completes to clojure.java.io.

  • Not imported classnames by their short name prefixes, e.g. BiFun completes to java.util.function.BiFunction.

You can learn all completion scenarios and features here.

If you only want to receive standard prefix-restricted completions (where the candidate must contain the prefix at the beginning verbatim), you can disable this feature by adding this to your config:

(cider-enable-cider-completion-style -1)

Completion annotations

Completion candidates will be annotated by default with an abbreviation corresponding to their type, and (contextually) their namespace. The function used to format the annotation can be configured by cider-annotate-completion-function. The abbreviations used are configured by cider-completion-annotations-alist and the context in which their namespace is included is configured by cider-completion-annotations-include-ns.

In frontends that support it (the built-in Completions, Corfu and Vertico) the type/namespace information is rendered as an aligned column, via an affixation-function; company shows the same information as a trailing annotation.

Aligned completion annotations in the *Completions* buffer
Completion annotations can be disabled by setting cider-annotate-completion-candidates to nil.

Completion styles

The CIDER completion at point function supports most completion styles, including partial-completion, orderless, flex, and its own custom completion style named cider. The latter is enabled by default. Sometimes the user may want to use a different completion style for the CIDER complete at point function. That can be achieved by setting completion-category-overrides, overwriting the completion style of the CIDER complete at point function. The following snippet accomplishes that:

(add-to-list 'completion-category-overrides '(cider (styles basic)))

For a better description of how those completion styles operate, refer to the official Emacs manual on how completion alternatives are chosen.

This specifies that the cider completion category should employ the basic completion style by default.

For example, to route CIDER’s candidates through orderless:

(add-to-list 'completion-category-overrides '(cider (styles orderless)))

CIDER’s candidates arrive already matched by the backend (see Rich candidate matching): compliment does its own prefix and fuzzy matching server-side and returns a ranked list for what you’ve typed. Client-side styles like orderless and flex then filter that list further - they narrow what the backend already returned and cannot surface candidates the backend didn’t send.

The default cider style does no client-side filtering, so it preserves the backend’s richer matches verbatim. Layering orderless/flex on top is handy for extra narrowing (e.g. `orderless’s space-separated components), but it is not a substitute for the backend’s matching - and an aggressive client style can hide backend matches that don’t look like a match to it (e.g. classname or initialism completions). If in doubt, keep the default.

Notes on class disambiguation

Sometimes, the completion user experience may be interrupted by a completing-read that asks for the Member in class. This is used for better Java completions and documentation.

However, if you are not interested in the current candidate, disambiguating it is of no use, and the prompt can be a nuisance.

If you are using Company for completions and IDO for completing-read, you can cause the <up> and <down> keys to cancel the prompt by customizing:

(advice-add 'cider-class-choice-completing-read
            :around
            (lambda (f a b)
              (cider--with-temporary-ido-keys "<up>" "<down>"
                (funcall f a b))))

Updating stale classes and methods cache

Sometimes, the completion fails to recognize new classes that came with dependencies that were loaded dynamically after the REPL was started (e.g. via Clojure 1.12 add-lib). Executing M-x cider-completion-flush-caches (or going through the menu CIDER Interaction->Misc->Flush completion cache) forces the completion backend to re-read all classes it can find on the classpath.

Embark integration

If you use Embark, you can teach it to act on Clojure symbols with CIDER commands. CIDER doesn’t ship this integration itself - which actions you want and how you bind them is personal, and it’s the kind of thing that belongs in your own config - but here’s a starting point you can drop in and tailor. Run embark-act (C-. in a typical Embark setup) on a Clojure symbol and you get a menu of CIDER actions:

CIDER actions in Embark
(defun my-cider-embark-doc (sym)
  "Show CIDER documentation for SYM."
  (interactive "sClojure symbol: ")
  (cider-doc-lookup sym))

(defun my-cider-embark-find-def (sym)
  "Jump to the definition of SYM."
  (interactive "sClojure symbol: ")
  (cider-find-var nil sym))

(defun my-cider-embark-fn-refs (sym)
  "Find references to SYM via the runtime."
  (interactive "sClojure symbol: ")
  (cider-xref-fn-refs nil sym))

(defun my-cider-embark-inspect (sym)
  "Inspect the value of SYM."
  (interactive "sClojure symbol: ")
  (cider-inspect-expr sym (cider-current-ns)))

(defun my-cider-embark-clojuredocs (sym)
  "Show the ClojureDocs entry for SYM."
  (interactive "sClojure symbol: ")
  (cider-clojuredocs-lookup sym))

(defun my-cider-embark-apropos (sym)
  "Search with apropos for SYM."
  (interactive "sClojure symbol: ")
  (cider-apropos sym))

(defvar my-cider-embark-symbol-map
  (let ((map (make-sparse-keymap)))
    (define-key map "d" #'my-cider-embark-doc)
    (define-key map "." #'my-cider-embark-find-def)
    (define-key map "r" #'my-cider-embark-fn-refs)
    (define-key map "i" #'my-cider-embark-inspect)
    (define-key map "c" #'my-cider-embark-clojuredocs)
    (define-key map "a" #'my-cider-embark-apropos)
    map)
  "Embark actions for a Clojure symbol.")

(defun my-cider-embark-target ()
  "Embark target finder for the Clojure symbol at point."
  (when (derived-mode-p 'clojure-mode 'clojurescript-mode 'clojurec-mode
                        'clojure-ts-mode 'cider-repl-mode)
    (when-let* ((bounds (bounds-of-thing-at-point 'symbol))
                (sym (cider-symbol-at-point)))
      (unless (string-empty-p sym)
        `(cider-clojure-symbol ,sym . ,bounds)))))

(with-eval-after-load 'embark
  (add-to-list 'embark-target-finders #'my-cider-embark-target)
  (add-to-list 'embark-keymap-alist '(cider-clojure-symbol my-cider-embark-symbol-map))
  ;; the `cider' completion category gets the same actions on its candidates
  (add-to-list 'embark-keymap-alist '(cider my-cider-embark-symbol-map)))

The target can be a symbol at point in a source or REPL buffer, or a candidate in a CIDER symbol prompt such as cider-doc or cider-find-var (see Completion in minibuffer prompts) - both resolve to the same keymap, so you can start cider-doc, highlight a candidate, and pivot to jumping to its definition without retyping.

Implementation Details

You don’t really need to know any of this if you’re using only cider-jack-in.

The bulk of the code completion logic resides in cider-nrepl completion middleware. Internally it delegates to compliment for the Clojure completion and clj-suitable for the ClojureScript completion.

nREPL also has a built-in completions op that CIDER will fallback to, in the absence of cider-nrepl. Its API is similar to that of the complete op in cider-nrepl and it can be configured to use different completion functions. The built-in op currently supports only Clojure. See the nREPL docs for more details.

Basically, you’ll get great code completion in the presence of cider-nrepl and basic completion otherwise.