diff options
author | Stuart Sierra <mail@stuartsierra.com> | 2010-08-07 16:41:53 -0400 |
---|---|---|
committer | Stuart Sierra <mail@stuartsierra.com> | 2010-08-07 16:41:53 -0400 |
commit | a6a92b9b3d2bfd9a56e1e5e9cfba706d1aeeaae5 (patch) | |
tree | f1f3da9887dc2dc557df3282b0bcbd4d701ec593 /modules/repl-utils | |
parent | e7930c85290f77815cdb00a60604feedfa2d0194 (diff) |
Split all namespaces into sub-modules.
* Examples and tests have not been copied over.
* Clojure test/compile phases are commented out in parent POM.
* May require installing parent POM before full build.
Diffstat (limited to 'modules/repl-utils')
-rw-r--r-- | modules/repl-utils/pom.xml | 31 | ||||
-rw-r--r-- | modules/repl-utils/src/main/clojure/clojure/contrib/repl_utils.clj | 213 |
2 files changed, 244 insertions, 0 deletions
diff --git a/modules/repl-utils/pom.xml b/modules/repl-utils/pom.xml new file mode 100644 index 00000000..449d7677 --- /dev/null +++ b/modules/repl-utils/pom.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http//www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 + http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.clojure.contrib</groupId> + <artifactId>parent</artifactId> + <version>1.3.0-SNAPSHOT</version> + <relativePath>../parent</relativePath> + </parent> + <artifactId>repl-utils</artifactId> + <dependencies> + <dependency> + <groupId>org.clojure.contrib</groupId> + <artifactId>javadoc</artifactId> + <version>1.3.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>org.clojure.contrib</groupId> + <artifactId>seq</artifactId> + <version>1.3.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>org.clojure.contrib</groupId> + <artifactId>string</artifactId> + <version>1.3.0-SNAPSHOT</version> + </dependency> + </dependencies> +</project>
\ No newline at end of file diff --git a/modules/repl-utils/src/main/clojure/clojure/contrib/repl_utils.clj b/modules/repl-utils/src/main/clojure/clojure/contrib/repl_utils.clj new file mode 100644 index 00000000..fdb321a5 --- /dev/null +++ b/modules/repl-utils/src/main/clojure/clojure/contrib/repl_utils.clj @@ -0,0 +1,213 @@ +; Copyright (c) Chris Houser, Dec 2008. All rights reserved. +; The use and distribution terms for this software are covered by the +; Common Public License 1.0 (http://opensource.org/licenses/cpl.php) +; which can be found in the file CPL.TXT at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +; Utilities meant to be used interactively at the REPL + +;; Deprecated in 1.2: source, get-source, and apropos. These are +;; available in clojure.repl as source, source-fn, and apropos, respectively. + +(ns + ^{:author "Chris Houser, Christophe Grand, Stephen Gilardi, Michel Salim", + :doc "Utilities meant to be used interactively at the REPL"} + clojure.contrib.repl-utils + (:import (java.io File LineNumberReader InputStreamReader PushbackReader) + (java.lang.reflect Modifier Method Constructor) + (clojure.lang RT Compiler Compiler$C)) + (:require [clojure.contrib.string :as s]) + (:use [clojure.contrib.seq :only (indexed)] + [clojure.contrib.javadoc.browse :only (browse-url)])) + +;; ---------------------------------------------------------------------- +;; Examine Java classes + +(defn- sortable [t] + (apply str (map (fn [[a b]] (str a (format "%04d" (Integer. b)))) + (partition 2 (concat (s/partition #"\d+" t) [0]))))) + +(defn- param-str [m] + (str " (" (s/join + "," (map (fn [[c i]] + (if (> i 3) + (str (.getSimpleName c) "*" i) + (s/join "," (replicate i (.getSimpleName c))))) + (reduce (fn [pairs y] (let [[x i] (peek pairs)] + (if (= x y) + (conj (pop pairs) [y (inc i)]) + (conj pairs [y 1])))) + [] (.getParameterTypes m)))) + ")")) + +(defn- member-details [m] + (let [static? (Modifier/isStatic (.getModifiers m)) + method? (instance? Method m) + ctor? (instance? Constructor m) + text (if ctor? + (str "<init>" (param-str m)) + (str + (when static? "static ") + (.getName m) " : " + (if method? + (str (.getSimpleName (.getReturnType m)) (param-str m)) + (str (.getSimpleName (.getType m))))))] + (assoc (bean m) + :sort-val [(not static?) method? (sortable text)] + :text text + :member m))) + +(defn show + "With one arg prints all static and instance members of x or (class x). + Each member is listed with a number which can be given as 'selector' + to return the member object -- the REPL will print more details for + that member. + + The selector also may be a string or regex, in which case only + members whose names match 'selector' as a case-insensitive regex + will be printed. + + Finally, the selector also may be a predicate, in which case only + members for which the predicate returns true will be printed. The + predicate will be passed a single argument, a map that includes the + :text that will be printed and the :member object itself, as well as + all the properies of the member object as translated by 'bean'. + + Examples: (show Integer) (show []) (show String 23) (show String \"case\")" + ([x] (show x (constantly true))) + ([x selector] + (let [c (if (class? x) x (class x)) + members (sort-by :sort-val + (map member-details + (concat (.getFields c) + (.getMethods c) + (.getConstructors c))))] + (if (number? selector) + (:member (nth members selector)) + (let [pred (if (ifn? selector) + selector + #(re-find (re-pattern (str "(?i)" selector)) (:name %)))] + (println "=== " (Modifier/toString (.getModifiers c)) c " ===") + (doseq [[i m] (indexed members)] + (when (pred m) + (printf "[%2d] %s\n" i (:text m))))))))) + +;; ---------------------------------------------------------------------- +;; Examine Clojure functions (Vars, really) + +(defn get-source + "Returns a string of the source code for the given symbol, if it can + find it. This requires that the symbol resolve to a Var defined in + a namespace for which the .clj is in the classpath. Returns nil if + it can't find the source. For most REPL usage, 'source' is more + convenient. + + Example: (get-source 'filter)" + {:deprecated "1.2"} + [x] + (when-let [v (resolve x)] + (when-let [filepath (:file (meta v))] + (when-let [strm (.getResourceAsStream (RT/baseLoader) filepath)] + (with-open [rdr (LineNumberReader. (InputStreamReader. strm))] + (dotimes [_ (dec (:line (meta v)))] (.readLine rdr)) + (let [text (StringBuilder.) + pbr (proxy [PushbackReader] [rdr] + (read [] (let [i (proxy-super read)] + (.append text (char i)) + i)))] + (read (PushbackReader. pbr)) + (str text))))))) + +(defmacro source + "Prints the source code for the given symbol, if it can find it. + This requires that the symbol resolve to a Var defined in a + namespace for which the .clj is in the classpath. + + Example: (source filter)" + {:deprecated "1.2"} + [n] + `(println (or (get-source '~n) (str "Source not found")))) + +(defn apropos + "Given a regular expression or stringable thing, return a seq of +all definitions in all currently-loaded namespaces that match the +str-or-pattern." + {:deprecated "1.2"} + [str-or-pattern] + (let [matches? (if (instance? java.util.regex.Pattern str-or-pattern) + #(re-find str-or-pattern (str %)) + #(s/substring? (str str-or-pattern) (str %)))] + (mapcat (fn [ns] + (filter matches? (keys (ns-publics ns)))) + (all-ns)))) + +;; ---------------------------------------------------------------------- +;; Handle Ctrl-C keystrokes + +(def ^{:doc "Threads to stop when Ctrl-C is pressed. See 'add-break-thread!'"} + break-threads (atom {})) + +(let [first-time (atom true)] + (defn start-handling-break + "Register INT signal handler. After calling this, Ctrl-C will cause + all break-threads to be stopped. See 'add-break-thread!'" + [] + (when (= :need-init + (swap! first-time + {:need-init false, false false, true :need-init})) + (sun.misc.Signal/handle + (sun.misc.Signal. "INT") + (proxy [sun.misc.SignalHandler] [] + (handle [sig] + (let [exc (Exception. (str sig))] + (doseq [tref (vals @break-threads) :when (.get tref)] + (.stop (.get tref) exc))))))))) + +(defn add-break-thread! + "Add the given thread to break-threads so that it will be stopped + any time the user presses Ctrl-C. Calls start-handling-break for + you. Adds the current thread if none is given." + ([] (add-break-thread! (Thread/currentThread))) + ([t] + (start-handling-break) + (let [tref (java.lang.ref.WeakReference. t)] + (swap! break-threads assoc (.getId t) tref)))) + +;; ---------------------------------------------------------------------- +;; Compiler hooks + +(defn expression-info + "Uses the Clojure compiler to analyze the given s-expr. Returns + a map with keys :class and :primitive? indicating what the compiler + concluded about the return value of the expression. Returns nil if + not type info can be determined at compile-time. + + Example: (expression-info '(+ (int 5) (float 10))) + Returns: {:class float, :primitive? true}" + [expr] + (let [fn-ast (Compiler/analyze Compiler$C/EXPRESSION `(fn [] ~expr)) + expr-ast (.body (first (.methods fn-ast)))] + (when (.hasJavaClass expr-ast) + {:class (.getJavaClass expr-ast) + :primitive? (.isPrimitive (.getJavaClass expr-ast))}))) + +;; ---------------------------------------------------------------------- +;; scgilardi at gmail + +(defn run* + "Loads the specified namespace and invokes its \"main\" function with + optional args." + [ns-sym & args] + (require ns-sym :reload-all) + (apply (ns-resolve ns-sym 'main) args)) + +(defmacro run + "Loads the specified namespace and invokes its \"main\" function with + optional args. ns-name is not evaluated." + [ns-name & args] + `(run* '~ns-name ~@args)) + + +(load "repl_utils/javadoc") |