Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ A release with known breaking changes is marked with:

* `rewrite-clj.zip/insert-right` and `rewrite-clj.zip/append-child` no longer insert a space when inserting/appending after a comment node.
{issue}346[#346] ({lread})
* `rewrite.clj.paredit`
** `pos` arguments now accepts vector `[row col]` in addition to map `{:row :col}`
{issue}344[#344] ({lread})

=== v1.1.49 - 2024-11-18 [[v1.1.49]]

Expand Down
46 changes: 30 additions & 16 deletions src/rewrite_clj/paredit.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
(:require [rewrite-clj.custom-zipper.utils :as u]
[rewrite-clj.node :as nd]
[rewrite-clj.zip :as z]
[rewrite-clj.zip.findz :as fz]
[rewrite-clj.zip.whitespace :as ws]))

#?(:clj (set! *warn-on-reflection* true))
Expand Down Expand Up @@ -130,16 +131,20 @@
- if inside string kills to end of string and stops there
- If inside comment kills to end of line (not including linebreak)

`pos` should provide `{:row :col }` which are relative to the start of the given form the zipper represents
`zloc` must be positioned at a node previous (given depth first) to the node at given pos"
- `zloc` location is (inclusive) starting point for `pos` depth-first search
- `pos` can be a `{:row :col}` map or a `[row col]` vector. The `row` and `col` values are
1-based and relative to the start of the source code the zipper represents.

Throws if `zloc` was not created with [position tracking](/doc/01-user-guide.adoc#position-tracking)."
[zloc pos]
(if-let [candidate (z/find-last-by-pos zloc pos)]
(cond
(string-node? candidate) (kill-in-string-node candidate pos)
(ws/comment? candidate) (kill-in-comment-node candidate pos)
(and (empty-seq? candidate)
(> (:col pos) (-> candidate z/node meta :col))) (z/remove candidate)
:else (kill candidate))
(let [pos (fz/pos-as-map pos)]
(cond
(string-node? candidate) (kill-in-string-node candidate pos)
(ws/comment? candidate) (kill-in-comment-node candidate pos)
(and (empty-seq? candidate)
(> (:col pos) (-> candidate z/node meta :col))) (z/remove candidate)
:else (kill candidate)))
zloc))


Expand Down Expand Up @@ -194,14 +199,21 @@
(defn kill-one-at-pos
"In string and comment aware kill for one node/word at `pos` in `zloc`.

- `zloc` location is (inclusive) starting point for `pos` depth-first search
- `pos` can be a `{:row :col}` map or a `[row col]` vector. The `row` and `col` values are
1-based and relative to the start of the source code the zipper represents.

Throws if `zloc` was not created with [position tracking](/doc/01-user-guide.adoc#position-tracking).

- `(+ |100 100) => (+ |100)`
- `(for |(bar do)) => (foo)`
- `\"|hello world\" => \"| world\"`
- ` ; |hello world => ; |world`"
[zloc pos]
(if-let [candidate (->> (z/find-last-by-pos zloc pos)
(ws/skip z/right* ws/whitespace?))]
(let [[bounds-row bounds-col] (z/position candidate)
(let [pos (fz/pos-as-map pos)
[bounds-row bounds-col] (z/position candidate)
kill-in-node? (not (and (= (:row pos) bounds-row)
(<= (:col pos) bounds-col)))]
(cond
Expand Down Expand Up @@ -457,17 +469,19 @@
(defn split-at-pos
"In string aware split

Perform split at given position `pos` Like split, but:
Perform split at given position `pos` Like split, but if inside string splits string into two strings.

- if inside string splits string into two strings
- `zloc` location is (inclusive) starting point for `pos` depth-first search
- `pos` can be a `{:row :col}` map or a `[row col]` vector. The `row` and `col` values are
1-based and relative to the start of the source code the zipper represents.

`pos` should provide `{:row :col }` which are relative to the start of the given form the zipper represents
`zloc` must be positioned at a node previous (given depth first) to the node at given pos"
Throws if `zloc` was not created with [position tracking](/doc/01-user-guide.adoc#position-tracking)."
[zloc pos]
(if-let [candidate (z/find-last-by-pos zloc pos)]
(if (string-node? candidate)
(split-string candidate pos)
(split candidate))
(let [pos (fz/pos-as-map pos)]
(if (string-node? candidate)
(split-string candidate pos)
(split candidate)))
zloc))

(defn- join-seqs [left right]
Expand Down
23 changes: 18 additions & 5 deletions src/rewrite_clj/zip.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -486,17 +486,30 @@

;; DO NOT EDIT FILE, automatically imported from: rewrite-clj.zip.findz
(defn find-last-by-pos
"Return `zloc` located to the last node spanning position `pos` that satisfies predicate `p?` else `nil`.
Search is depth-first from the current node.
"Return `zloc` located at the last node spanning position `pos` that satisfies the predicate `p?`, else `nil`.

NOTE: Does not ignore whitespace/comment nodes."
- `zloc` location is (inclusive) starting point for `pos` depth-first search
- `pos` can be a `{:row :col}` map or a `[row col]` vector. The `row` and `col` values are 1-based and relative to the
start of the form represented by the zipper.
- `p?` is optional and defaults to `(constantly true)`

Throws if `zloc` was not created with [position tracking](/doc/01-user-guide.adoc#position-tracking).

NOTE: Whitespace and comment nodes are included in the search."
([zloc pos] (rewrite-clj.zip.findz/find-last-by-pos zloc pos))
([zloc pos p?] (rewrite-clj.zip.findz/find-last-by-pos zloc pos p?)))

;; DO NOT EDIT FILE, automatically imported from: rewrite-clj.zip.findz
(defn find-tag-by-pos
"Return `zloc` located to the last node spanning position `pos` with tag `t` else `nil`.
Search is depth-first from the current node."
"Return `zloc` located at the last node spanning position `pos` with tag `t`, else `nil`.

- `zloc` location is (inclusive) starting point for `pos` depth-first search
- `pos` can be a `{:row :col}` map or a `[row col]` vector. The `row` and `col` values are 1-based and relative to the
start of the form represented by the zipper.

Throws if `zloc` was not created with [position tracking](/doc/01-user-guide.adoc#position-tracking).

NOTE: Whitespace and comment nodes are included in the search."
[zloc pos t] (rewrite-clj.zip.findz/find-tag-by-pos zloc pos t))

;; DO NOT EDIT FILE, automatically imported from: rewrite-clj.zip.insert
Expand Down
23 changes: 18 additions & 5 deletions src/rewrite_clj/zip/find.clj
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,16 @@

;; DO NOT EDIT FILE, automatically imported from: rewrite-clj.zip.findz
(defn find-last-by-pos
"Return `zloc` located to the last node spanning position `pos` that satisfies predicate `p?` else `nil`.
Search is depth-first from the current node.
"Return `zloc` located at the last node spanning position `pos` that satisfies the predicate `p?`, else `nil`.

NOTE: Does not ignore whitespace/comment nodes."
- `zloc` location is (inclusive) starting point for `pos` depth-first search
- `pos` can be a `{:row :col}` map or a `[row col]` vector. The `row` and `col` values are 1-based and relative to the
start of the form represented by the zipper.
- `p?` is optional and defaults to `(constantly true)`

Throws if `zloc` was not created with [position tracking](/doc/01-user-guide.adoc#position-tracking).

NOTE: Whitespace and comment nodes are included in the search."
([zloc pos] (rewrite-clj.zip.findz/find-last-by-pos zloc pos))
([zloc pos p?] (rewrite-clj.zip.findz/find-last-by-pos zloc pos p?)))

Expand Down Expand Up @@ -67,8 +73,15 @@

;; DO NOT EDIT FILE, automatically imported from: rewrite-clj.zip.findz
(defn find-tag-by-pos
"Return `zloc` located to the last node spanning position `pos` with tag `t` else `nil`.
Search is depth-first from the current node."
"Return `zloc` located at the last node spanning position `pos` with tag `t`, else `nil`.

- `zloc` location is (inclusive) starting point for `pos` depth-first search
- `pos` can be a `{:row :col}` map or a `[row col]` vector. The `row` and `col` values are 1-based and relative to the
start of the form represented by the zipper.

Throws if `zloc` was not created with [position tracking](/doc/01-user-guide.adoc#position-tracking).

NOTE: Whitespace and comment nodes are included in the search."
[zloc pos t] (rewrite-clj.zip.findz/find-tag-by-pos zloc pos t))

;; DO NOT EDIT FILE, automatically imported from: rewrite-clj.zip.findz
Expand Down
31 changes: 25 additions & 6 deletions src/rewrite_clj/zip/findz.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@
(additional node)))
#(= (base/tag %) t)))

(defn pos-as-vec [pos]
(if (map? pos) [(:row pos) (:col pos)] pos))

(defn pos-as-map [pos]
(if (map? pos) pos (zipmap [:row :col] pos)))

(defn- position-in-range? [zloc pos]
(let [[r c] (if (map? pos) [(:row pos) (:col pos)] pos)]
(let [[r c] (pos-as-vec pos)]
(when (or (<= r 0) (<= c 0))
(throw (ex-info "zipper row and col positions are ones-based" {:pos pos})))
(let [[[zstart-row zstart-col][zend-row zend-col]] (zraw/position-span zloc)]
Expand All @@ -44,10 +50,16 @@
(first))))

(defn find-last-by-pos
"Return `zloc` located to the last node spanning position `pos` that satisfies predicate `p?` else `nil`.
Search is depth-first from the current node.
"Return `zloc` located at the last node spanning position `pos` that satisfies the predicate `p?`, else `nil`.

- `zloc` location is (inclusive) starting point for `pos` depth-first search
- `pos` can be a `{:row :col}` map or a `[row col]` vector. The `row` and `col` values are 1-based and relative to the
start of the form represented by the zipper.
- `p?` is optional and defaults to `(constantly true)`

Throws if `zloc` was not created with [position tracking](/doc/01-user-guide.adoc#position-tracking).

NOTE: Does not ignore whitespace/comment nodes."
NOTE: Whitespace and comment nodes are included in the search."
([zloc pos] (find-last-by-pos zloc pos (constantly true)))
([zloc pos p?]
(->> zloc
Expand Down Expand Up @@ -102,8 +114,15 @@
(find-next zloc f))))

(defn find-tag-by-pos
"Return `zloc` located to the last node spanning position `pos` with tag `t` else `nil`.
Search is depth-first from the current node."
"Return `zloc` located at the last node spanning position `pos` with tag `t`, else `nil`.

- `zloc` location is (inclusive) starting point for `pos` depth-first search
- `pos` can be a `{:row :col}` map or a `[row col]` vector. The `row` and `col` values are 1-based and relative to the
start of the form represented by the zipper.

Throws if `zloc` was not created with [position tracking](/doc/01-user-guide.adoc#position-tracking).

NOTE: Whitespace and comment nodes are included in the search."
([zloc pos t]
(find-last-by-pos zloc pos #(= (base/tag %) t))))

Expand Down
21 changes: 15 additions & 6 deletions test/rewrite_clj/paredit_test.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@
["⊚\"\"" "◬"]]]]
(let [{:keys [pos s]} (th/pos-and-s s)
zloc (z/of-string* s {:track-position? true})]
(is (= expected (-> zloc (pe/kill-at-pos pos) th/root-locmarked-string))))))
(doseq [pos [pos [(:row pos) (:col pos)]]]
(testing (str s " @pos " pos)
(is (= expected (-> zloc (pe/kill-at-pos pos) th/root-locmarked-string))))))))

(deftest kill-one-at-pos-test
;; for this pos fn test, ⊚ in `s` represents character row/col the the `pos`
Expand Down Expand Up @@ -78,7 +80,9 @@
["\"foo bar ⊚do\n lorem\"" "⊚\"foo bar \n lorem\""]]]
(let [{:keys [pos s]} (th/pos-and-s s)
zloc (z/of-string* s {:track-position? true})]
(is (= expected (-> zloc (pe/kill-one-at-pos pos) th/root-locmarked-string))))))
(doseq [pos [pos [(:row pos) (:col pos)]]]
(testing (str s " @pos " pos)
(is (= expected (-> zloc (pe/kill-one-at-pos pos) th/root-locmarked-string))))))))

(deftest slurp-forward-test
(doseq [opts zipper-opts]
Expand Down Expand Up @@ -207,10 +211,15 @@
(is (= expected (-> zloc pe/split th/root-locmarked-string)) "string after"))))))

(deftest split-at-pos-test
(is (= "(⊚\"Hello \" \"World\")"
(-> (th/of-locmarked-string "⊚(\"Hello World\")" {:track-position? true})
(pe/split-at-pos {:row 1 :col 9})
th/root-locmarked-string))))
;; for this pos fn test, ⊚ in `s` represents character row/col the the `pos`
;; ⊚ in `expected` is at zipper node granularity
(doseq [[s expected]
[["(\"Hello ⊚World\")" "(⊚\"Hello \" \"World\")" ]]]
(let [{:keys [pos s]} (th/pos-and-s s)
zloc (z/of-string* s {:track-position? true})]
(doseq [pos [pos [(:row pos) (:col pos)]]]
(testing (str s " @pos " pos)
(is (= expected (-> zloc (pe/split-at-pos pos) th/root-locmarked-string))))))))

(deftest join-test
(doseq [opts zipper-opts]
Expand Down
Loading