Skip to content

Commit 50b6e10

Browse files
Carlos Requena Lópezbenedekfazekas
authored andcommitted
Fix #235. allow gen-class methods metadata extraction (#236)
* [Fix #235] streamline metadata extraction process (extract-ns-meta) will return a map with both the ns metadata, and the (:gen-class :methods) metadata. All in longhand notation * [Fix #235] make (extract-ns-meta) return nil values if no metadata it was returning {:ns-meta {}, :gc-methods-meta nil} * [Fix #235] adapt pprint-meta only for the top level meta information * [Fix #235] adapt (pprint-gen-class-form) to print metadata * [Fix #235] fix gc-methods-meta : keys with different value... can conflict. ^{:dynamic false} and ^:dynamic would conflict and it would only take into account one of them. * [Fix #235] allow mixed type notation in metadata both at the top level, and for gen-class methods add relevant tests
1 parent 00b27b5 commit 50b6e10

File tree

9 files changed

+146
-32
lines changed

9 files changed

+146
-32
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ The `clean-ns` op will perform the following cleanups on an ns form:
158158
* Remove any unused namespaces, referred symbols or imported classes.
159159
* Remove any duplication in the :require and :import form.
160160
* Prune or remove any `:rename` clause.
161+
* Use the shorthand version of metadata found if possible, and sort it
162+
alphabetically
161163

162164
The `clean-ns` requires a `path` which is the path to the file containing the `ns` to be operated upon.
163165

src/refactor_nrepl/core.clj

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -250,24 +250,40 @@
250250

251251
:else (-> fully-qualified-name str (.split "\\.") last))))
252252

253+
(defn extract-gen-class-methods-meta
254+
"Retrieve the metadata relative to :methods in the :gen-class top
255+
level component.
256+
257+
Returns nil if there is no :gen-class, or if there is no :methods
258+
inside :gen-class
259+
260+
.indexOf returns -1 if not found, and since the structure we are
261+
looking for comes after, by 'incing' by default we can just check
262+
for zero?"
263+
[ns-form]
264+
(let [gen-class (get-ns-component ns-form :gen-class)
265+
methods_index (inc (if-not (nil? gen-class)
266+
(.indexOf gen-class :methods)
267+
-1))]
268+
(if-not (zero? methods_index)
269+
(apply merge (map #(meta %) (nth gen-class methods_index)))
270+
nil)))
271+
253272
(defn extract-ns-meta
254273
"Retrieve the metadata for the ns, if there is any.
255274
256-
This is preferable to just doing (meta the-ns-form) because the
257-
compiler, as well as libraries like clojure.test, add quite a bit of
258-
metadata which shouldn't be printed back out."
275+
By parsing the ns as a string, and reading the metadata off it, all
276+
the metadata introduced by the compiler or clojure.test is not
277+
printed back out"
259278
[file-content]
260279
(let [ns-string (sexp/get-first-sexp file-content)
261-
meta? (-> ns-string (.replaceFirst "\\^\\{" "\\{")
262-
(StringReader.)
263-
(PushbackReader.)
264-
parse/read-ns-decl
265-
second)
266-
shorthand-meta? (re-seq #"\^:([^\s]+)\s" ns-string)]
267-
(cond
268-
(map? meta?) meta?
269-
shorthand-meta? {::shorthand-meta-coll (map (comp keyword second) shorthand-meta?)}
270-
:else nil)))
280+
ns-form (-> ns-string
281+
(StringReader.)
282+
(PushbackReader.)
283+
parse/read-ns-decl)
284+
ns-meta (meta (second ns-form))]
285+
{:top-level-meta ns-meta
286+
:gc-methods-meta (extract-gen-class-methods-meta ns-form)}))
271287

272288
(defn read-ns-form-with-meta
273289
"Read the ns form found at PATH.

src/refactor_nrepl/ns/pprint.clj

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
[clojure
44
[pprint :refer [pprint]]
55
[string :as str]]
6-
[refactor-nrepl.core :as core :refer [prefix-form?]])
6+
[refactor-nrepl
7+
[core :as core :refer [prefix-form?]]
8+
[util :as util :refer [replace-last]]])
79

810
(:import java.util.regex.Pattern))
911

@@ -52,17 +54,55 @@
5254
(and (sequential? form)
5355
(= (first form) type)))
5456

