PageRenderTime 52ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/src/main/clojure/clojure/contrib/repl_utils.clj

http://github.com/richhickey/clojure-contrib
Clojure | 207 lines | 161 code | 28 blank | 18 comment | 18 complexity | ad6a3060a8be33b8c32ef0350d8efb39 MD5 | raw file
  1. ; Copyright (c) Chris Houser, Dec 2008. All rights reserved.
  2. ; The use and distribution terms for this software are covered by the
  3. ; Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
  4. ; which can be found in the file CPL.TXT at the root of this distribution.
  5. ; By using this software in any fashion, you are agreeing to be bound by
  6. ; the terms of this license.
  7. ; You must not remove this notice, or any other, from this software.
  8. ; Utilities meant to be used interactively at the REPL
  9. (ns
  10. #^{:author "Chris Houser, Christophe Grand, Stephen Gilardi, Michel Salim",
  11. :doc "Utilities meant to be used interactively at the REPL"}
  12. clojure.contrib.repl-utils
  13. (:import (java.io File LineNumberReader InputStreamReader PushbackReader)
  14. (java.lang.reflect Modifier Method Constructor)
  15. (clojure.lang RT Compiler Compiler$C))
  16. (:require [clojure.contrib.string :as s])
  17. (:use [clojure.contrib.seq :only (indexed)]
  18. [clojure.contrib.javadoc.browse :only (browse-url)]))
  19. ;; ----------------------------------------------------------------------
  20. ;; Examine Java classes
  21. (defn- sortable [t]
  22. (apply str (map (fn [[a b]] (str a (format "%04d" (Integer. b))))
  23. (partition 2 (concat (s/partition #"\d+" t) [0])))))
  24. (defn- param-str [m]
  25. (str " (" (s/join
  26. "," (map (fn [[c i]]
  27. (if (> i 3)
  28. (str (.getSimpleName c) "*" i)
  29. (s/join "," (replicate i (.getSimpleName c)))))
  30. (reduce (fn [pairs y] (let [[x i] (peek pairs)]
  31. (if (= x y)
  32. (conj (pop pairs) [y (inc i)])
  33. (conj pairs [y 1]))))
  34. [] (.getParameterTypes m))))
  35. ")"))
  36. (defn- member-details [m]
  37. (let [static? (Modifier/isStatic (.getModifiers m))
  38. method? (instance? Method m)
  39. ctor? (instance? Constructor m)
  40. text (if ctor?
  41. (str "<init>" (param-str m))
  42. (str
  43. (when static? "static ")
  44. (.getName m) " : "
  45. (if method?
  46. (str (.getSimpleName (.getReturnType m)) (param-str m))
  47. (str (.getSimpleName (.getType m))))))]
  48. (assoc (bean m)
  49. :sort-val [(not static?) method? (sortable text)]
  50. :text text
  51. :member m)))
  52. (defn show
  53. "With one arg prints all static and instance members of x or (class x).
  54. Each member is listed with a number which can be given as 'selector'
  55. to return the member object -- the REPL will print more details for
  56. that member.
  57. The selector also may be a string or regex, in which case only
  58. members whose names match 'selector' as a case-insensitive regex
  59. will be printed.
  60. Finally, the selector also may be a predicate, in which case only
  61. members for which the predicate returns true will be printed. The
  62. predicate will be passed a single argument, a map that includes the
  63. :text that will be printed and the :member object itself, as well as
  64. all the properies of the member object as translated by 'bean'.
  65. Examples: (show Integer) (show []) (show String 23) (show String \"case\")"
  66. ([x] (show x (constantly true)))
  67. ([x selector]
  68. (let [c (if (class? x) x (class x))
  69. members (sort-by :sort-val
  70. (map member-details
  71. (concat (.getFields c)
  72. (.getMethods c)
  73. (.getConstructors c))))]
  74. (if (number? selector)
  75. (:member (nth members selector))
  76. (let [pred (if (ifn? selector)
  77. selector
  78. #(re-find (re-pattern (str "(?i)" selector)) (:name %)))]
  79. (println "=== " (Modifier/toString (.getModifiers c)) c " ===")
  80. (doseq [[i m] (indexed members)]
  81. (when (pred m)
  82. (printf "[%2d] %s\n" i (:text m)))))))))
  83. ;; ----------------------------------------------------------------------
  84. ;; Examine Clojure functions (Vars, really)
  85. (defn get-source
  86. "Returns a string of the source code for the given symbol, if it can
  87. find it. This requires that the symbol resolve to a Var defined in
  88. a namespace for which the .clj is in the classpath. Returns nil if
  89. it can't find the source. For most REPL usage, 'source' is more
  90. convenient.
  91. Example: (get-source 'filter)"
  92. [x]
  93. (when-let [v (resolve x)]
  94. (when-let [filepath (:file (meta v))]
  95. (when-let [strm (.getResourceAsStream (RT/baseLoader) filepath)]
  96. (with-open [rdr (LineNumberReader. (InputStreamReader. strm))]
  97. (dotimes [_ (dec (:line (meta v)))] (.readLine rdr))
  98. (let [text (StringBuilder.)
  99. pbr (proxy [PushbackReader] [rdr]
  100. (read [] (let [i (proxy-super read)]
  101. (.append text (char i))
  102. i)))]
  103. (read (PushbackReader. pbr))
  104. (str text)))))))
  105. (defmacro source
  106. "Prints the source code for the given symbol, if it can find it.
  107. This requires that the symbol resolve to a Var defined in a
  108. namespace for which the .clj is in the classpath.
  109. Example: (source filter)"
  110. [n]
  111. `(println (or (get-source '~n) (str "Source not found"))))
  112. (defn apropos
  113. "Given a regular expression or stringable thing, return a seq of
  114. all definitions in all currently-loaded namespaces that match the
  115. str-or-pattern."
  116. [str-or-pattern]
  117. (let [matches? (if (instance? java.util.regex.Pattern str-or-pattern)
  118. #(re-find str-or-pattern (str %))
  119. #(s/substring? (str str-or-pattern) (str %)))]
  120. (mapcat (fn [ns]
  121. (filter matches? (keys (ns-publics ns))))
  122. (all-ns))))
  123. ;; ----------------------------------------------------------------------
  124. ;; Handle Ctrl-C keystrokes
  125. (def #^{:doc "Threads to stop when Ctrl-C is pressed. See 'add-break-thread!'"}
  126. break-threads (atom {}))
  127. (let [first-time (atom true)]
  128. (defn start-handling-break
  129. "Register INT signal handler. After calling this, Ctrl-C will cause
  130. all break-threads to be stopped. See 'add-break-thread!'"
  131. []
  132. (when (= :need-init
  133. (swap! first-time
  134. {:need-init false, false false, true :need-init}))
  135. (sun.misc.Signal/handle
  136. (sun.misc.Signal. "INT")
  137. (proxy [sun.misc.SignalHandler] []
  138. (handle [sig]
  139. (let [exc (Exception. (str sig))]
  140. (doseq [tref (vals @break-threads) :when (.get tref)]
  141. (.stop (.get tref) exc)))))))))
  142. (defn add-break-thread!
  143. "Add the given thread to break-threads so that it will be stopped
  144. any time the user presses Ctrl-C. Calls start-handling-break for
  145. you. Adds the current thread if none is given."
  146. ([] (add-break-thread! (Thread/currentThread)))
  147. ([t]
  148. (start-handling-break)
  149. (let [tref (java.lang.ref.WeakReference. t)]
  150. (swap! break-threads assoc (.getId t) tref))))
  151. ;; ----------------------------------------------------------------------
  152. ;; Compiler hooks
  153. (defn expression-info
  154. "Uses the Clojure compiler to analyze the given s-expr. Returns
  155. a map with keys :class and :primitive? indicating what the compiler
  156. concluded about the return value of the expression. Returns nil if
  157. not type info can be determined at compile-time.
  158. Example: (expression-info '(+ (int 5) (float 10)))
  159. Returns: {:class float, :primitive? true}"
  160. [expr]
  161. (let [fn-ast (Compiler/analyze Compiler$C/EXPRESSION `(fn [] ~expr))
  162. expr-ast (.body (first (.methods fn-ast)))]
  163. (when (.hasJavaClass expr-ast)
  164. {:class (.getJavaClass expr-ast)
  165. :primitive? (.isPrimitive (.getJavaClass expr-ast))})))
  166. ;; ----------------------------------------------------------------------
  167. ;; scgilardi at gmail
  168. (defn run*
  169. "Loads the specified namespace and invokes its \"main\" function with
  170. optional args."
  171. [ns-sym & args]
  172. (require ns-sym :reload-all)
  173. (apply (ns-resolve ns-sym 'main) args))
  174. (defmacro run
  175. "Loads the specified namespace and invokes its \"main\" function with
  176. optional args. ns-name is not evaluated."
  177. [ns-name & args]
  178. `(run* '~ns-name ~@args))
  179. (load "repl_utils/javadoc")