summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStuart Halloway <stu@thinkrelevance.com>2010-05-07 13:12:49 -0400
committerStuart Halloway <stu@thinkrelevance.com>2010-05-07 18:44:22 -0400
commit40f3dc93b926721e94b75a10a9c88815ea4691aa (patch)
treeb1239c7b41f3c260beb4de534517606d9f5daa59
parent87edb1aa4e6c29d7fe386ed66ca80388ed13a488 (diff)
clojure.repl with -fn naming, #310
Signed-off-by: Stuart Halloway <stu@thinkrelevance.com>
-rw-r--r--src/clj/clojure/main.clj1
-rw-r--r--src/clj/clojure/repl.clj74
-rw-r--r--test/clojure/test_clojure.clj1
-rw-r--r--test/clojure/test_clojure/repl.clj30
-rw-r--r--test/clojure/test_clojure/repl/example.clj5
5 files changed, 111 insertions, 0 deletions
diff --git a/src/clj/clojure/main.clj b/src/clj/clojure/main.clj
index bc4ac2c0..1af45bf8 100644
--- a/src/clj/clojure/main.clj
+++ b/src/clj/clojure/main.clj
@@ -194,6 +194,7 @@
(catch Throwable e
(caught e)
(set! *e e)))
+ (use 'clojure.repl)
(prompt)
(flush)
(loop []
diff --git a/src/clj/clojure/repl.clj b/src/clj/clojure/repl.clj
new file mode 100644
index 00000000..232c728d
--- /dev/null
+++ b/src/clj/clojure/repl.clj
@@ -0,0 +1,74 @@
+; 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
+
+(ns
+ #^{:author "Chris Houser, Christophe Grand, Stephen Gilardi, Michel Salim, Christophe Grande"
+ :doc "Utilities meant to be used interactively at the REPL"}
+ clojure.repl
+ (:import (java.io LineNumberReader InputStreamReader PushbackReader)
+ (clojure.lang RT Reflector)))
+
+;; ----------------------------------------------------------------------
+;; Examine Clojure functions (Vars, really)
+
+(defn source-fn
+ "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: (source-fn 'filter)"
+ [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)"
+ [n]
+ `(println (or (source-fn '~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."
+ [str-or-pattern]
+ (let [matches? (if (instance? java.util.regex.Pattern str-or-pattern)
+ #(re-find str-or-pattern (str %))
+ #(.contains (str %) (str str-or-pattern)))]
+ (mapcat (fn [ns]
+ (filter matches? (keys (ns-publics ns))))
+ (all-ns))))
+
+(defn dir-fn
+ "Returns a sorted seq of symbols naming public vars in
+ a namespace"
+ [ns]
+ (sort (map first (ns-publics (the-ns ns)))))
+
+(defmacro dir
+ "Prints a sorted directory of public vars in a namespace"
+ [nsname]
+ `(doseq [v# (dir-fn '~nsname)]
+ (println v#)))
diff --git a/test/clojure/test_clojure.clj b/test/clojure/test_clojure.clj
index 04380914..9dd0bde2 100644
--- a/test/clojure/test_clojure.clj
+++ b/test/clojure/test_clojure.clj
@@ -56,6 +56,7 @@
:pprint
:serialization
:rt
+ :repl
])
(def test-namespaces
diff --git a/test/clojure/test_clojure/repl.clj b/test/clojure/test_clojure/repl.clj
new file mode 100644
index 00000000..1f7a147a
--- /dev/null
+++ b/test/clojure/test_clojure/repl.clj
@@ -0,0 +1,30 @@
+(ns clojure.test-clojure.repl
+ (:use clojure.test
+ clojure.repl
+ clojure.test-clojure.repl.example))
+
+(deftest test-source
+ (is (= "(defn foo [])" (source-fn 'clojure.test-clojure.repl.example/foo)))
+ (is (= "(defn foo [])\n" (with-out-str (source clojure.test-clojure.repl.example/foo))))
+ (is (nil? (source-fn 'non-existent-fn))))
+
+(deftest test-dir
+ (is (thrown? Exception (dir-fn 'non-existent-ns)))
+ (is (= '[bar foo] (dir-fn 'clojure.test-clojure.repl.example)))
+ (is (= "bar\nfoo\n" (with-out-str (dir clojure.test-clojure.repl.example)))))
+
+(deftest test-apropos
+ (testing "with a regular expression"
+ (is (= '[defmacro] (apropos #"^defmacro$")))
+ (is (some #{'defmacro} (apropos #"def.acr.")))
+ (is (= [] (apropos #"nothing-has-this-name"))))
+
+ (testing "with a string"
+ (is (some #{'defmacro} (apropos "defmacro")))
+ (is (some #{'defmacro} (apropos "efmac")))
+ (is (= [] (apropos "nothing-has-this-name"))))
+
+ (testing "with a symbol"
+ (is (some #{'defmacro} (apropos 'defmacro)))
+ (is (some #{'defmacro} (apropos 'efmac)))
+ (is (= [] (apropos 'nothing-has-this-name)))))
diff --git a/test/clojure/test_clojure/repl/example.clj b/test/clojure/test_clojure/repl/example.clj
new file mode 100644
index 00000000..3a5238e7
--- /dev/null
+++ b/test/clojure/test_clojure/repl/example.clj
@@ -0,0 +1,5 @@
+(ns clojure.test-clojure.repl.example)
+
+;; sample namespace for repl tests, don't add anything here
+(defn foo [])
+(defn bar [])