{"id":13442388,"url":"https://github.com/clojure-emacs/refactor-nrepl","last_synced_at":"2025-06-10T20:15:36.771Z","repository":{"id":18360896,"uuid":"21541051","full_name":"clojure-emacs/refactor-nrepl","owner":"clojure-emacs","description":"nREPL middleware to support refactorings in an editor agnostic way","archived":false,"fork":false,"pushed_at":"2025-03-18T08:20:48.000Z","size":1162,"stargazers_count":255,"open_issues_count":11,"forks_count":69,"subscribers_count":17,"default_branch":"master","last_synced_at":"2025-03-18T08:23:03.588Z","etag":null,"topics":["clj-refactor","clojure","nrepl-middleware","refactoring"],"latest_commit_sha":null,"homepage":null,"language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/clojure-emacs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-07-06T14:00:15.000Z","updated_at":"2024-10-08T14:28:58.000Z","dependencies_parsed_at":"2023-12-03T19:29:12.285Z","dependency_job_id":"3fb314b1-73ea-4878-af42-c251a2e9ab72","html_url":"https://github.com/clojure-emacs/refactor-nrepl","commit_stats":{"total_commits":826,"total_committers":53,"mean_commits":"15.584905660377359","dds":0.5786924939467313,"last_synced_commit":"e3da5e4ea88dbadfe2a5f05e0fbe195a1bb16db8"},"previous_names":[],"tags_count":43,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clojure-emacs%2Frefactor-nrepl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clojure-emacs%2Frefactor-nrepl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clojure-emacs%2Frefactor-nrepl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clojure-emacs%2Frefactor-nrepl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/clojure-emacs","download_url":"https://codeload.github.com/clojure-emacs/refactor-nrepl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244189549,"owners_count":20412985,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clj-refactor","clojure","nrepl-middleware","refactoring"],"created_at":"2024-07-31T03:01:45.115Z","updated_at":"2025-06-10T20:15:36.756Z","avatar_url":"https://github.com/clojure-emacs.png","language":"Clojure","funding_links":[],"categories":["Clojure"],"sub_categories":[],"readme":"[![CircleCI](https://circleci.com/gh/clojure-emacs/refactor-nrepl/tree/master.svg?style=svg)](https://circleci.com/gh/clojure-emacs/refactor-nrepl/tree/master)\n[![Clojars Project](https://img.shields.io/clojars/v/refactor-nrepl/refactor-nrepl.svg)](https://clojars.org/refactor-nrepl/refactor-nrepl)\n[![Dependencies Status](https://versions.deps.co/clojure-emacs/refactor-nrepl/status.svg)](https://versions.deps.co/clojure-emacs/refactor-nrepl)\n[![cljdoc badge](https://cljdoc.org/badge/refactor-nrepl/refactor-nrepl)](https://cljdoc.org/d/refactor-nrepl/refactor-nrepl/CURRENT)\n[![downloads badge](https://versions.deps.co/refactor-nrepl/refactor-nrepl/downloads.svg)](https://clojars.org/refactor-nrepl/refactor-nrepl)\n[![Discord](https://img.shields.io/badge/chat-on%20discord-7289da.svg?sanitize=true)](https://discord.com/invite/nFPpynQPME)\n\n# Refactor nREPL\n\n[nREPL][] middleware to support refactorings in an editor agnostic way.\n\nThe role of this nREPL middleware is to provide refactoring support for clients such as [clj-refactor.el][].\n\n## Usage\n\n### With CIDER and clj-refactor\n\nIf you're using [CIDER][] and clj-refactor you don't have to do anything\nexcept call `cider-jack-in`.  The dependencies are injected\nautomagically.\n\nBe aware that this isn't the case if you connect to an already running REPL process. See the [CIDER documentation](http://cider.readthedocs.io/en/latest/installation/) for more details.\n\n### Adding the middleware via Leiningen\n\nAdd the following, either in your project's `project.clj`,  or in the `:user` profile found at `~/.lein/profiles.clj`:\n\n```clojure\n:plugins [[refactor-nrepl \"3.11.0\"]\n          [cider/cider-nrepl \"0.31.0\"]]\n```\n\n### Embedded nREPL\n\nYou may want launch your own nREPL server with CIDER and refactor-nrepl in it. You'll be able to [`cider-connect`](https://github.com/clojure-emacs/cider/blob/6a17686799b7ef97bc15fa041016421e5c875bfb/cider.el#L1150) to said server.\n\nFor that, you can use the following (more info can be found in the [nREPL Server docs](https://nrepl.org/nrepl/usage/server.html#embedding-nrepl) and [CIDER docs](https://docs.cider.mx/cider/basics/middleware_setup.html#using-embedded-nrepl-server)):\n\n```clojure\n(def custom-nrepl-handler\n  \"We build our own custom nrepl handler, mimicking CIDER's.\"\n  (apply nrepl-server/default-handler\n         (conj cider.nrepl.middleware/cider-middleware 'refactor-nrepl.middleware/wrap-refactor)))\n\n(nrepl-server/start-server :port port :address bind-address :handler custom-nrepl-handler)\n```\n\nThe `cider-middleware` is optional but highly recommended.\n\n### Passing messages to and from refactor-nrepl\n\nWe've already called this a middleware, but we haven't really talked\nabout what that means.  refactor-nrepl is middleware for a REPL.\nSpecifically it's middleware for a networked REPL, which is managed by\n[nREPL][].\nrefactor-nrepl uses the running REPL to to gain insight about your\nproject, in order to offer various refactorings.\n\nMost likely you're already in an environment with a nREPL client available, so you don't have to worry about anything except sending and receiving messages:\n\n```clj\n=\u003e (require '[nrepl.core :as repl])\nnil\n=\u003e (with-open [conn (repl/connect :port 59258)]\n     (-\u003e (repl/client conn 1000)    ; message receive timeout required\n       (repl/message {:op \"eval\" :code \"(+ 2 3)\"})\n       repl/response-values))\n;;=\u003e [5]\n```\n\nIn the example above we're talking to one of the built-in nREPL ops, `eval`, passing it the data `:code \"(+ 2 3)\"`.  The rest of the readme details or own nREPL ops which provide various refactoring support.\n\n## Available features\n\n### Configuration\n\nConfiguration settings are passed along with each msg, currently the recognized options are as follows:\n\n```clj\n{\n ;; Verbose setting for debugging.  The biggest effect this has is\n ;; to not catch any exceptions to provide meaningful error\n ;; messages for the client.\n\n :debug false\n\n  ;; if `true`:\n  ;;   * files that can't be `read`, `require`d or analyzed (with `tools.analyzer`) will be ignored,\n  ;;     instead of aborting the early phases of refactor-nrepl execution.\n  ;;   * ops like `find-symbol` will carry on even if there is broken namespace which we can not build AST for.\n  ;; Setting `false` will be more strict, yielding possibly more correct usage,\n  ;; but it also needs that `:ignore-paths` is correctly set, that all namespaces are valid,\n  ;; that tools.analyzer is able to analyze all of them, etc\n  :ignore-errors true\n\n ;; When true `clean-ns` will remove unused symbols, otherwise just\n ;; sort etc\n :prune-ns-form true\n\n ;; Should `clean-ns` favor prefix forms in the ns macro?\n :prefix-rewriting true\n\n ;; Should `clean-ns` always return the ns form even if there were no structural changes? Useful for when there would only have been whitespace changes from pretty-printing it again.\n :always-return-ns-form false\n\n ;; Should `pprint-ns` place a newline after the `:require` and `:import` tokens?\n :insert-newline-after-require true\n\n ;; Some libspecs are side-effecting and shouldn't be pruned by `clean-ns`\n ;; even if they're otherwise unused.\n ;; This seq of strings will be used as regexp patterns to match\n ;; against the libspec name.\n ;; This value is automatically augmented with configured clj-kondo's :unused-namespace config.\n :libspec-whitelist [\"^cljsjs\"]\n\n ;; Regexes matching paths that are to be ignored\n :ignore-paths []\n}\n```\n\nAny configuration settings passed along with the message will replace the defaults above.\n\n### Artifact lookup\n\nThis middleware provides operations for obtaining information about artifacts from clojars, or mvn central.\nIf JVM system proxy properties are defined (e.g. http.proxyHost, http.proxyPort) they will be used for downloading the artifacts.\n\nTwo ops are available:\n\n#### artifact-list\n\nTakes no arguments and returns a list of all available artifacts.\n\n#### artifact-versions\n\nTakes one required argument, `artifact` which is the full name of the\nartifact e.g. `org.clojure/clojure`, and one optional argument `force`\nwhich optionally triggers a forced update of the cached artifacts.\n\nThe return value is a sorted list, in decreasing order of relevance, with all the available versions.\n\n### find-symbol\n\nThis op finds occurrences of a single symbol.\n\n`find-symbol` requires:\n\n`file` The absolute path to the file containing the symbol to lookup.\n\n`dir` Only files below this dir will be searched.\n\n`ns` The ns where the symbol is defined.\n\n`name` The name of the symbol.\n\n`line` The line number where the symbol occurrs, counting from 1.\n\n`column` The column number where the symbol occurs, counting from 1.\n\n`ignore-errors` [optional] if set find symbol carries on even if there is broken namespace which we can not build AST for\n\nThe return value is a stream of occurrences under the key `occurrence` which is an list of maps like this:\n\n`{:line-beg 5 :line-end 5 :col-beg 19 :col-end 26 :name a-name :file \\\"/aboslute/path/to/file.clj\\\" :match (fn-name some args)}`\n\nWhen the final `occurrence` has been sent a final message is sent with `count`, indicating the total number of matches, and `status` `done`.\n\nClients are advised to set `ignore-errors` on only for find usages as the rest of the operations built on find-symbol supposed to modify the project as well therefore can be destructive if some namespaces can not be analyzed.\n\n### clean-ns\n\nThe `clean-ns` op will perform the following cleanups on an ns form:\n\n* Eliminate :use clauses\n* Sort required libraries, imports and vectors of referred symbols\n* Rewrite to favor prefix form, e.g. [clojure [string test]] instead\n  of two separate libspecs\n* Raise errors if any inconsistencies are found (e.g. a libspec with more than\n  one alias.\n* Remove any unused namespaces, referred symbols or imported classes.\n* Remove any duplication in the :require and :import form.\n* Prune or remove any `:rename` clause.\n* Use the shorthand version of metadata found if possible, and sort it\n  alphabetically\n\nThe `clean-ns` requires a `path` which must be the absolute path to the file containing the `ns` to be operated upon. A client should also pass in a `relative-path`, which is the path relative to the project root, and which is used as a fallback when the `path` does not exist. (see [clj-refactor.el #380](https://github.com/clojure-emacs/clj-refactor.el/issues/380)).\n\nThe return value, `ns` is the entire `(ns ..)` form in prestine condition, or `nil` if nothing was done (so the client doesn't update the timestamp on files when nothing actually needs doing).\n\nPretty-printing the `(ns ..)` form is surprisingly difficult.  The current implementation just puts stuff on the right line and delegates the actual indentation to the client.\n\nIn the event of an error `clean-ns` will return `error` which is an error message intended for display to the user.\n\n**Warning**: The `clean-ns` op dependes on `tools.analyzer` to determine which vars in a file are actually being used.  This means the code is evaluated and any top-level occurrences of `(launch-missiles)` should be avoided.\n\nThis op can be [configured](#configuration).\n\n### resolve-missing\n\nThe goal of the op is to provide intelligent suggestions when the user wants to import or require the unresolvable symbol at point.\n\nThe op requires `symbol` which represents a name to look up on the\nclasspath.  This symbol can be qualified, e.g. `walk/postwalk` or\n`Pattern/quote` will yield the correct result, even though the first\nis a qualified reference to a clojure var and the second a reference\nto a static java method.\n\nThe op also now expects (although does not require) `ns`, the current namespace expressed as a string.\n\nThe return value `candidates` is a list of `({:name candidate.ns :type :ns} {:name candidate.package :type :type} ...)` where type is in `#{:type :class :ns\n:macro}` so we can branch on the various ways to make the symbol available.\n\n* `:type` means the symbol resolved to a var created by `defrecord` or `deftype`\n* `:class` is for Java classes but also includes interfaces.\n* `:macro` is only used if the op is called in a cljs context and means the var resolved to macro.\n\nThe additional property `:already-interned` (boolean) indicates if the current namespace (as passed as `ns`) already had the given suggested\ninterned (e.g.`Thread` and `Object` are interned by default in all JVM clojure namespaces). This helps avoiding the insertion of redundant requires/imports.\n\n### hotload-dependency\n\nLoads a new project dependency into the currently active repl.\n\nThe op requires `coordinates` which is a leiningen style dependency.\n\nThe return value is a `status` of `done` and `dependency` which is the coordinate vector that was hotloaded, or `error` when something went wrong.\n\n### find-used-locals\n\nThis op finds available and used local vars in a selected s-expression\nin a ns on the classpath.  In `clj-refactor` we use this as the\nunderlying op for the `extract-function` refactoring.\n\nThis op requires `file` which is the path of the file to work on as\nwell as `line` and `column`. The enclosing s-expression will be used\nto determine the available and used locals.\n\nBoth `line` and `column` start counting at 1.\n\nReturn values `status` of `done` and `used-locals` which is a list of\nunbound vars, or `error` when something went wrong.\n\nThe returned symbols' order is based on the order of their occurrence in\nthe macro expanded s-expression (that means reversed order for\nthreading macros naturally -- compared to what you actually see).\n\n### stubs-for-interface\n\n`stubs-for-interface` takes a single input `interface` which is a fully qualified symbol which resolves to either an interface or protocol.\n\nThe return value is `edn` and looks like this:\n\n```clj\nuser\u003e (stubs-for-interface {:interface \"java.lang.Iterable\"})\n({:parameter-list \"[^java.util.function.Consumer arg0]\", :name \"forEach\"}\n {:parameter-list \"[]\", :name \"iterator\"}\n {:parameter-list \"[]\", :name \"spliterator\"})\n```\n\nThe intended use-case for `stubs-for-interface` is to provide enough info to create skeleton implementations when implementing e.g. an interface in a defrecord.\n\n### extract-definition\n\n`extract-definition` is based on `find-symbol` so it takes the same input values.  The return value, `definition` is a string of edn which looks like this:\n\n```clj\n{:definition {:line-beg 4\n              :line-end 4\n              :col-beg 9\n              :col-end 21\n              :name \\\"another-val\\\"\n              :file \\\"core.clj\\\"\n              :match \\\"(let [another-val 321]\\\"\n              :definition \\\"321\\\"}\n :occurrences ({:match \\\"(println my-constant my-constant another-val)))\\\"\n                :file \\\"core.clj\\\"\n                :name \\\"another-val\\\"\n                :col-end 50\n                :col-beg 38\n                :line-end 5\n                :line-beg 5})}\n```\n\nThe key `:definition` contains information about the defining form, so the client can delete it.\n\nThe key `:occurrences` is a seq of all occurrences of the symbol which need to be inlined.  This means the definition itself is excluded to avoid any special handling by the client.\n\n### version\n\nThis op returns, `version`, which is the current version of this project.\n\n### warm-ast-cache\n\nEagerly builds, and caches ASTs for all clojure files in the project.  Returns `status` `done` on success and stats for the ASTs built: a list of namespace names as the odd members of the list and either 'OK' as the even member or the error message generated when the given namespace was analyzed. For example\n\n```clojure\n'(com.foo \"OK\" com.bar \"OK\" com.baz '(\"error\" \"Could not resolve var: keyw\"))\n```\n\n\u003e If a given end user sets their `clojure.tools.namespace.repl/refresh-dirs`, files outside said `refresh-dirs` won't be analyzed, resulting in safer, more efficient analysis.\n\n### rename-file-or-dir\n\nThe `rename-file-or-dir` op takes an `old-path` and a `new-path` which are absolute paths to a file or directory.\n\nIf `old-path` is a directory, all files, including any non-clj files, are moved to `new-path`.\n\nThe op returns `touched` which is a list of all files that were affected by the move, and needs to be visited by the client to indent the updated ns form while we await proper pretty printing support in the middleware.\n\nThis op can cause serious havoc if it crashes midway through the\nrefactoring.  I recommend not running it without first creating a\nrestore point in your version control system.\n\n### namespace-aliases\n\nReturns `namespace-aliases` which is a list of all the namespace aliases that are in use in the\nproject. The reply looks like this:\n\n```clj\n{:clj\n {t (clojure.test),\n  set (clojure.set),\n  tracker (refactor-nrepl.ns.tracker clojure.tools.namespace.track)},\n :cljs {set (clojure.set), pprint (cljs.pprint)}}\n```\nThe list of suggestions is sorted by frequency in decreasing order, so the first element is always the best suggestion.\n\nThis op accepts a `:suggest` option, default falsey. If truthy, it will also include suggested aliases, following [Sierra's convention](https://stuartsierra.com/2015/05/10/clojure-namespace-aliases),\nfor existing files that haven't been aliased yet.\n\n### find-used-publics\n\nIn case namespace B depends on namespace A this operation finds occurrences of symbols in namespace B defined in namespace A.\n\n`file` The absolute path to the file being analyzed (namespace B).\n\n`used-ns` The namespace that defines symbols we are searching for (namespace A).\n\nPossible application of this operation to refactor a `:refer :all` style require into a refer or aliased style require.\n\n### Errors\n\nThe middleware returns errors under one of two keys: `:error` or\n`:err`.  The key `:error` contains an error string which is intended\nfor the end user.  The key `:err` is used for unexpected failures and\ncontains among other things a full stacktrace.\n\n## Development with `mranderson`\n\n[mranderson][] is used to avoid classpath collisions between any application deps and the deps of `refactor-nrepl` itself.\n\nFirst make sure you have Leiningen 2.9.1 or later, `lein upgrade` if necessary.\n\n    lein version\n\nTo work with `mranderson` the first thing to do is:\n\n    lein do clean, inline-deps\n\nThis creates the munged local dependencies in `target/srcdeps` directory.\nAfter that you can run your tests or your REPL with:\n\n    lein with-profile +plugin.mranderson/config repl\n    lein with-profile +plugin.mranderson/config test\n\nNote the plus sign (`+`) before the leiningen profile.\n\nIf you want to use `mranderson` while developing locally with the REPL, the source has to be modified in the `target/srcdeps` directory.\n\nWhen you want to release locally to the following:\n\n    PROJECT_VERSION=1.2.3 make install\n\nAnd here's how to deploy to Clojars:\n\n```bash\ngit tag -a v1.2.3 -m \"1.2.3\"\ngit push --tags\n```\n\n## Changelog\n\nAn extensive changelog is available [here](CHANGELOG.md).\n\n## License\n\nCopyright © 2013-2025 Benedek Fazekas, Magnar Sveen, Alex Baranosky, Lars Andersen, Bozhidar Batsov\n\nDistributed under the Eclipse Public License, the same as Clojure.\n\n[nREPL]:https://github.com/nrepl/nrepl\n[CIDER]:https://github.com/clojure-emacs/cider\n[clj-refactor.el]:https://github.com/clojure-emacs/clj-refactor.el\n[mranderson]:https://github.com/benedekfazekas/mranderson\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclojure-emacs%2Frefactor-nrepl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fclojure-emacs%2Frefactor-nrepl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclojure-emacs%2Frefactor-nrepl/lists"}