Skip to content

Commit 7e87777

Browse files
Add cycling privacy, collection type, if/if-not
Migrate cycle privacy, cycle collection type and cycle if/if-not implementations from clj-refactor.el. Additionally refactor `def-threading-test` macro to use it for testing cycling stuff too and fix duplicate test names in `clojure-mode-refactor-threading-test`
1 parent cff06c4 commit 7e87777

6 files changed

+314
-52
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
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+
59
## 5.4.0 (2016-05-21)
610

711
### New features

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ 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)
3435
- [Related packages](#related-packages)
3536
- [REPL Interaction](#repl-interaction)
3637
- [Basic REPL](#basic-repl)
@@ -258,6 +259,20 @@ argument do not thread the last form. See demonstration on the
258259
Unwind and remove the threading macro. See demonstration on the
259260
[clj-refactor.el wiki](https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-unwind-all).
260261

262+
### Cycling things
263+
264+
* Cycle privacy
265+
266+
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).
267+
268+
* Cycle collection type
269+
270+
Cycle the collection at point between list, map, vector and set literals. See demonstration on the [clj-refactor.el wiki](https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-cycle-coll).
271+
272+
* Cycle if/if-not
273+
274+
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).
275+
261276
## Related packages
262277

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

clojure-mode.el

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,12 @@ 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-c") #'clojure-cycle-collection)
211+
(define-key map (kbd "C-c C-r c") #'clojure-cycle-collection)
212+
(define-key map (kbd "C-c C-r C-i") #'clojure-cycle-if)
213+
(define-key map (kbd "C-c C-r i") #'clojure-cycle-if)
208214
(define-key map (kbd "C-c C-r n i") #'clojure-insert-ns-form)
209215
(define-key map (kbd "C-c C-r n h") #'clojure-insert-ns-form-at-point)
210216
(define-key map (kbd "C-c C-r n u") #'clojure-update-ns)
@@ -225,6 +231,10 @@ Out-of-the box clojure-mode understands lein, boot and gradle."
225231
"--"
226232
["Unwind once" clojure-unwind]
227233
["Fully unwind a threading macro" clojure-unwind-all])
234+
("Cycle things"
235+
["Cycle privacy" clojure-cycle-privacy]
236+
["Cycle collection type" clojure-cycle-collection]
237+
["Cycle if, if-not" clojure-cycle-if])
228238
"--"
229239
["Clojure-mode version" clojure-mode-display-version]))
230240
map)
@@ -1629,6 +1639,8 @@ This will skip over sexps that don't represent objects, so that ^hints and
16291639
;; Refactoring support
16301640
;;
16311641
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1642+
1643+
;;; Threading macros related
16321644
(defcustom clojure-thread-all-but-last nil
16331645
"Non-nil means do not thread the last expression.
16341646
This means that `clojure-thread-first-all' and
@@ -1824,6 +1836,94 @@ When BUT-LAST is passed the last expression is not threaded."
18241836
(interactive "P")
18251837
(clojure--thread-all "->> " but-last))
18261838

1839+
;;; Cycling stuff
1840+
1841+
(defcustom clojure-use-metadata-for-privacy nil
1842+
"If nil, `clojure-cycle-privacy' will use (defn- f []).
1843+
If t, it will use (defn ^:private f [])."
1844+
:package-version '(clojure-mode . "5.5.0")
1845+
:safe #'booleanp
1846+
:type 'boolean)
1847+
1848+
;;;###autoload
1849+
(defun clojure-cycle-privacy ()
1850+
"Make public the current private def, or vice-versa.
1851+
See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-cycle-privacy"
1852+
(interactive)
1853+
(save-excursion
1854+
(ignore-errors (forward-char 7))
1855+
(search-backward-regexp "(defn?\\(-\\| ^:private\\)?\\_>")
1856+
(if (match-string 1)
1857+
(replace-match "" nil nil nil 1)
1858+
(goto-char (match-end 0))
1859+
(insert (if (or clojure-use-metadata-for-privacy
1860+
(equal (match-string 0) "(def"))
1861+
" ^:private"
1862+
"-")))))
1863+
1864+
;;;###autoload
1865+
(defun clojure-cycle-collection ()
1866+
"Convert the coll at (point) from (x) -> {x} -> [x] -> -> #{x} -> (x) recur.
1867+
1868+
See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-cycle-coll"
1869+
(interactive)
1870+
(save-excursion
1871+
(while (and
1872+
(not (bobp))
1873+
(not (looking-at "(\\|#{\\|{\\|[")))
1874+
(backward-char))
1875+
1876+
(cond
1877+
((eq ?\( (char-after))
1878+
(insert "{" (substring (clojure-delete-and-extract-sexp) 1 -1) "}"))
1879+
1880+
((eq ?\# (char-after))
1881+
(delete-char 1)
1882+
(insert "(" (substring (clojure-delete-and-extract-sexp) 1 -1) ")"))
1883+
1884+
((eq ?\{ (char-after))
1885+
(if (not (equal ?\# (char-before)))
1886+
(insert "[" (substring (clojure-delete-and-extract-sexp) 1 -1) "]")
1887+
(backward-char)
1888+
(delete-char 1)
1889+
(insert "(" (substring (clojure-delete-and-extract-sexp) 1 -1) ")")))
1890+
1891+
((eq ?\[ (char-after))
1892+
(insert "#{" (substring (clojure-delete-and-extract-sexp) 1 -1) "}"))
1893+
1894+
((bobp)
1895+
(user-error "Beginning of file reached, collection is not found")))))
1896+
1897+
(defun clojure--goto-if ()
1898+
(when (in-string-p)
1899+
(while (or (not (looking-at "("))
1900+
(in-string-p))
1901+
(backward-char)))
1902+
(while (not (looking-at "\\((if \\)\\|\\((if-not \\)"))
1903+
(condition-case nil
1904+
(backward-up-list)
1905+
(scan-error (user-error "No if or if-not found")))))
1906+
1907+
;;;###autoload
1908+
(defun clojure-cycle-if ()
1909+
"Change a surrounding if to if-not, or vice-versa.
1910+
1911+
See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-cycle-if"
1912+
(interactive)
1913+
(save-excursion
1914+
(clojure--goto-if)
1915+
(cond
1916+
((looking-at "(if-not")
1917+
(forward-char 3)
1918+
(delete-char 4)
1919+
(forward-sexp 2)
1920+
(transpose-sexps 1))
1921+
((looking-at "(if")
1922+
(forward-char 3)
1923+
(insert "-not")
1924+
(forward-sexp 2)
1925+
(transpose-sexps 1)))))
1926+
18271927

18281928
;;; ClojureScript
18291929
(defconst clojurescript-font-lock-keywords

test/clojure-mode-cycling-test.el

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
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, collection type 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-collection-list-map
87+
"(:a 1 :b 2)"
88+
"{:a 1 :b 2}"
89+
(backward-sexp)
90+
(clojure-cycle-collection))
91+
92+
(def-refactor-test test-cycle-collection-map-vector
93+
"{:a 1 :b 2}"
94+
"[:a 1 :b 2]"
95+
(backward-sexp)
96+
(clojure-cycle-collection))
97+
98+
(def-refactor-test test-cycle-collection-vector-set
99+
"[1 2 3]"
100+
"#{1 2 3}"
101+
(backward-sexp)
102+
(clojure-cycle-collection))
103+
104+
(def-refactor-test test-cycle-collection-set-list
105+
"#{1 2 3}"
106+
"(1 2 3)"
107+
(backward-sexp)
108+
(clojure-cycle-collection))
109+
110+
(def-refactor-test test-cycle-if-inner-if
111+
"(if this
112+
(if that
113+
(then AAA)
114+
(else BBB))
115+
(otherwise CCC))"
116+
"(if this
117+
(if-not that
118+
(else BBB)
119+
(then AAA))
120+
(otherwise CCC))"
121+
(beginning-of-buffer)
122+
(search-forward "BBB)")
123+
(clojure-cycle-if))
124+
125+
(def-refactor-test test-cycle-if-outer-if
126+
"(if-not this
127+
(if that
128+
(then AAA)
129+
(else BBB))
130+
(otherwise CCC))"
131+
"(if this
132+
(otherwise CCC)
133+
(if that
134+
(then AAA)
135+
(else BBB)))"
136+
(beginning-of-buffer)
137+
(search-forward "BBB))")
138+
(clojure-cycle-if))
139+
140+
(provide 'clojure-mode-cycling-test)
141+
142+
;;; clojure-mode-cycling-test.el ends here

0 commit comments

Comments
 (0)