-
Notifications
You must be signed in to change notification settings - Fork 26
/
core.clj
135 lines (118 loc) · 4.8 KB
/
core.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
(ns bultitude.core
(:require [clojure.java.io :as io]
[clojure.string :as string]
[dynapath.util :as dp])
(:import (java.util.jar JarFile JarEntry)
(java.util.zip ZipException)
(java.io File BufferedReader PushbackReader InputStreamReader)
(clojure.lang DynamicClassLoader)))
(defn- clj? [^File f]
(and (not (.isDirectory f))
(.endsWith (.getName f) ".clj")))
(defn- clj-jar-entry? [^JarEntry f]
(and (not (.isDirectory f))
(.endsWith (.getName f) ".clj")))
(defn- jar? [^File f]
(and (.isFile f) (.endsWith (.getName f) ".jar")))
(defn- read-ns-form
"Given a reader on a Clojure source file, read until an ns form is found."
[rdr]
(let [form (try (read rdr false ::done)
(catch Exception e ::done))]
(if (try
(and (list? form) (= 'ns (first form)))
(catch Exception _))
(try
(str form) ;; force the read to read the whole form, throwing on error
(second form)
(catch Exception _))
(when-not (= ::done form)
(recur rdr)))))
(defn ns-form-for-file [file]
(with-open [r (PushbackReader. (io/reader file))] (read-ns-form r)))
(defn namespaces-in-dir
"Return a seq of all namespaces found in Clojure source files in dir."
[dir]
(for [^File f (file-seq (io/file dir))
:when (and (clj? f) (.canRead f))
:let [ns-form (ns-form-for-file f)]
:when ns-form]
ns-form))
(defn- ns-in-jar-entry [^JarFile jarfile ^JarEntry entry]
(with-open [rdr (-> jarfile
(.getInputStream entry)
InputStreamReader.
BufferedReader.
PushbackReader.)]
(read-ns-form rdr)))
(defn- namespaces-in-jar [^File jar]
(try
(let [jarfile (JarFile. jar)]
(for [entry (enumeration-seq (.entries jarfile))
:when (clj-jar-entry? entry)
:let [ns-form (ns-in-jar-entry jarfile entry)]
:when ns-form]
ns-form))
(catch ZipException e
(throw (Exception. (str "jar file corrupt: " jar) e)))))
(defn- split-classpath [^String classpath]
(.split classpath (System/getProperty "path.separator")))
(defn loader-classpath
"Returns a sequence of File objects from a classloader."
[loader]
(map io/as-file (dp/classpath-urls loader)))
(defn classpath-files
"Returns a sequence of File objects of the elements on the classpath."
([classloader]
(map io/as-file (dp/all-classpath-urls classloader)))
([] (classpath-files (clojure.lang.RT/baseLoader))))
(defn- classpath->collection [classpath]
(if (coll? classpath)
classpath
(split-classpath classpath)))
(defn- classpath->files [classpath]
(map io/file classpath))
(defn file->namespaces
"Map a classpath file to the namespaces it contains. `prefix` allows for
reducing the namespace search space. For large directories on the classpath,
passing a `prefix` can provide significant efficiency gains."
[^String prefix ^File f]
(cond
(.isDirectory f) (namespaces-in-dir
(if prefix
(io/file f (-> prefix
(.replaceAll "\\." "/")
(.replaceAll "-" "_")))
f))
(jar? f) (let [ns-list (namespaces-in-jar f)]
(if prefix
(filter #(and % (.startsWith (name %) prefix)) ns-list)
ns-list))))
(defn namespaces-on-classpath
"Return symbols of all namespaces matching the given prefix both on disk and
inside jar files. If :prefix is passed, only return namespaces that begin with
this prefix. If :classpath is passed, it should be a seq of File objects or a
classpath string. If it is not passed, default to java.class.path and the
current classloader, assuming it is a dynamic classloader."
[& {:keys [prefix classpath] :or {classpath (classpath-files)}}]
(mapcat
(partial file->namespaces prefix)
(->> classpath
classpath->collection
classpath->files)))
(defn path-for
"Transform a namespace into a .clj file path relative to classpath root."
[namespace]
(str (-> (str namespace)
(.replace \- \_)
(.replace \. \/))
".clj"))
(defn doc-from-ns-form
"Extract the docstring from a given ns form without evaluating the form. The docstring returned should be the return value of (:doc (meta namespace-symbol)) if the ns-form were to be evaluated."
[ns-form]
(let [meta-docstring (:doc (meta (second ns-form)))
references (next (next ns-form))
docstring (when (string? (first references)) (first references))
references (if docstring (next references) references)
attribute-docstring (:doc (when (map? (first references)) (first references)))]
(or attribute-docstring docstring meta-docstring)))