Skip to content

Commit 1bba6ea

Browse files
committed
Introduce commands to convert collection type
1 parent c7a3555 commit 1bba6ea

File tree

5 files changed

+202
-9
lines changed

5 files changed

+202
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
`clojure-ts-thread-last-all`.
1616
- [#90](https://github.com/clojure-emacs/clojure-ts-mode/pull/90): Introduce `clojure-ts-cycle-privacy`.
1717
- [#91](https://github.com/clojure-emacs/clojure-ts-mode/pull/91): Introduce `clojure-ts-cycle-keyword-string`.
18+
- Add commands to convert between collections types.
1819

1920
## 0.3.0 (2025-04-15)
2021

README.md

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -400,17 +400,33 @@ vice versa.
400400
explicitly with setting `clojure-ts-use-metadata-for-defn-privacy` to `t` for
401401
`defn`s too.
402402

403+
### Convert collection
404+
405+
Convert any given collection at point to list, quoted list, map, vector or
406+
set. The following commands are available:
407+
408+
- `clojure-ts-convert-collection-to-list`
409+
- `clojure-ts-convert-collection-to-quoted-list`
410+
- `clojure-ts-convert-collection-to-map`
411+
- `clojure-ts-convert-collection-to-vector`
412+
- `clojure-ts-convert-collection-to-set`
413+
403414
### Default keybindings
404415

405-
| Keybinding | Command |
406-
|:----------------------------|:----------------------------------|
407-
| `C-:` | `clojure-ts-cycle-keyword-string` |
408-
| `C-c SPC` | `clojure-ts-align` |
409-
| `C-c C-r t` / `C-c C-r C-t` | `clojure-ts-thread` |
410-
| `C-c C-r u` / `C-c C-r C-u` | `clojure-ts-unwind` |
411-
| `C-c C-r f` / `C-c C-r C-f` | `clojure-ts-thread-first-all` |
412-
| `C-c C-r l` / `C-c C-r C-l` | `clojure-ts-thread-last-all` |
413-
| `C-c C-r p` / `C-c C-r C-p` | `clojure-ts-cycle-privacy` |
416+
| Keybinding | Command |
417+
|:----------------------------|:-----------------------------------------------|
418+
| `C-:` | `clojure-ts-cycle-keyword-string` |
419+
| `C-c SPC` | `clojure-ts-align` |
420+
| `C-c C-r t` / `C-c C-r C-t` | `clojure-ts-thread` |
421+
| `C-c C-r u` / `C-c C-r C-u` | `clojure-ts-unwind` |
422+
| `C-c C-r f` / `C-c C-r C-f` | `clojure-ts-thread-first-all` |
423+
| `C-c C-r l` / `C-c C-r C-l` | `clojure-ts-thread-last-all` |
424+
| `C-c C-r p` / `C-c C-r C-p` | `clojure-ts-cycle-privacy` |
425+
| `C-c C-r (` / `C-c C-r C-(` | `clojure-ts-convert-collection-to-list` |
426+
| `C-c C-r '` / `C-c C-r C-'` | `clojure-ts-convert-collection-to-quoted-list` |
427+
| `C-c C-r {` / `C-c C-r C-{` | `clojure-ts-convert-collection-to-map` |
428+
| `C-c C-r [` / `C-c C-r C-[` | `clojure-ts-convert-collection-to-vector` |
429+
| `C-c C-r #` / `C-c C-r C-#` | `clojure-ts-convert-collection-to-set` |
414430

415431
### Customize refactoring commands prefix
416432

clojure-ts-mode.el

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2048,6 +2048,76 @@ value is `clojure-ts-thread-all-but-last'."
20482048
(user-error "No string or keyword at point")))
20492049
(goto-char pos)))
20502050

2051+
(defun clojure-ts--collection-node-at-point ()
2052+
"Return node at point that represent a collection."
2053+
(when-let* ((node (thread-first (point)
2054+
(treesit-node-at 'clojure)
2055+
(treesit-parent-until (rx bol
2056+
(or "map_lit"
2057+
"vec_lit"
2058+
"set_lit"
2059+
"list_lit"
2060+
"quoting_lit")
2061+
eol)))))
2062+
(cond
2063+
;; If node is a list, check if it's quoted.
2064+
((string= (treesit-node-type node) "list_lit")
2065+
(if-let* ((parent (treesit-node-parent node))
2066+
((string= (treesit-node-type parent) "quoting_lit")))
2067+
parent
2068+
node))
2069+
;; If the point is at the quote character, check if the child node is a
2070+
;; list.
2071+
((string= (treesit-node-type node) "quoting_lit")
2072+
(when-let* ((first-child (clojure-ts--node-child-skip-metadata node 0))
2073+
((string= (treesit-node-type first-child) "list_lit")))
2074+
node))
2075+
(t node))))
2076+
2077+
(defun clojure-ts--convert-collection (coll-open coll-close &optional prefix)
2078+
"Convert collection at point to another collection type.
2079+
2080+
The original collection is being unwrapped and wrapped between COLL-OPEN
2081+
and COLL-CLOSE. If PREFIX is non-nil it's inserted before COLL-OPEN."
2082+
(if-let* ((coll-node (clojure-ts--collection-node-at-point)))
2083+
(save-excursion
2084+
(goto-char (treesit-node-start coll-node))
2085+
(when (string-match-p (rx (or "set_lit" "quoting_lit"))
2086+
(treesit-node-type coll-node))
2087+
(delete-char 1))
2088+
(let ((parens-require-spaces nil)
2089+
(delete-pair-blink-delay 0))
2090+
(when prefix
2091+
(insert-char prefix))
2092+
(insert-pair 1 coll-open coll-close)
2093+
(delete-pair 1)))
2094+
(user-error "No collection at point to convert")))
2095+
2096+
(defun clojure-ts-convert-collection-to-list ()
2097+
"Convert collection at point to list."
2098+
(interactive)
2099+
(clojure-ts--convert-collection ?\( ?\)))
2100+
2101+
(defun clojure-ts-convert-collection-to-quoted-list ()
2102+
"Convert collection at point to quoted list."
2103+
(interactive)
2104+
(clojure-ts--convert-collection ?\( ?\) ?'))
2105+
2106+
(defun clojure-ts-convert-collection-to-map ()
2107+
"Convert collection at point to map."
2108+
(interactive)
2109+
(clojure-ts--convert-collection ?{ ?}))
2110+
2111+
(defun clojure-ts-convert-collection-to-vector ()
2112+
"Convert collection at point to vector."
2113+
(interactive)
2114+
(clojure-ts--convert-collection ?\[ ?\]))
2115+
2116+
(defun clojure-ts-convert-collection-to-set ()
2117+
"Convert collection at point to set."
2118+
(interactive)
2119+
(clojure-ts--convert-collection ?{ ?} ?#))
2120+
20512121
(defvar clojure-ts-refactor-map
20522122
(let ((map (make-sparse-keymap)))
20532123
(keymap-set map "C-t" #'clojure-ts-thread)
@@ -2060,6 +2130,16 @@ value is `clojure-ts-thread-all-but-last'."
20602130
(keymap-set map "l" #'clojure-ts-thread-last-all)
20612131
(keymap-set map "C-p" #'clojure-ts-cycle-privacy)
20622132
(keymap-set map "p" #'clojure-ts-cycle-privacy)
2133+
(keymap-set map "C-(" #'clojure-ts-convert-collection-to-list)
2134+
(keymap-set map "(" #'clojure-ts-convert-collection-to-list)
2135+
(keymap-set map "C-'" #'clojure-ts-convert-collection-to-quoted-list)
2136+
(keymap-set map "'" #'clojure-ts-convert-collection-to-quoted-list)
2137+
(keymap-set map "C-{" #'clojure-ts-convert-collection-to-map)
2138+
(keymap-set map "{" #'clojure-ts-convert-collection-to-map)
2139+
(keymap-set map "C-[" #'clojure-ts-convert-collection-to-vector)
2140+
(keymap-set map "[" #'clojure-ts-convert-collection-to-vector)
2141+
(keymap-set map "C-#" #'clojure-ts-convert-collection-to-set)
2142+
(keymap-set map "#" #'clojure-ts-convert-collection-to-set)
20632143
map)
20642144
"Keymap for `clojure-ts-mode' refactoring commands.")
20652145

@@ -2074,6 +2154,12 @@ value is `clojure-ts-thread-all-but-last'."
20742154
["Toggle between string & keyword" clojure-ts-cycle-keyword-string]
20752155
["Align expression" clojure-ts-align]
20762156
["Cycle privacy" clojure-ts-cycle-privacy]
2157+
("Convert collection"
2158+
["Convert to list" clojure-ts-convert-collection-to-list]
2159+
["Convert to quoted list" clojure-ts-convert-collection-to-quoted-list]
2160+
["Convert to map" clojure-ts-convert-collection-to-map]
2161+
["Convert to vector" clojure-ts-convert-collection-to-vector]
2162+
["Convert to set" clojure-ts-convert-collection-to-set])
20772163
("Refactor -> and ->>"
20782164
["Thread once more" clojure-ts-thread]
20792165
["Fully thread a form with ->" clojure-ts-thread-first-all]
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
;;; clojure-ts-mode-convert-collection-test.el --- Clojure[TS] Mode convert collection type. -*- lexical-binding: t; -*-
2+
3+
;; Copyright (C) 2025 Roman Rudakov
4+
5+
;; Author: Roman Rudakov <[email protected]>
6+
7+
;; This program is free software; you can redistribute it and/or modify
8+
;; it under the terms of the GNU General Public License as published by
9+
;; the Free Software Foundation, either version 3 of the License, or
10+
;; (at your option) any later version.
11+
12+
;; This program is distributed in the hope that it will be useful,
13+
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
;; GNU General Public License for more details.
16+
17+
;; You should have received a copy of the GNU General Public License
18+
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
19+
20+
;;; Commentary:
21+
22+
;; Adapted from `clojure-mode'.
23+
24+
;;; Code:
25+
26+
(require 'clojure-ts-mode)
27+
(require 'buttercup)
28+
(require 'test-helper "test/test-helper")
29+
30+
(describe "clojure-ts-convert-collection-to-map"
31+
(when-refactoring-it "should convert a list to a map"
32+
"(:a 1 :b 2)"
33+
"{:a 1 :b 2}"
34+
(backward-sexp)
35+
(down-list)
36+
(clojure-ts-convert-collection-to-map)))
37+
38+
(describe "clojure-ts-convert-collection-to-vector"
39+
(when-refactoring-it "should convert a map to a vector"
40+
"{:a 1 :b 2}"
41+
"[:a 1 :b 2]"
42+
(backward-sexp)
43+
(down-list)
44+
(clojure-ts-convert-collection-to-vector)))
45+
46+
(describe "clojure-ts-convert-collection-to-set"
47+
(when-refactoring-it "should convert a vector to a set"
48+
"[1 2 3]"
49+
"#{1 2 3}"
50+
(backward-sexp)
51+
(down-list)
52+
(clojure-ts-convert-collection-to-set)))
53+
54+
(describe "clojure-ts-convert-collection-to-list"
55+
(when-refactoring-it "should convert a set to a list"
56+
"#{1 2 3}"
57+
"(1 2 3)"
58+
(backward-sexp)
59+
(down-list)
60+
(clojure-ts-convert-collection-to-list)))
61+
62+
(describe "clojure-ts-convert-collection-to-quoted-list"
63+
(when-refactoring-it "should convert a set to a quoted list"
64+
"#{1 2 3}"
65+
"'(1 2 3)"
66+
(backward-sexp)
67+
(down-list)
68+
(clojure-ts-convert-collection-to-quoted-list)))
69+
70+
(describe "clojure-ts-convert-collection-to-set"
71+
(when-refactoring-it "should convert a quoted list to a set"
72+
"'(1 2 3)"
73+
"#{1 2 3}"
74+
(backward-sexp)
75+
(down-list)
76+
(clojure-ts-convert-collection-to-set)))
77+
78+
79+
(provide 'clojure-ts-mode-convert-collection-test)
80+
;;; clojure-ts-mode-convert-collection-test.el ends here

test/samples/refactoring.clj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,13 @@
8282
(definline bad-sqr [x] `(* ~x ~x))
8383

8484
(defmulti service-charge (juxt account-level :tag))
85+
86+
;; Convert collections.
87+
88+
#{1 2 3}
89+
90+
[1 2 3]
91+
92+
;; TODO: Define indentation rule for `ns_map_lit`
93+
#:hello{:name "Roma"
94+
:world true}

0 commit comments

Comments
 (0)