clojure-emacs / clj-refactor.el Goto Github PK
View Code? Open in Web Editor NEWA CIDER extension that provides powerful commands for refactoring Clojure code.
License: GNU General Public License v3.0
A CIDER extension that provides powerful commands for refactoring Clojure code.
License: GNU General Public License v3.0
Example:
(defn foo []
(let [x (range 10)]
(doseq [x (range 10)]
(let [x2 (* x x)]))
_(+ 1 1))) ; move to let on the _
(defn foo []
(let [x (range 10)]
(doseq [x (range 10)]
(let [x2 (* x x)
something (+ 1 1)]))
something)) ;; woops
I get this error whenever I try to use several functions. One example is ar
.
(defn plus [a b]
(-> | a (+ b))) ;; | is my cursor
When I call cljr-unwind
, I get:
(defn plus [a b]
(-> (+ b))) () a
However clr-unwind
on the below works fine:
(defn plus [a b]
(-> | a ;; | is my cursor
(+ b)))
I have a (not uncommon) project structure of
src/
clojure/...
java/...
and the rename file refactoring then renames my namespaces to clojure.my.namespace instead of my.namespace.
Thanks for this great tool
Sometimes when I'm adding a new referral to the namespace declaration with au
, I realize I've already referenced this namespace. It would be nice if clj-refactored noticed that and collapsed them.
Example: I've got this declaration:
(ns my-example
(:require [my-example.homeless :refer [assoc-if]]))
then I do the au
refactoring, giving me:
(ns my-example
(:require [my-example.homeless :refer [assoc-if]]
[ :refer []]))
I'll type in:
(ns my-example
(:require [my-example.homeless :refer [assoc-if]]
[my-example.homeless :refer [update-if]]))
and clj-refactor could then collapse them to:
(ns my-example
(:require [my-example.homeless :refer [assoc-if update-if]]))
Just a suggestion. Here are some really useful features for refactoring.
let
form)Hi,
first of all thanks for your work. I'm glad I'm only just starting with clojure and the like, since the last months seem to be somewhat silent ;)
When trying to use the unwind refactoring I always get the message:
cljr--nothing-more-to-unwind: Wrong number of arguments: (lambda nil "Move backward an S-expression, or up an S-expression backward.
If there are no more S-expressions in this one before the opening
delimiter, move past that opening delimiter backward; otherwise, move
move backward past the S-expression preceding the point." (interactive) (condition-case nil (backward-sexp) (scan-error (if (paredit-in-string-p) (backward-char) (backward-up-list))))), 1
Where am I supposed to place the cursor? I tried it somewhere within (-> ... )
Something I find myself doing a lot is taking a sexp like this
(inc (dec (inc 4)))
Then wrapping it in a ->
like this:
(-> (inc (dec (inc 4))))
And immediately threading it 3-4 times to get this:
(-> 4 inc dec inc)
What if we added functions to encapsulate the steps of wrapping in ->
or ->>
then calling the thread function repeatedly until it is fully expanded?
Also, we can add the reverse function to fully unthread as well?
Maybe call them cljr-thread-first-all
, cljr-thread-last-all
, and cljr-unwind-all
it seems that the code expects []
type brackets in :require section which is typically used but not always. and it is absolutely valid to use ()
.
reported by @jonpither
When I perform expand-let, the form gets correctly expanded, but if there's another let-form earlier in the file then suddenly point jumps to that instead of staying at the newly expanded let. If there's no let-forms before the one being expanded I get the following:
cljr--goto-let: Search failed: "(\\(when-let\\|if-let\\|let\\)\\( \\|\\[\\)"
So it would appear that the search gets performed before the form is expanded.
When a clojure file name includes dashes, it's a mistake - they should be underscores. How about prompting for a rename?
All the hard ones. Even thought they're relatively harder, I'd like to talk more about how we could implement them here.
It would be really cool to have the ability to toggle between (-> ...) and (->> ..) inside the threading macro, as I often find myself changing it as I write the thread body.
Currently when threading we get this:
(-> 1
(inc)
(dec))
Which I then use M-s on to get this:
(-> 1
inc
dec)
It'd be nice if the threading refactor automatically gave us the second one.
I think it makes sense to have this project under the clojure-emacs
hat. It will raise the visibility of the project and keep it aligned with development in related projects like clojure-mode
and cider
.
I think I should have all the dependencies installed, but I get this message when I try to use cljr-cycle-stringlike
. From the source I see that otherwise
appears to be kind of like clojure :else
clause in cond
or case
forms, and I'm guessing that the reason it's not working for me is something really silly.
cljr--project-dir
and cljr--project-file
look for project.clj to indicate the project root. This is not ideal, because not everyone uses Leiningen.
Taking a quick look at some of the clojure contrib projects that use maven, I see that we could maybe look for pom.xml?
As title..
Wouldn't it be better if you just typed in the name of the stuff in the buffer and it'd automatically insert it instead of jumping to the top and back again?
I haven't been writing Clojure code for that long, so I often forget that the compiler is single-pass. Thus, I often have to re-order my function definitions to make the compiler happy. I imagine I'll use this refactoring less and less as I get more experienced. Would this be a useful refactoring, or just a crutch for nublets?
This could probably be build on to of cljr-rename-file
:)
cljr-thread-first-all fails for:
|(->binary (not (s-acc/mobile? session))) ;; cursor at the pipe
Presumably also the single-step thread-first fn fails for this too. The ->binary
is my suspect.
Refactoring to add a dependency to project.clj
without having to:
project.clj
project.clj
I imagine this going something like this:
I think the easiest way to do this is to use use our new middlewere super powers to access ancient-clj, which is what lein ancient uses do do its thing.
I just started a new project with a bajillion deps and the existing workflow really pisses me off.
Is it possible to display a list of all options on entering the prefix key?
Perhaps something like pressing ?
after the prefix key can show a list of all the options shown in the README.
(ns payments.schema_test
(:require [payments.utils :refer [uuid]]
[payments.schemavalidation :refer [account-create
order-schema
credit-card-schema
payment-base-schema
payment-status-schema]]
[clojure.test :refer :all]
[closchema.core :refer [validate report-errors]]))
This seems very slow when calling cljr-remove-unused-requires.
(ns payments.core
(:require [clojure.tools.cli :refer [parse-opts]]
[clojure.tools.nrepl.server :as nrepl]
[compojure.route :as route]
[payments.configuration.properties
:refer [config merge-config-from-file! log-configuration]]
[payments.db :as db]
[payments.version :as version]
[ring.adapter.jetty :as jetty])
(:use [clojure.java.io]
[clojure.tools.logging]
[compojure.core :only [defroutes routes GET POST]]
[metrics.core :only [report-to-console]]
[metrics.ring.instrument :only [instrument]]
[payments.configuration.schematables :only [set-keyspace!]]
[payments.exceptions.http :only [all-http-exceptions-middleware]]
[payments.http.account :only [create-account
retrieve-account]]
[payments.http.transaction :only [payment-request]]
[payments.sso.middleware :only [ensure-auth-cookie-middleware
refresh-cookie-middleware]]
[ring.middleware.cookies :only [wrap-cookies]]
[ring.middleware.json]
[ring.middleware.stacktrace]
[ring.util.response])
(:import com.yammer.metrics.reporting.GraphiteReporter)
(:import [org.apache.commons.daemon DaemonContext Daemon])
(:gen-class
:implements [org.apache.commons.daemon.Daemon]))
It fails with Scan Error: "Unbalanced parenthesis, 1, 4381
How about something which would take the current defn/def/etc form and move it to a namespace which you specify (and optionally :use/:require that namespace)?
I love sorting things :)
I'd love a function to sort maps for easier reading.
Also, sorted maps diff better in git when you make test changes. (You may only change things slightly but the way the map prints garbles the whole commit by rearranging the map totally, making it look like more things have changed than eally have changed.)
As it says on the tin - when I have this in the (ns)
form:
[sablono.core :as html :refer-macros [html]]
Running remove unused requires strips this require out, even though the code in the file uses html
:
(render-state [_ state]
(html
[:div ...]))
Add cljr-cycle-collection
and cljr-cycle-stringlike
inspired from emacs-live.
They're pretty useful...
Note:
(defun live-delete-and-extract-sexp ()
"Delete the sexp and return it."
(interactive)
(let* ((begin (point)))
(forward-sexp)
(let* ((result (buffer-substring-no-properties begin (point))))
(delete-region begin (point))
result)))
(defun cljr-cycle-stringlike ()
"convert the string or keyword at (point) from string->keyword or keyword->string."
(interactive)
(let* ((original-point (point)))
(while (and
(> (point) 1)
(not (equal "\"" (buffer-substring-no-properties (point) (+ 1 (point)))))
(not (equal ":" (buffer-substring-no-properties (point) (+ 1 (point))))))
(backward-char))
(cond
((equal 1 (point))
(message "beginning of file reached, this was probably a mistake."))
((equal "\"" (buffer-substring-no-properties (point) (+ 1 (point))))
(insert ":" (substring (live-delete-and-extract-sexp) 1 -1)))
((equal ":" (buffer-substring-no-properties (point) (+ 1 (point))))
(insert "\"" (substring (live-delete-and-extract-sexp) 1) "\"")))
(goto-char original-point)))
(defun cljr-cycle-coll ()
"convert the coll at (point) from (x) -> {x} -> [x] -> -> #{x} -> (x) recur"
(interactive)
(let* ((original-point (point)))
(while (and
(> (point) 1)
(not (equal "(" (buffer-substring-no-properties (point) (+ 1 (point)))))
(not (equal "#{" (buffer-substring-no-properties (point) (+ 2 (point)))))
(not (equal "{" (buffer-substring-no-properties (point) (+ 1 (point)))))
(not (equal "[" (buffer-substring-no-properties (point) (+ 1 (point))))))
(backward-char))
(cond
((equal "(" (buffer-substring-no-properties (point) (+ 1 (point))))
(insert "{" (substring (live-delete-and-extract-sexp) 1 -1) "}"))
((equal "#" (buffer-substring-no-properties (point) (+ 1 (point))))
(progn
(delete-char 1)
(insert "(" (substring (live-delete-and-extract-sexp) 1 -1) ")")))
((equal "{" (buffer-substring-no-properties (point) (+ 1 (point))))
(if (equal ?# (char-before))
(progn
(backward-char)
(delete-char 1)
(insert "(" (substring (live-delete-and-extract-sexp) 1 -1) ")"))
(insert "[" (substring (live-delete-and-extract-sexp) 1 -1) "]")))
((equal "[" (buffer-substring-no-properties (point) (+ 1 (point))))
(insert "#{" (substring (live-delete-and-extract-sexp) 1 -1) "}"))
((equal 1 (point))
(message "beginning of file reached, this was probably a mistake.")))
(goto-char original-point)))
(ns payments.billing.accounts
(:require [clojure.data.json :as json]
[clojure.tools.logging :refer [fatal]]
[org.httpkit.client :as http]
[payments.billing.endpoints :as endpoints]
[payments.configuration.properties :refer [config]]
[payments.configuration.schematables :as tables]
[payments.gateways :as gateways])
(:use [clojurewerkz.cassaforte.cql]
[clojurewerkz.cassaforte.query]
[payments.exceptions.http :only [unauthorised not-found
forbidden internal-server-error]]
[payments.utils :only [force-uuid]]))
Turns into:
(ns payments.billing.accounts
(:require [clojure.data.json :as json]
[clojure.tools.logging :refer [fatal]]
[clojurewerkz.cassaforte.cql :refer :all]
[clojurewerkz.cassaforte.query :refer :all]
[org.httpkit.client :as http]
[payments.billing.endpoints :as endpoints]
[payments.configuration.properties :refer [config]]
[payments.configuration.schematables :as tables]
[payments.gateways :as gateways]
[payments.utils :refer [force-uuid]]))
Which has lost certain uses.
Any ideas?
cljr-cycle-stringlike
is in clojure-mode
, since version 2.1 and I don't think that it makes sense duplicating the same functionality here. I guess that in general useful refactorings whose implementations don't depend on third-party libs like paredit
, multiple-cursors
, etc should live in clojure-mode
.
I love the magic requires feature, but I would love it even more if I could easily customize the list of magic namespaces.
When I type (io/
I'd like clj-refactor to import clojure.java.io
automatically. Maybe it should optionally prompt me first.
On the list are at least:
str
to clojure.string
io
to clojure.java.io
set
to clojure.set
Any other ideas?
(ns payments.authnet.transactions
(:require [payments.authnet.client :refer [create-client]]
[payments.utils :refer [uuid]])
(:use [clojurewerkz.cassaforte.cql :as cql]
[clojurewerkz.cassaforte.query]))
Turns into:
(ns payments.authnet.transactions
(:require [clojurewerkz.cassaforte.cql.:as :refer :all] ;; <- here
[clojurewerkz.cassaforte.cql.cql :refer :all]
[clojurewerkz.cassaforte.query :refer :all]
[payments.authnet.client :refer [create-client]]
[payments.utils :refer [uuid]]))
@magnars what do you think of adding cljr-sort-ns-form
, and then also making it so that ns forms automatically get sorted when you call on of the clj-refactor mode's ns-altering fns? We could also make that auto-sorting option if we want.
This is troublesome if you're moving something which is relied on earlier in the let.
Example:
(deftest retrieve-order-body-test
(let [item (get-in X(retrieve-order-body order-item-response-str)))
Move to let on the X results in:
(deftest retrieve-order-body-test
(let [item (get-in something)
something (retrieve-order-body order-item-response-str))
It should push stuff to the top of the let OR just before the current form if already inside the let.
Agree Y/N?
(ns furtive.test.stress.riemann.client-tests
(:use clojure.test)
(:require [furtive.config :as config]
[furtive.dev :refer :all]
[furtive.riemann.client :as client]
[furtive.riemann.driver :as driver]))
... I call cljr-replace-use
(ns furtive.test.stress.riemann.client-tests
(:require [furtive.config :as config]
[furtive.dev :refer :all]
[furtive.riemann.client :as client]
[furtive.riemann.driver :as driver]))
Notice that clojure.test got removed entirely.
cljr-project-clean and friends change the timestamps on files whose content doesn't change.
Also, individual functions (cljr-remove-unused-requires and cljr-sort-ns (maybe others?)) change buffers when their contents don't really change (so you end up with modified buffers that have the same contents as on file).
It's a minor annoyance, but changing things so that no "real" change cause no actual change would be a nice thing.
Can we put cleanup-project-clj-files
from Benedek's blog into clj-refactor, now that his version doesn't depend on projectile?
The doc implies that after I finish adding a require or import, point should jump back to where it was before I started. But this does not seem to happen. Is this broken or is there something I need to do to trigger this?
BTW, Even with a pretty limited set of refactorings this lib is very cool. Thanks for making it!
cljr-cycle-privacy
will convert
(def ^:dynamic ^:private *obj* "The dynamic object." nil)
to
(def ^:private ^:dynamic ^:private *obj* "The dynamic object." nil)
which is not the expected result.
Similarly,
cljr-cycle-privacy
cannot handle
(def ^{:dynamic true
:private true
:doc "The dynamic object."}
*obj* nil)
correctly.
Do you have any interest in a feature like this: johnmastro/clj-refactor.el@0a4a77b? I wouldn't say it quite qualifies as refactoring but it can be handy nonetheless.
The idea is to make it easy to vertically align paired expressions (like bindings or map entries). So starting from something like:
(let [x 1
much-longer 2]
(+ x much-longer))
If you move point to the opening [
or immediately after the closing ]
and invoke M-x cljr-align-pairs
the result will be:
(let [x 1
much-longer 2]
(+ x much-longer))
With a prefix arg it will search up and out (using paredit-backward-up
) for the nearest enclosing binding form or map so that you don't have to position point yourself.
There are some remaining things I'm not happy with or unsure about:
What do you think, is this a good candidate for inclusion? If so I'll work on addressing the above - any feedback appreciated.
I'm using clj-refactor in a big clojurescript project and would to make all functions work with cljs files as well. Do you think it's as simple as changing all code in clj-refactor.el that specifically checks for .clj files to look for .cljs, too? If so, I'd gladly try my best with a patch. Or am I missing some pitfalls here, or other reasons nobody did that before?
I often find myself requiring the same stuff over and over e.g. schemas.core :as s
com.stuartsierra.component :as component
, java.util.UUID
.
What if we added a prefix version of these functions that would give you a completing read of recent require/use/import statements?
I have very long-lived emacs sessions, so one possible way to implement this would be to just have cljr--recent-{require, use, import}
arrays that we add to every time the user calls the non-prefix version.
Alternatively we could make a middlewere that collects all the requires, imports and use statements from the entire project.
Instead of listing all use :refer
variants, they should be combined in some sensible way. E.g. instead of listing tons of variants of clojure.core.async :refer [ ... ]
in the completing read, it would show just one, with the top n most commonly referred symbols, and drop by the :refer
vector to let you edit.
Sorting the namespace required libraries when you have trailing parentheses on a new
line will eat the required libraries.
As expected
(ns org.app.test
(:require [clojure.test :refer :all]
[clojure.edn :as edn]))
Sort namespace and you get:
(ns org.app.test
(:require [clojure.edn :as edn]
[clojure.test :refer :all]))
With trailing parentheses on new line libraries get eaten
(ns org.app.test
(:require [clojure.test :refer :all]
[clojure.edn :as edn]
))
Sort namespace and you get:
(ns org.app.test
(:require)
Thanks for the work, have to say that clj-refactor is a delight to use!
as they can be very disturbing in production code. add something like cljr-remove-println
can be helpful as a project clean function as well.
any thoughts?
@magnars @AlexBaranosky @expez please see the branch created with this commit cdf1c16 if you have a bit of time. this is very rough the only reason I pushed that there are lots of questions here and would appreciate some comments, discussion.
although there are more rough edges but a very naive implementation of searching an AST representation of a big file (approx 600 lines) for referred functions shows fundamentally improved performance.
my test was to take the ns declaration from #51 and create a 600 lines long clj file under it and then run remove-unused-requires
. whilst it takes several seconds with regexp search it is practically instant with AST based search
I will quickly list my experiences, problems and open questions related to this experiment
clojure.tools.analyzer
and friends on the classpath. we can not really piggyback on an already running cider REPL unless we magic in the these dependencies -- which perhaps possible but not very nice.require
it) but only load it if it is available in the user's environment anyway. if it is not there just fall back to simpler, slower refactorings(require 'cider)
line(def e (ana/empty-env))
. might make sense to work on thisFor cljr-cycle-defn-privacy
look for the nearest defn
or defn-
and toggle them.
Correspondingly for cljr-cycle-def-privacy
for def
with ^:private metadata
The defn
version should be easy to implement. The def
slightly more interesting but doable.
If you do cljr-remove-unused-requires
on this namespace:
(ns bug
(:require [clojure.string :refer [join] :as str]))
(join "+" (map str/upper-case ["a" "b" "c"]))
Then the :as str
is removed, and str
is moved into the refer vector.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.