Skip to content

Instantly share code, notes, and snippets.

@ohpauleez
Last active August 29, 2015 14:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ohpauleez/4ca0b646b89512f38ea2 to your computer and use it in GitHub Desktop.
Save ohpauleez/4ca0b646b89512f38ea2 to your computer and use it in GitHub Desktop.
Another debug repl
;; Simpler debug-repl based on https://github.com/stuarthalloway/circumspec/blob/master/src/clojure/contrib/debug.clj
;; This `debug-repl` also tries to capture line metadata from forms/files, as well as exceptions
;; Some people may want to rename `debug-repl` to `break`, and `quit-dr` to `continue`
(defmacro local-bindings
"Produces a map of the names of local bindings to their values."
[]
(let [symbols (map key @clojure.lang.Compiler/LOCAL_ENV)]
(zipmap (map (fn [sym] `(quote ~sym)) symbols) symbols)))
(def ^:dynamic *locals*)
(defn eval-with-locals
"Evals a form with given locals.
The locals should be a map of symbols to values."
[locals form]
(binding [*locals* locals]
(eval
`(let ~(vec (mapcat #(list % `(*locals* '~%)) (keys locals)))
~form))))
(defn dr-read
[request-prompt request-exit]
(let [input (clojure.main/repl-read request-prompt request-exit)]
(if (#{'() request-exit request-prompt} input)
request-exit
input)))
(def ^{:dynamic true :tag 'long} level (long 0))
(def counter (atom 1000))
(defn inc-counter []
(swap! counter inc))
(def element (atom nil))
(def quit-dr-exception
(proxy [Exception java.util.Enumeration] []
(nextElement [] @element)))
(defn quit-dr [& form]
(reset! element (first form))
(throw quit-dr-exception))
(defn caught [exc]
(if (or (= exc quit-dr-exception)
(= (.getCause ^Throwable exc) quit-dr-exception))
(throw quit-dr-exception)
(clojure.main/repl-caught exc)))
(defmacro debug-repl
"Starts a REPL with the local bindings available."
([]
`(debug-repl nil ~(meta &form)))
([retform]
`(debug-repl retform ~(meta &form)))
([retform form-meta]
`(let [debug-meta# ~(assoc form-meta
:ns *ns*
:file *file*)
eval-fn# (fn [e-form#]
(eval-with-locals (merge {(symbol "debug-meta") debug-meta#
(symbol "in-debug-ns") (fn [] (in-ns (ns-name (:ns debug-meta#))))}
(local-bindings)
{(symbol "quit-dr") ~quit-dr}) e-form#))]
(try
(binding [level (unchecked-inc level)]
(clojure.main/repl
:init (fn []
(use 'clojure.core)
(use 'clojure.repl))
:prompt #(print (str "dr-" level "-" (inc-counter) " [" *ns* "]=> "))
:eval eval-fn#
:read dr-read
:caught caught))
(catch Exception e#
(if (= e# quit-dr-exception)
(if-let [new-form# (.nextElement ^java.util.Enumeration quit-dr-exception)]
(eval-fn# new-form#)
(eval-fn# ~retform))
(throw e#)))))))
(defmacro assert-repl [assertion-form]
`(when *assert*
(if-not ~assertion-form
(debug-repl)
;;Forward standard assert behavior, even if that's just pr-str
(assert ~assertion-form))))
(defmacro try-repl [body-form]
`(try
~body-form
(catch Throwable ex#
(let [~'debug-exception ex#]
(debug-repl)))))
(defn make-error-handler
"This takes a function that takes two args
and returns an UncaughtExceptionHandler that will run that function with the
thread/runnable and the Exception."
[f]
(proxy [Thread$UncaughtExceptionHandler] []
(uncaughtException [thread exception]
(f thread exception))))
(def debug-ex-handler (make-error-handler (fn [thread debug-exception] (debug-repl))))
(defn debug-uncaught-exceptions! []
(Thread/setDefaultUncaughtExceptionHandler debug-ex-handler))
;; Easy searching for the function I want :: https://gist.github.com/alandipert/1619740
(defn findcore
"Returns a lazy sequence of functions in clojure.core that, when applied to args,
return ret."
([args ret]
(findcore (filter #(not (:macro (meta %)))
(vals (ns-publics 'clojure.core))) args ret))
([[f & fns] args ret]
(lazy-seq
(when f
(if (binding [*out* (proxy [java.io.Writer] []
(write [_])
(close [])
(flush []))]
(try
(= ret (apply f args))
(catch Throwable t)))
(cons (:name (meta f)) (findcore fns args ret))
(findcore fns args ret))))))
(defmacro fc [args ret]
`(findcore '~args '~ret))
;; Useful one-off functions
;; Construct
;; (construct [inc dec] [10 10]) => (11 9)
;(def construct (partial map deliver))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment