GithubHelp home page GithubHelp logo

clojure-emacs / clj-refactor.el Goto Github PK

View Code? Open in Web Editor NEW
766.0 30.0 109.0 6.4 MB

A CIDER extension that provides powerful commands for refactoring Clojure code.

License: GNU General Public License v3.0

Emacs Lisp 71.43% Gherkin 28.37% Makefile 0.19%
refactorings refactoring-functions clojure emacs emacs-mode emacs-lisp cider nrepl

clj-refactor.el's People

Contributors

aeronotix avatar alexbaranosky avatar areina avatar bbatsov avatar benedekfazekas avatar dgtized avatar dotemacs avatar expez avatar iarenaza avatar lewang avatar lins05 avatar magnars avatar malabarba avatar mbuczko avatar peeja avatar philjackson avatar pierre-rouleau avatar plexus avatar prertik avatar purcell avatar raxod502 avatar robert-stuttaford avatar samhandev avatar sduckett avatar tanzoniteblack avatar timothyandrew avatar vedang avatar vemv avatar yantonov avatar yuhan0 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

clj-refactor.el's Issues

Sort namespace & closing parentheses on new line

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!

Using it to refactor cljs

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?

thread and unthread ideas

@magnars

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

This particular ns decl fails to have cljr-replace-use ran on it

(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

bug in cljr-thread-first-all

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.

cljr-project-clean and friends change the timestamps on files whose content doesn't change

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.

cljr-unwind failure case

(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)))

cljr-replace-use seems to destroy certain use declarations

(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?

Feature to vertically align expressions

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:

  1. Is searching up optionally a good compromise? Or should it either just do it by default, or not do it at all?
  2. It doesn't handle metadata in bindings at all.
  3. How should multiple bindings / entries on a single line be handled?
  4. It needs tests.
  5. The function names are awkward

What do you think, is this a good candidate for inclusion? If so I'll work on addressing the above - any feedback appreciated.

Jump back after adding require/import/use not working

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!

Add cljr-cycle-defn-privacy and cljr-cycle-defn-privacy

For 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.

Collapse duplicate requires after add-to-ns

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]]))

Add cljr-cycle-collection and cljr-cycle-stringlike

Add cljr-cycle-collection and cljr-cycle-stringlike inspired from emacs-live.

They're pretty useful...

Note:

  • I modified the former to cycle through a set version as well
  • Could consider having the latter cycle through symbols as well. "foo" -> :foo ->foo -> "foo"
(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)))

"Symbol's value as variable is void: otherwise"

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.

Move to let inside the let form puts the moved item to the end of the let

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?

Add project dependency refactoring

Refactoring to add a dependency to project.clj without having to:

  1. Open project.clj
  2. Google the lib to find the latest version
  3. Copy/paste the artifact into project.clj

I imagine this going something like this:

  1. Start the refactoring
  2. Completing read among libraries
  3. Completing read among versions (defaulting to latest stable)

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.

File renaming assumes src/ namespace root

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

Sorting ns forms

@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.

Move var to other namespace

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)?

customizable magic requires

I love the magic requires feature, but I would love it even more if I could easily customize the list of magic namespaces.

More refactoring features

Just a suggestion. Here are some really useful features for refactoring.

  • Extract function
  • Extract local (for Clojure, maybe extract into a let form)
  • Rename function (probably not easy as you have to look in all the project files)

unwinding gives error

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 (-> ... )

remove ('s when threading

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.

Some thing only work if you use Leningen

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?

cljr-cycle-stringlike duplicates clojure-mode functionality

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.

Project cleanup function?

Can we put cleanup-project-clj-files from Benedek's blog into clj-refactor, now that his version doesn't depend on projectile?

cljr-replace-use doesn't work with uses with :as clauses

(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]]))

Refactoring to re-order defns to please the compiler

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?

extract fn / inline fn / rename

  • extract defn or defn-
  • inline function
  • rename within project

All the hard ones. Even thought they're relatively harder, I'd like to talk more about how we could implement them here.

Move project to clojure-emacs org

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.

Toggle thread first/last

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.

Caching for add-{require,use,import}-to-ns?

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.

Magic imports

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?

remove printlns

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?

More interactive refactoring

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.

smarter refactorings with cider building an AST

@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.

the good

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

the bad and the ugly

I will quickly list my experiences, problems and open questions related to this experiment

  • I think we need a dedicated cider session because we need 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.
  • I am experimenting with starting up this REPL whenever clj-refactor minor mode is invoked, check if we have cider available and start the REPL if we don't have one yet. this kinda works, but sometimes more than one repl gets started
  • I think we should hide the REPL -- hide as not show it in the foreground if possible. could not figure out yet how
  • I think the nice thing would be not to explicitly depend on cider (as 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
  • the above is not done yet so if you want to test the AST based stuff uncomment the (require 'cider) line
  • we might want to cache ASTs with a filename (incl path from project directory) and timestamp as cachekey. perhaps in the repl session itself?
  • I did not give much thought to how to create a meaningful environment when the AST is created, only use (def e (ana/empty-env)). might make sense to work on this

cljr-replace-use removing (:use clojure.test) entirely

(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.

Expand-let search problem

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.

Some way to sort keys :)

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.)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.