|
3 | 3 | ;;;; Distributed under the Eclipse Public License, the same as Clojure.
|
4 | 4 | (ns refactor-nrepl.ns.slam.hound.search
|
5 | 5 | "Search the classpath for vars and classes."
|
6 |
| - (:require [orchard.java.classpath :as cp] |
7 |
| - [clojure.java.io :refer [file]] |
8 |
| - [clojure.string :as string]) |
| 6 | + (:require |
| 7 | + [clojure.java.io :refer [file]] |
| 8 | + [clojure.string :as string] |
| 9 | + [refactor-nrepl.util :as util]) |
9 | 10 | (:import
|
10 |
| - [java.io File FilenameFilter] |
11 |
| - [java.util.jar JarFile JarEntry] |
12 |
| - java.util.regex.Pattern |
13 |
| - java.util.StringTokenizer)) |
14 |
| - |
15 |
| -;;; Mostly taken from leiningen.util.ns and swank.util.class-browse. |
16 |
| - |
17 |
| -;; TODO: replace with bultitude? but that doesn't do classes |
18 |
| - |
19 |
| -;;; Clojure namespaces |
| 11 | + (java.io File FilenameFilter) |
| 12 | + (java.util StringTokenizer) |
| 13 | + (java.util.jar JarEntry JarFile) |
| 14 | + (java.util.regex Pattern))) |
20 | 15 |
|
21 | 16 | (defn jar? [^File f]
|
22 | 17 | (and (.isFile f) (.endsWith (.getName f) ".jar")))
|
23 | 18 |
|
24 | 19 | (defn class-file? [^String path]
|
25 | 20 | (.endsWith path ".class"))
|
26 | 21 |
|
27 |
| -(defn clojure-fn-file? [f] |
28 |
| - (re-find #"\$.*__\d+\.class" f)) |
| 22 | +(defn clojure-fn-file? [^String file] |
| 23 | + ;; originally this logic was: (re-find #"\$.*__\d+\.class" f) |
| 24 | + ;; however that doesn't cover e.g. "clojure/spec/alpha$double_in.class" |
| 25 | + ;; so we mimic the logic that e.g. Compliment has: |
| 26 | + (or (.contains file "__") |
| 27 | + (.contains file "$"))) |
29 | 28 |
|
30 | 29 | (defn clojure-ns-file? [^String path]
|
31 | 30 | (.endsWith path "__init.class"))
|
32 | 31 |
|
33 |
| -;;; Java classes |
34 |
| - |
35 |
| -;; could probably be simplified |
36 |
| - |
37 | 32 | (def jar-filter
|
38 | 33 | (proxy [FilenameFilter] []
|
39 | 34 | (accept [d n] (jar? (file n)))))
|
|
47 | 42 | (.. f getParentFile (list jar-filter))
|
48 | 43 | [f])))
|
49 | 44 |
|
50 |
| -(defn class-or-ns-name |
51 |
| - "Returns the Java class or Clojure namespace name for a class relative path." |
| 45 | +(def resource-separator |
| 46 | + "Please do not use File/separator see e.g. https://git.io/Jzig3" |
| 47 | + "/") |
| 48 | + |
| 49 | +(defn class-name |
52 | 50 | [^String path]
|
53 |
| - (-> (if (clojure-ns-file? path) |
54 |
| - (-> path (.replace "__init.class" "") (.replace "_" "-")) |
55 |
| - (.replace path ".class" "")) |
56 |
| - (.replace File/separator "."))) |
| 51 | + (-> path |
| 52 | + (.replace ".class" "") |
| 53 | + (.replace resource-separator "."))) |
57 | 54 |
|
58 | 55 | (defmulti path-class-files
|
59 |
| - "Returns a list of classes found on the specified path location |
60 |
| - (jar or directory), each comprised of a map with the following keys: |
61 |
| - :name Java class or Clojure namespace name |
62 |
| - :loc Classpath entry (directory or jar) on which the class is located |
63 |
| - :file Path of the class file, relative to :loc" |
64 |
| - (fn [^File f _] |
65 |
| - (cond (.isDirectory f) :dir |
66 |
| - (jar? f) :jar |
67 |
| - (class-file? (.getName f)) :class))) |
| 56 | + (fn [^File f _loc] |
| 57 | + (cond |
| 58 | + (.isDirectory f) :dir |
| 59 | + (jar? f) :jar |
| 60 | + (class-file? (.getName f)) :class))) |
68 | 61 |
|
69 | 62 | (defmethod path-class-files :default [& _] [])
|
70 | 63 |
|
|
77 | 70 | (comp
|
78 | 71 | (map #(.getName ^JarEntry %))
|
79 | 72 | (filter class-file?)
|
80 |
| - (map class-or-ns-name)) |
| 73 | + (remove clojure-fn-file?) |
| 74 | + (map class-name)) |
81 | 75 | (enumeration-seq (.entries (JarFile. f))))
|
82 |
| - (catch Exception _e [])))) ; fail gracefully if jar is unreadable |
| 76 | + (catch Exception e |
| 77 | + (util/maybe-log-exception e) |
| 78 | + ;; fail gracefully if jar is unreadable: |
| 79 | + [])))) |
83 | 80 |
|
84 | 81 | (defmethod path-class-files :dir
|
85 | 82 | ;; Dispatch directories and files (excluding jars) recursively.
|
|
93 | 90 | ;; Build class info using file path relative to parent classpath entry
|
94 | 91 | ;; location. Make sure it decends; a class can't be on classpath directly.
|
95 | 92 | [^File f ^File loc]
|
96 |
| - (let [fp (str f), lp (str loc) |
97 |
| - loc-pattern (re-pattern (Pattern/quote (str "^" loc)))] |
98 |
| - (if (re-find loc-pattern fp) ; must be descendent of loc |
| 93 | + (let [fp (str f) |
| 94 | + lp (str loc)] |
| 95 | + (if (re-find (re-pattern (Pattern/quote (str "^" loc))) fp) ; must be descendent of loc |
99 | 96 | (let [fpr (.substring fp (inc (count lp)))]
|
100 |
| - [(class-or-ns-name fpr)]) |
| 97 | + [(class-name fpr)]) |
101 | 98 | [])))
|
102 | 99 |
|
103 | 100 | (defn path-entries-seq
|
104 | 101 | "Split a string on the 'path separator', i.e. ':'. Used for splitting multiple
|
105 | 102 | classpath entries."
|
106 | 103 | [path-str]
|
107 |
| - (enumeration-seq |
108 |
| - (StringTokenizer. path-str File/pathSeparator))) |
109 |
| - |
110 |
| -(defn all-classpath-entries [] |
111 |
| - (mapcat cp/classpath-seq (cp/classpath))) |
| 104 | + (-> path-str |
| 105 | + (StringTokenizer. File/pathSeparator) |
| 106 | + enumeration-seq)) |
112 | 107 |
|
113 | 108 | (defn- get-available-classes []
|
114 | 109 | (into ()
|
115 |
| - (comp (mapcat path-entries-seq) |
116 |
| - (mapcat expand-wildcard) |
117 |
| - (mapcat #(path-class-files % %)) |
| 110 | + (comp (mapcat expand-wildcard) |
| 111 | + (mapcat (fn [file] |
| 112 | + (path-class-files file file))) |
118 | 113 | (remove clojure-fn-file?)
|
119 | 114 | (distinct)
|
120 | 115 | (map symbol))
|
121 |
| - (all-classpath-entries))) |
| 116 | + ;; We use `(System/getProperty "java.class.path")` (at least for the time being) because |
| 117 | + ;; This code was originally written to handle that string, not a list |
| 118 | + ;; (this code was broken for a while as `orchard.java.classpath` was being incompatibly used instead) |
| 119 | + (path-entries-seq (System/getProperty "java.class.path")))) |
122 | 120 |
|
123 | 121 | (def available-classes
|
124 | 122 | (delay (get-available-classes)))
|
125 | 123 |
|
126 |
| -(defn- get-available-classes-by-last-segment |
127 |
| - [] |
128 |
| - (delay |
129 |
| - (group-by #(symbol (peek (string/split (str %) #"\."))) @available-classes))) |
| 124 | +(defn- get-available-classes-by-last-segment [] |
| 125 | + (group-by #(symbol (peek (string/split (str %) #"\."))) @available-classes)) |
130 | 126 |
|
131 | 127 | (def available-classes-by-last-segment
|
132 | 128 | (delay (get-available-classes-by-last-segment)))
|
133 | 129 |
|
134 | 130 | (defn reset
|
135 | 131 | "Reset the cache of classes"
|
136 | 132 | []
|
137 |
| - (alter-var-root #'available-classes (constantly (get-available-classes))) |
138 |
| - (alter-var-root #'available-classes-by-last-segment (constantly (get-available-classes-by-last-segment)))) |
| 133 | + (alter-var-root #'available-classes (constantly (delay (get-available-classes)))) |
| 134 | + (alter-var-root #'available-classes-by-last-segment (constantly (delay (get-available-classes-by-last-segment))))) |
0 commit comments