summaryrefslogtreecommitdiff
path: root/vim/bundle/slimv/swank-clojure/swank/commands/inspector.clj
diff options
context:
space:
mode:
Diffstat (limited to 'vim/bundle/slimv/swank-clojure/swank/commands/inspector.clj')
-rw-r--r--vim/bundle/slimv/swank-clojure/swank/commands/inspector.clj323
1 files changed, 323 insertions, 0 deletions
diff --git a/vim/bundle/slimv/swank-clojure/swank/commands/inspector.clj b/vim/bundle/slimv/swank-clojure/swank/commands/inspector.clj
new file mode 100644
index 0000000..f8d490c
--- /dev/null
+++ b/vim/bundle/slimv/swank-clojure/swank/commands/inspector.clj
@@ -0,0 +1,323 @@
+(ns swank.commands.inspector
+ (:use (swank util core commands)
+ (swank.core connection)))
+
+;;;; Inspector for basic clojure data structures
+
+;; This a mess, I'll clean up this code after I figure out exactly
+;; what I need for debugging support.
+
+(def inspectee (ref nil))
+(def inspectee-content (ref nil))
+(def inspectee-parts (ref nil))
+(def inspectee-actions (ref nil))
+(def inspector-stack (ref nil))
+(def inspector-history (ref nil))
+
+(defn reset-inspector []
+ (dosync
+ (ref-set inspectee nil)
+ (ref-set inspectee-content nil)
+ (ref-set inspectee-parts [])
+ (ref-set inspectee-actions [])
+ (ref-set inspector-stack nil)
+ (ref-set inspector-history [])))
+
+(defn inspectee-title [obj]
+ (cond
+ (instance? clojure.lang.LazySeq obj) (str "clojure.lang.LazySeq@...")
+ :else (str obj)))
+
+(defn print-part-to-string [value]
+ (let [s (inspectee-title value)
+ pos (position #{value} @inspector-history)]
+ (if pos
+ (str "#" pos "=" s)
+ s)))
+
+(defn assign-index [o dest]
+ (dosync
+ (let [index (count @dest)]
+ (alter dest conj o)
+ index)))
+
+(defn value-part [obj s]
+ (list :value (or s (print-part-to-string obj))
+ (assign-index obj inspectee-parts)))
+
+(defn action-part [label lambda refresh?]
+ (list :action label
+ (assign-index (list lambda refresh?)
+ inspectee-actions)))
+
+(defn label-value-line
+ ([label value] (label-value-line label value true))
+ ([label value newline?]
+ (list* (str label) ": " (list :value value)
+ (if newline? '((:newline)) nil))))
+
+(defmacro label-value-line* [& label-values]
+ `(concat ~@(map (fn [[label value]]
+ `(label-value-line ~label ~value))
+ label-values)))
+
+;; Inspection
+
+;; This is the simple version that only knows about clojure stuff.
+;; Many of these will probably be redefined by swank-clojure-debug
+(defmulti emacs-inspect
+ (fn known-types [obj]
+ (cond
+ (map? obj) :map
+ (vector? obj) :vector
+ (var? obj) :var
+ (string? obj) :string
+ (seq? obj) :seq
+ (instance? Class obj) :class
+ (instance? clojure.lang.Namespace obj) :namespace
+ (instance? clojure.lang.ARef obj) :aref
+ (.isArray (class obj)) :array)))
+
+(defn inspect-meta-information [obj]
+ (when (> (count (meta obj)) 0)
+ (concat
+ '("Meta Information: " (:newline))
+ (mapcat (fn [[key val]]
+ `(" " (:value ~key) " = " (:value ~val) (:newline)))
+ (meta obj)))))
+
+(defmethod emacs-inspect :map [obj]
+ (concat
+ (label-value-line*
+ ("Class" (class obj))
+ ("Count" (count obj)))
+ '("Contents: " (:newline))
+ (inspect-meta-information obj)
+ (mapcat (fn [[key val]]
+ `(" " (:value ~key) " = " (:value ~val)
+ (:newline)))
+ obj)))
+
+(defmethod emacs-inspect :vector [obj]
+ (concat
+ (label-value-line*
+ ("Class" (class obj))
+ ("Count" (count obj)))
+ '("Contents: " (:newline))
+ (inspect-meta-information obj)
+ (mapcat (fn [i val]
+ `(~(str " " i ". ") (:value ~val) (:newline)))
+ (iterate inc 0)
+ obj)))
+
+(defmethod emacs-inspect :array [obj]
+ (concat
+ (label-value-line*
+ ("Class" (class obj))
+ ("Count" (alength obj))
+ ("Component Type" (.getComponentType (class obj))))
+ '("Contents: " (:newline))
+ (mapcat (fn [i val]
+ `(~(str " " i ". ") (:value ~val) (:newline)))
+ (iterate inc 0)
+ obj)))
+
+(defmethod emacs-inspect :var [#^clojure.lang.Var obj]
+ (concat
+ (label-value-line*
+ ("Class" (class obj)))
+ (inspect-meta-information obj)
+ (when (.isBound obj)
+ `("Value: " (:value ~(var-get obj))))))
+
+(defmethod emacs-inspect :string [obj]
+ (concat
+ (label-value-line*
+ ("Class" (class obj)))
+ (inspect-meta-information obj)
+ (list (str "Value: " (pr-str obj)))))
+
+(defmethod emacs-inspect :seq [obj]
+ (concat
+ (label-value-line*
+ ("Class" (class obj)))
+ '("Contents: " (:newline))
+ (inspect-meta-information obj)
+ (mapcat (fn [i val]
+ `(~(str " " i ". ") (:value ~val) (:newline)))
+ (iterate inc 0)
+ obj)))
+
+(defmethod emacs-inspect :default [obj]
+ (let [fields (. (class obj) getDeclaredFields)
+ names (map (memfn getName) fields)
+ get (fn [f]
+ (try (.setAccessible f true)
+ (catch java.lang.SecurityException e))
+ (try (.get f obj)
+ (catch java.lang.IllegalAccessException e
+ "Access denied.")))
+ vals (map get fields)]
+ (concat
+ `("Type: " (:value ~(class obj)) (:newline)
+ "Value: " (:value ~obj) (:newline)
+ "---" (:newline)
+ "Fields: " (:newline))
+ (mapcat
+ (fn [name val]
+ `(~(str " " name ": ") (:value ~val) (:newline))) names vals))))
+
+(defmethod emacs-inspect :class [#^Class obj]
+ (let [meths (. obj getMethods)
+ fields (. obj getFields)]
+ (concat
+ `("Type: " (:value ~(class obj)) (:newline)
+ "---" (:newline)
+ "Fields: " (:newline))
+ (mapcat (fn [f]
+ `(" " (:value ~f) (:newline))) fields)
+ '("---" (:newline)
+ "Methods: " (:newline))
+ (mapcat (fn [m]
+ `(" " (:value ~m) (:newline))) meths))))
+
+(defmethod emacs-inspect :aref [#^clojure.lang.ARef obj]
+ `("Type: " (:value ~(class obj)) (:newline)
+ "Value: " (:value ~(deref obj)) (:newline)))
+
+(defn ns-refers-by-ns [#^clojure.lang.Namespace ns]
+ (group-by (fn [#^clojure.lang.Var v] (. v ns))
+ (map val (ns-refers ns))))
+
+(defmethod emacs-inspect :namespace [#^clojure.lang.Namespace obj]
+ (concat
+ (label-value-line*
+ ("Class" (class obj))
+ ("Count" (count (ns-map obj))))
+ '("---" (:newline)
+ "Refer from: " (:newline))
+ (mapcat (fn [[ns refers]]
+ `(" "(:value ~ns) " = " (:value ~refers) (:newline)))
+ (ns-refers-by-ns obj))
+ (label-value-line*
+ ("Imports" (ns-imports obj))
+ ("Interns" (ns-interns obj)))))
+
+(defn inspector-content [specs]
+ (letfn [(spec-seq [seq]
+ (let [[f & args] seq]
+ (cond
+ (= f :newline) (str \newline)
+
+ (= f :value)
+ (let [[obj & [str]] args]
+ (value-part obj str))
+
+ (= f :action)
+ (let [[label lambda & options] args
+ {:keys [refresh?]} (apply hash-map options)]
+ (action-part label lambda refresh?)))))
+ (spec-value [val]
+ (cond
+ (string? val) val
+ (seq? val) (spec-seq val)))]
+ (map spec-value specs)))
+
+;; Works for infinite sequences, but it lies about length. Luckily, emacs doesn't
+;; care.
+(defn content-range [lst start end]
+ (let [amount-wanted (- end start)
+ shifted (drop start lst)
+ taken (take amount-wanted shifted)
+ amount-taken (count taken)]
+ (if (< amount-taken amount-wanted)
+ (list taken (+ amount-taken start) start end)
+ ;; There's always more until we know there isn't
+ (list taken (+ end 500) start end))))
+
+(defn inspect-object [o]
+ (dosync
+ (ref-set inspectee o)
+ (alter inspector-stack conj o)
+ (when-not (filter #(identical? o %) @inspector-history)
+ (alter inspector-history conj o))
+ (ref-set inspectee-content (inspector-content (emacs-inspect o)))
+ (list :title (inspectee-title o)
+ :id (assign-index o inspectee-parts)
+ :content (content-range @inspectee-content 0 500))))
+
+(defslimefn init-inspector [string]
+ (with-emacs-package
+ (reset-inspector)
+ (inspect-object (eval (read-string string)))))
+
+(defn inspect-in-emacs [what]
+ (letfn [(send-it []
+ (with-emacs-package
+ (reset-inspector)
+ (send-to-emacs `(:inspect ~(inspect-object what)))))]
+ (cond
+ *current-connection* (send-it)
+ (comment (first @connections))
+ ;; TODO: take a second look at this, will probably need garbage collection on connections
+ (comment
+ (binding [*current-connection* (first @connections)]
+ (send-it))))))
+
+(defslimefn inspect-frame-var [frame index]
+ (if (and (zero? frame) *current-env*)
+ (let [locals *current-env*
+ object (locals (nth (keys locals) index))]
+ (with-emacs-package
+ (reset-inspector)
+ (inspect-object object)))))
+
+(defslimefn inspector-nth-part [index]
+ (get @inspectee-parts index))
+
+(defslimefn inspect-nth-part [index]
+ (with-emacs-package
+ (inspect-object ((slime-fn 'inspector-nth-part) index))))
+
+(defslimefn inspector-range [from to]
+ (content-range @inspectee-content from to))
+
+(defn ref-pop [ref]
+ (let [[f & r] @ref]
+ (ref-set ref r)
+ f))
+
+(defslimefn inspector-call-nth-action [index & args]
+ (let [[fn refresh?] (get @inspectee-actions index)]
+ (apply fn args)
+ (if refresh?
+ (inspect-object (dosync (ref-pop inspector-stack)))
+ nil)))
+
+(defslimefn inspector-pop []
+ (with-emacs-package
+ (cond
+ (rest @inspector-stack)
+ (inspect-object
+ (dosync
+ (ref-pop inspector-stack)
+ (ref-pop inspector-stack)))
+ :else nil)))
+
+(defslimefn inspector-next []
+ (with-emacs-package
+ (let [pos (position #{@inspectee} @inspector-history)]
+ (cond
+ (= (inc pos) (count @inspector-history)) nil
+ :else (inspect-object (get @inspector-history (inc pos)))))))
+
+(defslimefn inspector-reinspect []
+ (inspect-object @inspectee))
+
+(defslimefn quit-inspector []
+ (reset-inspector)
+ nil)
+
+(defslimefn describe-inspectee []
+ (with-emacs-package
+ (str @inspectee)))