57+
(defn pprint-meta
58+
"Given some metadata m, print the shorthand metadata first, and the
59+
longhand metadata second, trying to convert to shorthand notation if
60+
possible
61+
62+
If newlines is true, it prints a newline after each piece of
63+
longhand metadata"
64+
[m & {:keys [newlines]
65+
:or {newlines false}}]
66+
(let [short? #(= (str %) "true")
67+
shorthand (sort (filter (fn [[k v]] (short? v)) m))
68+
longhand (remove (fn [[k v]] (short? v)) m)]
69+
(doseq [[k v] shorthand]
70+
(print "^" (str k) ""))
71+
(when-not (empty? longhand)
72+
(printf "^{")
73+
(doseq [[k v] longhand]
74+
(print k)
75+
(if newlines
76+
(print (with-out-str (pprint v)))
77+
(print (replace-last (with-out-str (pprint v)) #"\s" ""))))
78+
(if newlines
79+
(println "}")
80+
(print "}")))))
81+
5582
(defn- pprint-gen-class-form
56-
[[_ & elems]]
83+
"Prints the gen class form and :methods metadata (if any)."
84+
[[_ & elems] metadata]
5785
(if (empty? elems)
5886
(println "(:gen-class)")
5987
(println "(:gen-class"))
6088
(dorun
6189
(map-indexed
6290
(fn [idx [key val]]
63-
(if (= idx (dec (count (partition 2 elems))))
64-
(printf "%s %s)\n" key val)
65-
(println key val)))
91+
(if (= key :methods)
92+
(do
93+
(print key "[")
94+
(doseq [method val] ;val are all the methods
95+
(pprint-meta (filter (fn [[k v]]
96+
(contains? metadata k))
97+
(meta method)))
98+
(print method)
99+
(when-not (= method (last val))
100+
(println)))
101+
(print "]"))
102+
(print key val))
103+
(when (= idx (dec (count (partition 2 elems))))
104+
(print ")"))
105+
(println))
66106
(partition 2 elems))))
67107

68108
(defn- pprint-import-form
@@ -76,25 +116,17 @@
76116
(println import)))
77117
imports)))
78118

79-
(defn pprint-meta
80-
[m]
81-
(if-let [shorthand-meta-coll (::core/shorthand-meta-coll m)]
82-
(doseq [shorthand-meta shorthand-meta-coll]
83-
(print (str "^" shorthand-meta " ")))
84-
(printf (.replaceAll (str "^" (into (sorted-map) m) "\n")
85-
", " "\n"))))
86-
87119
(defn pprint-ns
88120
[[_ name & more :as ns-form]]
89121
(let [docstring? (when (string? (first more)) (first more))
90122
attrs? (when (map? (second more)) (second more))
91123
forms (cond (and docstring? attrs?) (nthrest more 2)
92124
(not (or docstring? attrs?)) more
93125
:else (rest more))
94-
ns-meta (meta ns-form)]
126+
ns-meta (:top-level-meta (meta ns-form))]
95127
(-> (with-out-str
96128
(printf "(ns ")
97-
(when (seq ns-meta) (pprint-meta ns-meta))
129+
(when (seq ns-meta) (pprint-meta ns-meta :newlines true))
98130
(print name)
99131
(if (or docstring? attrs? forms)
100132
(println)
@@ -116,11 +148,11 @@
116148
(str/trim-newline
117149
(with-out-str
118150
(cond (form-is? form :require) (pprint-require-form form)
119-
(form-is? form :gen-class) (pprint-gen-class-form form)
151+
(form-is? form :gen-class) (pprint-gen-class-form form (:gc-methods-meta (meta ns-form)))
120152
(form-is? form :import) (pprint-import-form form)
121153
:else (pprint form)))))
122154
(cond (form-is? form :require) (pprint-require-form form)
123-
(form-is? form :gen-class) (pprint-gen-class-form form)
155+
(form-is? form :gen-class) (pprint-gen-class-form form (:gc-methods-meta (meta ns-form)))
124156
(form-is? form :import) (pprint-import-form form)
125157
:else (pprint form))))
126158
forms)))

src/refactor_nrepl/util.clj

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
(ns refactor-nrepl.util
2+
(:require [clojure.string :as string])
23
(:import java.util.regex.Pattern))
34

45
(defn normalize-to-unix-path
@@ -41,3 +42,13 @@
4142
(if (seq xs)
4243
(apply conj coll xs)
4344
coll)))
45+
46+
(defn replace-last
47+
"Replaces the last instance of match from the string s with rep"
48+
[s match rep]
49+
(as-> s _
50+
(reverse _)
51+
(apply str _)
52+
(string/replace-first _ match rep)
53+
(reverse _)
54+
(apply str _)))

