-
Notifications
You must be signed in to change notification settings - Fork 3
/
dev.clj
95 lines (77 loc) · 3.01 KB
/
dev.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
(ns gdl.backends.libgdx.dev
"Starts a dev loop using clojure.tools.namespace.repl/refresh in order to restart the app without
restarting the JVM.
Also starts an nrepl server which will keep up even between app crashes and restarts.
How to use:
lein run -m gdl.backends.libgdx.dev ~app-namespace~ ~app-fn-with-no-args~
Example:
lein run -m gdl.backends.libgdx.dev gdl.simple-test app
See also project.clj for the `lein dev` shortcut.
In case of an error, the console prints `WAITING FOR RESTART` and
the `/restart!` function will restart the app and call `refresh`.
You can bind this on a key for smooth dev experience, here in VIM:
``` vimscript
nmap <F5> :Eval (do (in-ns 'gdl.backends.libgdx.dev)(restart!))
```"
(:require [clojure.java.io :as io]
[nrepl.server :refer [start-server]]
[clojure.tools.namespace.repl :refer [disable-reload!
refresh]]
[clj-commons.pretty.repl :as p]))
(disable-reload!) ; keep same connection/nrepl-server up throughout refreshs
(declare ^:private app-ns
^:private app-fn)
(defn- start-app []
(eval `(do ; old namespace/var bindings are unloaded with refresh-all so always evaluate them fresh
(require (quote ~app-ns))
(~app-fn))))
(def ^:private ^Object obj (Object.))
(defn- wait! []
(locking obj
(println "\n\n>>> WAITING FOR RESTART <<<")
(.wait obj)))
(def ^:private app-start-failed (atom false))
(defn restart!
"Calls refresh on all namespaces with file changes and restarts the application.
(has to be started with `lein run -m dev`)"
[]
(reset! app-start-failed false)
(locking obj
(println "\n\n>>> RESTARTING <<<")
(.notify obj)))
(declare ^:private refresh-result)
(defn ^:no-doc dev-loop []
(println "start-app")
(try (start-app)
(catch Throwable t
(p/pretty-pst t)
(reset! app-start-failed true)))
(loop []
(when-not @app-start-failed
(do
(println "refresh")
(.bindRoot #'refresh-result (refresh :after 'gdl.backends.libgdx.dev/dev-loop))
(p/pretty-pst refresh-result)
(println "error on refresh")))
(wait!)
(recur)))
; ( I dont know why nrepl start-server does not have this included ... )
(defn- save-port-file
"Writes a file relative to project classpath with port number so other tools
can infer the nREPL server port.
Takes nREPL server map and processed CLI options map.
Returns nil."
[server]
;; Many clients look for this file to infer the port to connect to
(let [port (:port server)
port-file (io/file ".nrepl-port")]
(.deleteOnExit ^java.io.File port-file)
(spit port-file port)))
(declare ^:private nrepl-server)
(defn -main [& [app-namespace app-start-fn]]
(.bindRoot #'app-ns (symbol app-namespace))
(.bindRoot #'app-fn (symbol (str app-namespace "/" app-start-fn)))
(.bindRoot #'nrepl-server (start-server))
(save-port-file nrepl-server)
;(println "Started nrepl server on port" (:port nrepl-server))
(dev-loop))