Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* [#2241](https://github.com/clojure-emacs/cider/pull/2241): Make `cider-test-ediff` diff eval'ed values.
* Add support for shadow-cljs to `cider-jack-in`.
* [#2244](https://github.com/clojure-emacs/cider/issues/2244): Display the REPL type in the modeline.
* [#2238](https://github.com/clojure-emacs/cider/pull/2238): Allow specifying predicates for entries in `cider-jack-in-lein-plugins` and `cider-jack-in-nrepl-middlewares`.

### Bugs Fixed

Expand Down
68 changes: 60 additions & 8 deletions cider.el
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,10 @@ version from the CIDER package or library.")
:package-version '(cider . "0.17.0"))

(defcustom cider-clojure-cli-parameters
"-e '(require (quote cider-nrepl.main)) (cider-nrepl.main/init [\"cider.nrepl/cider-middleware\"])'"
"Params passed to clojure to start an nREPL server via `cider-jack-in'."
"-e '(require (quote cider-nrepl.main)) (cider-nrepl.main/init %s)'"
"Params passed to clojure to start an nREPL server via `cider-jack-in'.
This is evaluated using `format', with the first argument being the Clojure
vector of middleware variables as a string."
:type 'string
:group 'cider
:safe #'stringp
Expand Down Expand Up @@ -346,7 +348,14 @@ Throws an error if PROJECT-TYPE is unknown. Known types are
(pcase project-type
("lein" cider-lein-parameters)
("boot" cider-boot-parameters)
("clojure-cli" cider-clojure-cli-parameters)
("clojure-cli" (format cider-clojure-cli-parameters
(concat
"["
(mapconcat
(apply-partially #'format "\"%s\"")
(cider-jack-in-normalized-nrepl-middlewares)
", ")
"]")))
("shadow-cljs" cider-shadow-cljs-parameters)
("gradle" cider-gradle-parameters)
(_ (user-error "Unsupported project type `%s'" project-type))))
Expand Down Expand Up @@ -385,17 +394,60 @@ specifying the artifact ID, and the second element the version number."
(string :tag "Version"))))

(defvar cider-jack-in-lein-plugins nil
"List of Leiningen plugins where elements are lists of artifact name and version.")
"List of Leiningen plugins to be injected at jack-in.
Each element is a list of artifact name and version, followed optionally by
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm reasonably certain without some examples most people would be really confused what are they supposed to put here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bbatsov Do you mean in terms of the format of the keyword arguments, or why you would want to use a predicate?

If the latter, I'm not sure that this is really meant to be an end-user feature. The only current application is in clj-refactor.el, after all. But I would not at all be opposed to adding examples.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. That makes sense, but it's exactly the kind of info that has to be documented. First I thought this might be something users would want to use in general or something.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the right solution would be just add some logic that removes those plugins from the deps if they are present there and add cider-nrepl to the list of default deps where it belong. Then we stop using this variable for non lein projects.

keyword arguments. The only keyword argument currently accepted is
`:predicate', which should be given a function that takes the list (name,
version, and keyword arguments) and returns non-nil to indicate that the
plugin should actually be injected. (This is useful primarily for packages
that extend CIDER, not for users. For example, a refactoring package might
want to inject some middleware only when within a project context.)")
(put 'cider-jack-in-lein-plugins 'risky-local-variable t)
(cider-add-to-alist 'cider-jack-in-lein-plugins
"cider/cider-nrepl" (upcase cider-version))

(defun cider-jack-in-normalized-lein-plugins ()
"Return a normalized list of Leiningen plugins to be injected.
See `cider-jack-in-lein-plugins' for the format, except that the list
returned by this function does not include keyword arguments."
(thread-last cider-jack-in-lein-plugins
(seq-filter
(lambda (spec)
(if-let* ((pred (plist-get (seq-drop spec 2) :predicate)))
(funcall pred spec)
t)))
(mapcar
(lambda (spec)
(seq-take spec 2)))))

(defvar cider-jack-in-nrepl-middlewares nil
"List of Clojure variable names.
Each of these Clojure variables should hold a vector of nREPL middlewares.")
Each of these Clojure variables should hold a vector of nREPL middlewares.
Instead of a string, an element can be a list containing a string followed
by optional keyword arguments. The only keyword argument currently
accepted is `:predicate', which should be given a function that takes the
list (string and keyword arguments) and returns non-nil to indicate that
the middlewares should actually be injected.")
(put 'cider-jack-in-nrepl-middlewares 'risky-local-variable t)
(add-to-list 'cider-jack-in-nrepl-middlewares "cider.nrepl/cider-middleware")

(defun cider-jack-in-normalized-nrepl-middlewares ()
"Return a normalized list of middleware variable names.
See `cider-jack-in-nrepl-middlewares' for the format, except that the list
returned by this function only contains strings."
(thread-last cider-jack-in-nrepl-middlewares
(seq-filter
(lambda (spec)
(or (not (listp spec))
(if-let* ((pred (plist-get (cdr spec) :predicate)))
(funcall pred spec)
t))))
(mapcar
(lambda (spec)
(if (listp spec)
(car spec)
spec)))))

