Skip to content

Commit 2fa46e6

Browse files
committed
Merge pull request #381 from clojure-emacs/cycling-things
Add cycling privacy, collection type, if/if-not
2 parents 173b67d + 0c67be5 commit 2fa46e6

7 files changed

+391
-52
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
## master (unreleased)
44

5+
### New features
6+
7+
* Port cycle privacy, cycle collection type and cycle if/if-not from clj-refactor.el.
8+
* Rework cycle collection type into convert collection to list, quoted list, map, vector, set.
9+
510
## 5.4.0 (2016-05-21)
611

712
### New features

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ specific `clojure-mode` release.**
3131
- [Vertical alignment](#vertical-alignment)
3232
- [Refactoring support](#refactoring-support)
3333
- [Threading macros](#threading-macros-related-features)
34+
- [Cycling things](#cycling-things)
35+
- [Convert collection](#convert-collection)
3436
- [Related packages](#related-packages)
3537
- [REPL Interaction](#repl-interaction)
3638
- [Basic REPL](#basic-repl)
@@ -258,6 +260,20 @@ argument do not thread the last form. See demonstration on the
258260
Unwind and remove the threading macro. See demonstration on the
259261
[clj-refactor.el wiki](https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-unwind-all).
260262

263+
### Cycling things
264+
265+
* Cycle privacy
266+
267+
Cycle privacy of `def`s or `defn`s. Use metadata explicitly with setting `clojure-use-metadata-for-privacy` to `t` for `defn`s too. See demonstration on the [clj-refactor.el wiki](https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-cycle-privacy).
268+
269+
* Cycle if/if-not
270+
271+
Find the closest if or if-not up the syntax tree and toggle it. Also transpose the "else" and "then" branches, keeping the semantics the same as before. See demonstration on the [clj-refactor.el wiki](https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-cycle-if).
272+
273+
### Convert collection
274+
275+
Convert any given collection at point to list, quoted list, map, vector or set.
276+
261277
## Related packages
262278

263279
* [clojure-mode-extra-font-locking][] provides additional font-locking

clojure-mode.el

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,20 @@ Out-of-the box clojure-mode understands lein, boot and gradle."
205205
(define-key map (kbd "C-c C-r l") #'clojure-thread-last-all)
206206
(define-key map (kbd "C-c C-r C-a") #'clojure-unwind-all)
207207
(define-key map (kbd "C-c C-r a") #'clojure-unwind-all)
208+
(define-key map (kbd "C-c C-r C-p") #'clojure-cycle-privacy)
209+
(define-key map (kbd "C-c C-r p") #'clojure-cycle-privacy)
210+
(define-key map (kbd "C-c C-r C-(") #'clojure-convert-collection-to-list)
211+
(define-key map (kbd "C-c C-r (") #'clojure-convert-collection-to-list)
212+
(define-key map (kbd "C-c C-r C-'") #'clojure-convert-collection-to-quoted-list)
213+
(define-key map (kbd "C-c C-r '") #'clojure-convert-collection-to-quoted-list)
214+
(define-key map (kbd "C-c C-r C-{") #'clojure-convert-collection-to-map)
215+
(define-key map (kbd "C-c C-r {") #'clojure-convert-collection-to-map)
216+
(define-key map (kbd "C-c C-r C-[") #'clojure-convert-collection-to-vector)
217+
(define-key map (kbd "C-c C-r [") #'clojure-convert-collection-to-vector)
218+
(define-key map (kbd "C-c C-r C-#") #'clojure-convert-collection-to-set)
219+
(define-key map (kbd "C-c C-r #") #'clojure-convert-collection-to-set)
220+
(define-key map (kbd "C-c C-r C-i") #'clojure-cycle-if)
221+
(define-key map (kbd "C-c C-r i") #'clojure-cycle-if)
208222
(define-key map (kbd "C-c C-r n i") #'clojure-insert-ns-form)
209223
(define-key map (kbd "C-c C-r n h") #'clojure-insert-ns-form-at-point)
210224
(define-key map (kbd "C-c C-r n u") #'clojure-update-ns)
@@ -213,11 +227,19 @@ Out-of-the box clojure-mode understands lein, boot and gradle."
213227
'("Clojure"
214228
["Toggle between string & keyword" clojure-toggle-keyword-string]
215229
["Align expression" clojure-align]
230+
["Cycle privacy" clojure-cycle-privacy]
231+
["Cycle if, if-not" clojure-cycle-if]
216232
("ns forms"
217233
["Insert ns form at the top" clojure-insert-ns-form]
218234
["Insert ns form here" clojure-insert-ns-form-at-point]
219235
["Update ns form" clojure-update-ns]
220236
["Sort ns form" clojure-sort-ns])
237+
("Convert collection"
238+
["Convert to list" clojure-convert-collection-to-list]
239+
["Convert to quoted list" clojure-convert-collection-to-quoted-list]
240+
["Convert to map" clojure-convert-collection-to-map]
241+
["Convert to vector" clojure-convert-collection-to-vector]
242+
["Convert to set" clojure-convert-collection-to-set])
221243
("Refactor -> and ->>"
222244
["Thread once more" clojure-thread]
223245
["Fully thread a form with ->" clojure-thread-first-all]
@@ -1629,6 +1651,8 @@ This will skip over sexps that don't represent objects, so that ^hints and
16291651
;; Refactoring support
16301652
;;
16311653
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1654+
1655+
;;; Threading macros related
16321656
(defcustom clojure-thread-all-but-last nil
16331657
"Non-nil means do not thread the last expression.
16341658
This means that `clojure-thread-first-all' and
@@ -1824,6 +1848,106 @@ When BUT-LAST is passed the last expression is not threaded."
18241848
(interactive "P")
18251849
(clojure--thread-all "->> " but-last))
18261850

1851+
;;; Cycling stuff
1852+
1853+
(defcustom clojure-use-metadata-for-privacy nil
1854+
"If nil, `clojure-cycle-privacy' will use (defn- f []).
1855+
If t, it will use (defn ^:private f [])."
1856+
:package-version '(clojure-mode . "5.5.0")
1857+
:safe #'booleanp
1858+
:type 'boolean)
1859+
1860+
;;;###autoload
1861+
(defun clojure-cycle-privacy ()
1862+
"Make public the current private def, or vice-versa.
1863+
See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-cycle-privacy"
1864+
(interactive)
1865+
(save-excursion
1866+
(ignore-errors (forward-char 7))
1867+
(search-backward-regexp "(defn?\\(-\\| ^:private\\)?\\_>")
1868+
(if (match-string 1)
1869+
(replace-match "" nil nil nil 1)
1870+
(goto-char (match-end 0))
1871+
(insert (if (or clojure-use-metadata-for-privacy
1872+
(equal (match-string 0) "(def"))
1873+
" ^:private"
1874+
"-")))))
1875+
1876+
(defun clojure--convert-collection (coll-open coll-close)
1877+
"Convert the collection at (point) by unwrapping it an wrapping it between COLL-OPEN and COLL-CLOSE."
1878+
(save-excursion
1879+
(while (and
1880+
(not (bobp))
1881+
(not (looking-at "(\\|{\\|\\[")))
1882+
(backward-char))
1883+
(when (or (eq ?\# (char-before))
1884+
(eq ?\' (char-before)))
1885+
(delete-char -1))
1886+
(when (and (bobp)
1887+
(not (memq (char-after) '(?\{ ?\( ?\[))))
1888+
(user-error "Beginning of file reached, collection is not found"))
1889+
(insert coll-open (substring (clojure-delete-and-extract-sexp) 1 -1) coll-close)))
1890+
1891+
;;;###autoload
1892+
(defun clojure-convert-collection-to-list ()
1893+
"Convert collection at (point) to list."
1894+
(interactive)
1895+
(clojure--convert-collection "(" ")"))
1896+
1897+
;;;###autoload
1898+
(defun clojure-convert-collection-to-quoted-list ()
1899+
"Convert collection at (point) to quoted list."
1900+
(interactive)
1901+
(clojure--convert-collection "'(" ")"))
1902+
1903+
;;;###autoload
1904+
(defun clojure-convert-collection-to-map ()
1905+
"Convert collection at (point) to map."
1906+
(interactive)
1907+
(clojure--convert-collection "{" "}"))
1908+
1909+
;;;###autoload
1910+
(defun clojure-convert-collection-to-vector ()
1911+
"Convert collection at (point) to vector."
1912+
(interactive)
1913+
(clojure--convert-collection "[" "]"))
1914+
1915+
;;;###autoload
1916+
(defun clojure-convert-collection-to-set ()
1917+
"Convert collection at (point) to set."
1918+
(interactive)
1919+
(clojure--convert-collection "#{" "}"))
1920+
1921+
(defun clojure--goto-if ()
1922+
(when (in-string-p)
1923+
(while (or (not (looking-at "("))
1924+
(in-string-p))
1925+
(backward-char)))
1926+
(while (not (looking-at "\\((if \\)\\|\\((if-not \\)"))
1927+
(condition-case nil
1928+
(backward-up-list)
1929+
(scan-error (user-error "No if or if-not found")))))
1930+
1931+
;;;###autoload
1932+
(defun clojure-cycle-if ()
1933+
"Change a surrounding if to if-not, or vice-versa.
1934+
1935+
See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-cycle-if"
1936+
(interactive)
1937+
(save-excursion
1938+
(clojure--goto-if)
1939+
(cond
1940+
((looking-at "(if-not")
1941+
(forward-char 3)
1942+
(delete-char 4)
1943+
(forward-sexp 2)
1944+
(transpose-sexps 1))
1945+
((looking-at "(if")
1946+
(forward-char 3)
1947+
(insert "-not")
1948+
(forward-sexp 2)
1949+
(transpose-sexps 1)))))
1950+
18271951

18281952
;;; ClojureScript
18291953
(defconst clojurescript-font-lock-keywords
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
;;; clojure-mode-convert-collection-test.el --- Clojure Mode: convert collection type -*- lexical-binding: t; -*-
2+
3+
;; Copyright (C) 2016 Benedek Fazekas <benedek.fazekas@gmail.com>
4+
5+
;; This file is not part of GNU Emacs.
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 <http://www.gnu.org/licenses/>.
19+
20+
;;; Commentary:
21+
22+
;; The convert collection code originally was implemented
23+
;; as cycling collection type in clj-refactor.el and is the work
24+
;; of the clj-reafctor.el team.
25+
26+
;;; Code:
27+
28+
(require 'clojure-mode)
29+
(require 'ert)
30+
31+
(def-refactor-test test-convert-collection-list-map
32+
"(:a 1 :b 2)"
33+
"{:a 1 :b 2}"
34+
(backward-sexp)
35+
(down-list)
36+
(clojure-convert-collection-to-map))
37+
38+
(def-refactor-test test-convert-collection-map-vector
39+
"{:a 1 :b 2}"
40+
"[:a 1 :b 2]"
41+
(backward-sexp)
42+
(down-list)
43+
(clojure-convert-collection-to-vector))
44+
45+
(def-refactor-test test-convert-collection-vector-set
46+
"[1 2 3]"
47+
"#{1 2 3}"
48+
(backward-sexp)
49+
(down-list)
50+
(clojure-convert-collection-to-set))
51+
52+
(def-refactor-test test-convert-collection-set-list
53+
"#{1 2 3}"
54+
"(1 2 3)"
55+
(backward-sexp)
56+
(down-list)
57+
(clojure-convert-collection-to-list))
58+
59+
(def-refactor-test test-convert-collection-set-quoted-list
60+
"#{1 2 3}"
61+
"'(1 2 3)"
62+
(backward-sexp)
63+
(down-list)
64+
(clojure-convert-collection-to-quoted-list))
65+
66+
(def-refactor-test test-convert-collection-quoted-list-set
67+
"'(1 2 3)"
68+
"#{1 2 3}"
69+
(backward-sexp)
70+
(down-list)
71+
(clojure-convert-collection-to-set))
72+
73+
(provide 'clojure-mode-convert-collection-test)
74+
75+
;;; clojure-mode-convert-collection-test.el ends here

test/clojure-mode-cycling-test.el

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
;;; clojure-mode-cycling-test.el --- Clojure Mode: cycling things tests -*- lexical-binding: t; -*-
2+
3+
;; Copyright (C) 2016 Benedek Fazekas <benedek.fazekas@gmail.com>
4+
5+
;; This file is not part of GNU Emacs.
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 <http://www.gnu.org/licenses/>.
19+
20+
;;; Commentary:
21+
22+
;; The cycling privacy and if/if-not code is ported from
23+
;; clj-refactor.el and the work of the clj-reafctor.el team.
24+
25+
;;; Code:
26+
27+
(require 'clojure-mode)
28+
(require 'ert)
29+
30+
(def-refactor-test test-cycle-privacy-public-defn-private-defn
31+
"(defn add [a b]
32+
(+ a b))"
33+
"(defn- add [a b]
34+
(+ a b))"
35+
(clojure-cycle-privacy))
36+
37+
(def-refactor-test test-cycle-privacy-from-sexp-beg
38+
"(defn- add [a b]
39+
(+ a b))"
40+
"(defn add [a b]
41+
(+ a b))"
42+
(backward-sexp)
43+
(clojure-cycle-privacy))
44+
45+
(def-refactor-test test-cycle-privacy-public-defn-private-defn-metadata
46+
"(defn add [a b]
47+
(+ a b))"
48+
"(defn ^:private add [a b]
49+
(+ a b))"
50+
(let ((clojure-use-metadata-for-privacy t))
51+
(clojure-cycle-privacy)))
52+
53+
(def-refactor-test test-cycle-privacy-private-defn-public-defn
54+
"(defn- add [a b]
55+
(+ a b))"
56+
"(defn add [a b]
57+
(+ a b))"
58+
(clojure-cycle-privacy))
59+
60+
(def-refactor-test test-cycle-privacy-private-defn-public-defn-metadata
61+
"(defn ^:private add [a b]
62+
(+ a b))"
63+
"(defn add [a b]
64+
(+ a b))"
65+
(let ((clojure-use-metadata-for-privacy t))
66+
(clojure-cycle-privacy)))
67+
68+
(def-refactor-test test-cycle-privacy-public-def-private-def
69+
"(def ^:dynamic config
70+
\"docs\"
71+
{:env \"staging\"})"
72+
"(def ^:private ^:dynamic config
73+
\"docs\"
74+
{:env \"staging\"})"
75+
(clojure-cycle-privacy))
76+
77+
(def-refactor-test test-cycle-privacy-private-def-public-def
78+
"(def ^:private config
79+
\"docs\"
80+
{:env \"staging\"})"
81+
"(def config
82+
\"docs\"
83+
{:env \"staging\"})"
84+
(clojure-cycle-privacy))
85+
86+
(def-refactor-test test-cycle-if-inner-if
87+
"(if this
88+
(if that
89+
(then AAA)
90+
(else BBB))
91+
(otherwise CCC))"
92+
"(if this
93+
(if-not that
94+
(else BBB)
95+
(then AAA))
96+
(otherwise CCC))"
97+
(beginning-of-buffer)
98+
(search-forward "BBB)")
99+
(clojure-cycle-if))
100+
101+
(def-refactor-test test-cycle-if-outer-if
102+
"(if-not this
103+
(if that
104+
(then AAA)
105+
(else BBB))
106+
(otherwise CCC))"
107+
"(if this
108+
(otherwise CCC)
109+
(if that
110+
(then AAA)
111+
(else BBB)))"
112+
(beginning-of-buffer)
113+
(search-forward "BBB))")
114+
(clojure-cycle-if))
115+
116+
(provide 'clojure-mode-cycling-test)
117+
118+
;;; clojure-mode-cycling-test.el ends here

0 commit comments

Comments
 (0)