rewrite-clj

2021-11-03T13:36:20.117600Z

here’s a question that i’m struggling to answer: i am iterating over the above changed files, hoping to remove all of the deftest blocks that are empty. when I call z/remove when the zloc is at the <list: (deftest blahtest)> node, it changes the zloc to the deepest token immediately above/before, meaning I have to traverse all the way back to the level of the deftests every time I call it. if I go too far, then i’m doing a lot of extra work as I then have to iterate over all of the forms above the zloc again.

2021-11-03T13:37:58.118100Z

that means I have to special case my find-root-deftest to also look for ns forms because sometimes the empty deftest is the first test in the file and when I remove it, i am placed at <token: :all> which is in <vector: [clojure.test :refer :all]> which is in <list: (:require … [clojure.test :refer :all])> which is in <list: (ns game.cards.example (:require … ))> which is of course not a deftest lol

2021-11-03T13:41:10.120900Z

i would expect that the zloc doesn’t “change” when removing something, that it would be like I called z/right (or maybe z/left like we’re stepping back to reassess). given that I can’t just call z/right when at the deepest node behind the removed zloc makes me do a lot of extra work to get back to where I was, with as far as I can tell, no visible benefit

lread 2021-11-03T16:29:23.125700Z

@nbtheduke, yeah z/remove can be a bit tricky. The behavior matches that of clojure.zip/remove. The location does kinda have to change tho, right? The location you z/removed is now gone. I think maybe (?) you are talking about this kind of case:

(-> "([1 [2 [3]]] 8 9)"
    z/of-string
    z/down
    z/right
    z/remove
    ((juxt z/string z/root-string)))
;; => ["3" "([1 [2 [3]]] 9)"]
After deleting 8 you are now at located at a nested 3.

2021-11-03T16:44:36.126100Z

