forked from weavejester/codox
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.clj
162 lines (143 loc) · 5.5 KB
/
main.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
156
157
158
159
160
161
162
(ns codox.main
"Main namespace for generating documentation"
(:use [codox.utils :only (add-source-paths)])
(:require [clojure.string :as str]
[clojure.java.shell :as shell]
[codox.reader.clojure :as clj]
[codox.reader.clojurescript :as cljs]
[codox.reader.plaintext :as text]))
(defn- writer [{:keys [writer]}]
(let [writer-sym (or writer 'codox.writer.html/write-docs)
writer-ns (symbol (namespace writer-sym))]
(try
(require writer-ns)
(catch Exception e
(throw
(Exception. (str "Could not load codox writer " writer-ns) e))))
(if-let [writer (resolve writer-sym)]
writer
(throw
(Exception. (str "Could not resolve codox writer " writer-sym))))))
(def ^:private namespace-readers
{:clojure clj/read-namespaces
:clojurescript cljs/read-namespaces})
(defn- var-symbol [namespace var]
(symbol (name (:name namespace)) (name (:name var))))
(defn- remove-matching-vars [vars re namespace]
(remove (fn [var]
(when (and re (re-find re (name (:name var))))
(println "Excluding var" (var-symbol namespace var))
true))
vars))
(defn- remove-excluded-vars [namespaces exclude-vars]
(map #(update-in % [:publics] remove-matching-vars exclude-vars %) namespaces))
(defn- add-var-defaults [vars defaults]
(for [var vars]
(-> (merge defaults var)
(update-in [:members] add-var-defaults defaults))))
(defn- add-ns-defaults [namespaces defaults]
(for [namespace namespaces]
(-> (merge defaults namespace)
(update-in [:publics] add-var-defaults defaults))))
(defn- ns-matches? [{ns-name :name} pattern]
(cond
(instance? java.util.regex.Pattern pattern) (re-find pattern (str ns-name))
(string? pattern) (= pattern (str ns-name))
(symbol? pattern) (= pattern (symbol ns-name))))
(defn- filter-namespaces [namespaces ns-filters]
(if (and ns-filters (not= ns-filters :all))
(filter #(some (partial ns-matches? %) ns-filters) namespaces)
namespaces))
(defn- read-namespaces
"Returns {<language> <namespace-seq>} for cross-platform opts,
or <namespace-seq> otherwise."
[{:keys [language root-path source-paths namespaces metadata exclude-vars] :as opts}]
(if (:cross-platform? opts)
(reduce
(fn [m language]
(assoc m language
(read-namespaces
(assoc opts
:language language
:cross-platform? false))))
{}
(:languages opts))
(let [reader (namespace-readers language)]
(-> (reader source-paths (select-keys opts [:exception-handler]))
(filter-namespaces namespaces)
(remove-excluded-vars exclude-vars)
(add-source-paths root-path source-paths)
(add-ns-defaults metadata)))))
(defn- get-var-langs
"Returns {<ns> {<var> <set-of-languages>}} for given namespaces."
([language namespaces var-langs]
(reduce
(fn [var-langs ns]
(reduce
(fn [var-langs public-var]
(update-in var-langs [(:name ns) (:name public-var)]
#(conj (or % #{}) language)))
var-langs
(:publics ns)))
var-langs
namespaces))
([options namespaces]
(if-not (:cross-platform? options)
(get-var-langs (:language options) namespaces {})
(reduce
(fn [var-langs language]
(get-var-langs language (get namespaces language) var-langs))
{}
(:languages options)))))
(comment (get-var-langs {:languages #{:clojure :clojurescript}}
'({:name codox.main :publics ({:name defaults} {:name bar})}
{:name codox.foo :publics ({:name bar})})))
(defn- read-documents [{:keys [doc-paths doc-files] :or {doc-files :all}}]
(cond
(not= doc-files :all) (map text/read-file doc-files)
(seq doc-paths) (->> doc-paths
(apply text/read-documents)
(sort-by :name))))
(defn- git-commit [dir]
(let [{:keys [out exit] :as result} (shell/sh "git" "rev-parse" "HEAD" :dir dir)]
(when-not (zero? exit)
(throw (ex-info "Error getting git commit" result)))
(str/trim out)))
(def defaults
(let [root-path (System/getProperty "user.dir")]
{:language :clojure ; #{:clojure :clojurescript}
;; :base-language :clojure
:root-path root-path
:output-path "target/doc"
:source-paths ["src"]
:doc-paths ["doc"]
:doc-files :all
:namespaces :all
:exclude-vars #"^(map)?->\p{Upper}"
:metadata {}
:themes [:default]
:git-commit (delay (git-commit root-path))}))
(defn- cross-platform-options [{:keys [language] :as opts}]
(if-not (set? language)
opts ; {:language <keyword>}
(if (= (count language) 1)
(assoc opts :language (first language)) ; {:language <keyword>}
;; Cross-platform case: {:language nil, :languages <set>}
(assoc opts
:language nil
:languages language
:cross-platform? true))))
(defn generate-docs
"Generate documentation from source files."
([]
(generate-docs {}))
([options]
(let [options (-> (merge defaults options) cross-platform-options)
write-fn (writer options)
namespaces (read-namespaces options)
documents (read-documents options)
var-langs (get-var-langs options namespaces)]
(write-fn (assoc options
:namespaces namespaces
:documents documents
:var-langs var-langs)))))