-
-
Notifications
You must be signed in to change notification settings - Fork 99
/
repl.clj
179 lines (165 loc) · 7.44 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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
;; copyright (c) 2018-2024 sean corfield, all rights reserved
(ns org.corfield.dev.repl
"Invoke org.corfield.dev.repl/-main to start a REPL based on
what tooling you have available on your classpath."
(:require [clojure.repl :refer [demunge]]
[clojure.string :as str]))
(when-not (resolve 'requiring-resolve)
(throw (ex-info ":dev/repl and repl.clj require at least Clojure 1.10"
*clojure-version*)))
(defn- socket-repl-port
"Return truthy if it looks like a Socket REPL Server is wanted,
else return nil. The truthy value is the port number to use.
Checks the following for port numbers:
* SOCKET_REPL_PORT env var - a value of none suppresses it
* socket-repl-port property - a value of none suppresses it
* .socket-repl-port file - assumed to contain a port number"
[]
(let [s-port (or (System/getenv "SOCKET_REPL_PORT")
(System/getProperty "socket-repl-port")
(try (slurp ".socket-repl-port") (catch Throwable _)))]
(when-not (= "none" s-port)
(try
(Long/parseLong s-port)
(catch Throwable _)))))
(defn -main
"If Jedi Time is on the classpath, require it (so that Java Time
objects will support datafy/nav).
If Datomic Dev Datafy is on the classpath, require it (so that
Datomic objects will support datafy/nav).
Start a Socket REPL server, if requested. The port is selected from:
* SOCKET_REPL_PORT environment variable if present, else
* socket-repl-port JVM property if present, else
* .socket-repl-port file if present
Writes the selected port back to .socket-repl-port for next time.
Use a value of none to suppress the Socket Server startup.
Then pick a REPL as follows:
* if Figwheel Main is on the classpath then start that, else
* if Rebel Readline is on the classpath then start that, else
* start a plain ol' Clojure REPL."
[& args]
;; jedi-time?
(try
(require 'jedi-time.core)
(println "Java Time is Datafiable...")
(catch Throwable _))
;; datomic/dev.datafy?
(try
((requiring-resolve 'datomic.dev.datafy/datafy!))
(println "Datomic Datafiers Enabled...")
(catch Throwable _))
;; socket repl handling:
(when-let [s-port (socket-repl-port)]
;; if there is already a 'repl' Socket REPL open, don't open another:
(when-not (get (deref (requiring-resolve 'clojure.core.server/servers)) "repl")
(try
(let [server-name (str "REPL-" s-port)]
((requiring-resolve 'clojure.core.server/start-server)
{:port s-port :name server-name
:accept 'clojure.core.server/repl})
(let [s-port' (.getLocalPort
(get-in @(requiring-resolve 'clojure.core.server/servers)
[server-name :socket]))]
(println "Selected port" s-port' "for the Socket REPL...")
;; write the actual port we selected (for Chlorine/Clover to read):
(spit ".socket-repl-port" (str s-port'))))
(catch Throwable t
(println "Unable to start the Socket REPL on port" s-port)
(println (ex-message t))))))
;; if Portal and clojure.tools.logging are both present,
;; cause all (successful) logging to also be tap>'d:
(try
;; if we have Portal on the classpath...
(require 'portal.console)
;; ...then install a tap> ahead of tools.logging:
(let [log-star (requiring-resolve 'clojure.tools.logging/log*)
log*-fn (deref log-star)]
(alter-var-root
log-star
(constantly
(fn [logger level throwable message]
(try
(let [^StackTraceElement frame (nth (.getStackTrace (Throwable. "")) 2)
class-name (symbol (demunge (.getClassName frame)))]
;; only called for enabled log levels:
(tap>
(with-meta
{:form '()
:level level
:result (or throwable message)
:ns (symbol (or (namespace class-name)
;; fully-qualified classname - strip class:
(str/replace (name class-name) #"\.[^\.]*$" "")))
:file (.getFileName frame)
:line (.getLineNumber frame)
:column 0
:time (java.util.Date.)
:runtime :clj}
{:dev.repl/logging true})))
(catch Throwable _))
(log*-fn logger level throwable message)))))
(println "Logging will be tap>'d...")
(catch Throwable _))
;; select and start a main REPL:
(let [;; figure out what middleware we might want to supply to nREPL:
middleware
(into []
(filter #(try (requiring-resolve (second %)) true (catch Throwable _)))
[["Portal" 'portal.nrepl/wrap-portal]
["Notebook" 'portal.nrepl/wrap-notebook]
["CIDER" 'cider.nrepl/cider-middleware]])
mw-args
(when (seq middleware)
["--middleware" (str (mapv second middleware))])
[repl-name repl-fn]
(or (try ; Figwheel?
(let [figgy (requiring-resolve 'figwheel.main/-main)]
["Figwheel Main" #(figgy "-b" "dev" "-r")])
(catch Throwable _))
(try ; Rebel Readline?
(let [rebel-main (requiring-resolve 'rebel-readline.main/-main)]
(try
(require 'nrepl.cmdline)
;; both Rebel Readline and nREPL are on the classpath!
[(str "Rebel Readline + nREPL Server"
(when (seq middleware)
(str " with " (str/join ", " (map first middleware)))))
(fn []
;; see https://github.com/practicalli/clojure-cli-config/blob/03c91cfd0638d880c32e6be09937e69ea8559cd2/deps.edn#L158-L167
(apply (resolve 'clojure.main/main)
"-e" "(apply require clojure.main/repl-requires)"
"-m" "nrepl.cmdline"
(into (or mw-args [])
["--interactive"
"-f" "rebel-readline.main/-main"])))]
(catch Throwable _
;; only Rebel Readline is on the classpath:
["Rebel Readline" rebel-main])))
(catch Throwable _))
(try ; nREPL?
[(str "nREPL Server"
(when (seq middleware)
(str " with " (str/join ", " (map first middleware)))))
(let [nrepl (requiring-resolve 'nrepl.cmdline/-main)]
(fn []
(apply nrepl mw-args)))]
(catch Throwable _))
;; fallback to plain REPL:
["clojure.main" (resolve 'clojure.main/main)])]
(println "Starting" repl-name "as the REPL...")
(repl-fn)
;; ensure a smooth exit after the REPL is closed
(System/exit 0)))
(in-ns 'user)
(defn uptime []
(-> (java.lang.management.ManagementFactory/getRuntimeMXBean)
(.getUptime)
(java.time.Duration/ofMillis)
(as-> t (map #(% t) [#(.toHours %) #(.toMinutesPart %) #(.toSecondsPart %)])
(let [[h & ms] t]
(map vector
(into ((juxt #(long (/ % 24)) #(mod % 24)) h) ms)
[" days, " " hours, " " minutes, " " seconds"])))
(->> (filter (comp pos? first))
(map #(apply str %)))
(clojure.string/join)))