Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP - Retrieve java classes with resolve and fix #86 again #93

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 51 additions & 58 deletions src/orchard/info.clj
Original file line number Diff line number Diff line change
Expand Up @@ -12,82 +12,76 @@
[orchard.misc :as misc]
[orchard.java.resource :as resource]))

(defn qualify-sym
"Qualify a symbol, if any in `sym`, with `ns`.

Return nil if `sym` is nil, attempting to generate a valid symbol even
in case some `ns` is missing."
{:added "0.5"}
[ns sym]
(when sym (symbol (some-> ns str) (str sym))))

(defn qualified-symbol?
"Return true if `x` is a symbol with a namespace

This is only available from Clojure 1.9 so we backport it until we
drop support for Clojure 1.8."
{:added "0.5"}
[x]
(boolean (and (symbol? x) (namespace x) true)))

(defn normalize-params
"Normalize the info params.

If :sym is unqualified we assoc a :qualified-sym key with it. The
namespace used is :ns first and then :context-ns.

If :sym is already qualified with assoc a :computed-ns key
and :unqualified-sym key.
If :sym is qualified we compute :sym-ns and :unqualified-sym.

If :dialect is nil, we assoc :clj, our default."
We always assoc :unqualified-sym by calling name on :sym.
We always assoc :dialect defaulting to :clj."
{:added "0.5"}
[params]
(let [{:keys [sym ns context-ns]} params]
(cond-> (update params :dialect #(or % :clj))
(cond-> params
;; If :sym is qualified, we have to use (name), cause:
;; (namespace 'mount.core) ;;=> nil
;; (name 'mount.core) ;;=> "mount.core
(qualified-symbol? sym)
(assoc :qualified-sym sym
:unqualified-sym (misc/name-sym sym)
:computed-ns (misc/namespace-sym sym))
(misc/qualified-symbol? sym)
(assoc :sym-ns (misc/namespace-sym sym)
:qualified-symbol? true)

true
(update :dialect #(or % :clj))

true
(assoc :unqualified-sym (misc/name-sym sym)))))

(defn referred-meta
[{:keys [ns sym-ns qualified-symbol? unqualified-sym]}]

(and sym (not (qualified-symbol? sym)))
(assoc :unqualified-sym (-> sym name symbol))
(let [refer-meta (some-> ns
(m/resolve-refer unqualified-sym)
(m/var-meta))]
(println "----" sym-ns unqualified-sym qualified-symbol? (:ns refer-meta))
(cond
(and refer-meta qualified-symbol? (= sym-ns (:ns refer-meta)))
refer-meta

;; if :sym is missing we still assoc :unqualified-sym from :ns
(and (not sym) ns)
(assoc :unqualified-sym ns)
(and refer-meta (not qualified-symbol?))
refer-meta

(and sym (not (qualified-symbol? sym)) (or ns context-ns))
(assoc :qualified-sym (qualify-sym (or ns context-ns) sym)))))
:else nil)))

(defn clj-meta
{:added "0.5"}
[{:keys [dialect ns sym computed-ns unqualified-sym]}]
[{:keys [dialect ns sym sym-ns qualified-symbol? unqualified-sym] :as params}]
{:pre [(= dialect :clj)]}
(let [ns (or ns computed-ns)]
(let [ns (or ns sym-ns)]
(or
;; it's a special (special-symbol?)
(m/special-sym-meta sym)
;; it's a var
(some-> ns (m/resolve-var sym) (m/var-meta))
;; it's a Java constructor/static member symbol
(some-> ns (java/resolve-symbol sym))
;; it's an unqualified sym maybe referred
(some-> ns (m/resolve-var unqualified-sym) (m/var-meta))
;; it's a Java class/record type symbol
(some-> ns (java/resolve-type unqualified-sym))

;; it's a referred symbol
;; (!) refer should never resolve qualified symbols - we let m/resolve-var do that
(some-> ns (m/resolve-refer sym) (m/var-meta))

;; it's an alias for another ns
(some-> ns (m/resolve-aliases) (get sym) (m/ns-meta))
;; We use :unqualified-sym *exclusively* here because because our :ns is
;; too ambiguous.
;;
;; Observe the incorrect behavior (should return nil, there is a test):
;;
;; (info '{:ns clojure.core :sym non-existing}) ;;=> {:author "Rich Hickey" :ns clojure.core ...}
(some-> ns (m/resolve-aliases) (get unqualified-sym) (m/ns-meta))

;; it's a namespace symbol
;; (!) We use :unqualified-sym *exclusively* here because because our :ns is
;; too ambiguous.
;;
(some-> (find-ns unqualified-sym) (m/ns-meta)))))
;; Observe the following incorrect behavior (should return nil, there is a test):
;; (info '{:ns clojure.core :sym non-existing}) ;;=> {:author "Rich Hickey" :ns clojure.core ...}
(some-> (find-ns unqualified-sym) (m/ns-meta))

;; it's a var
;; (!) has to come before Java resolution - see Integer/max test
(some-> ns (m/resolve-var sym) (m/var-meta))

;; it's a Java class/member symbol
(some-> ns (java/resolve-symbol sym)))))

(defn cljs-meta
{:added "0.5"}
Expand Down Expand Up @@ -142,10 +136,9 @@
in as :env key in params."
[params]
(let [params (normalize-params params)
dialect (:dialect params)
meta (cond
(= dialect :clj) (clj-meta params)
(= dialect :cljs) (cljs-meta params))]
meta (condp = (:dialect params)
:clj (clj-meta params)
:cljs (cljs-meta params))]

;; TODO: Split the responsibility of finding meta and normalizing the meta map.
(some->
Expand Down
89 changes: 55 additions & 34 deletions src/orchard/java.clj
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
(ns orchard.java
"Info for Java classes and members"
{:author "Jeff Valk"}
(:refer-clojure :exclude [qualified-symbol?])
(:require
[clojure.java.io :as io]
[clojure.java.javadoc :as javadoc]
Expand Down Expand Up @@ -112,7 +113,7 @@
(do (require '[orchard.java.parser :as src])
(resolve 'src/source-info))
(if jdk-tools
(do (require '[orchard.java.legacy-parser :as src])
(do #_(require '[orchard.java.legacy-parser :as src])
(resolve 'src/source-info))
(constantly nil))))

Expand Down Expand Up @@ -256,14 +257,14 @@
defined contructors, the line and column returned will be for the first of
these for more convenient `jump` navigation."
[class]
(let [info (class-info class)
ctor (->> (get-in info [:members class])
(vals)
(sort-by :line)
(filter :line)
(first))]
(merge (dissoc info :members)
(select-keys ctor [:line :column]))))
(when-let [info (class-info class)]
(let [ctor (->> (get-in info [:members class])
(vals)
(sort-by :line)
(filter :line)
(first))]
(merge (dissoc info :members)
(select-keys ctor [:line :column])))))

(defn member-info
"For the class and member symbols, return Java member info. If the member is
Expand Down Expand Up @@ -321,14 +322,53 @@
(->> (vals (ns-imports ns))
(map #(member-info (-> ^Class % .getName symbol) sym))
(filter identity)
(distinct))))
(distinct)
(not-empty))))

(defn trim-one-dot
(defn- trim-one-dot
"Trim leading/trailing (one only) dot."
[s]
(str/replace s #"^\.|\.$" ""))

(defn- split-class+member
"Split (qualified) sym and return a tuple of symbols [class member]."
[sym]
(->> (str/split sym #"/" 2)
(map #(when % (symbol %)))))

(defn resolve-qualified
[ns sym]
{:pre [(every? symbol? [ns sym])
(misc/qualified-symbol? sym)]}
(let [qualified-sym (-> sym str trim-one-dot)
[class static-member] (split-class+member qualified-sym)]
(if-let [c (resolve-class ns class)]
(if static-member
(member-info (:class c) static-member) ;; SomeClass/methodCall
(type-info (:class c)))))) ;; SomeClass

(defn resolve-type
"Return type info, for a Java class, interface or record."
[ns sym]
{:pre [(every? symbol? [ns sym])
(not (misc/qualified-symbol? sym))]}
(some->> (resolve-class ns sym)
:class
type-info))

(defn resolve-unqualified
[ns sym]
{:pre [(every? symbol? [ns sym])
(not (misc/qualified-symbol? sym))]}
(let [unqualified-sym (-> sym str trim-one-dot symbol)]
(or (resolve-type ns unqualified-sym) ;; defrecord/deftype
(when-let [ms (seq (resolve-member ns unqualified-sym))] ;; methodCall
(if (= 1 (count ms))
(first ms)
{:candidates (zipmap (map :class ms) ms)})))))

(defn resolve-symbol
"Return the info map for a Java member symbol.
"Return the info map for a Java member symbol (qualified or unqualified).

Constructors and static calls are resolved to the class
unambiguously. Instance members are resolved unambiguously if defined
Expand All @@ -337,28 +377,9 @@
`:candidates`."
[ns sym]
{:pre [(every? symbol? [ns sym])]}
(let [sym (-> sym str trim-one-dot)
sym* (symbol sym)
[class static-member] (->> (str/split sym #"/" 2)
(map #(when % (symbol %))))]
(if-let [c (resolve-class ns class)]
(when static-member
(member-info (:class c) static-member)) ; SomeClass/methodCall
(when-let [ms (seq (resolve-member ns sym*))] ; methodCall
(if (= 1 (count ms))
(first ms)
{:candidates (zipmap (map :class ms) ms)})))))

(defn resolve-type
"Return type info, for a Java class, interface or record."
[ns sym]
(let [sym (-> sym str trim-one-dot)
sym-split (->> (str/split sym #"/" 2)
(map #(when % (symbol %))))]
(some->> (first sym-split)
(resolve-class ns)
:class
type-info)))
(if (misc/qualified-symbol? sym)
(resolve-qualified ns sym)
(resolve-unqualified ns sym)))

(def javadoc-base-urls
"Copied from clojure.java.javadoc. These are the base urls for
Expand Down
Loading