Editing the ns Form

CIDER can add requires and imports to the ns form of the current buffer, so you don’t have to jump to the top of the file and back whenever you need another namespace, and remove the ones the code doesn’t use.

The edits are plain text manipulation, so adding a require or an import works without a REPL. When one is connected, the prompts complete against it. Adding what a symbol needs and cleaning the ns form are the exceptions, since they ask the REPL.

If you’re coming from clj-refactor, see Coming from clj-refactor for what replaces its commands.

Adding a Require

C-c M-n a (cider-ns-add-require) prompts for a namespace, completing over the namespaces loaded in the REPL, and then for an alias. The alias starts out as the last segment of the namespace’s name. Edit it, or clear it to require the namespace without an alias.

Requiring clojure.string as str turns

(ns my.app
  (:require [clojure.set :as set]))

into

(ns my.app
  (:require [clojure.set :as set]
            [clojure.string :as str]))

The :require clause gets created when there isn’t one, ahead of the :import clause if the ns form has it. When the namespace is already required without an alias, the alias goes into its libspec. CIDER refuses to give the namespace a second alias, or to use an alias another namespace already has.

CIDER doesn’t look into reader conditionals (#?(:clj …​)): a require inside one doesn’t count, and a new require always goes into the top-level :require clause. In a .cljc file that means it applies to both Clojure and ClojureScript, so wrap it in a reader conditional yourself if only one side should see it. The same goes for imports.

Adding an Import

C-c M-n i (cider-ns-add-import) prompts for a fully qualified class name, completing over the classes the REPL knows about. The class joins an existing import list for its package, so importing java.util.Map turns

(ns my.app
  (:import (java.util Date List)))

into

(ns my.app
  (:import (java.util Date List Map)))

Without such a list, the fully qualified name goes into the :import clause, which gets created when it’s missing.

Adding What a Symbol Needs

C-c M-n m (cider-ns-add-missing) takes the symbol at point and asks the REPL where it could come from: the loaded namespaces with a public var of that name, and the classes of that name. With more than one candidate, you pick one. A symbol that already resolves gets nothing, so the command never changes what it means.

What gets added depends on how the symbol is written:

Symbol Added to the ns form

join

[clojure.string :refer [join]]

str/join

[clojure.string :as str]

clojure.string/join

clojure.string

Date or Date.

java.util.Date (an import)

When the namespace is already required, its libspec gets the refer or the alias, so join turns [clojure.string :as str] into [clojure.string :as str :refer [join]]. When that can’t work, CIDER says why and changes nothing: the namespace already has another alias, or another libspec already has the alias or refers the name. A type made by deftype or defrecord gets imported, and the namespace that defines it gets required as well, because the class doesn’t exist until that namespace is loaded.

On an auto-resolved keyword like ::config/aero, it requires the namespace config stands for, going by the same namespaces as magic requires. When there’s more than one, it asks which. A plain keyword like :config/aero needs no require, so there’s nothing to do.

Only namespaces that are loaded in the REPL count, so load the one you’re after first if it isn’t. In ClojureScript buffers the ClojureScript REPL gets asked, and it knows about namespaces but not about JavaScript classes.

This needs cider-nrepl 0.64.0 or newer. With an older one, the command says the op it needs is missing.

Magic Requires

With magic requires on, typing a slash after an alias the ns form doesn’t have yet adds the require it needs. Type str/ in a buffer without a str alias, and [clojure.string :as str] goes into the :require clause, while you carry on typing str/join where you are.

They’re off by default. To turn them on:

(setq cider-ns-magic-requires t)

With t, CIDER adds the namespace right away when it’s the only one the alias could stand for, and asks which when there are several. Set it to prompt to be asked every time. Picking none leaves that alias alone in the buffer from then on. The require is an undo step of its own, so C-/ right after it takes it back without touching what you typed.

The project’s own source files decide what an alias stands for: the namespaces they give it, the most used first, so the codebase’s conventions carry over. For an alias the project doesn’t use yet, CIDER falls back on cider-ns-magic-require-aliases, which has the usual aliases of Clojure’s own namespaces (str, set, io and so on), and on the project namespaces named like the alias, such as acme.billing for billing. An entry of cider-ns-magic-require-aliases can list the platforms its namespace is there on:

(add-to-list 'cider-ns-magic-require-aliases '("json" "cheshire.core" "clj"))

The slash counts only where an alias can go: not in strings, comments or discarded forms, and not after a keyword like :foo/ (though ::foo/ counts), a number, a quoted symbol, a namespace or class name, or a name in a :keys vector. An alias with dots, like utils.strings/, counts too. In ClojureScript, js/ is left alone. In a .cljc file, the namespace has to work for both Clojure and ClojureScript.

Magic requires work in the buffers where cider-mode is on, which is the Clojure buffers of a project you’re connected to. Going by the project’s source files needs cider-nrepl 0.64.0 or newer. With an older one, CIDER uses cider-ns-magic-require-aliases only.

clj-refactor 4.1 and newer leave / to CIDER when cider-ns-magic-requires is on. Older versions bind it to their own magic requires, so CIDER’s don’t run in buffers where clj-refactor-mode is on. To use CIDER’s there, unbind it:
(with-eval-after-load 'clj-refactor
  (define-key clj-refactor-map (kbd "/") nil))

Cleaning the ns Form

C-c M-n c (cider-ns-clean) removes what the ns form requires, refers and imports, but the code doesn’t use. With

(ns my.app
  (:require [clojure.set :as set]
            [clojure.string :as str :refer [join split]])
  (:import (java.util Date List)))

(join ", " [(Date.)])

it leaves

(ns my.app
  (:require [clojure.string :refer [join]])
  (:import (java.util Date)))

The REPL tells what goes unused by reading the code after the ns form, without loading it, so the code doesn’t have to compile, only to read. That makes it cautious: a name counts as used wherever it comes up, a comment form, a quoted form or a local of the same name included. These always count as used:

  • a require with no alias and nothing to refer, like [my.app.specs], as it’s likely there for its side effects;

  • :refer :all and :rename, as telling what they bring in takes loading the namespace;

  • prefix lists, like [clojure [set :as set]], and what’s in a reader conditional.

A namespace that’s still used fully qualified, as in clojure.string/join, or that defines a class the ns form imports, stays required, without the alias or the referred names it doesn’t need. Everything else in the ns form stays the way it’s written, comments included, and cider-ns-edit-actions applies afterwards, as after any other edit.

To clean the ns form whenever you save:

(add-hook 'clojure-mode-hook
          (lambda () (add-hook 'before-save-hook #'cider-ns-clean nil t)))

(With clojure-ts-mode, that’s clojure-ts-mode-hook.) Run from a hook, cider-ns-clean does nothing without a REPL that can tell what’s unused, and code that doesn’t read yet gets a message in the echo area rather than stopping the save. A .cljc file with code that only reads once a platform is picked, like #?@ splicing into a map literal, can’t be cleaned.

This needs cider-nrepl 0.64.0 or newer. With an older one, the command says the op it needs is missing.

After an Edit

cider-ns-edit-actions says what happens after a change to the ns form. By default CIDER sorts the libspecs and, when a REPL is connected, evaluates the ns form, so a new alias works right away.

To keep the libspecs in the order you wrote them, drop the sorting:

(setq cider-ns-edit-actions '(eval))

CIDER never removes anything from the ns form on its own, only when you clean it.

Each change, together with the sorting, is a single undo step.

Coming from clj-refactor

These commands and the magic requires take over from clj-refactor’s, which clj-refactor 4.1 deprecates. They don’t need yasnippet or refactor-nrepl.

clj-refactor CIDER Keybinding

cljr-add-require-to-ns

cider-ns-add-require

C-c M-n a

cljr-add-import-to-ns

cider-ns-add-import

C-c M-n i

cljr-add-missing-libspec

cider-ns-add-missing

C-c M-n m

cljr-slash

cider-ns-magic-requires (an option)

/

cljr-clean-ns

cider-ns-clean

C-c M-n c

Cleaning the ns form never happens on its own, unlike with cljr-auto-clean-ns, though you can run it on save.