|
12 | 12 | (ns ^{:doc "Functional hierarchical zipper, with navigation, editing,
|
13 | 13 | and enumeration. See Huet"
|
14 | 14 | :author "Rich Hickey"}
|
15 |
| - rewrite-clj.zip.zip |
| 15 | + rewrite-clj.custom-zipper.core |
16 | 16 | (:refer-clojure :exclude (replace remove next))
|
17 |
| - (:require [rewrite-clj.node.protocols :as node])) |
| 17 | + (:require [rewrite-clj.node.protocols :as node] |
| 18 | + [clojure.zip :as clj-zip])) |
18 | 19 |
|
19 |
| -(defn ^:no-doc zipper |
20 |
| - "Creates a new zipper structure." |
| 20 | +;; ## Switch |
| 21 | +;; |
| 22 | +;; To not force users into using this custom zipper, the following flag |
| 23 | +;; is used to dispatch to `clojure.zip` when set to `false`. |
| 24 | + |
| 25 | +(defn ^:no-doc custom-zipper |
21 | 26 | [root]
|
22 |
| - {:node root |
| 27 | + {::custom? true |
| 28 | + :node root |
23 | 29 | :position [1 1]
|
24 |
| - :parent nil |
25 |
| - :left [] |
26 |
| - :right '()}) |
| 30 | + :parent nil |
| 31 | + :left [] |
| 32 | + :right '()}) |
| 33 | + |
| 34 | +(defn ^:no-doc zipper |
| 35 | + [root] |
| 36 | + (clj-zip/zipper |
| 37 | + node/inner? |
| 38 | + (comp seq node/children) |
| 39 | + node/replace-children |
| 40 | + root)) |
| 41 | + |
| 42 | +(defn ^:no-doc custom-zipper? |
| 43 | + [value] |
| 44 | + (::custom? value)) |
| 45 | + |
| 46 | +(defmacro ^:private defn-switchable |
| 47 | + [sym docstring params & body] |
| 48 | + (let [placeholders (repeatedly (count params) gensym)] |
| 49 | + `(defn ~sym |
| 50 | + ~docstring |
| 51 | + [~@placeholders] |
| 52 | + (if (custom-zipper? ~(first placeholders)) |
| 53 | + (let [~@(interleave params placeholders)] |
| 54 | + ~@body) |
| 55 | + (~(symbol "clojure.zip" (name sym)) ~@placeholders))))) |
| 56 | + |
| 57 | +;; ## Implementation |
27 | 58 |
|
28 |
| -(defn node |
| 59 | +(defn-switchable node |
29 | 60 | "Returns the node at loc"
|
30 | 61 | [{:keys [node]}]
|
31 | 62 | node)
|
32 | 63 |
|
33 |
| -(defn branch? |
| 64 | +(defn-switchable branch? |
34 | 65 | "Returns true if the node at loc is a branch"
|
35 | 66 | [{:keys [node]}]
|
36 | 67 | (node/inner? node))
|
37 | 68 |
|
38 |
| -(defn children |
| 69 | +(defn-switchable children |
39 | 70 | "Returns a seq of the children of node at loc, which must be a branch"
|
40 | 71 | [{:keys [node] :as loc}]
|
41 | 72 | (if (branch? loc)
|
42 | 73 | (seq (node/children node))
|
43 | 74 | (throw (Exception. "called children on a leaf node"))))
|
44 | 75 |
|
45 |
| -(defn ^:no-doc make-node |
| 76 | +(defn-switchable ^:no-doc make-node |
46 | 77 | "Returns a new branch node, given an existing node and new
|
47 | 78 | children. The loc is only used to supply the constructor."
|
48 | 79 | [loc node children]
|
|
51 | 82 | (defn position
|
52 | 83 | "Returns the ones-based [row col] of the start of the current node"
|
53 | 84 | [loc]
|
54 |
| - (:position loc)) |
| 85 | + (if (custom-zipper? loc) |
| 86 | + (:position loc) |
| 87 | + (throw |
| 88 | + (IllegalStateException. |
| 89 | + (str |
| 90 | + "to use the 'position' function, please construct your zipper with " |
| 91 | + "':track-position?' set to true."))))) |
55 | 92 |
|
56 |
| -(defn lefts |
| 93 | +(defn-switchable lefts |
57 | 94 | "Returns a seq of the left siblings of this loc"
|
58 | 95 | [loc]
|
59 | 96 | (map first (:left loc)))
|
60 | 97 |
|
61 |
| -(defn down |
| 98 | +(defn-switchable down |
62 | 99 | "Returns the loc of the leftmost child of the node at this loc, or
|
63 | 100 | nil if no children"
|
64 | 101 | [loc]
|
65 | 102 | (when (branch? loc)
|
66 | 103 | (let [{:keys [node path] [row col] :position} loc
|
67 | 104 | [c & cnext :as cs] (children loc)]
|
68 | 105 | (when cs
|
69 |
| - {:node c |
| 106 | + {::custom? true |
| 107 | + :node c |
70 | 108 | :position [row (+ col (node/leader-length node))]
|
71 |
| - :parent loc |
72 |
| - :left [] |
73 |
| - :right cnext})))) |
| 109 | + :parent loc |
| 110 | + :left [] |
| 111 | + :right cnext})))) |
74 | 112 |
|
75 |
| -(defn up |
| 113 | +(defn-switchable up |
76 | 114 | "Returns the loc of the parent of the node at this loc, or nil if at
|
77 | 115 | the top"
|
78 | 116 | [loc]
|
|
86 | 124 | (concat (map first left) (cons node right))))
|
87 | 125 | parent))))
|
88 | 126 |
|
89 |
| -(defn root |
| 127 | +(defn-switchable root |
90 | 128 | "zips all the way up and returns the root node, reflecting any changes."
|
91 | 129 | [{:keys [end?] :as loc}]
|
92 | 130 | (if end?
|
|
96 | 134 | (recur p)
|
97 | 135 | (node loc)))))
|
98 | 136 |
|
99 |
| -(defn right |
| 137 | +(defn-switchable right |
100 | 138 | "Returns the loc of the right sibling of the node at this loc, or nil"
|
101 | 139 | [loc]
|
102 | 140 | (let [{:keys [node parent position left] [r & rnext :as right] :right} loc]
|
|
107 | 145 | :right rnext
|
108 | 146 | :position (node/+extent position (node/extent node))))))
|
109 | 147 |
|
110 |
| -(defn rightmost |
| 148 | +(defn-switchable rightmost |
111 | 149 | "Returns the loc of the rightmost sibling of the node at this loc, or self"
|
112 | 150 | [loc]
|
113 | 151 | (if-let [next (right loc)]
|
114 | 152 | (recur next)
|
115 | 153 | loc))
|
116 | 154 |
|
117 |
| -(defn left |
| 155 | +(defn-switchable left |
118 | 156 | "Returns the loc of the left sibling of the node at this loc, or nil"
|
119 | 157 | [loc]
|
120 | 158 | (let [{:keys [node parent left right]} loc]
|
|
126 | 164 | :left (pop left)
|
127 | 165 | :right (cons node right))))))
|
128 | 166 |
|
129 |
| -(defn leftmost |
| 167 | +(defn-switchable leftmost |
130 | 168 | "Returns the loc of the leftmost sibling of the node at this loc, or self"
|
131 | 169 | [loc]
|
132 | 170 | (let [{:keys [node parent left right]} loc]
|
|
139 | 177 | :right (concat (map first (rest left)) [node] right)))
|
140 | 178 | loc)))
|
141 | 179 |
|
142 |
| -(defn insert-left |
| 180 | +(defn-switchable insert-left |
143 | 181 | "Inserts the item as the left sibling of the node at this loc,
|
144 | 182 | without moving"
|
145 | 183 | [loc item]
|
|
151 | 189 | :left (conj left [item position])
|
152 | 190 | :position (node/+extent position (node/extent item))))))
|
153 | 191 |
|
154 |
| -(defn insert-right |
| 192 | +(defn-switchable insert-right |
155 | 193 | "Inserts the item as the right sibling of the node at this loc,
|
156 | 194 | without moving"
|
157 | 195 | [loc item]
|
|
162 | 200 | :changed? true
|
163 | 201 | :right (cons item right)))))
|
164 | 202 |
|
165 |
| -(defn replace |
| 203 | +(defn-switchable replace |
166 | 204 | "Replaces the node at this loc, without moving"
|
167 | 205 | [loc node]
|
168 | 206 | (assoc loc :changed? true :node node))
|
169 | 207 |
|
170 | 208 | (defn edit
|
171 | 209 | "Replaces the node at this loc with the value of (f node args)"
|
172 | 210 | [loc f & args]
|
173 |
| - (replace loc (apply f (node loc) args))) |
| 211 | + (if (custom-zipper? loc) |
| 212 | + (replace loc (apply f (node loc) args)) |
| 213 | + (apply clj-zip/edit loc f args))) |
174 | 214 |
|
175 |
| -(defn insert-child |
| 215 | +(defn-switchable insert-child |
176 | 216 | "Inserts the item as the leftmost child of the node at this loc,
|
177 | 217 | without moving"
|
178 | 218 | [loc item]
|
179 | 219 | (replace loc (make-node loc (node loc) (cons item (children loc)))))
|
180 | 220 |
|
181 |
| -(defn append-child |
| 221 | +(defn-switchable append-child |
182 | 222 | "Inserts the item as the rightmost child of the node at this loc,
|
183 | 223 | without moving"
|
184 | 224 | [loc item]
|
185 | 225 | (replace loc (make-node loc (node loc) (concat (children loc) [item]))))
|
186 | 226 |
|
187 |
| -(defn next |
| 227 | +(defn-switchable next |
188 | 228 | "Moves to the next loc in the hierarchy, depth-first. When reaching
|
189 | 229 | the end, returns a distinguished loc detectable via end?. If already
|
190 | 230 | at the end, stays there."
|
|
199 | 239 | (or (right (up p)) (recur (up p)))
|
200 | 240 | (assoc p :end? true))))))
|
201 | 241 |
|
202 |
| -(defn prev |
| 242 | +(defn-switchable prev |
203 | 243 | "Moves to the previous loc in the hierarchy, depth-first. If already
|
204 | 244 | at the root, returns nil."
|
205 | 245 | [loc]
|
|
210 | 250 | loc))
|
211 | 251 | (up loc)))
|
212 | 252 |
|
213 |
| -(defn end? |
| 253 | +(defn-switchable end? |
214 | 254 | "Returns true if loc represents the end of a depth-first walk"
|
215 | 255 | [loc]
|
216 | 256 | (:end? loc))
|
217 | 257 |
|
218 |
| -(defn remove |
| 258 | +(defn-switchable remove |
219 | 259 | "Removes the node at loc, returning the loc that would have preceded
|
220 | 260 | it in a depth-first walk."
|
221 | 261 | [loc]
|
|
0 commit comments