rewrite-clj

lukasz 2024-02-29T15:47:11.765119Z

Can rewrite-clj read metadata? I'm particularly interested in ^:deprecated and ^:added - I wrote a tiny documentation generator using Babashka, and that' the only bit that I'm missing and I couldn't figure out how to do. I did notice that the ^:deprecated string appears when I use rewrite-clj.node but I don't see an obvious way of reading that programmatically without using regexes (uh)

borkdude 2024-02-29T15:48:19.441569Z

yes

borkdude 2024-02-29T15:49:09.869609Z

btw you could maybe also use the clj-kondo analysis data, this spits out data about vars as well by reading rewrite-clj nodes

borkdude 2024-02-29T15:49:26.442819Z

This is used in #quickdoc for documentation generation

lukasz 2024-02-29T15:50:37.724069Z

I noticed that your doc generator uses kondo pod, I was thinking of taking that route eventually but just for my own education I'd love to learn to how to do this just with rewrite-clj :-)

borkdude 2024-02-29T15:50:47.599249Z

alright

lukasz 2024-02-29T15:51:40.883379Z

(I also looked into how Kondo does this, but couldn't dive that deep because of time constraints)

borkdude 2024-02-29T15:52:02.718139Z

user=> (p/parse-string "^:deprecated x")
<meta: ^:deprecated x>
This returns a meta node

borkdude 2024-02-29T15:52:10.780439Z

What is the part, from there, that you don't understand?

lukasz 2024-02-29T15:55:04.565059Z

that part, I get but it's not clear how to read the meta part, here's the relevant part of my code: https://gist.github.com/lukaszkorecki/bddc0c296e044b9e6d0ddfc5623adfd7

lukasz 2024-02-29T15:55:22.303129Z

when I use (z/sexpr) - metadata is not included

borkdude 2024-02-29T15:56:11.554359Z

user=> (meta (n/sexpr (p/parse-string "^:deprecated x")))
{:deprecated true}

lukasz 2024-02-29T15:56:50.603309Z

hmm, I remember trying that out - let me double check

borkdude 2024-02-29T15:58:48.120359Z

user=> (meta (z/sexpr (z/of-string "^:deprecated x")))
{:deprecated true}

lukasz 2024-02-29T16:06:28.076129Z

I see, the above works, but why this doesn't?

# bb
Babashka v1.3.188 REPL.
Use :repl/quit or :repl/exit to quit the REPL.
Clojure rocks, Bash reaches.

user=> (require '[rewrite-clj.zip :as z] '[rewrite-clj.parser :as p])
user=> (meta (z/sexpr (z/of-string "(defn ^:deprecated create \"some doc\" [] :x)")))
nil
user=>

borkdude 2024-02-29T16:07:04.082239Z

because the metadata is not on defn but on the create symbol

lukasz 2024-02-29T16:07:12.006439Z

ohh

lukasz 2024-02-29T16:09:01.107149Z

I assumed I can do something like (into [] (s/expr (z/of-string ...fn source...))) and read the meta that way

lukasz 2024-02-29T16:09:07.786539Z

ok, that's helpful - I'll keep digging, thank you!

lukasz 2024-02-29T16:49:15.562579Z

thank you @borkdude, that was super useful, final impl looks like this:

(defn get-fns-info [parsed]
  (let [fn-info (atom [])]
    (-> parsed
        (z/prewalk (fn [thing]
                     (when (and
                            (z/list? thing)
                            (= "defn" (str (first (z/sexpr thing)))))
                       (let [sexpr (z/sexpr thing)
                             [_f name docstring & _rest] sexpr
                             node (z/node thing)
                             metadata (when-let [meta? (first (filter #(= :meta (:tag %)) (remove n/whitespace? (n/children node))))]
                                        (meta (n/sexpr meta?)))
                             source (str node)
                             position (zipmap [:line :column] (z/position thing))]

                         (swap! fn-info conj
                                {:name name
                                 :metadata metadata
                                 :position position
                                 :source source
                                 :docstring (when (string? docstring)
                                              docstring)})))

                     thing)))
    (sort-by #(-> % :position :line) (deref fn-info))))