A collection of simple clojure refactoring functions. Please send help.
I highly recommend installing clj-refactor through elpa.
It's available on marmalade and melpa:
M-x package-install clj-refactor
You can also install the dependencies on your own, and just dump clj-refactor in your path somewhere:
(require 'clj-refactor)
(add-hook 'clojure-mode-hook (lambda ()
(clj-refactor-mode 1)
;; insert keybinding setup here
))
You'll also have to set up the keybindings in the lambda. Read on.
All functions in clj-refactor have a two-letter mnemonic shortcut. For
instance, rename-file is rf
. You get to choose how those are bound.
Here's how:
(cljr-add-keybindings-with-prefix "C-c C-m")
;; eg. rename files with `C-c C-m rf`.
If you would rather have a modifier key, instead of a prefix, do:
(cljr-add-keybindings-with-modifier "C-s-")
;; eg. rename files with `C-s-r C-s-f`.
If neither of these appeal to your sense of keyboard layout aesthetics, feel free to pick and choose your own keybindings with a smattering of:
(define-key clj-refactor-map (kbd "C-x C-r") 'cljr-rename-file)
This is it so far:
th
: thread another expressionuw
: unwind a threaded expressionua
: fully unwind a threaded expressiontf
: wrap in thread-first (->) and fully threadtl
: wrap in thread-last (->>) and fully threadil
: introduce letel
: expand letml
: move to letrf
: rename file, update ns-declaration, and then query-replace new ns in project.ar
: add require to namespace declaration, then jump back (see optional setup)au
: add "use" (ie require refer all) to namespace declaration, then jump backai
: add import to namespace declaration, then jump backru
: replace all:use
in namespace with:refer :all
sn
: sort :use, :require and :import in the ns formsr
: stop referring (removes:refer []
from current require, fixing references)cc
: cycle surrounding collection typecp
: cycle privacy ofdefn
s anddef
scs
: cycle between "string" -> :string -> "string"ad
: add declaration for current top-level formdk
: destructure keysmf
: move one or more forms to another namespace,:refer
any functions
Combine with your keybinding prefix/modifier.
Given this:
(map square (filter even? [1 2 3 4 5]))
Start by wrapping it in a threading macro:
(->> (map square (filter even? [1 2 3 4 5])))
And start threading away, using cljr-thread
:
(->> (filter even? [1 2 3 4 5])
(map square))
And again:
(->> [1 2 3 4 5]
(filter even?)
(map square))
You can also do all of these steps in one go.
Start again with:
(map square (filter even? [1 2 3 4 5]))
Put your cursor in front of the s-exp, and call cljr-thread-last-all
:
(->> [1 2 3 4 5]
(filter even?)
(map square))
There is a corresponding cljr-thread-first-all
as well.
To revert this, there's cljr-unwind
to unwind one step at a time. Or
there's cljr-unwind-all
to unwind the entire expression at once.
To see how that works, just read the examples in the other direction.
Given this:
(defn handle-request
{:status 200
:body (find-body abc)})
With the cursor in front of (find-body abc)
, I do cljr-introduce-let
:
(defn handle-request
{:status 200
:body (let [X (find-body abc)]
X)})
Now I have two cursors where the X
es are. Just type out the name,
and press enter. Of course, that's not where I wanted the let
statement. So I do cljr-expand-let
:
(defn handle-request
(let [body (find-body abc)]
{:status 200
:body body}))
Yay.
Next with the cursor in front of 200
, I do cljr-move-to-let
:
(defn handle-request
(let [body (find-body abc)
X 200]
{:status X
:body body}))
Again I have two cursors where the X
es are, so I type out the name,
and press enter:
(defn handle-request
(let [body (find-body abc)
status 200]
{:status status
:body body}))
Pretty handy. And it works with if-let
and when-let
too.
Given this function:
(defn add [a b]
(+ a b))
I do cljr-cycle-privacy
:
(defn- add [a b]
(+ a b))
I do cljr-cycle-privacy
again to return to the original:
(defn add [a b]
(+ a b))
Given this def:
(def config
"docs"
{:env "staging"})
I do cljr-cycle-privacy
:
(def ^:private config
"docs"
{:env "staging"})
I do cljr-cycle-privacy
again to return to the original:
(def config
"docs"
{:env "staging"})
Given this collection:
(:a 1 :b 2)
I do cljr-cycle-coll
to return:
{:a 1 :b 2}
... and then 3 more times:
[:a 1 :b 2]
#{:a 1 :b 2}
(:a 1 :b 2)
Given this string:
"refactor"
I do cljr-cycle-stringlike
to return:
:refactor
... and then 3 more times:
"refactor"
:refactor
"refactor"
Thanks to Jay Fields and emacs-live for these cycling features. Good idea!
Given this:
(defn- render-recommendation [rec]
(list [:h3 (:title rec)]
[:p (:by rec)]
[:p (:blurb rec) " "
(render-link (:link rec))]))
I place the cursor on rec
inside [rec]
and do cljr-destructure-keys
:
(defn- render-recommendation [{:keys [title by blurb link]}]
(list [:h3 title]
[:p by]
[:p blurb " "
(render-link link)]))
If rec
had still been in use, it would have added an :as
clause.
For now this feature is limited to top-level symbols in a let form. PR welcome.
Given this:
(ns cljr.core
(:require [my.lib :as lib :refer [a b]]))
(+ (a 1) (b 2))
I place cursor on my.lib
and do cljr-stop-referring
:
(ns cljr.core
(:require [my.lib :as lib]))
(+ (lib/a 1) (lib/b 2))
If you're not using yasnippet, then the "jumping back"-part of adding to namespace won't work. To remedy that, enable the mode with either:
(yas/global-mode 1)
or
(add-hook 'clojure-mode-hook (lambda () (yas/minor-mode 1)))
It is an excellent package, so I recommend looking into it. Here are some snippet packages for Clojure:
- David Nolen has created some clojure-snippets
- I've made some datomic-snippets
When you open a blank .clj
-file, clj-refactor inserts the namespace
declaration for you.
It will also add the relevant :use
clauses in test files, normally
using clojure.test
, but if you're depending on midje in your
project.clj
it uses that instead.
Like clojure-mode, clj-refactor presumes that you are postfixing your
test files with _test
.
Prefer to insert your own ns-declarations? Then:
(setq clj-add-ns-to-blank-clj-files nil)
You might also like
- align-cljlet - which is an Emacs package for aligning let-like forms.
- Add
cljr-stop-referring
- Add
cljr-destructure-keys
- Add
cljr-sort-ns
AlexBaranosky
- Add
cljr-replace-use
Lars Andersen - Add
cljr-add-declaration
Lars Andersen
- Add
cljr-cycle-stringlike
AlexBaranosky - Add
cljr-cycle-coll
AlexBaranosky - Add
cljr-cycle-privacy
AlexBaranosky
- Add
cljr-thread-first-all
,cljr-thread-last-all
andcljr-unwind-all
AlexBaranosky
- Add
cljr-move-to-let
AlexBaranosky
Yes, please do. There's a suite of tests, so remember to add tests for your specific feature, or I might break it later.
You'll find the repo at:
https://github.com/magnars/clj-refactor.el
To fetch the test dependencies, install cask if you haven't already, then:
$ cd /path/to/clj-refactor
$ cask
Run the tests with:
$ ./run-tests.sh
- AlexBaranosky added a bunch of features. See the Changelog for details.
- Lars Andersen added
cljr-replace-use
,cljr-add-declaration
andcljr-move-form
.
Thanks!
Copyright © 2012-2013 Magnar Sveen
Authors: Magnar Sveen magnars@gmail.com Keywords: clojure convenience
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.