(defun cider--list-as-boot-artifact (list)
"Return a boot artifact string described by the elements of LIST.
LIST should have the form (ARTIFACT-NAME ARTIFACT-VERSION). The returned
Expand Down Expand Up @@ -523,14 +575,14 @@ dependencies."
(cider-add-clojure-dependencies-maybe
cider-jack-in-dependencies)
cider-jack-in-dependencies-exclusions
cider-jack-in-lein-plugins))
(cider-jack-in-normalized-lein-plugins)))
("boot" (cider-boot-jack-in-dependencies
global-opts
params
(cider-add-clojure-dependencies-maybe
cider-jack-in-dependencies)
cider-jack-in-lein-plugins
cider-jack-in-nrepl-middlewares))
(cider-jack-in-normalized-lein-plugins)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is some odd legacy, but why are we passing lein plugins to boot? //cc @benedekfazekas

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because they are implemented as such (both cider-nrepl and refactor-nrepl). boot either has machinery to handle them or just handles them as ordinary deps.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I know why it was done. :-) I'm just saying it looks really strange to be passing something named lein-plugins to boot. For something like tools.deps and shadow-cljs the middleware packages would be regular deps.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right. unfortunately lein needs them separately. this is unfortunately the smallest denominator in terms of interface. have not checked the details of this PR yet but I am sure this can be improved.

(cider-jack-in-normalized-nrepl-middlewares)))
("clojure-cli" (cider-clojure-cli-jack-in-dependencies
global-opts
params
Expand Down
57 changes: 56 additions & 1 deletion test/cider-tests.el
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,62 @@
:to-equal "-C -o -i \"(require 'cider.tasks)\" -d org.clojure/tools.nrepl\\:0.2.12 -d cider/cider-nrepl\\:0.11.0 cider.tasks/add-middleware -m cider.nrepl/cider-middleware repl -s wait"))
(it "can concat in a gradle project"
(expect (cider-inject-jack-in-dependencies "-m" "--no-daemon clojureRepl" "gradle")
:to-equal "-m --no-daemon clojureRepl"))))
:to-equal "-m --no-daemon clojureRepl")))

(describe "when there are predicates"
:var (plugins-predicate middlewares-predicate)

(before-each
(fset 'plugins-predicate (lambda (&rest _) t))
(fset 'middlewares-predicate (lambda (&rest _) t))
(setq-local cider-jack-in-lein-plugins '(("refactor-nrepl" "2.0.0" :predicate plugins-predicate) ("cider/cider-nrepl" "0.11.0")))
(setq-local cider-jack-in-nrepl-middlewares '(("refactor-nrepl.middleware/wrap-refactor" :predicate middlewares-predicate) "cider.nrepl/cider-middleware" ("another/middleware"))))
(it "includes plugins whose predicates return true"
(expect (cider-jack-in-normalized-lein-plugins)
:to-equal '(("refactor-nrepl" "2.0.0") ("cider/cider-nrepl" "0.11.0"))))
(it "includes middlewares whose predicates return true"
(expect (cider-jack-in-normalized-nrepl-middlewares)
:to-equal '("refactor-nrepl.middleware/wrap-refactor" "cider.nrepl/cider-middleware" "another/middleware")))
(it "ignores plugins whose predicates return false"
(spy-on 'plugins-predicate :and-return-value nil)
(expect (cider-jack-in-normalized-lein-plugins)
:to-equal '(("cider/cider-nrepl" "0.11.0"))))
(it "ignores plugins whose predicates return false"
(spy-on 'middlewares-predicate :and-return-value nil)
(expect (cider-jack-in-normalized-nrepl-middlewares)
:to-equal '("cider.nrepl/cider-middleware" "another/middleware")))
(it "calls plugin predicates with the whole list entry"
(spy-on 'plugins-predicate)
(cider-jack-in-normalized-lein-plugins)
(expect 'plugins-predicate
:to-have-been-called-with '("refactor-nrepl" "2.0.0" :predicate plugins-predicate)))
(it "calls middleware predicates with the whole list entry"
(spy-on 'middlewares-predicate)
(cider-jack-in-normalized-nrepl-middlewares)
(expect 'middlewares-predicate
:to-have-been-called-with '("refactor-nrepl.middleware/wrap-refactor" :predicate middlewares-predicate)))
(it "only calls plugin predicates for their own entries"
(spy-on 'plugins-predicate)
(cider-jack-in-normalized-lein-plugins)
(expect 'plugins-predicate :to-have-been-called-times 1))
(it "only calls middleware predicates for their own entries"
(spy-on 'middlewares-predicate)
(cider-jack-in-normalized-nrepl-middlewares)
(expect 'middlewares-predicate :to-have-been-called-times 1)))

(describe "when the middleware and plugin lists have been normalized"
(before-each
(spy-on 'cider-jack-in-normalized-nrepl-middlewares
:and-return-value '("refactor-nrepl.middleware/wrap-refactor" "cider.nrepl/cider-middleware"))
(spy-on 'cider-jack-in-normalized-lein-plugins
:and-return-value '(("refactor-nrepl" "2.0.0") ("cider/cider-nrepl" "0.11.0")))
(setq-local cider-jack-in-dependencies-exclusions '()))
(it "uses them in a lein project"
(expect (cider-inject-jack-in-dependencies "" "repl :headless" "lein")
:to-equal "update-in :dependencies conj \\[org.clojure/tools.nrepl\\ \\\"0.2.12\\\"\\] -- update-in :plugins conj \\[refactor-nrepl\\ \\\"2.0.0\\\"\\] -- update-in :plugins conj \\[cider/cider-nrepl\\ \\\"0.11.0\\\"\\] -- repl :headless"))
(it "uses them in a boot project"
(expect (cider-inject-jack-in-dependencies "" "repl -s wait" "boot")
:to-equal "-i \"(require 'cider.tasks)\" -d org.clojure/tools.nrepl\\:0.2.12 -d refactor-nrepl\\:2.0.0 -d cider/cider-nrepl\\:0.11.0 cider.tasks/add-middleware -m refactor-nrepl.middleware/wrap-refactor -m cider.nrepl/cider-middleware repl -s wait"))))

(describe "cider-jack-in-auto-inject-clojure"
(it "injects `cider-minimum-clojure-version' when `cider-jack-in-auto-inject-clojure' is set to minimal"
Expand Down