diff --git a/README.md b/README.md index eb1b5c41b..16dece411 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,7 @@ Middleware | Op(s) | Description `wrap-info` | `info/eldoc` | File/line, arglists, docstrings and other metadata for vars. `wrap-inspect` |`inspect-(start/refresh/pop/push/reset)` | Inspect a Clojure expression. `wrap-macroexpand`| `macroexpand/macroexpand-1/macroexpand-all` | Macroexpand a Clojure form. -`wrap-ns` | `ns-list/ns-vars` | Namespace browsing. +`wrap-ns` | `ns-list/ns-vars/ns-path` | Namespace browsing. `wrap-pprint` | | Adds pretty-printing support to code evaluation. It also installs a dummy `pprint-middleware` op. Thus `wrap-pprint` is discoverable through the `describe` op. `wrap-refresh` | `refresh/refresh-all` | Code reloading. `wrap-resource` | `resource` | Return resource path. diff --git a/src/cider/nrepl/middleware/ns.clj b/src/cider/nrepl/middleware/ns.clj index 042265c92..2622d4f56 100644 --- a/src/cider/nrepl/middleware/ns.clj +++ b/src/cider/nrepl/middleware/ns.clj @@ -1,9 +1,16 @@ (ns cider.nrepl.middleware.ns - (:require [clojure.tools.nrepl.transport :as transport] - [clojure.tools.nrepl.middleware :refer [set-descriptor!]] - [clojure.tools.nrepl.misc :refer [response-for]] - [cider.nrepl.middleware.util.cljs :as cljs] - [cljs-tooling.util.analysis :as cljs-analysis])) + (:require [cider.nrepl.middleware.util.cljs :as cljs] + [cljs-tooling.util.analysis :as cljs-analysis] + [clojure.java.classpath :as cp] + [clojure.java.io :as io] + [clojure.tools.namespace + [file :refer [read-file-ns-decl]] + [find :refer [clojure-sources-in-jar find-clojure-sources-in-dir]]] + [clojure.tools.nrepl + [middleware :refer [set-descriptor!]] + [misc :refer [response-for]] + [transport :as transport]]) + (:import java.util.jar.JarFile)) (defn ns-list-clj [] (->> (all-ns) @@ -51,6 +58,42 @@ (transport/send transport (response-for msg :ns-vars (ns-vars msg))) (transport/send transport (response-for msg :status :done))) +(defn- jar-file? + "Returns true if file is a normal file with a .jar or .JAR extension." + [f] + (let [file (io/file f)] + (and (.isFile file) + (.endsWith (.. file getName toLowerCase) ".jar")))) + +(defn- get-clojure-sources-in-jar + [^JarFile jar] + (let [path-to-jar (.getName jar)] + (map #(str "jar:file:" path-to-jar "!/" %) (clojure-sources-in-jar jar)))) + +(defn- all-clj-files-on-cp [] + (let [dirs-on-cp (filter #(.isDirectory %) (cp/classpath)) + jars-on-cp (map #(JarFile. %) (filter jar-file? (cp/classpath)))] + (concat (->> dirs-on-cp + (mapcat find-clojure-sources-in-dir) + (map #(.getAbsolutePath %))) + (mapcat get-clojure-sources-in-jar jars-on-cp)))) + +(defn- ns-path + [{:keys [ns]}] + (let [ns (symbol ns)] + (loop [paths (all-clj-files-on-cp)] + (when (seq paths) + (let [file-ns (second (read-file-ns-decl (first paths)))] + (if (= file-ns ns) + (first paths) + (recur (rest paths)))))))) + +(defn- ns-path-reply + [{:keys [transport] :as msg}] + (when-let [path (ns-path msg)] + (transport/send transport (response-for msg :path (ns-path msg)))) + (transport/send transport (response-for msg :status :done))) + (defn wrap-ns "Middleware that provides ns listing/browsing functionality." [handler] @@ -58,6 +101,7 @@ (case op "ns-list" (ns-list-reply msg) "ns-vars" (ns-vars-reply msg) + "ns-path" (ns-path-reply msg) (handler msg)))) (set-descriptor! @@ -69,5 +113,9 @@ :returns {"status" "done"}} "ns-vars" {:doc "Returns a sorted list of all vars in a namespace." - :requires {"ns" "The namespace to browse"} - :returns {"status" "done"}}}})) + :requires {"ns" "The namespace to browse."} + :returns {"status" "done"}} + "ns-path" + {:doc "Returns the path to the file containing ns." + :requires {"ns" "The namespace to find."} + :return {"status" "done" "path" "The path to the file containing ns."}}}})) diff --git a/test/clj/cider/nrepl/middleware/ns_test.clj b/test/clj/cider/nrepl/middleware/ns_test.clj index 7e36a8998..49fb5e178 100644 --- a/test/clj/cider/nrepl/middleware/ns_test.clj +++ b/test/clj/cider/nrepl/middleware/ns_test.clj @@ -25,3 +25,11 @@ :ns "clojure.walk"}))] (is (sequential? ns-vars)) (is (every? string? ns-vars)))) + +(deftest ns-path-integration-test + (let [ns-path (:path (session/message {:op "ns-path" + :ns "cider.nrepl.middleware.ns"})) + core-path (:path (session/message {:op "ns-path" + :ns "clojure.core"}))] + (is (.endsWith ns-path "cider/nrepl/middleware/ns.clj")) + (is (.endsWith core-path "clojure/core.clj"))))