Skip to content

Commit adffc6a

Browse files
mbuczkoexpez
authored andcommitted
[Fix #415] add and hot-load dependencies for deps.edn
Projects using deps.edn, instead of leiningen or boot, for dependency management now have the ability to add and hotload project dependencies.
1 parent ab081b2 commit adffc6a

File tree

5 files changed

+202
-58
lines changed

5 files changed

+202
-58
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Unreleased
44

5+
- [#415](https://github.com/clojure-emacs/clj-refactor.el/issues/415) Support for deps.edn - based projects
56
- [#394](https://github.com/clojure-emacs/clj-refactor.el/issues/394) New config option `cljr-assume-language-context`: by default, when clj-refactor encounters an ambiguous context (clj vs cljs) it creates a popup asking user which context is meant. If this option is changed to "clj" or "cljs", clj-refactor will use that as the assumed context in such ambigous cases.
67
- [#391](https://github.com/clojure-emacs/clj-refactor.el/issues/391) Prevent refactor-nrepl from being injected when starting a REPL outside a project, and add an option `cljr-suppress-outside-project-warning` to suppress the resultant warning.
78

clj-refactor.el

Lines changed: 114 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,9 @@ Otherwise open the file and do the changes non-interactively."
315315
(and cljr-use-multiple-cursors
316316
(not (bound-and-true-p evil-mode))))
317317

318+
(defun cljr--vector-at-point-p ()
319+
(eq (char-after) ?\[))
320+
318321
(defun cljr--fix-special-modifier-combinations (key)
319322
(cl-case key
320323
("C-s-i" "s-TAB")
@@ -570,8 +573,12 @@ errors."
570573
(join-line)
571574
(paredit-forward-delete 1))
572575

573-
(defun cljr--looking-at-dependency-vector-p ()
574-
(looking-at "\\[[^[[:space:]]+[[:space:]]+\""))
576+
(defun cljr--looking-at-dependency-p ()
577+
(or
578+
;; boot & leiningen dependency vector
579+
(looking-at "\\[[^[[:space:]]+[[:space:]]+\"")
580+
;; clj dependency style
581+
(looking-at "\\([a-z0-9\-\./]+\\)[[:space:]]*\{.*\\(:mvn\\|:local\\|:git\\)/\\(root\\|version\\|url\\)[[:space:]]+\\(\"[^\"]+\"\\)")))
575582

576583
(defun cljr--just-one-blank-line ()
577584
"Ensure there's only one blank line at POINT."
@@ -787,7 +794,7 @@ A new record is created to define this constructor."
787794

788795
(defun cljr--project-dir ()
789796
(or
790-
(thread-last '("project.clj" "build.boot" "pom.xml")
797+
(thread-last '("project.clj" "build.boot" "pom.xml" "deps.edn")
791798
(mapcar 'cljr--locate-project-file)
792799
(delete 'nil)
793800
car)
@@ -804,6 +811,8 @@ A new record is created to define this constructor."
804811
(let ((file (expand-file-name "build.boot" project-dir)))
805812
(and (file-exists-p file) file))
806813
(let ((file (expand-file-name "pom.xml" project-dir)))
814+
(and (file-exists-p file) file))
815+
(let ((file (expand-file-name "deps.edn" project-dir)))
807816
(and (file-exists-p file) file)))))
808817

809818
(defun cljr--project-files ()
@@ -815,6 +824,9 @@ A new record is created to define this constructor."
815824
"-not -regex \".*svn.*\""
816825
1000))))
817826

827+
(defun cljr--project-with-deps-p (project-file)
828+
(string-match "/deps.edn$" project-file))
829+
818830
(defun cljr--buffers-visiting-dir (dir)
819831
(seq-filter (lambda (buf)
820832
(when-let (path (buffer-file-name buf))
@@ -1979,7 +1991,7 @@ See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-project-clean"
19791991
(cljr--post-command-message "Project clean done.")))
19801992

19811993
(defun cljr--extract-dependency-name ()
1982-
(cl-assert (cljr--looking-at-dependency-vector-p))
1994+
(cl-assert (cljr--looking-at-dependency-p))
19831995
(forward-char)
19841996
(prog1
19851997
(buffer-substring-no-properties
@@ -1998,9 +2010,9 @@ See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-project-clean"
19982010

19992011
(defun cljr--extract-next-dependency-name ()
20002012
(while (not (or (cljr--empty-buffer-p)
2001-
(cljr--looking-at-dependency-vector-p)))
2013+
(cljr--looking-at-dependency-p)))
20022014
(delete-char 1))
2003-
(when (cljr--looking-at-dependency-vector-p)
2015+
(when (cljr--looking-at-dependency-p)
20042016
(cljr--extract-dependency-name)))
20052017

20062018
(defun cljr--get-sorted-dependency-names (deps)
@@ -2074,19 +2086,22 @@ See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-project-clean"
20742086
20752087
See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-sort-project-dependencies"
20762088
(interactive)
2077-
(cljr--update-file (cljr--project-file)
2078-
(goto-char (point-min))
2079-
(while (re-search-forward ":dependencies" (point-max) t)
2080-
(forward-char)
2081-
(thread-first (buffer-substring-no-properties (point)
2082-
(cljr--point-after 'paredit-forward))
2083-
cljr--get-sorted-dependency-names
2084-
(cljr--sort-dependency-vectors (thread-last (clojure-delete-and-extract-sexp)
2085-
(string-remove-prefix "[")
2086-
(string-remove-suffix "]")))
2087-
insert))
2088-
(indent-region (point-min) (point-max))
2089-
(save-buffer)))
2089+
(let ((project-file (cljr--project-file)))
2090+
(if (cljr--project-with-deps-p project-file)
2091+
(user-error "Dependencies sorting not supported in deps.edn yet.")
2092+
(cljr--update-file project-file
2093+
(goto-char (point-min))
2094+
(while (re-search-forward ":dependencies" (point-max) t)
2095+
(forward-char)
2096+
(thread-first (buffer-substring-no-properties (point)
2097+
(cljr--point-after 'paredit-forward))
2098+
cljr--get-sorted-dependency-names
2099+
(cljr--sort-dependency-vectors (thread-last (clojure-delete-and-extract-sexp)
2100+
(string-remove-prefix "[")
2101+
(string-remove-suffix "]")))
2102+
insert))
2103+
(indent-region (point-min) (point-max))
2104+
(save-buffer)))))
20902105

20912106
(defun cljr--call-middleware-sync (request &optional key)
20922107
"Call the middleware with REQUEST.
@@ -2182,18 +2197,34 @@ possible choices. If the choice is trivial, return it."
21822197
(completing-read prompt choices nil nil nil nil (car choices)))
21832198
(read-from-minibuffer prompt)))
21842199

2200+
(defun cljr--insert-into-leiningen-dependencies (artifact version)
2201+
(re-search-forward ":dependencies")
2202+
(paredit-forward)
2203+
(paredit-backward-down)
2204+
(newline-and-indent)
2205+
(insert "[" artifact " \"" version "\"]"))
2206+
2207+
(defun cljr--insert-into-clj-dependencies (artifact version)
2208+
(re-search-forward ":deps")
2209+
(forward-sexp)
2210+
(backward-char)
2211+
(newline-and-indent)
2212+
(insert artifact " {:mvn/version \"" version "\"}"))
2213+
21852214
(defun cljr--add-project-dependency (artifact version)
2186-
(cljr--update-file (cljr--project-file)
2187-
(goto-char (point-min))
2188-
(re-search-forward ":dependencies")
2189-
(paredit-forward)
2190-
(paredit-backward-down)
2191-
(newline-and-indent)
2192-
(insert "[" artifact " \"" version "\"]")
2193-
(cljr--post-command-message "Added %s version %s as a project dependency" artifact version)
2194-
(when cljr-hotload-dependencies
2195-
(paredit-backward-down)
2196-
(cljr-hotload-dependency))))
2215+
(let* ((project-file (cljr--project-file))
2216+
(deps (cljr--project-with-deps-p project-file)))
2217+
(cljr--update-file project-file
2218+
(goto-char (point-min))
2219+
(if deps
2220+
(cljr--insert-into-clj-dependencies artifact version)
2221+
(cljr--insert-into-leiningen-dependencies artifact version))
2222+
(cljr--post-command-message "Added %s version %s as a project dependency" artifact version)
2223+
(when cljr-hotload-dependencies
2224+
(if deps
2225+
(back-to-indentation)
2226+
(paredit-backward-down))
2227+
(cljr-hotload-dependency)))))
21972228

21982229
;;;###autoload
21992230
(defun cljr-add-project-dependency (force)
@@ -2209,22 +2240,27 @@ See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-add-project-depe
22092240
(cljr--add-project-dependency lib-name version)))
22102241

22112242
;;;###autoload
2212-
(defun cljr-update-project-dependency ()
2243+
(defun cljr-update-project-dependency (&optional version)
22132244
"Update the version of the dependency at point."
22142245
(interactive)
22152246
(cljr--ensure-op-supported "artifact-list")
2216-
(unless (cljr--looking-at-dependency-vector-p)
2217-
(user-error "Place cursor in front of dependency vector to update."))
2247+
(unless (cljr--looking-at-dependency-p)
2248+
(user-error "Place cursor in front of dependency to update."))
22182249
(save-excursion
2219-
(let (lib-name)
2220-
(paredit-forward-down)
2250+
(let (lib-name
2251+
(lein-style (cljr--vector-at-point-p)))
2252+
(if lein-style
2253+
(paredit-forward-down))
22212254
(setq lib-name (cljr--extract-sexp))
22222255
(paredit-forward)
22232256
(skip-syntax-forward " ")
2224-
(let ((version (thread-last (cljr--get-versions-from-middleware lib-name)
2225-
(cljr--prompt-user-for (concat lib-name " version: ")))))
2257+
(let ((artifact-version (or version
2258+
(thread-last (cljr--get-versions-from-middleware lib-name)
2259+
(cljr--prompt-user-for (concat lib-name " version: "))))))
22262260
(cljr--delete-sexp)
2227-
(insert "\"" version "\""))))
2261+
(if lein-style
2262+
(insert "\"" artifact-version "\"")
2263+
(insert "\{:mvn/version \"" artifact-version "\"\}")))))
22282264
(when cljr-hotload-dependencies
22292265
(cljr-hotload-dependency)
22302266
(cljr--ensure-op-supported "artifact-list")))
@@ -2236,19 +2272,25 @@ See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-add-project-depe
22362272
See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-update-project-dependencies"
22372273
(interactive)
22382274
(cljr--ensure-op-supported "artifact-list")
2239-
(find-file (cljr--project-file))
2240-
(goto-char (point-min))
2241-
(let (cljr-hotload-dependencies)
2242-
(while (re-search-forward ":dependencies" (point-max) t)
2243-
(paredit-forward-down)
2244-
(cljr--skip-past-whitespace-and-comments)
2245-
(while (not (looking-at "]"))
2246-
(let ((highlight (cljr--highlight-sexp)))
2247-
(unwind-protect
2248-
(cljr-update-project-dependency)
2249-
(delete-overlay highlight)))
2250-
(paredit-forward)
2251-
(cljr--skip-past-whitespace-and-comments)))))
2275+
(let ((project-file (cljr--project-file)))
2276+
(find-file project-file)
2277+
(goto-char (point-min))
2278+
(let (cljr-hotload-dependencies)
2279+
(if (cljr--project-with-deps-p project-file)
2280+
(cljr--update-dependencies ":deps" "}" 2)
2281+
(cljr--update-dependencies ":dependencies" "]" 1)))))
2282+
2283+
(defun cljr--update-dependencies (keyword dependency-closing-brace forward-count)
2284+
(while (re-search-forward keyword (point-max) t)
2285+
(paredit-forward-down)
2286+
(cljr--skip-past-whitespace-and-comments)
2287+
(while (not (looking-at dependency-closing-brace))
2288+
(let ((highlight (cljr--highlight-sexp)))
2289+
(unwind-protect
2290+
(cljr-update-project-dependency)
2291+
(delete-overlay highlight)))
2292+
(paredit-forward forward-count)
2293+
(cljr--skip-past-whitespace-and-comments))))
22522294

22532295
(defun cljr--skip-past-whitespace-and-comments ()
22542296
(skip-syntax-forward " >")
@@ -2844,13 +2886,25 @@ See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-add-missing-libs
28442886
(cljr--maybe-clean-ns)
28452887
(cljr--maybe-eval-ns-form))
28462888

2847-
(defun cljr--dependency-vector-at-point ()
2889+
(defun cljr--dependency-at-point ()
2890+
"Returns project dependency at point.
2891+
2892+
Recognizes both leiningen- and deps.edn-style dependencies, but the latter is always
2893+
transformed back to leiningen dependency vector which is what nrepl backend
2894+
expects for hot-loading."
28482895
(save-excursion
28492896
(ignore-errors
2850-
(while (not (cljr--looking-at-dependency-vector-p))
2897+
(while (not (cljr--looking-at-dependency-p))
28512898
(paredit-backward-up))
2852-
(buffer-substring-no-properties (point)
2853-
(cljr--point-after 'paredit-forward)))))
2899+
2900+
(if (cljr--vector-at-point-p)
2901+
(buffer-substring-no-properties (point)
2902+
(cljr--point-after 'paredit-forward))
2903+
(concat "["
2904+
(match-string-no-properties 1)
2905+
" "
2906+
(match-string-no-properties 4)
2907+
"]")))))
28542908

28552909
(defun cljr--hotload-dependency-callback (response)
28562910
(cljr--maybe-rethrow-error response)
@@ -2866,10 +2920,11 @@ See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-add-missing-libs
28662920
(with-temp-buffer
28672921
(insert string)
28682922
(goto-char (point-min))
2869-
(cl-assert (cljr--looking-at-dependency-vector-p) nil
2923+
(cl-assert (cljr--looking-at-dependency-p) nil
28702924
(format
28712925
(concat "Expected dependency vector of type "
2872-
"[org.clojure \"1.7.0\"], but got '%s'")
2926+
"[org.clojure \"1.7.0\"] or "
2927+
"org.clojure {:mvn/version \"1.7.0\"}, but got '%s'")
28732928
string)))
28742929
string)
28752930

@@ -2882,8 +2937,9 @@ Defaults to the dependency vector at point, but prompts if none is found.
28822937
See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-hotload-dependency"
28832938
(interactive)
28842939
(cljr--ensure-op-supported "hotload-dependency")
2885-
(let ((dependency-vector (or (cljr--dependency-vector-at-point)
2940+
(let ((dependency-vector (or (cljr--dependency-at-point)
28862941
(cljr--prompt-user-for "Dependency vector: "))))
2942+
28872943
(cljr--assert-dependency-vector dependency-vector)
28882944
(cljr--call-middleware-async
28892945
(cljr--create-msg "hotload-dependency" "coordinates" dependency-vector)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
Feature: Add or update dependency in clj project
2+
3+
Background:
4+
Given I have a clj project with dependencies "cljr" in "tmp"
5+
And I open file "tmp/deps.edn"
6+
7+
Scenario: New dependency is added
8+
When I add dependency artifact "org.clojure/tools.namespace" with version "0.3.0-alpha4"
9+
Then I should see:
10+
"""
11+
{:paths ["src" "resources"]
12+
:deps {org.clojure/clojure {:mvn/version "1.9.0"}
13+
org.clojure/tools.namespace {:mvn/version "0.3.0-alpha4"}}}
14+
"""
15+
16+
Scenario: Existing dependency is updated
17+
When I locate dependency artifact "org.clojure/clojure"
18+
And I update artifact version to "1.9.1"
19+
Then I should see:
20+
"""
21+
{:paths ["src" "resources"]
22+
:deps {org.clojure/clojure {:mvn/version "1.9.1"}}}
23+
"""
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
Feature: Add or update dependency in leiningen project
2+
3+
Background:
4+
Given I have a leiningen project with dependencies "cljr" in "tmp"
5+
And I open file "tmp/project.clj"
6+
7+
Scenario: New dependency is added
8+
When I add dependency artifact "org.clojure/tools.namespace" with version "0.3.0-alpha4"
9+
Then I should see:
10+
"""
11+
(defproject cljr "0.1.0-SNAPSHOT"
12+
:dependencies [[org.clojure/clojure "1.9.0"]
13+
[org.clojure/tools.namespace "0.3.0-alpha4"]])
14+
"""
15+
16+
Scenario: Existing dependency is updated
17+
When I locate dependency artifact "org.clojure/clojure"
18+
And I update artifact version to "1.9.1"
19+
Then I should see:
20+
"""
21+
(defproject cljr "0.1.0-SNAPSHOT"
22+
:dependencies [[org.clojure/clojure "1.9.1"]])
23+
"""

features/step-definitions/clj-refactor-steps.el

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,30 @@
2121
(with-temp-file (expand-file-name "project.clj" dir-name)
2222
(insert "(defproject " project-name " \"0.1.0-SNAPSHOT\")"))))
2323

24+
(Given "^I have a \\(clj\\|leiningen\\) project with dependencies \"\\([^\"]+\\)\" in \"\\([^\"]+\\)\"$"
25+
(lambda (project-type project-name dir-name)
26+
(setq default-directory clj-refactor-root-path)
27+
28+
;; delete old directory
29+
(when (file-exists-p dir-name)
30+
(delete-directory dir-name t))
31+
32+
;; create directory structure
33+
(mkdir (expand-file-name project-name (expand-file-name "src" dir-name)) t)
34+
(mkdir (expand-file-name project-name (expand-file-name "test" dir-name)) t)
35+
36+
(cond
37+
38+
;; add project.clj
39+
((string= project-type "leiningen")
40+
(with-temp-file (expand-file-name "project.clj" dir-name)
41+
(insert "(defproject " project-name " \"0.1.0-SNAPSHOT\"\n :dependencies [[org.clojure/clojure \"1.9.0\"]])")))
42+
43+
;; add deps.edn
44+
((string= project-type "clj")
45+
(with-temp-file (expand-file-name "deps.edn" dir-name)
46+
(insert "{:paths [\"src\" \"resources\"]\n :deps {org.clojure/clojure {:mvn/version \"1.9.0\"}}}"))))))
47+
2448
(Given "^I have a clojure-file \"\\([^\"]+\\)\"$"
2549
(lambda (file-name)
2650
(setq default-directory clj-refactor-root-path)
@@ -483,3 +507,20 @@ pprint (cljs.pprint)}}"))))
483507
(And "^I disable cljr-clean-ns$"
484508
(lambda ()
485509
(defun cljr-clean-ns ()(interactive))))
510+
511+
(When "^I add dependency artifact \"\\([^ ]+\\)\" with version \"\\([^\"]+\\)\"$"
512+
(lambda (artifact version)
513+
(setq cljr-hotload-dependencies nil)
514+
(cljr--add-project-dependency artifact version)))
515+
516+
(When "^I locate dependency artifact \"\\([^ ]+\\)\"$"
517+
(lambda (artifact)
518+
(goto-char (point-min))
519+
(re-search-forward artifact)
520+
(cljr--dependency-at-point)
521+
(goto-char
522+
(match-beginning 0))))
523+
524+
(And "^I update artifact version to \"\\([^\"]+\\)\"$"
525+
(lambda (version)
526+
(cljr-update-project-dependency version)))

0 commit comments

Comments
 (0)