From f9bd359af587aa402c10e1bdb328dfc91fa065d3 Mon Sep 17 00:00:00 2001 From: Radon Rosborough Date: Wed, 14 Mar 2018 15:23:08 -0700 Subject: [PATCH] Allow predicates in plugin and middleware lists See https://github.com/clojure-emacs/clj-refactor.el/pull/392#issuecomment-352399191 --- CHANGELOG.md | 1 + cider.el | 68 +++++++++++++++++++++++++++++++++++++++------ test/cider-tests.el | 57 ++++++++++++++++++++++++++++++++++++- 3 files changed, 117 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca1d5de42..5c2802a61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/cider.el b/cider.el index 4147c11c9..29fb94751 100644 --- a/cider.el +++ b/cider.el @@ -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 @@ -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)))) @@ -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 +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 @@ -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) + (cider-jack-in-normalized-nrepl-middlewares))) ("clojure-cli" (cider-clojure-cli-jack-in-dependencies global-opts params diff --git a/test/cider-tests.el b/test/cider-tests.el index 54e8a2853..179fcd71f 100644 --- a/test/cider-tests.el +++ b/test/cider-tests.el @@ -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"