diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4f7f2a569..4220414cb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@
* [#903](https://github.com/clojure-emacs/cider/pull/903): Isolate
`nrepl-client` connection logic from CIDER. New hooks `cider-connected-hook`
and `cider-disconnected-hook`.
+* [#920](https://github.com/clojure-emacs/cider/issues/920): Support `cider-jack-in` for boot-based projects.
### Changes
diff --git a/README.md b/README.md
index ae325a4c2..7e1033af8 100644
--- a/README.md
+++ b/README.md
@@ -531,22 +531,24 @@ enable `paredit` in the REPL buffer as well:
## Basic Usage
The only requirement to use CIDER is to have a nREPL server to
-which it may connect. Many Clojurians favour the use of the Leiningen tool
-to start a nREPL server, but the use of Leiningen is not a prerequisite to use
-CIDER (but it's required if you want to use the `cider-jack-in` command).
+which it may connect. Many Clojurians favour the use of the Leiningen or Boot tools
+to start an nREPL server, but the use of Leiningen or Boot is not a prerequisite to use
+CIDER (but it *is* required if you want to use the `cider-jack-in` command).
-### Setting up a Leiningen project (optional)
+### Setting up a Leiningen or Boot project (optional)
-[Leiningen](http://leiningen.org/) is the de facto standard
-build/project management tool for Clojure. It has a similar scope to
-the Maven build tool favoured by Java developers (Leiningen actually
-reuses many things from the Maven ecosystem).
+[Leiningen](http://leiningen.org/) is the de facto standard build/project
+management tool for Clojure. [Boot](http://boot-clj.com/) is a newer build tool
+offering abstractions and libraries to construct more complex build
+scenarios. Both have a similar scope to the Maven build tool favoured by Java
+developers (and they actually reuse many things from the Maven ecosystem).
CIDER features a command called `cider-jack-in` that will start an nREPL server
-for a particular Leiningen project and connect to it automatically. This
-functionality depends on Leiningen 2.x (preferably 2.5+). Older versions are not
-supported. Follow the installation instructions on Leiningen's web site to get
-it up and running and afterwards create a project like this:
+for a particular Leiningen or Boot project and connect to it automatically.
+This functionality depends on Leiningen 2.x (preferably 2.5+) or Boot
+2.0.0+. Older versions are not supported. For Leiningen, follow the installation
+instructions on its web site to get it up and running and afterwards create a
+project like this:
```
$ lein new demo
@@ -556,12 +558,12 @@ The two main ways to obtain an nREPL connection are discussed in the following s
### Launch a nREPL server and client from Emacs
-Simply open in Emacs a file belonging to your `lein` project (like
+Simply open in Emacs a file belonging to your `lein` or `boot` project (like
`foo.clj`) and type M-x cider-jack-in. This will start a nREPL with
all the deps loaded in, plus an `CIDER` client connected to it.
Alternative you can use C-u M-x cider-jack-in to specify the name of
-a lein project, without having to visit any file in it.
+a `lein` or `boot` project, without having to visit any file in it.
### Connect to a running nREPL server
@@ -572,6 +574,12 @@ You can go to your project's dir in a terminal and type there
$ lein repl
```
+Or with Boot:
+
+```
+$ boot repl wait
+```
+
Alternatively you can start nREPL either manually or by the facilities provided by your
project build tool (Maven, etc).
diff --git a/cider.el b/cider.el
index 6c04c6e2d..6a0bfadb7 100644
--- a/cider.el
+++ b/cider.el
@@ -84,6 +84,20 @@ version from the CIDER package or library.")
:type 'string
:group 'cider)
+(defcustom cider-boot-command
+ "boot"
+ "The command used to execute Boot."
+ :type 'string
+ :group 'cider
+ :package-version '(cider . "0.9.0"))
+
+(defcustom cider-boot-parameters
+ "repl -s wait"
+ "Params passed to boot to start an nREPL server via `cider-jack-in'."
+ :type 'string
+ :group 'cider
+ :package-version '(cider . "0.9.0"))
+
(defcustom cider-known-endpoints nil
"Specify a list of custom endpoints where each endpoint is a list.
For example: '((\"label\" \"host\" \"port\")).
@@ -122,6 +136,21 @@ Sub-match 1 must be the project path.")
(interactive)
(message "CIDER %s" (cider--version)))
+(defun cider-command-present-p (project-type)
+ (pcase project-type
+ ("lein" 'cider--lein-present-p)
+ ("boot" 'cider--boot-present-p)))
+
+(defun cider-jack-in-command (project-type)
+ (pcase project-type
+ ("lein" cider-lein-command)
+ ("boot" cider-boot-command)))
+
+(defun cider-jack-in-params (project-type)
+ (pcase project-type
+ ("lein" cider-lein-parameters)
+ ("boot" cider-boot-parameters)))
+
;;;###autoload
(defun cider-jack-in (&optional prompt-project)
"Start a nREPL server for the current project and connect to it.
@@ -129,22 +158,23 @@ If PROMPT-PROJECT is t, then prompt for the project for which to
start the server."
(interactive "P")
(setq cider-current-clojure-buffer (current-buffer))
- (if (cider--lein-present-p)
- (let* ((nrepl-create-client-buffer-function #'cider-repl-create)
- (project (when prompt-project
- (read-directory-name "Project: ")))
- (project-dir (nrepl-project-directory-for
- (or project (nrepl-current-dir))))
- (lein-params (if prompt-project
- (read-string (format "nREPL server command: %s "
- cider-lein-command)
- cider-lein-parameters)
- cider-lein-parameters))
- (cmd (format "%s %s" cider-lein-command lein-params)))
- (when (nrepl-check-for-repl-buffer nil project-dir)
- (nrepl-start-server-process project-dir cmd)))
- (message "The %s executable (specified by `cider-lein-command') isn't on your exec-path"
- cider-lein-command)))
+ (let ((project-type (cider-project-type)))
+ (if (funcall (cider-command-present-p project-type))
+ (let* ((nrepl-create-client-buffer-function #'cider-repl-create)
+ (project (when prompt-project
+ (read-directory-name "Project: ")))
+ (project-dir (nrepl-project-directory-for
+ (or project (nrepl-current-dir))))
+ (params (if prompt-project
+ (read-string (format "nREPL server command: %s "
+ (cider-jack-in-params project-type))
+ (cider-jack-in-params project-type))
+ (cider-jack-in-params project-type)))
+ (cmd (format "%s %s" (cider-jack-in-command project-type) params)))
+ (when (nrepl-check-for-repl-buffer nil project-dir)
+ (nrepl-start-server-process project-dir cmd)))
+ (message "The %s executable (specified by `cider-lein-command' or `cider-boot-command') isn't on your exec-path"
+ (cider-jack-in-command project-type)))))
;;;###autoload
(defun cider-connect (host port)
@@ -239,6 +269,18 @@ use `cider-ps-running-nrepls-command' and `cider-ps-running-nrepl-path-regexp-li
(setq paths (cons (match-string 1) paths)))))
(-distinct paths)))
+(defun cider-project-type ()
+ "Determine the type, either leiningen or boot, of the current project.
+If both project file types are present, prompt the user to choose."
+ (let* ((default-directory (nrepl-project-directory-for (nrepl-current-dir)))
+ (lein-project-exists (file-exists-p "project.clj"))
+ (boot-project-exists (file-exists-p "build.boot")))
+ (cond ((and lein-project-exists boot-project-exists)
+ (completing-read "Which command should be used? " '("lein" "boot") nil
+ t "lein"))
+ (lein-project-exists "lein")
+ (boot-project-exists "boot"))))
+
;; TODO: Implement a check for `cider-lein-command' over tramp
(defun cider--lein-present-p ()
"Check if `cider-lein-command' is on the `exec-path'.
@@ -248,6 +290,14 @@ In case `default-directory' is non-local we assume the command is available."
(executable-find cider-lein-command)
(executable-find (concat cider-lein-command ".bat"))))
+(defun cider--boot-present-p ()
+ "Check if `cider-boot-command' is on the `exec-path'.
+
+In case `default-directory' is non-local we assume the command is available."
+ (or (file-remote-p default-directory)
+ (executable-find cider-boot-command)
+ (executable-find (concat cider-boot-command ".exe"))))
+
(defun cider--connected-handler ()
"Handle cider initialization after nREPL connection has been established.
This function is appended to `nrepl-connected-hook' in the client process
diff --git a/nrepl-client.el b/nrepl-client.el
index 09afb2b6b..fe2a6a2c2 100644
--- a/nrepl-client.el
+++ b/nrepl-client.el
@@ -264,7 +264,8 @@ Bind the value of the provided KEYS and execute BODY."
(defun nrepl-project-directory-for (dir-name)
"Return the project directory for the specified DIR-NAME."
(when dir-name
- (locate-dominating-file dir-name "project.clj")))
+ (or (locate-dominating-file dir-name "project.clj")
+ (locate-dominating-file dir-name "build.boot"))))
(defun nrepl-check-for-repl-buffer (endpoint project-directory)
"Check whether a matching connection buffer already exists.