refactor-nrepl, 在编辑器不可知的情况下,支持重构的nREPL中间件

分享于 

16分钟阅读

GitHub

  繁體 雙語
nREPL middleware to support refactorings in an editor agnostic way
  • 源代码名称:refactor-nrepl
  • 源代码网址:http://www.github.com/clojure-emacs/refactor-nrepl
  • refactor-nrepl源代码文档
  • refactor-nrepl源代码下载
  • Git URL:
    git://www.github.com/clojure-emacs/refactor-nrepl.git
    Git Clone代码到本地:
    git clone http://www.github.com/clojure-emacs/refactor-nrepl
    Subversion代码到本地:
    $ svn co --depth empty http://www.github.com/clojure-emacs/refactor-nrepl
    Checked out revision 1.
    $ cd repo
    $ svn up trunk
    

    Build StatusGitter

    重构 nREPL

    支持编辑器中重构的nREPL中间件。

    这里nREPL中间件的作用是为客户端提供重构支持,例如 clj-refactor.el

    用法

    带有苹果酒和clj重构的

    如果你使用苹果酒和clj重构,除了调用 cider-jack-in 之外,你不需要做任何事情。 自动注入依赖项。

    注意,如果你连接到运行的REPL进程,则这里情况不是这样的。 有关详细信息,请参阅苹果酒文档。

    通过Leiningen添加中间件

    在项目 project.clj 中,或者在 ~/.lein/profiles.clj 中找到的:user 配置文件中添加以下内容:

    :plugins [[refactor-nrepl "2.3.1"]
     [cider/cider-nrepl "0.14.0"]]

    通过引导添加中间件

    ~/.boot/profile.boot 中添加以下内容:

    (require 'boot.repl)
    (swap! boot.repl/*default-dependencies* conj
     '[refactor-nrepl "2.3.1"]
     '[cider/cider-nrepl "0.14.0"])
    (swap! boot.repl/*default-middleware* conj
     'refactor-nrepl.middleware/wrap-refactor)

    传递消息到重构 nrepl

    我们已经把它称作中间件,但我们并没有真正讨论它是什么意思。 重构nrepl的中间件。 具体来说,它是一个网络化REPL的中间件,由 clojure.tools.nrepl 管理。 重构nrepl使用运行的REPL来了解你的项目,以便提供各种重构。

    你可以能已经在一个具有nREPL客户端的环境中,因这里你不必担心发送接收消息:

    => (require '[clojure.tools.nrepl :as repl])nil=> (with-open [conn (repl/connect:port59258)]
     (-> (repl/client conn 1000) ; message receive timeout required (repl/message {:op"eval":code"(+ 2 3)"})
     repl/response-values));;=> [5]

    在上面的例子中,我们与一个内置的nREPL操作系统( eval ) 进行通信, :code"(+ 2 3)" readme详细信息或者自己的nREPL ops,提供各种重构支持。

    可用特性

    配置

    配置设置随每个msg一起传递,当前可以识别的选项如下所示:

    {
     ;; Verbose setting for debugging. The biggest effect this has is;; to not catch any exceptions to provide meaningful error;; messages for the client.:debugfalse;; When true `clean-ns` will remove unused symbols, otherwise just;; sort etc:prune-ns-formtrue;; Should `clean-ns` favor prefix forms in the ns macro?:prefix-rewritingtrue;; Some libspecs are side-effecting and shouldn't be pruned by `clean-ns`;; even if they're otherwise unused.;; This seq of strings will be used as regexp patterns to match;; against the libspec name.:libspec-whitelist ["^cljsjs"]
     ;; Regexes matching paths that are to be ignored:ignore-paths []
    }

    随消息传递的任何配置设置都将替换上述默认设置。

    基于的工件查找

    这个中间件提供了从clojars或者mvn中心中获取工件信息的操作。

    有两个操作可以供选择:

    工件列表

    不接受参数并返回所有可用工件的列表。

    工件版本

    采用一个必需的参数,artifact 是工件 比如 org.clojure/clojure的完整 NAME,可选的参数 force 可以触发缓存的构件。

    返回值是按照相关性降序排序的排序列表,所有可用版本。

    查找符号

    这里操作查找单个符号的出现。

    find-symbol 需要:

    file 包含要查找的符号的文件的绝对路径。

    dir 只搜索这里目录下的文件。

    ns 定义符号的。

    name 符号的NAME。

    line 表示符号发生的行号,从 1计数。

    column 符号发生的列号,从 1计数。

    ignore-errors [optional] 如果设置查找符号继续进行,即使有断开的命名空间,我们无法构建

    返回值是关键 occurrence 下的一个事件流,它是这样的映射的列表:

    {: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)}

    发送 final occurrence 时,会向 count 发送 final 消息,指示 MATCHES 和 statusdone的总数。

    建议客户端仅为查找使用的用法设置 ignore-errors,因这里,如果找不到某些命名空间,则可能会破坏。

    清洁 ns

    clean-ns op将按格式执行以下清理操作:

    • 消除:使用子句
    • 排序所需符号的库,导入和向量
    • 重写为前缀形式,比如 [clojure [string test] ],而不是两个单独的libspecs
    • 如果发现任何不一致性,则引发错误( 比如。 具有多个别名的libspec。
    • 删除任何未使用的命名空间,引用的符号或者导入的类。
    • 删除:要求和:导入表单中的任何重复。
    • 删除或者删除任何 :rename 子句。

    clean-ns 需要一个 path,它是包含要运行的ns的文件的路径。

    返回值,ns 是prestine条件中的整个 (ns.. ) 表单,如果没有完成( 所以客户机不会在没有任何实际需要的情况下更新文件的时间戳),则返回 nil

    漂亮的打印 (ns.. ) 表单非常困难。 当前的实现只是将内容放在正确的行,并将实际的缩进委托给客户端。

    发生错误时 clean-ns 将返回 error,是一个错误消息,用于显示给用户。

    警告: tools.analyzer 上的clean-ns 操作 dependes,确定文件中的哪个var实际上使用。 这意味着代码被评估,并且应该避免 (launch-missiles)的任何顶层出现。

    这个操作可以是配置。

    解析丢失

    op的目标是在用户想要导入或者需要不可以解析的符号时提供智能建议。

    op需要表示在类路径上查找的NAME的symbol。 这个符号可以被限定,比如 walk/postwalk 或者 Pattern/quote 将产生正确的结果,即使第一个是对 clojure var的限定引用,第二个是对 static java方法的引用。

    返回值 candidates 是一个 ({:name candidate.ns :type :ns} {:name candidate.package :type :type}.. .) 其中类型为 #{:type :class :ns :macro} 所以我们可以用不同的方法使符号可用。 :type 表示符号解析为由 defrecord 或者 deftype 创建的var,:class 用于Java类,但也包括接口。 仅在cljs上下文中调用op并表示var解析为宏时才使用 :macro

    hotload依赖项

    将新项目依赖项加载到当前活动的。

    op需要 coordinates,它是一个leiningen样式依赖项。

    返回值是 donedependencystatus,它是hotloaded的坐标向量,或者在出现错误时是 error

    find-used-locals

    在类路径中,在类路径中的一个所选s 表达式中,这个。 在 clj-refactor 中,我们将它用作 extract-function 重构的底层操作。

    这个操作需要 file,它是要处理的文件路径,以及 linecolumn。 封闭的表达式将用于确定可用的和已经使用的局部变量。

    linecolumn 都开始计数 1.

    返回 doneused-locals的值 status,这是未绑定的var的列表,当出现错误时,返回 error

    返回的符号'顺序基于它们在宏展开表达式中出现的顺序( 这意味着与你实际看到的代码相比,线程 MACROS 自然--的逆序顺序。)。

    stubs-for-interface

    stubs-for-interface 接受单个输入 interface,它是一个完全限定的符号,它解析为接口或者协议。

    返回值为 edn,如下所示:

    user> (stubs-for-interface {:interface"java.lang.Iterable"})
    ({:parameter-list"[^java.util.function.Consumer arg0]", :name"forEach"}
     {:parameter-list"[]", :name"iterator"}
     {:parameter-list"[]", :name"spliterator"})

    面向 stubs-for-interface的预期用例是提供足够的信息,在实现 比如 中的接口时创建框架实现。

    提取定义

    extract-definition 基于 find-symbol,因此它采用相同的输入值。 返回值,definition 是一个如下所示的字符串:

    {:definition {:line-beg4:line-end4:col-beg9:col-end21:name"another-val"
     :file"core.clj"
     :match"(let [another-val 321]"
     :definition"321"}
     :occurrences ({:match"(println my-constant my-constant another-val)))"
     :file"core.clj"
     :name"another-val"
     :col-end50:col-beg38:line-end5:line-beg5})}

    :definition 包含有关定义表单的信息,因此客户端可以对它的进行 delete。

    :occurrences 是需要内联的符号的所有出现的序列。 这意味着定义本身被排除,以避免客户端进行任何特殊处理。

    版本

    这个操作返回,version,它是这个项目的当前版本。

    warm-ast-cache

    Eagerly构建,并为项目中的所有clojure文件缓存 ast。 在成功时返回 statusdone,并返回生成的ast的统计信息: 作为列表的奇数成员,或者在分析给定命名空间时生成的错误消息的名称空间名称列表。 例如

    '(com.foo "OK" com.bar "OK" com.baz '("error""Could not resolve var: keyw"))

    rename-file-or-dir

    rename-file-or-dir op接受 old-pathnew-path,它们是文件或者目录的绝对路径。

    如果 old-path 是一个目录,所有文件( 包括任何非clj文件) 都将被移到 new-path

    op返回所有受移动影响且需要客户端访问的文件,客户端需要访问这些文件,以便在中间件中进行适当的打印支持,以便在中间件中正确打印。

    这个操作可能会造成严重破坏,如果它在重构过程中崩溃。 建议不要在版本控制系统中首先创建还原点而运行它。

    命名空间别名

    返回 namespace-aliases,它是项目中使用的所有命名空间别名的列表。 回复的内容如下:

    {:clj {t (clojure.test),
     set (clojure.set),
     tracker (refactor-nrepl.ns.tracker clojure.tools.namespace.track)},
     :cljs {set (clojure.set), pprint (cljs.pprint)}}

    建议列表按频率按降序排序,因此第一个元素始终是最佳建议。

    find-used-publics

    如果命名空间B 依赖于命名空间,这里操作将在命名空间A 中定义的命名空间B 中查找符号匹配项。

    file 分析的文件的绝对路径( 命名空间B )。

    used-ns 定义我们搜索的符号的命名空间( 命名空间)。

    这里操作的可以能应用程序重构 :refer :all 样式需要引用或者别名样式。

    错误

    中间件返回以下两个键之一的错误: :error 或者 :err 密钥 :error 包含一个用于最终用户的错误字符串。 密钥 :err 用于意外失败,其中包含了完整的事件。

    使用开发基于的插件

    mranderson 用于避免类路径冲突。

    要使用 mranderson,首先要做的是:

    lein do clean, source-deps :prefix-exclusions"["classlojure"]"

    这将在目标/srcdeps目录中创建转换的本地

    之后,你可以运行你的测试或者你的repl:

    lein with-profile +plugin.mranderson/config repl

    lein with-profile +plugin.mranderson/config test

    注意leiningen配置文件之前的加号。

    如果你想使用repl在本地开发 mranderson,则必须在 target/srcdeps 目录中修改源代码。

    当你想在本地发布时:

    lein with-profile plugin.mranderson/config install

    向::

    lein with-profile plugin.mranderson/config deploy clojars

    或者运行

    ./build.sh install

    ./build.sh deploy clojars

    通过正确的参数运行 build.sh 清理,运行测试,运行测试,然后运行提供的lein目标。

    变更日志

    这里有一个广泛的变更日志,可以在这里找到。

    许可证

    版权所有 © 2013 -2017 Benedek Fazekas,Magnar Sveen,Alex Baranosky,Lars

    在 Eclipse public 许可证下分发,与Clojure相同。


    SUP  EDI  REF  中间件  refactor  Nrepl