right, good example. that’s the issue i’m running into. i’d love for a z/remove that ends up either pointing to [1 [2 [3]] or 9

lread 2021-11-03T16:45:32.126400Z

But what if 8 had no siblings?

2021-11-03T16:46:58.127800Z

(-> "[8]" z/of-string z/down z/remove ((juxt z/string z/root-string))) ;; => ["[]", "[]"], right?

2021-11-03T16:47:16.128200Z

if it has no siblings, it goes up one level

2021-11-03T16:47:33.128900Z

or maybe it stays at an “empty” space within the vector/list

lread 2021-11-03T16:47:51.129300Z

I sometimes wonder if a remove-right and a remove-left might be helpful.

lread 2021-11-03T16:48:17.129500Z

But maybe not.

lread 2021-11-03T16:49:10.130400Z

When removing things I will sometimes resort to using a sub zipper. I dig down and remove what I want but don’t lose my spot.

lread 2021-11-03T16:54:51.131600Z

Other times a walk can be handy

(-> "([1 [2 [3]]] 8 9 3 10)"
    z/of-string
    z/up
    (z/prewalk #(= "3" (z/string %)) z/remove)
    z/root-string)
;; => "([1 [2 []]] 8 9 10)"

2021-11-03T16:55:17.131800Z

huh interesting

lread 2021-11-03T16:56:25.133100Z

There are also some kill function in the paredit API that I have not experimented with much yet. I don’t think they fit your use case though.

2021-11-03T16:56:47.133600Z

yeah, i looked at those but they were a fundamental shift in how to think about it so i didn’t pursue

lread 2021-11-03T17:00:09.134100Z

The walk technique might work for you.

2021-11-03T17:04:03.134400Z

that does indeed! excellent, thank you

lread 2021-11-03T17:04:24.135Z

Cool!

2021-11-03T17:04:35.135200Z

(defn find-root-deftest [zloc]
  (if (or (-> zloc z/down deftest?)
          (-> zloc z/down z/string (= "ns")))
    zloc
    (recur (z/up zloc))))

(defn clean-deftest [zloc]
  (if (and (deftest? (z/down zloc))
           (-> zloc z/down z/right z/right z/end?))
    (-> zloc z/remove find-root-deftest)
    zloc))

(defn clean-file [zloc]
  (let [zloc (clean-deftest zloc)]
    (if (z/end? (z/right zloc))
      zloc
      (recur (z/right zloc)))))
to
(defn clean-file [zloc]
  (z/prewalk
    (z/up zloc)
    (fn [zloc]
      (and (-> zloc z/down deftest?)
           (-> zloc z/down z/right z/right z/end?)))
    z/remove))

2021-11-03T17:05:03.135500Z

chefs_kiss

lread 2021-11-03T17:06:18.136400Z

Hey, curious, what is (-> zloc z/down z/right z/right z/end?) doing?

2021-11-03T17:07:05.137500Z

given (deftest example) , i want to see if it’s empty after the name (“example”)

2021-11-03T17:07:23.138Z

down to enter the list, right right to move one past “example”

lread 2021-11-03T17:07:25.138100Z

(remember that end? is for the end last node in a depth-first walk)

2021-11-03T17:07:57.138800Z

if there’s a better way to check that the list is only 2 non-whitespace, non-comment elements long, i’m all for it

2021-11-03T17:08:30.139300Z

in a macro, i’d say (and (= 'deftest (first l)) (= 2 (count l)))

lread 2021-11-03T18:00:15.140600Z

@nbtheduke would something like this work?

(-> "(deftest empty)

(deftest empty2


)

(deftest has-stuff-so-keep 1)

(deftest with-comment
;; don't delete me, I have a comment!
)

(;; don't delete me either!
deftest with-comment2

)

(deftest ;; comment
   with-comment3 
)
"
    z/of-string
    z/up
    (z/prewalk (fn [zloc]
                 (and
                   (z/list? zloc)
                   (= "deftest" (-> zloc z/down z/string))
                   ;; no sibs?
                   (not (-> zloc z/down z/right z/right))
                   ;; no comments? (we use * here because comments are otherwise skipped)
                   (not (-> zloc z/down*
                            (z/find z/right* #(n/comment? (z/node %)))))))
               z/remove)
    z/print-root)
Ouputs:
(deftest has-stuff-so-keep 1)

(deftest with-comment
;; don't delete me, I have a comment!
)

(;; don't delete me either!
deftest with-comment2

)

(deftest ;; comment
   with-comment3 
)

2021-11-03T18:01:38.141600Z

I intend to delete empty deftests even with comments. Otherwise looks right to me!

lread 2021-11-03T18:03:49.142700Z

Ah! Ok, simpler then:

(-> "(deftest empty)

(deftest empty2


)

(deftest has-stuff-so-keep 1)

(deftest with-comment
;; don't delete me, I have a comment!
)

(;; don't delete me either!
deftest with-comment2

)

(deftest ;; comment
   with-comment3 
)
"
    z/of-string
    z/up
    (z/prewalk (fn [zloc]
                 (and
                   (z/list? zloc)
                   (= "deftest" (-> zloc z/down z/string))
                   ;; no sibs?
                   (not (-> zloc z/down z/right z/right))))
               z/remove)
    z/print-root)
Outputs:
(deftest has-stuff-so-keep 1)

lread 2021-11-03T18:05:26.143800Z

So z/end? just happened to work for you because (z/end? nil) returns true! So basically your original was just fine. Just omit the z/end?!

lread 2021-11-03T18:07:35.144600Z

Oh and negate that test. So (-> zloc z/down z/right z/right z/end?) becomes (not (-> zloc z/down z/right z/right))

lread 2021-11-03T18:07:42.144800Z

Or equivalent.

2021-11-03T18:22:51.145Z

interesting, okay, cool

2021-11-03T18:24:08.145300Z

thank you very much, i appreciate all of the help

lread 2021-11-03T19:03:26.146400Z

You are most welcome. Thanks for the interesting questions!