Skip to content

Instantly share code, notes, and snippets.

@jafingerhut
Created February 7, 2012 05:27
Show Gist options
  • Save jafingerhut/1757414 to your computer and use it in GitHub Desktop.
Save jafingerhut/1757414 to your computer and use it in GitHub Desktop.
apropos2 and unresolve
;; Example use of unresolve:
;; user=> (unresolve #'replace)
;; (replace clojure.core/replace)
;; user=> (use 'clojure.string)
;; WARNING: replace already refers to: #'clojure.core/replace in namespace: user, being replaced by: #'clojure.string/replace
;; WARNING: reverse already refers to: #'clojure.core/reverse in namespace: user, being replaced by: #'clojure.string/reverse
;; nil
;; user=> (unresolve #'replace)
;; (replace clojure.string/replace)
;; user=> (unresolve #'clojure.core/replace)
;; (clojure.core/replace)
(defn unresolve
"Given a var, return a sequence of all symbols that resolve to the
var from the current namespace *ns*."
[var]
(when-not (instance? clojure.lang.Var var)
(throw (Exception. (format "unresolve: first arg must be Var"))))
(let [home-ns (.ns var)
sym-name-str (second (re-find #"/(.*)$" (str var)))]
(sort-by
#(count (str %))
(concat
;; The symbols in the current namespace that map to the var, if
;; any
(->> (ns-map *ns*)
(filter (fn [[k v]] (= var v)))
(map first))
;; This is the "canonical" symbol that resolves to the var, with
;; full namespace/symbol-name
(list (symbol (str home-ns) sym-name-str))
;; There might be one or more aliases for the symbol's home
;; namespace defined in the current namespace.
(->> (ns-aliases *ns*)
(filter (fn [[ns-alias ns]] (= ns home-ns)))
(map first)
(map (fn [ns-alias-symbol]
(symbol (str ns-alias-symbol) sym-name-str))))))))
;; Example use of apropos2:
;; user=> (apropos "replace")
;; (postwalk-replace prewalk-replace replace)
;; user=> (doc postwalk-replace)
;; nil
;; user=> (apropos2 "replace")
;; (clojure.walk/prewalk-replace replace clojure.walk/postwalk-replace)
;; user=> (use 'clojure.walk)
;; nil
;; user=> (apropos2 "replace")
;; (prewalk-replace replace postwalk-replace)
(defn apropos2
"Given a regular expression or stringable thing, calculate a
sequence of all symbols in all currently-loaded namespaces such that
it matches the str-or-pattern, with at most one such symbol per Var.
The sequence returned contains symbols that map to those Vars, and are
the shortest symbols that map to the Var, when qualified with the
namespace name or alias, if that qualification is necessary to name
the Var. Note that it is possible the symbol returned does not match
the str-or-pattern itself, e.g. if the symbol-to-var mapping was
created with :rename.
Searches through all non-Java symbols in the current namespace, but
only public symbols of other namespaces."
[str-or-pattern & opts]
(let [matches? (if (instance? java.util.regex.Pattern str-or-pattern)
#(re-find str-or-pattern (str %))
#(.contains (str %) (str str-or-pattern)))]
(map #(first (unresolve %))
(set
(mapcat (fn [ns]
(map second
(filter (fn [[s v]] (matches? s))
(if (= ns *ns*)
(concat (ns-interns ns) (ns-refers ns))
(ns-publics ns)))))
(all-ns))))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment