Skip to content

Commit

Permalink
Add rewrite-clj.zip of-string* and of-file* (#196)
Browse files Browse the repository at this point in the history
* Add rewrite-clj.zip of-string* and of-file*

These new zip API functions are the same as their of-string and of-file
counterparts except that they do no auto navigation at all.

Closes #189

* usage of deprecated edn functions in user guide

switch to of-node functions instead

* minor typo in user guide example
  • Loading branch information
lread authored Oct 13, 2022
1 parent f4d570d commit c0e9184
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 77 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@ For a list of breaking changes see link:#v1-breaking[breaking changes].

=== Unreleased

* added new `rewrite-clj.zip` functions `of-string*` and `of-file*`, these are versions of `of-string` and `of-file` that do no auto-navigation
https://github.com/clj-commons/rewrite-clj/issues/189[#189]
(thanks @mainej for the analysis work)
* a lazy sequence now coerces to a rewrite-clj list node https://github.com/clj-commons/rewrite-clj/pull/180[#180] (thanks @borkdude!)
* exceptions thrown while reading now include `:row` and `:col` keys in `ex-data` https://github.com/clj-commons/rewrite-clj/pull/181[#181] (thanks @ferdinand-beyer)
* docs
** a docstring typo fix https://github.com/clj-commons/rewrite-clj/pull/191[#191] (thanks @BTowersCoding!)


=== v1.1.45

* dropped the alpha status
Expand Down
24 changes: 13 additions & 11 deletions doc/01-user-guide.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Read link:design/01-merging-rewrite-clj-and-rewrite-cljs.adoc[Merging rewrite-cl
Thanks to @xsc's transfer of rewrite-clj to clj-commons we'll continue on with the rewrite-clj namespace and clojars deploy target.

To upgrade to rewrite-clj v1, update your project dependencies.
If you were using both rewrite-cljs and rewrite-clj v0 in your project you can now drop the rewrite-cljs dependency.
If you were using both rewrite-cljs and rewrite-clj v0 in your project you should now drop the rewrite-cljs dependency.

Rewrite-clj unit tests are run against the current version of ClojureScript and Clojure versions >= v1.8.0.
We recommend that while bumping your rewrite-clj dependency to v1, that you also bump your Clojure and ClojureScript dependencies to current official releases.
Expand Down Expand Up @@ -229,8 +229,10 @@ You may want to refer to link:#nodes[rewrite-clj nodes] while reviewing this int
====
The zip location movement functions (`right`, `left`, `up`, `down`, etc) skip over Clojure whitespace nodes and comment nodes.
Remember that Clojure whitespace includes commas.
If you want to navigate over all nodes, use the `+*+` counterparts (`right*`, `left*`, `up*`, `down*`, etc).
Similarily, the zipper creation functions `of-node`, `of-string` and `of-file` automatically skip over the the first Clojure whitespace and comment nodes.
This is usually appropriate, but if you don't want this auto-navigation on create use the `+*+` counterparts `of-node*`, `of-string*`, and `of-file*`.
====

See link:{cljdoc-api-url}/rewrite-clj.zip[zip API docs].
Expand Down Expand Up @@ -410,23 +412,23 @@ Let's explore:
(require '[rewrite-clj.zip :as z])
;; Let's contrive an example with multiple top level forms:
(def zloc (z/of-string "(def x 1) (def y [2 3 [4 [5]]])"))
;; Let's contrive an example with multiple top level forms:
(def s "(def x 1) (def y [2 3 [4 [5]]])")
;; Now let's add 100 to all numbers:
(-> zloc
(-> (z/of-string s)
(z/postwalk (fn select [zloc] (number? (z/sexpr zloc)))
(fn visit [zloc] (z/edit zloc + 100)))
z/root-string)
;; => "(def x 101) (def y [2 3 [4 [5]]])"
;; Hmmm... what happened? Only the first number was affected.
;; A new zipper automaticaly navigates to the first non-whitespace/non-comment node.
;; A new zipper created by of-string automaticaly navigates to the first non-whitespace/non-comment node.
;; In our example, this is node (def x 1).
;; Our walk was isolated to current node (def x 1) so that's all that got updated
;; We can adapt to walk all nodes with a movement up to the top level prior to our walk
(-> zloc
z/up
;; We can adapt to walk all nodes by instead using of-string* which does no auto navigation
(-> (z/of-string* s)
(z/postwalk (fn select [zloc] (number? (z/sexpr zloc)))
(fn visit [zloc] (z/edit zloc + 100)))
z/root-string)
Expand Down Expand Up @@ -532,7 +534,7 @@ The options passed into the original zipper on creation will not be automaticall
z/down z/right z/right
(z/edit inc)
z/root ;; <- applying changes and getting root node
(z/edn zip-opts) ;; <- pass the original zip-opts on creation of new zipper
(z/of-node zip-opts) ;; <- pass the original zip-opts on creation of new zipper
z/down z/right z/right
(z/edit inc)
(z/root-string))
Expand All @@ -547,7 +549,7 @@ The link:#zip-api[zip API] makes use of the parser API to parse Clojure into zip
If your focus is parsing instead of rewriting, you might find this lower level API useful.
Keep in mind that if you forgo the zip API, you forgo niceties such as the automatic handling of whitespace.

You can choose to parse the first, or all forms from a string or a file.footnote:file[]
You can choose to parse the first, or all forms from a string or, if using Clojure, a file.

Here we parse a single form from a string:

Expand Down Expand Up @@ -1176,7 +1178,7 @@ For example, if you know namespace and alias info for the code rewrite-clj is op
The `:auto-resolve` option is accepted in the `opts` map arg for:

* The `rewrite-clj.node` namespace functions `sexpr` and `child-sexpr`.
* The `rewrite-clj.zip` namespace zipper creation functions `edn*`, `edn`, `of-string` and `of-file`.
* The `rewrite-clj.zip` namespace zipper creation functions `of-node*`, `of-node`, `of-string*`, `of-string`, `of-file*` and `of-file`.
The resulting zipper will then automatically apply your `:auto-resolve` within any zip operation that makes use of sexpr, namely:
** `sexpr`
** `find-value` and `find-next-value` - sexpr is applied to each node to get the "value" for comparison
Expand Down
57 changes: 44 additions & 13 deletions src/rewrite_clj/zip.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,16 @@
Because this API contains many functions, we offer the following categorized listing:
**Create a zipper**
**Create a zipper and move to first non-whitespace/comment node**
[[of-node]]
[[of-node*]]
[[of-string]]
[[of-file]]
**Create a zipper without skipping any nodes**
[[of-node*]]
[[of-string*]]
[[of-file*]]
**Move**
[[left]]
[[right]]
Expand Down Expand Up @@ -225,7 +229,6 @@
"Create and return zipper from a rewrite-clj `node` (likely parsed by [[rewrite-clj.parser]]),
and move to the first non-whitespace/non-comment child. If node is not forms node, is wrapped in forms node
for a consistent root.
Optional `opts` can specify:
- `:track-position?` set to `true` to enable ones-based row/column tracking, see [docs on position tracking](/doc/01-user-guide.adoc#position-tracking).
- `:auto-resolve` specify a function to customize namespaced element auto-resolve behavior, see [docs on namespaced elements](/doc/01-user-guide.adoc#namespaced-elements)"
Expand Down Expand Up @@ -270,14 +273,28 @@

;; DO NOT EDIT FILE, automatically imported from: rewrite-clj.zip.base
(defn of-string
"Create and return zipper from all forms in Clojure/ClojureScript/EDN string `s`.
"Create and return zipper from all forms in Clojure/ClojureScript/EDN string `s`, and move to the first non-whitespace/non-comment child.
See [[of-string*]] for same but with no automatic move.
Optional `opts` can specify:
- `:track-position?` set to `true` to enable ones-based row/column tracking, see [docs on position tracking](/doc/01-user-guide.adoc#position-tracking).
- `:auto-resolve` specify a function to customize namespaced element auto-resolve behavior, see [docs on namespaced elements](/doc/01-user-guide.adoc#namespaced-elements)"
([s] (rewrite-clj.zip.base/of-string s))
([s opts] (rewrite-clj.zip.base/of-string s opts)))

;; DO NOT EDIT FILE, automatically imported from: rewrite-clj.zip.base
(defn ^{:added "1.1.46"} of-string*
"Create and return zipper from all forms in Clojure/ClojureScript/END string `s`, and do no automatic move.
See [[of-string]] for same but with automatic move to first interesting node.
Optional `opts` can specify:
- `:track-position?` set to `true` to enable ones-based row/column tracking, see [docs on position tracking](/doc/01-user-guide.adoc#position-tracking).
- `:auto-resolve` specify a function to customize namespaced element auto-resolve behavior, see [docs on namespaced elements](/doc/01-user-guide.adoc#namespaced-elements)"
([s] (rewrite-clj.zip.base/of-string* s))
([s opts] (rewrite-clj.zip.base/of-string* s opts)))

;; DO NOT EDIT FILE, automatically imported from: rewrite-clj.zip.base
(defn ^{:added "0.4.0"} string
"Return string representing the current node in `zloc`."
Expand Down Expand Up @@ -797,12 +814,11 @@
When `p?` is not specified `f` is called on all locations.
Note that by default a newly created zipper automatically navigates to the first non-whitespace
node. If you want to be sure to walk all forms in a zipper, you'll want to navigate one up prior to your walk:
To walk all nodes, you'll want to walk from the root node.
You can do this by, for example, using [[of-string*]] instead of [[of-string]].
```Clojure
(-> (zip/of-string \"my clojure forms\")
zip/up
(-> (zip/of-string* \"my clojure forms\")
(zip/prewalk ...))
```
Expand Down Expand Up @@ -840,12 +856,11 @@
When `p?` is not specified `f` is called on all locations.
Note that by default a newly created zipper automatically navigates to the first non-whitespace
node. If you want to be sure to walk all forms in a zipper, you'll want to navigate one up prior to your walk:
To walk all nodes, you'll want to walk from the root node.
You can do this by, for example, using [[of-string*]] instead of [[of-string]].
```Clojure
(-> (zip/of-string \"my clojure forms\")
zip/up
(-> (zip/of-string* \"my clojure forms\")
(zip/postwalk ...))
```
Expand Down Expand Up @@ -943,14 +958,30 @@

;; DO NOT EDIT FILE, automatically imported from: rewrite-clj.zip.base
(defn of-file
"Create and return zipper from all forms in Clojure/ClojureScript/EDN File `f`.
"Create and return zipper from all forms in Clojure/ClojureScript/EDN File `f`, and move to the first non-whitespace/non-comment child.
See [[of-file*]] for same but with no automatic move.
Optional `opts` can specify:
- `:track-position?` set to `true` to enable ones-based row/column tracking, see [docs on position tracking](/doc/01-user-guide.adoc#position-tracking).
- `:auto-resolve` specify a function to customize namespaced element auto-resolve behavior, see [docs on namespaced elements](/doc/01-user-guide.adoc#namespaced-elements)"
([f] (rewrite-clj.zip.base/of-file f))
([f opts] (rewrite-clj.zip.base/of-file f opts))))

#?(:clj

;; DO NOT EDIT FILE, automatically imported from: rewrite-clj.zip.base
(defn ^{:added "1.1.46"} of-file*
"Create and return zipper from all forms in Clojure/ClojureScript/EDN File `f`, and do no automatic move.
See [[of-file]] for same but with automatic move to first interesting node.
Optional `opts` can specify:
- `:track-position?` set to `true` to enable ones-based row/column tracking, see [docs on position tracking](/doc/01-user-guide.adoc#position-tracking).
- `:auto-resolve` specify a function to customize namespaced element auto-resolve behavior, see [docs on namespaced elements](/doc/01-user-guide.adoc#namespaced-elements)"
([f] (rewrite-clj.zip.base/of-file* f))
([f opts] (rewrite-clj.zip.base/of-file* f opts))))


;; DO NOT EDIT FILE, automatically imported from: rewrite-clj.custom-zipper.core
(defn right*
Expand Down
38 changes: 33 additions & 5 deletions src/rewrite_clj/zip/base.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
"Create and return zipper from a rewrite-clj `node` (likely parsed by [[rewrite-clj.parser]]),
and move to the first non-whitespace/non-comment child. If node is not forms node, is wrapped in forms node
for a consistent root.
Optional `opts` can specify:
- `:track-position?` set to `true` to enable ones-based row/column tracking, see [docs on position tracking](/doc/01-user-guide.adoc#position-tracking).
- `:auto-resolve` specify a function to customize namespaced element auto-resolve behavior, see [docs on namespaced elements](/doc/01-user-guide.adoc#namespaced-elements)"
Expand Down Expand Up @@ -95,26 +94,55 @@
(some-> zloc zraw/node node/value))

;; ## Read
(defn of-string*
"Create and return zipper from all forms in Clojure/ClojureScript/END string `s`, and do no automatic move.
See [[of-string]] for same but with automatic move to first interesting node.
Optional `opts` can specify:
- `:track-position?` set to `true` to enable ones-based row/column tracking, see [docs on position tracking](/doc/01-user-guide.adoc#position-tracking).
- `:auto-resolve` specify a function to customize namespaced element auto-resolve behavior, see [docs on namespaced elements](/doc/01-user-guide.adoc#namespaced-elements)"
([s] (of-string* s {}))
([s opts]
(some-> s p/parse-string-all (of-node* opts))))

(defn of-string
"Create and return zipper from all forms in Clojure/ClojureScript/EDN string `s`.
"Create and return zipper from all forms in Clojure/ClojureScript/EDN string `s`, and move to the first non-whitespace/non-comment child.
See [[of-string*]] for same but with no automatic move.
Optional `opts` can specify:
- `:track-position?` set to `true` to enable ones-based row/column tracking, see [docs on position tracking](/doc/01-user-guide.adoc#position-tracking).
- `:auto-resolve` specify a function to customize namespaced element auto-resolve behavior, see [docs on namespaced elements](/doc/01-user-guide.adoc#namespaced-elements)"
([s] (of-string s {}))
([s opts]
(some-> s p/parse-string-all (edn opts))))
(some-> s p/parse-string-all (of-node opts))))

#?(:clj
(defn of-file*
"Create and return zipper from all forms in Clojure/ClojureScript/EDN File `f`, and do no automatic move.
See [[of-file]] for same but with automatic move to first interesting node.
Optional `opts` can specify:
- `:track-position?` set to `true` to enable ones-based row/column tracking, see [docs on position tracking](/doc/01-user-guide.adoc#position-tracking).
- `:auto-resolve` specify a function to customize namespaced element auto-resolve behavior, see [docs on namespaced elements](/doc/01-user-guide.adoc#namespaced-elements)"
([f] (of-file* f {}))
([f opts]
(some-> f p/parse-file-all (of-node* opts)))))

#?(:clj
(defn of-file
"Create and return zipper from all forms in Clojure/ClojureScript/EDN File `f`.
"Create and return zipper from all forms in Clojure/ClojureScript/EDN File `f`, and move to the first non-whitespace/non-comment child.
See [[of-file*]] for same but with no automatic move.
Optional `opts` can specify:
- `:track-position?` set to `true` to enable ones-based row/column tracking, see [docs on position tracking](/doc/01-user-guide.adoc#position-tracking).
- `:auto-resolve` specify a function to customize namespaced element auto-resolve behavior, see [docs on namespaced elements](/doc/01-user-guide.adoc#namespaced-elements)"
([f] (of-file f {}))
([f opts]
(some-> f p/parse-file-all (edn opts)))))
(some-> f p/parse-file-all (of-node opts)))))

;; ## Write

Expand Down
14 changes: 6 additions & 8 deletions src/rewrite_clj/zip/walk.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,11 @@
When `p?` is not specified `f` is called on all locations.
Note that by default a newly created zipper automatically navigates to the first non-whitespace
node. If you want to be sure to walk all forms in a zipper, you'll want to navigate one up prior to your walk:
To walk all nodes, you'll want to walk from the root node.
You can do this by, for example, using [[of-string*]] instead of [[of-string]].
```Clojure
(-> (zip/of-string \"my clojure forms\")
zip/up
(-> (zip/of-string* \"my clojure forms\")
(zip/prewalk ...))
```
Expand Down Expand Up @@ -109,12 +108,11 @@
When `p?` is not specified `f` is called on all locations.
Note that by default a newly created zipper automatically navigates to the first non-whitespace
node. If you want to be sure to walk all forms in a zipper, you'll want to navigate one up prior to your walk:
To walk all nodes, you'll want to walk from the root node.
You can do this by, for example, using [[of-string*]] instead of [[of-string]].
```Clojure
(-> (zip/of-string \"my clojure forms\")
zip/up
(-> (zip/of-string* \"my clojure forms\")
(zip/postwalk ...))
```
Expand Down
14 changes: 12 additions & 2 deletions template/rewrite_clj/zip.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@
Because this API contains many functions, we offer the following categorized listing:
**Create a zipper**
**Create a zipper and move to first non-whitespace/comment node**
[[of-node]]
[[of-node*]]
[[of-string]]
[[of-file]]
**Create a zipper without skipping any nodes**
[[of-node*]]
[[of-string*]]
[[of-file*]]
**Move**
[[left]]
[[right]]
Expand Down Expand Up @@ -184,6 +188,7 @@
length
^{:deprecated "0.4.0"} value
of-string
^{:added "1.1.46"} of-string*
^{:added "0.4.0"} string
^{:deprecated "0.4.0"} ->string
^{:added "0.4.0"} root-string
Expand Down Expand Up @@ -255,6 +260,11 @@
{:from [[rewrite-clj.zip.base
of-file]]}})

#?(:clj
#_{:import-vars/import
{:from [[rewrite-clj.zip.base
^{:added "1.1.46"} of-file*]]}})

#_{:import-vars/import
{:opts {:sym-to-pattern "@@orig-name@@*"
:doc-to-pattern "Raw version of [[@@orig-name@@]].\n\n@@orig-doc@@\n\nNOTE: This function does not skip, nor provide any special handling for whitespace/comment nodes."}
Expand Down
Loading

0 comments on commit c0e9184

Please sign in to comment.