forked from weavejester/codox
-
Notifications
You must be signed in to change notification settings - Fork 0
/
clojure.clj
155 lines (130 loc) · 4.86 KB
/
clojure.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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
(ns codox.reader.clojure
"Read raw documentation information from Clojure source directory."
(:import java.util.jar.JarFile
java.io.FileNotFoundException)
(:use [codox.utils :only (assoc-some update-some correct-indent)])
(:require [clojure.java.io :as io]
[clojure.tools.namespace.find :as ns]
[clojure.string :as str]))
(defn try-require [namespace]
(try
(require namespace)
(catch FileNotFoundException _ nil)))
(defn core-typed? []
(find-ns 'clojure.core.typed.check))
(defn var->symbol [var]
(let [{:keys [ns name]} (meta var)]
(symbol (str ns) (str name))))
(defn typecheck-namespace [namespace]
((find-var 'clojure.core.typed/check-ns-info) namespace))
(defn typecheck-var [var]
((find-var 'clojure.core.typed/check-form-info) (var->symbol var)))
(defn- sorted-public-vars [namespace]
(->> (ns-publics namespace)
(vals)
(sort-by (comp :name meta))))
(defn- no-doc? [var]
(let [{:keys [skip-wiki no-doc]} (meta var)]
(or skip-wiki no-doc)))
(defn- proxy? [var]
(re-find #"proxy\$" (-> var meta :name str)))
(defn- macro? [var]
(:macro (meta var)))
(defn- multimethod? [var]
(instance? clojure.lang.MultiFn (var-get var)))
(defn- protocol? [var]
(let [value (var-get var)]
(and (map? value)
(not (sorted? value)) ; workaround for CLJ-1242
(:on-interface value))))
(defn- protocol-method? [vars var]
(if-let [p (:protocol (meta var))]
(some #{p} vars)))
(defn- protocol-methods [protocol vars]
(filter #(= protocol (:protocol (meta %))) vars))
(defn- var-type [var]
(cond
(macro? var) :macro
(multimethod? var) :multimethod
(protocol? var) :protocol
:else :var))
(defn core-typed-type [var]
(let [{:keys [delayed-errors ret]} (typecheck-var var)]
(if (empty? delayed-errors)
(:t ret))))
(defn- read-var [vars var]
(-> (meta var)
(select-keys [:name :file :line :arglists :doc :dynamic
:added :deprecated :doc/format])
(update-some :doc correct-indent)
(assoc-some :type (var-type var)
:type-sig (if (core-typed?) (core-typed-type var))
:members (seq (map (partial read-var vars)
(protocol-methods var vars))))))
(defn- read-publics [namespace]
(let [vars (sorted-public-vars namespace)]
(->> vars
(remove proxy?)
(remove no-doc?)
(remove (partial protocol-method? vars))
(map (partial read-var vars))
(sort-by (comp str/lower-case :name)))))
(defn- read-ns [namespace exception-handler]
(try-require 'clojure.core.typed.check)
(when (core-typed?)
(typecheck-namespace namespace))
(try
(require namespace)
(-> (find-ns namespace)
(meta)
(assoc :name namespace)
(assoc :publics (read-publics namespace))
(update-some :doc correct-indent)
(list))
(catch Exception e
(exception-handler e namespace))))
(defn- default-exception-handler [e namespace]
(println
(format "Could not generate clojure documentation for %s - root cause: %s %s"
namespace
(.getName (class e))
(.getMessage e)))
(.printStackTrace e))
(defn- jar-file? [file]
(and (.isFile file)
(-> file .getName (.endsWith ".jar"))))
(defn- find-namespaces [file]
(cond
(.isDirectory file) (set (ns/find-namespaces-in-dir file))
(jar-file? file) (set (ns/find-namespaces-in-jarfile (JarFile. file)))))
(defn read-namespaces
"Read Clojure namespaces from a set of source directories (defaults
to [\"src\"]), and return a list of maps suitable for documentation
purposes.
Supported options using the second argument:
:exception-handler - function (fn [ex ns]) to handle exceptions
while reading a namespace
Any namespace with {:no-doc true} in its metadata will be skipped.
The keys in the maps are:
:name - the name of the namespace
:doc - the doc-string on the namespace
:author - the author of the namespace
:publics
:name - the name of a public function, macro, or value
:file - the file the var was declared in
:line - the line at which the var was declared
:arglists - the arguments the function or macro takes
:doc - the doc-string of the var
:type - one of :macro, :protocol, :multimethod or :var
:added - the library version the var was added in
:deprecated - the library version the var was deprecated in"
([] (read-namespaces ["src"] {}))
([paths] (read-namespaces paths {}))
([paths {:keys [exception-handler]
:or {exception-handler default-exception-handler}}]
(mapcat (fn [path]
(->> (io/file path)
(find-namespaces)
(mapcat #(read-ns % exception-handler))
(remove :no-doc)))
paths)))