diff options
-rw-r--r-- | src/clj/clojure/main.clj | 1 | ||||
-rw-r--r-- | src/clj/clojure/repl.clj | 74 | ||||
-rw-r--r-- | test/clojure/test_clojure.clj | 1 | ||||
-rw-r--r-- | test/clojure/test_clojure/repl.clj | 30 | ||||
-rw-r--r-- | test/clojure/test_clojure/repl/example.clj | 5 |
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 []) |