test/refactor_nrepl/ns/clean_ns_test.clj

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,11 @@
3838
(def cljc-ns-same-clj-cljs-cleaned (core/read-ns-form-with-meta (absolute-path "test/resources/cljcns_same_clj_cljs_cleaned.cljc")))
3939

4040
(def ns-with-shorthand-meta (clean-msg "test/resources/ns_with_shorthand_meta.clj"))
41-
4241
(def ns-with-multiple-shorthand-meta (clean-msg "test/resources/ns_with_multiple_shorthand_meta.clj"))
42+
(def ns-with-gen-class-methods-meta (clean-msg "test/resources/ns_with_gen_class_methods_meta.clj"))
43+
(def ns-with-gen-class-methods-meta-clean (clean-msg "test/resources/ns_with_gen_class_methods_meta_clean.clj"))
44+
(def ns-with-lots-of-meta (clean-msg "test/resources/ns_with_lots_of_meta.clj"))
45+
(def ns-with-lots-of-meta-clean (clean-msg "test/resources/ns_with_lots_of_meta_clean.clj"))
4346

4447
(def ns-with-inner-classes (clean-msg "test/resources/ns_with_inner_classes.clj"))
4548

@@ -199,11 +202,21 @@
199202
(let [cleaned (pprint-ns (clean-ns ns-with-shorthand-meta))]
200203
(is (re-find #"\^:automation" cleaned))))
201204

202-
(deftest preservres-multiple-shortand-meta
205+
(deftest preserves-multiple-shortand-meta
203206
(let [cleaned (pprint-ns (clean-ns ns-with-multiple-shorthand-meta))]
204207
(is (re-find #"\^:automation" cleaned))
205208
(is (re-find #"\^:multiple" cleaned))))
206209

210+
(deftest preserves-gen-class-methods-meta
211+
(let [actual (pprint-ns (clean-ns ns-with-gen-class-methods-meta))
212+
expected (slurp (:path ns-with-gen-class-methods-meta-clean))]
213+
(is (= expected actual))))
214+
215+
(deftest preserves-all-meta
216+
(let [actual (pprint-ns (clean-ns ns-with-lots-of-meta))
217+
expected (slurp (:path ns-with-lots-of-meta-clean))]
218+
(is (= expected actual))))
219+
207220
(deftest does-not-remove-dollar-sign-if-valid-symbol
208221
(let [cleaned (pprint-ns (clean-ns ns-using-dollar))]
209222
(is (re-find #"\[\$\]" cleaned))))
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
(ns ns-with-gen-class-methods-meta
2+
(:gen-class
3+
:methods [^:static [foo [String] String]
4+
^{:test true} [bar [String] String]
5+
^{:other "text"} [baz [String] String]]
6+
:name Name)
7+
(:require [clojure.string :as s]
8+
[clojure.pprint :refer [fresh-line]]))
9+
10+
(defn useit
11+
[]
12+
(fresh-line))
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
(ns ns-with-gen-class-methods-meta
2+
(:gen-class
3+
:methods [^:static [foo [String] String]
4+
^:test [bar [String] String]
5+
^{:other "text"} [baz [String] String]]
6+
:name Name)
7+
(:require [clojure.pprint :refer [fresh-line]]))
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
(ns ^:md1 ^:md2 ^{:longhand "as well" :really? "yes" :ok true} ns-with-gen-class-methods-meta
2+
(:gen-class
3+
:methods [^:static [foo [String] String]
4+
^{:test true} [bar [String] String]
5+
^{:other "text"} [baz [String] String]]
6+
:name Name)
7+
(:require [clojure.string :as s]
8+
[clojure.pprint :refer [fresh-line]]))
9+
10+
(defn useit
11+
[]
12+
(fresh-line))
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
(ns ^:md1 ^:md2 ^:ok ^{:longhand "as well"
2+
:really? "yes"}
3+
ns-with-gen-class-methods-meta
4+
(:gen-class
5+
:methods [^:static [foo [String] String]
6+
^:test [bar [String] String]
7+
^{:other "text"} [baz [String] String]]
8+
:name Name)
9+
(:require [clojure.pprint :refer [fresh-line]]))

0 commit comments

Comments
 (0)