Up and Running
To use CIDER, you’ll need to connect it to a running nREPL server that is associated with your program. Most Clojure developers use standard build tooling such as Leiningen, Boot, or Gradle, and CIDER can automatically work with those tools to get you up and running quickly. But those tools are not required; CIDER can connect to an nREPL server that is already started and is managed separately.
CIDER will automatically work with Leiningen 2.9.0+ or Boot 2.8.3+. Older versions are not supported. |
There are two ways to connect CIDER to an nREPL server:
-
CIDER can launch an nREPL server for your project from Emacs.
-
You can connect CIDER to an already-running nREPL server, managed separately.
The following sections describe each of these methods.
Launch an nREPL Server From Emacs
If you have a Clojure project in your file system and want CIDER to
launch an nREPL session for it, simply visit a file that belongs to
the project, and type M-x cider-jack-in
RET.[1]
CIDER will start an nREPL server and automatically connect to it.
In Clojure(Script) buffers the command cider-jack-in is bound to C-c C-x (C-)j (C-)j.
|
The process of jacking-in is pretty simple:
-
CIDER determines the build system for your project (e.g. Leiningen) and picks the necessary command to start an nREPL server.
-
CIDER shells out and runs a command like
lein repl :headless
to start an nREPL server. -
CIDER waits for the nREPL server to start. CIDER figures out this by parsing the output from the command and waiting for a line like
nREPL server started on port 53005 on host localhost - nrepl://localhost:53005
to appear there. -
CIDER extracts the port of the nREPL from the preceding message.
-
It connects to the running nREPL server.
You can see the exact command that cider-jack-in invoked in your minibuffer, while
waiting for nREPL to start. You can also find this command in Emacs’s *Messages* buffer.
|
In some cases one project might have multiple project markers in it - e.g. project.clj
and deps.edn
.
When this happens CIDER will prompt you to select the build tool to use. You can override this behavior
by setting the variable cider-preferred-build-tool
. While you can set it globally in your Emacs config,
most of the time you’d probably want to have a project-specific setting for it in your .dir-locals.el
:
((clojure-mode
(cider-preferred-build-tool . lein)))
Auto-Injecting Dependencies
While CIDER’s core functionality requires nothing more than an nREPL server,
there are many advanced features that depend on the presence of additional
nREPL middleware. In the early versions of CIDER (up to CIDER 0.11) users had
to add those dependencies themselves, which was a painful and error-prone process.
Fortunately today that’s handled auto-magically when you’re using cider-jack-in
.
If your project uses lein
, boot
or tools.deps
(deps.edn
), CIDER will
automatically inject all the necessary nREPL dependencies (e.g. cider-nrepl
or
piggieback
) when it starts the server. The injection process is extremely
simple - CIDER simply passes the extra dependencies and nREPL configuration to
your build tool in the command it runs to start the nREPL server. Here’s how
this looks for tools.deps
:
$ clojure -Sdeps '{:deps {nrepl {:mvn/version "0.6.0"} cider/cider-nrepl {:mvn/version "0.41.0"}}}' -m nrepl.cmdline --middleware '["cider.nrepl/cider-middleware"]'
If you don’t want cider-jack-in to inject dependencies automatically, set
cider-inject-dependencies-at-jack-in to nil . Note that you’ll have to setup
the dependencies yourself (see nREPL Middleware Setup),
just as in CIDER 0.10 and older.
|
Normally cider-jack-in
would inject only cider-nrepl
and cider-jack-in-cljs
would
add piggieback
as well. The injection mechanism is configurable and
you can easily add more libraries there. Some CIDER extensions would use
this mechanism to auto-inject their own dependencies.
Here’s how you can modify the injected dependencies for cider-jack-in-clj
:
;; auto-inject version 1.0 of the library foo/bar
(cider-add-to-alist 'cider-jack-in-dependencies
"foo/bar" "1.0")
;; if you want to have full control over the coordinate description set it as an alist
;; auto-inject {:git/sha "6ae2b6f71773de7549d7f22759e8b09fec27f0d9" for library org.clojure/tools.deps
;; :git/url "https://github.com/clojure/tools.deps/"}
(cider-add-to-alist 'cider-jack-in-dependencies
"org.clojure/tools.deps"
'(("git/sha" . "6ae2b6f71773de7549d7f22759e8b09fec27f0d9")
("git/url" . "https://github.com/clojure/tools.deps/")))
Always use the fully qualified group/artifact (e.g. re-frame/re-frame ) in these dependencies, since only Leiningen supports the bare re-frame syntax.
|
CIDER will also inject the most recent version of nREPL that it supports. This is a simple trick to override the version of nREPL bundled with your build tool (e.g. Leiningen), so you can gain access to the newest nREPL features. Generally that’s one aspect of CIDER’s inner workings that end-users will rarely have to think about.
You can override the injected versions of cider-nrepl
and nREPL by customizing
cider-injected-middleware-version
and cider-injected-nrepl-version
.
Generally you should avoid doing this, but it may be useful if you want to try
a newer version or you encounter some regression that forces you to temporarily use
an older version.
CIDER can also inject a Clojure dependency into your project, which is useful,
for example, if your project defaults to an older version of Clojure than that
supported by the CIDER middleware. Set cider-jack-in-auto-inject-clojure
appropriately to enable this.
Jacking-in without a Project
If you try to run cider-jack-in
outside a project
directory, CIDER will warn you and ask you to confirm whether you
really want to do this; more often than not, this is an accident. If
you decide to proceed, CIDER will invoke the command configured in
cider-jack-in-default
. Prior to CIDER 0.17, this defaulted to lein
but was subsequently switched to clj
, Clojure’s basic startup command.
You can set cider-allow-jack-in-without-project to t if you’d like to
disable the warning displayed when jacking-in outside a project.
|
Universal jack-in
cider-jack-in-universal
C-c C-x j u is another way to quickly
jack in without a project choosing from a list of pre-configured
Clojure build tools. When this command is called from outside of a
project, the user is given the option to select to jack in with one of
the pre-configured tools, as well as to confirm the root directory to
use as a base. If the command is called from within a project
directory, it behaves exactly the same as cider-jack-in
does.
It utilizes Emacs’s
numeric
prefix arguments to quickly jack in with a specific build tool. Numeric prefix
arguments can be set with the Meta
key followed by a number.
The following Clojure build tools are supported so far
-
M-1 C-c C-x j u jack-in using clojure-cli.
-
M-2 C-c C-x j u jack-in using leiningen.
-
M-3 C-c C-x j u jack-in using babashka.
-
M-4 C-c C-x j u jack-in using nbb.
Here is an example of how to bind F12 for quickly bringing up a babashka REPL:
(global-set-key (kbd "<f12>") (lambda ()
(interactive)
(cider-jack-in-universal 3)))
The list of available build tools to consider is configured in
cider-jack-in-universal-options
. Each element of the list consists
of the tool name and its setup options. Taking nbb
as an example
from the list:
(nbb (:prefix-arg 4 :cmd (:jack-in-type cljs :project-type nbb :cljs-repl-type nbb :edit-project-dir t)))
with
-
:prefix-arg
assigns thenbb
tool name a numerical argument prefix of 4. -
:cmd
how to invoke the command.-
:jack-in-type
use acljs
repl. -
:project-type
usenbb
(seejack-in-command
) to bring up the nREPL server. -
:cljs-repl-type
client uses thenbb
cljs repl type (seecider-cljs-repl-types
) to initialize server. -
:edit-project-dir
ask the user to confirm root directory to use as base.
-
Customizing the Jack-in Command Behavior
You can use C-u M-x cider-jack-in
RET to
specify the exact command that cider-jack-in
would run.
This option is very useful is you want to specify a something like a lein
or deps.edn
profile.
Alternatively you can C-u C-u M-x cider-jack-in
RET, which is a
variation of the previous command. This command will first prompt you for the
project you want to launch cider-jack-in
in, which is pretty handy if you’re
in some other directory currently. This option is also useful if your project
contains some combination of project.clj, build.boot and deps.edn and you want
to launch a REPL for one or the other.
The examples use only cider-jack-in , but this behavior is consistent
for all cider-jack-in-\* commands.
|
You can further customize the command line CIDER uses for cider-jack-in
by
modifying the some options. Those differ a bit between the various tools,
so we’ll examine them tool by tool.
Leiningen Options
-
cider-lein-command
- the name of the Leiningen executable (lein
by default) -
cider-lein-parameters
- the command-line params to start a REPL (e.g.repl :headless
or -o to enable offline mode)
Clojure CLI Options
-
cider-clojure-cli-command
- the name of theclojure
executable (clojure
by default) -
cider-clojure-cli-parameters
- the command-line parameters to start a REPL -
cider-clojure-cli-aliases
- a list of aliases to be used at jack-in time
To use cider-jack-in
with tools.deps
on Windows set the
cider-clojure-cli-command
to "powershell"
. This happens by default
if you are on Windows and no clojure
executable is found. Using
"powershell"
will Base64 encode the clojure launch command before
passing it to PowerShell and avoids shell-escaping issues.
Alternatively you can use WSL (e.g. to run nREPL and Emacs there), which will likely result in a better overall development experience. |
Boot Options
-
cider-boot-command
- the name of the Boot executable (boot
by default) -
cider-boot-parameters
- these are usually task names and their parameters (e.g.,dev
for launching boot’s dev task instead of the standardrepl -s wait
)
Override the Jack-In Command
Which Jack-In Command is used is based on the project type. You can override the Jack-In Command either project-wide or as an argument in Lisp. This allows for fine-grained control over how cider starts the nrepl-server.
The precedence order for determining the Jack-In Command is:
1) :jack-in-cmd if provided as a parameter,
2) cider-jack-in-command
if set as a directory local variable, and
3) inferred from the project type (the default).
Setting a project-wide command
You can set a local variable cider-jack-in-command
to override the jack-in command.
((nil
(cider-jack-in-cmd . "nbb nrepl-server")))
Passing the Command Programmatically as a Parameter
You can provide an override Jack-In command as an argument to cider-jack-in
.
Here is an example Nbb Jack-In command, providing a custom :jack-in-cmd
.
(defun cider-jack-in-nbb-2 ()
"Start a Cider nREPL server with the 'nbb nrepl-server' command."
(interactive)
(cider-jack-in-clj '(:jack-in-cmd "nbb nrepl-server")))
Connect to a Running nREPL Server
If you have an nREPL server already running, CIDER can connect to it. For instance, if you have a Leiningen-based project, go to your project’s directory in a terminal session and type:
$ lein repl :headless
This will start the project’s nREPL server.
If your project uses boot
, do this instead:
$ boot repl -s wait (or whatever task launches a repl)
It is also possible for plain clj
, although the command is somewhat longer:
$ clj -Sdeps '{:deps {cider/cider-nrepl {:mvn/version "0.41.0"}}}' -m nrepl.cmdline --middleware "[cider.nrepl/cider-middleware]"
Alternatively, you can start nREPL either manually or using the facilities provided by your project’s build tool (Gradle, Maven, etc).
After you get your nREPL server running, go back to Emacs and connect
to it: M-x cider-connect
RET. CIDER will
prompt you for the host and port information, which should have been
printed when the previous commands started the nREPL server in your
project.
In Clojure(Script) buffers the command cider-connect is bound to C-c C-x c s.
|
If you frequently connect to the same hosts and ports, you can tell
CIDER about them and it will use the information to do completing
reads for the host and port prompts when you invoke
cider-connect
. You can identify each host with an optional label.
(setq cider-known-endpoints
'(("host-a" "10.10.10.1" "7888")
("host-b" "7888")))
Working with Remote Hosts
While most of the time you’d be connecting to a locally running nREPL
server, that was started manually or via cider-jack-in-\*
, there’s
also the option to connect to remote nREPL hosts. For the sake of security
CIDER has the ability to tunnel a connection over SSH in such cases.
This behavior is controlled by
nrepl-use-ssh-fallback-for-remote-hosts
: when true, CIDER will attempt to
connect via ssh to remote hosts when unable to connect directly. It’s
nil
by default.
There’s also nrepl-force-ssh-for-remote-hosts
which will force the use
of ssh for remote connection unconditionally.
As nREPL connections are insecure by default you’re encouraged to use only SSH tunneling when connecting to servers running outside of your network. |
There’s a another case in which CIDER may optionally leverage the ssh
command - when
trying to figure out potential target hosts and ports when you’re doing cider-connect-\*
.
If cider-infer-remote-nrepl-ports
is true, CIDER will use ssh to try to infer
nREPL ports on remote hosts (for a direct connection). That option is also set to nil
by default.
Enabling either of these causes CIDER to use
TRAMP for some SSH operations, which parses
config files such as ~/.ssh/config and ~/.ssh/known_hosts . This is known to
cause problems with complex or nonstandard ssh configs.
|
You can run cider-jack-in-*
while working with remote files over TRAMP. CIDER
will reuse existing SSH connection’s parameters (like port and username) for establishing a SSH tunnel.
The same will happen if you try to cider-connect-\*
to a host that matches the one you’re currently
connected to.
For Docker containers, running cider-jack-in-\* over TRAMP may technically work but it may give mixed results.
Please check out the following section for the recommended approaches.
|
Working with Containers (Docker or others)
By 'containers' we mean Docker containers, or similar technologies, for running the JVM that will host our nREPL server. The files which we edit may / may not be edited using TRAMP. They could as well be mounted inside the container, so they appear as local.
Because CIDER can’t always detect if it’s dealing with remote files, it’s advisable to not rely on cider-jack-in
and its remote support described above, but to
start the nREPL server via command line from inside the container, and cider-connect
to it.
This requires to first get a shell inside the running container and then start a nREPL server manually, or configure the container to start an nREPL automatically when the container starts.
In order to connect Emacs to nREPL, we need to make sure that the port of nREPL is reachable to our local Emacs,
so that we can cider-connect
to it.
There are several solutions for this depending on the concrete scenario.
Working with Containers running on localhost
The nREPL port should be set to a fixed value as we need to give this during the docker start
command in order to forward the port from
container to host. This requires as well that nREPL server listens to "0.0.0.0" and not only to "localhost".
Working with Containers running on remote hosts
In case we have the container running on a remote machine, we need to do the same setup as above and additionally use ssh
to forward the already-forwarded port again to our local machine.
This can be done using a command such as "ssh -L 12345:localhost:12345 remote-server", assuming that 12345 was the nREPL port exposed by the container.
Working with devcontainers locally or remotely
Development Containers is a standard to describe container-based development environments. It includes a CLI. It uses Docker/Podman behind the scenes. So the principles of making sure that the nREPL port becomes available stay the same, but there are slightly different ways to configure this (given by the devcontainer standard).
There are several CLI tools to manage devcontainers, as there are several container technologies. The following example uses the devcontainers cli, but there are others (devpod, gitpod…).
Example: Working with containers (using devcontainer
) on a remote server
In this scenario and assuming a folder /home/me/my-clj-code
containing the relevant devcontainer
related config files
(devcontainer.json) we can first start remotely a devcontainer via:
# executed on MY_REMOTE_SERVER
devcontainer up --workspace-folder /home/me/my-clj-code
Then we can start a nREPL server inside the container on the remote host as shown below (executed from from your local machine). The command tunnels as well the remote port 12345 to local machine on port 12345:
ssh -t -L 12345:localhost:12345 MY_REMOTE_SERVER \
devcontainer exec --workspace-folder /home/me/my-clj-code \
"clojure -Sdeps '{:deps {nrepl/nrepl {:mvn/version \"0.8.3\"} cider/cider-nrepl {:mvn/version \"0.25.5\"}}}' -m nrepl.cmdline -p 12345 -b 0.0.0.0 --middleware '[\"cider.nrepl/cider-middleware\"]' "
For this to work, we need as well to configure devcontainer.json
with a snippet that exposes port 12345
from the container to the (remote) host:
"appPort": [
// will make container port 12345 available as 12345 on remote host
// (which will be further tunneled to 12345 on local machine)
"12345:12345"
],
This results then in having port 12345 available locally and we can cider-connect
to it, using localhost:12345
.
Editing of the files can then happen via TRAMP. As the files are "on the remote machine"
and also mounted inside the container on the remote machine, we have two possible TRAMP file syntaxes to edit them - either of:
-
/ssh:MY_REMOTE_SERVER:/home/me/my-clj-code/…
-
/ssh:MY_REMOTE_SERVER|docker:DOCKER_CONTAINER_ID:/workspaces/my-clj-code/…
Connecting via unix domain file socket
Unix socket support was introduced in nREPL 0.9. Currently CIDER’s support for Unix sockets is considered experimental and its interface might change in future CIDER releases. |
When locally running nREPL servers, there is the option to listen on a socket file instead of opening a network port. As long as access to the parent directory of the socket is sufficiently protected, this is much more secure than the network port, since any local user can access the port-provided REPL. It can also be be helpful in other cases, e.g. when working with virtual networks (containers) where sharing a file socket can be vastly simpler than managing bridge networks and firewall setups.
After having started an nREPL server on a file socket, e.g. with the
clj
command (see https://nrepl.org/nrepl/usage/server.html for other
examples),
$ clj -R:nREPL -m nrepl.cmdline --socket nrepl.sock
you can then connect CIDER by using the local-unix-domain-socket
special hostname with cider-connect
: M-x cider-connect
RET local-unix-domain-socket
RET nrepl.sock
RET.
At the moment only with leiningen
, commands like cider-jack-in
will detect and use the unix domain socket if one is requested via the
:socket
argument. This can be arranged by specifying a prefix
argument to cider-jack-in
, e.g. C-u M-x cider-jack-in
,
or by adjusting cider-lein-parameters
.
What’s Next?
So, what to do next now that CIDER’s ready for action? Here are a few ideas:
-
Get familiar with interactive programming and cider-mode
-
Configure CIDER to your liking
-
Explore the additional packages that can make you more productive