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.