A zipper library with extra implementations for useful data structures (mixed edn collections, hiccup, other map structures as well as the normal seq/vector/xml.parse maps). It also has more regular nil usage and functionality for searching and “marking” nodes to find later.
com.jessesherlock/zing {:mvn/version "0.1.0"} / [com.jessesherlock/zing "0.1.0"]
Zing has the standard zipper fns from clojure.zip except that the end isn’t detected by an end? fn as nil is returned when navigating past the end, to match the result when navigating outside the zipper going up, left or right.
There are a few useful additions, such as next-leaf and root-loc that are missing from clojure.zip.
Given a predicate and a zipper loc find scans forward until the predicate matches a loc, search returns a vector of matching locs. reverse-find and reverse-search scan backwards. loc-pred turns a node predicate into a loc predicate.
When editing with a zipper it can be useful to mark locations in order to return to them later. mark-loc edits a loc to “mark” it, with and optional label argument.
Rebound marks the current location, scans forward until a predicate is matched, edits the matching location with the provided function, returns to the original location and removes the mark.
(-> [:a [:b :c] :d [:e [:f [:g :h]] :i]]
;; nav to :b
down right down
;; find :g, replace with :X and return to :b
(rebound (loc-pred #(= :g %)) #(replace % :X))
;; we're back at :b so go up to root
up up
node)
[:a [:b :c] :d [:e [:f [:X :h]] :i]]The standard clojure.zip seq-zip, vector-zip and xml-zip are included as well as a more general tree format, tree-zip (a map with with :label and :children keys for branches), edn-zip for collections of mixed lists, vectors, sets and maps and hiccup-zip for hiccup data.
Uses the standard branch?/children/make-node fns in the zing.zip/Zipper protocol, although they take a loc as an argument instead of a node as the other zipper libraries do. Also the make-node function always takes 3 args. A default implementation of the protocol is supplied for plain maps.
Either define a record with node and path fields or use a regular map like so:
;; vector-zip implemented as a regular map
{:node [:a [:b :c] :d [[:e] :f]]
:path nil
:zing.zip/branch? (comp vector? sut/node)
:zing.zip/children (comp seq sut/node)
:zing.zip/make-node (fn [this loc children] (vec children))}