-
Notifications
You must be signed in to change notification settings - Fork 0
/
repl.clj
140 lines (119 loc) · 3.6 KB
/
repl.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
(ns com.gfredericks.repl
"My repl utilities."
(:refer-clojure :exclude [comment])
(:require [clojure.java.classpath :as cp]
[clojure.java.shell :as shell]
[clojure.pprint :as pprint]
[clojure.test :as test]
[clojure.tools.namespace.find :as ns]
[clojure.walk :as walk]))
;;;
;;; Enhanced version of clojure.core/comment
;;;
(defmacro comment
"Like clojure.core/comment, but allows uncommenting top-level
forms with ~, e.g.:
(repl/comment
(don't do this)
(or this)
~(but do this)
~(and this)
(but not this))"
[& args]
(->> args
(filter #(and (seq? %)
(= 'clojure.core/unquote (first %))))
(map second)
(cons 'do)))
;;;
;;; Slightly better pprint stuff
;;;
(defn ^:private canonize
[ob]
(walk/postwalk (fn [x]
(cond (map? x)
(try
(into (sorted-map) x)
(catch ClassCastException _
x))
(set? x)
(try
(into (sorted-set) x)
(catch ClassCastException _
x))
:else
x))
ob))
(defn pp
"Combines functionality of clojure.pprint/pprint and clojure.pprint/pp,
but also canonizes the object before printing."
([] (pp *1))
([x] (pprint/pprint (canonize x))))
;;
;; Debugging with locals-access
;;
(defmacro locals
[]
(let [names (keys &env)]
(zipmap (map #(list 'quote %) names) names)))
(defmacro throw-locals
([] `(throw-locals "Throwing locals"))
([msg] `(throw (ex-info ~msg (locals)))))
;;
;; Interrupt-friendly infinite loop
;;
(defmacro forever
"Executes body repeatedly, watching for thread interrupts."
[& body]
`(while (not (Thread/interrupted))
~@body))
;;
;; Running all the tests
;;
(defn run-all-tests
"Like clojure.test/run-all-tests, but also requires namespaces first."
[]
(let [nses (->> (cp/classpath)
(remove #(re-find #"\.jar" (str %)))
(ns/find-namespaces))]
(doseq [ns nses] (require ns))
(let [nses-with-tests
(filter (fn [ns]
(some #(:test (meta %))
(vals (ns-publics ns))))
nses)]
(apply test/run-tests nses-with-tests))))
;;
;; Shelling out to bash
;;
(defn bash
([cmd]
(bash cmd {}))
([cmd opts]
(if (:in opts)
(apply shell/sh "bash" "-c" cmd (apply concat opts))
(apply shell/sh "bash" :in cmd (apply concat opts)))))
;;
;; def
;;
(defmacro def
"A variant of def that makes the definition in whatever namespace
this macro itself is a part of.
Intended to be used with tools like lein-shorthand or dot-slash-2,
where this macro can be aliased as ./def and then (./def x 42)
would result in the var #'./x being created.
Alternately, (./def foo/x 42) will define x in the foo namespace,
unless foo is an alias for a longer namespace in which case it will
use that."
[var-name val]
(let [ns-prefix (symbol (or (namespace (first &form))
(namespace var-name)))
sym (symbol ns-prefix)
actual-ns (or (get (ns-aliases *ns*) sym)
(try
(the-ns sym)
(catch Exception e
(create-ns sym))))
v (doto (intern actual-ns (symbol (name var-name)))
(alter-meta! merge (meta var-name)))]
`(doto ~v (alter-var-root (constantly ~val)))))