Fork me on GitHub
#rewrite-clj
<
2022-11-11
>
zharinov12:11:14

Please help me figure out how to attach custom metadata to the nodes using walk functions?

(->
  (z/of-string "(1 2 3)" {:track-position? true})
  (z/up)
  (z/postwalk
    (fn [zloc]
      (z/subedit-node zloc #(vary-meta % merge {:foo/bar :baz}))))
  (z/down)
  (z/next)
  (z/node)
  (meta))
;; => {:row 1, :col 2, :end-row 1, :end-col 3}
I've been expecting to see my custom data here, but seems like either my approach is wrong or metadata was just ignored here

borkdude12:11:01

clojure postwalk is known to lose metadata in some cases, perhaps rewrite-clj calls the clojure one? can you check the source?

zharinov14:11:38

Seems like it have something to do with subedit-node, not the postwalk functionality:

(->
    (z/of-string "(1 2 3)")
    (z/subedit-node #(vary-meta % merge {:foo/bar :baz}))
    (z/node)
    (meta))
;; => {:row 1, :col 1, :end-row 1, :end-col 8}

lread13:11:27

@s_zharinov I'll take a peek sometime soon and lend a hand.

❤️ 1
zharinov15:11:40

What I've found so far: • z/subedit-node and z/edit-node apply function to zloc, not the node • z/edit applies function to the underlying data, i.e. literally things like 1, 2, 3 and (1 2 3) • The only way I was able to find is zloc direct modification:

(->
  "(1 2 3)"
  (z/of-string)
  (z/up)
  (z/postwalk
    (fn [zloc]
      (assoc zloc
        :changed? true
        :node (vary-meta (:node zloc) merge {:foo (rand-int 100)}))))
  (z/postwalk
    (fn [zloc]
      (prn (meta (z/node zloc)))
      zloc)))
I don't like to rely on implementation details, but I think I'm okay with it as far as my code is tested 😊

lread15:11:50

Ok here's maybe something cleaner:

(require '[rewrite-clj.zip :as z])

(-> "(1 2 3)"
    (z/of-string  {:track-position? true})
    z/up
    (z/postwalk
      (fn [zloc]
        (z/edit* zloc #(vary-meta % merge {:foo/bar :baz}))))
    z/down
    z/next
    z/node
    meta)
;; => {:row 1, :col 2, :end-row 1, :end-col 3, :foo/bar :baz}

lread15:11:47

The z/edit* works directly on the node without attempting any coercion.

lread15:11:22

Here's me dumping all nodes, just to verify we seem all good with this approach:

(-> "(1 2 3)"
    (z/of-string  {:track-position? true})
    z/up
    (z/postwalk
      (fn [zloc]
        (z/edit* zloc #(vary-meta % merge {:foo/bar :baz}))))
    (->> (iterate z/next)
         (take-while identity)
         (take-while (complement z/end?))
         (map (fn [zloc]
                [(z/string zloc)
                 (-> zloc z/node meta)]))))
;; => (["(1 2 3)" {:row 1, :col 1, :end-row 1, :end-col 8, :foo/bar :baz}]
;;     ["(1 2 3)" {:row 1, :col 1, :end-row 1, :end-col 8, :foo/bar :baz}]
;;     ["1" {:row 1, :col 2, :end-row 1, :end-col 3, :foo/bar :baz}]
;;     ["2" {:row 1, :col 4, :end-row 1, :end-col 5, :foo/bar :baz}]
;;     ["3" {:row 1, :col 6, :end-row 1, :end-col 7, :foo/bar :baz}])

lread15:11:10

Does that work for your use case @s_zharinov?

zharinov15:11:22

This is exactly what I was looking for, thanks! 👍👍

👍 1