Skip to content

Commit 7bc7d67

Browse files
authored
Support var linking across namespaces + wikilinks syntax (#20)
* Support var linking across namespaces This commit introduces a significant change in how var linking is performed: - MD sections are anchored with stringified qualified symbols - Munging down while generating TOC is gone - A map of `namespace` -> `#{vars}` is prepared - During var linking, that map is used to detect if a value should be linked to a namespace or a var * Ensure defaults are applied The core `quickdoc` function was indeed applying defaults using the `:or` construct in destructuring. However, raw options were then passed to inner functions, resulting in defaults not being enforced downstream. * Support wikilinks-style vars Introduces a new option `:var-pattern` deciding how vars are detected in docstrings. Set to `:backticks` by default. Users can also choose `:wikilinks` (aka double brackets) so that is behave like Codox and Cljdoc.
1 parent 66e4835 commit 7bc7d67

File tree

2 files changed

+66
-57
lines changed

2 files changed

+66
-57
lines changed

src/quickdoc/api.cljc

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
* `:source-paths` - sources that are scanned for vars. Defaults to `[\"src\"]`.
2424
* `:toc` - generate table of contents. Defaults to `true`.
2525
* `:var-links` - generate links to vars within the same namespace. Defauls to `true`.
26+
* `:var-pattern` - detecting vars for linking, either `:backticks` (default) or `:wikilinks` (double brackets)
2627
* `:overrides` - overrides in the form `{namespace {:no-doc true var {:no-doc true :doc ...}}}`.
2728
2829
Returns a map containing the generated markdown string under the key `:markdown`."
@@ -35,19 +36,21 @@
3536
:toc :boolean
3637
:var-links :boolean}
3738
:collect {:source-paths []}}}
38-
[{:keys [github/repo
39-
git/branch
40-
outfile
41-
source-paths
42-
toc var-links
43-
overrides]
44-
:or {branch "main"
45-
outfile "API.md"
46-
source-paths ["src"]
47-
toc true
48-
var-links true}
49-
:as opts}]
50-
(let [ana (-> (clj-kondo/run! {:lint source-paths
39+
[opts]
40+
(let [{:as opts
41+
:keys [outfile
42+
source-paths
43+
overrides]} (merge {:git/branch "main"
44+
:outfile "API.md"
45+
:source-paths ["src"]
46+
:toc true
47+
:var-links true
48+
:var-pattern :backticks}
49+
opts)
50+
opts (assoc opts :var-regex (case (:var-pattern opts)
51+
:backticks #"`(.*?)`"
52+
:wikilinks #"\[\[(.*?)\]\]"))
53+
ana (-> (clj-kondo/run! {:lint source-paths
5154
:config {:skip-comments true
5255
:output {:analysis
5356
{:arglists true
@@ -61,26 +64,13 @@
6164
ns-defs (:namespace-definitions ana)
6265
ns-defs (group-by :name ns-defs)
6366
nss (group-by :ns var-defs)
64-
memo (atom {})
65-
toc (with-out-str (impl/print-toc memo nss ns-defs opts overrides))
67+
ns->vars (update-vals nss (comp set (partial map :name)))
68+
toc (with-out-str (impl/print-toc nss ns-defs opts overrides))
6669
docs (with-out-str
6770
(run! (fn [[ns-name vars]]
68-
(impl/print-namespace ns-defs ns-name vars opts overrides))
71+
(impl/print-namespace ns-defs ns->vars ns-name vars opts overrides))
6972
(sort-by first nss)))
70-
docs (str toc docs)
71-
quoted (re-seq #" `(.*?)`([,. ])" docs)
72-
docs (if (:var-links opts)
73-
(reduce (fn [docs [raw inner suffix]]
74-
(let [munged (impl/md-munge inner)]
75-
(if-let [i (get @memo munged)]
76-
(str/replace docs raw
77-
(format " [`%s`](#%s)%s"
78-
inner
79-
(str munged (if (pos? i) (str "-" i) ""))
80-
suffix))
81-
docs)))
82-
docs quoted)
83-
docs)]
73+
docs (str toc docs)]
8474
(when outfile
8575
(spit outfile docs))
8676
{:markdown docs}))

src/quickdoc/impl.clj

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,44 @@
7373
(str/replace "{end-row}" (str (:end-row var)))
7474
(str/replace "{end-col}" (str (:end-col var))))))
7575

76-
(defn print-var [var _source {:keys [collapse-vars] :as opts}]
76+
(defn print-docstring [ns->vars current-ns docstring opts]
77+
(println
78+
(if-some [var-regex (:var-regex opts)]
79+
(reduce (fn [docstring [raw inner]]
80+
(cond
81+
;; Looks qualified
82+
(str/includes? inner "/")
83+
(let [split (str/split inner #"/")]
84+
(if (and (= (count split) 2)
85+
(get-in ns->vars [(symbol (first split))
86+
(symbol (second split))]))
87+
(str/replace docstring raw (format "[`%s`](#%s)" inner inner))
88+
docstring))
89+
;; Not qualified, maybe a namespace
90+
(contains? ns->vars (symbol inner))
91+
(str/replace docstring raw (format "[`%s`](#%s)" inner inner))
92+
;; Not qualified, maybe a var in the current namespace
93+
(get-in ns->vars [current-ns (symbol inner)])
94+
(str/replace docstring raw (format "[`%s`](#%s/%s)" inner current-ns inner))
95+
;; Just regular markdown backticks
96+
:else
97+
docstring))
98+
docstring
99+
(re-seq var-regex docstring))
100+
docstring)))
101+
102+
(defn print-var [ns->vars ns-name var _source {:keys [collapse-vars] :as opts}]
77103
(when (var-filter var)
78104
(when collapse-vars (println "<details>\n\n"))
79105
(when collapse-vars
80106
(println (str "<summary><code>" (:name var) "</code>"
81107
(when-let [summary (var-summary var)]
82108
(str " - " summary)))
83109
"</summary>\n\n"))
84-
(println "##" (format "`%s`" (:name var)))
110+
(println "##" (format "<a name=\"%s/%s\">`%s`</a>"
111+
ns-name
112+
(:name var)
113+
(:name var)))
85114
(when-let [arg-lists (or (when-let [quoted-arglists (-> var :meta :arglists)]
86115
(if (and (seq? quoted-arglists)
87116
(= 'quote (first quoted-arglists)))
@@ -105,19 +134,12 @@
105134
(println)
106135
(when (:macro var)
107136
(println "Macro.\n\n"))
108-
(println doc)
137+
(print-docstring ns->vars ns-name doc opts)
109138
(print "<br>"))
110139
(println (var-source var opts))
111140
(when collapse-vars (println "</details>\n\n"))))
112141

113-
(defn with-idx [s memo]
114-
(let [v (swap! memo update s (fnil inc -1))
115-
c (get v s)]
116-
(if (zero? c)
117-
s
118-
(str s "-" c))))
119-
120-
(defn print-namespace [ns-defs ns-name vars opts overrides]
142+
(defn print-namespace [ns-defs ns->vars ns-name vars opts overrides]
121143
(let [ns (get-in ns-defs [ns-name 0])
122144
filename (:filename ns)
123145
source (try (slurp filename)
@@ -134,20 +156,17 @@
134156
collapse-nss (:collapse-nss opts)]
135157
(when collapse-nss (println "<details>\n\n"))
136158
(when collapse-nss (println "<summary><code>" ns-name "</code></summary>\n\n"))
137-
(println "#" ns-name "\n\n")
159+
(println (format "# <a name=\"%s\">%s</a>\n\n" ns-name ns-name))
138160
(when-let [doc (:doc ns)]
139-
(println doc))
161+
(print-docstring ns->vars ns-name doc opts))
140162
(println "\n\n")
141163
(run! (fn [[_ vars]]
142164
(let [var (last vars)]
143-
(print-var var source opts)))
165+
(print-var ns->vars ns-name var source opts)))
144166
(sort-by first ana))
145167
(when collapse-nss (println "</details>\n\n"))))))))
146168

147-
(defn md-munge [s]
148-
(str/replace s #"[\*\.!]" ""))
149-
150-
(defn print-toc* [memo nss ns-defs _opts overrides]
169+
(defn print-toc* [nss ns-defs _opts overrides]
151170
(println "# Table of contents")
152171
(doseq [[ns-name vars] (sort-by first nss)]
153172
(let [ns (get-in ns-defs [ns-name 0])
@@ -156,8 +175,9 @@
156175
mns (merge mns overriden-ns)]
157176
(when (and (not (:no-doc mns))
158177
(not (:skip-wiki mns)))
159-
(println "- " (format "[`%s`](#%s) %s" ns-name
160-
(str (with-idx (md-munge ns-name) memo))
178+
(println "- " (format "[`%s`](#%s) %s"
179+
ns-name
180+
ns-name
161181
(str (when-let [summary (var-summary ns)]
162182
(str " - " summary)))))
163183
(let [vars (group-by :name vars)
@@ -167,13 +187,12 @@
167187
(when (var-filter (merge v (get overriden-ns var-name)))
168188
(println
169189
" - "
170-
(str (format "[`%s`](#%s)" var-name (with-idx (md-munge var-name) memo))
190+
(str (format "[`%s`](#%s)"
191+
var-name
192+
(str ns-name "/" var-name))
171193
(when-let [summary (var-summary v)]
172194
(str " - " summary))))))))))))
173195

174-
(defn print-toc [memo nss ns-defs opts overrides]
175-
(if (:toc opts)
176-
(print-toc* memo nss ns-defs opts overrides)
177-
(when (:var-links opts)
178-
;; we run toc anyway to populate memo, but suppress output
179-
(with-out-str (print-toc* memo nss ns-defs opts overrides)))))
196+
(defn print-toc [nss ns-defs opts overrides]
197+
(when (:toc opts)
198+
(print-toc* nss ns-defs opts overrides)))

0 commit comments

Comments
 (0)