|
5 | 5 | view conjure.aniseed.view
|
6 | 6 | client conjure.client
|
7 | 7 | mapping conjure.mapping
|
| 8 | + fs conjure.fs |
8 | 9 | text conjure.text
|
9 | 10 | log conjure.log
|
10 | 11 | config conjure.config
|
|
19 | 20 | {:fennel
|
20 | 21 | {:aniseed
|
21 | 22 | {:mapping {:run_buf_tests "tt"
|
22 |
| - :run_all_tests "ta"} |
| 23 | + :run_all_tests "ta" |
| 24 | + :reset_repl "rr" |
| 25 | + :reset_all_repls "ra"} |
23 | 26 | :aniseed_module_prefix :conjure.aniseed.
|
24 | 27 | :use_metadata true}}}})
|
25 | 28 |
|
|
38 | 41 | (defn- anic [mod f-name ...]
|
39 | 42 | ((ani mod f-name) ...))
|
40 | 43 |
|
| 44 | +(defonce- repls {}) |
| 45 | + |
| 46 | +(defn reset-repl [filename] |
| 47 | + (let [filename (or filename (fs.localise-path (extract.file-path)))] |
| 48 | + (tset repls filename nil) |
| 49 | + (log.append [(.. "; Reset REPL for " filename)] {:break? true}))) |
| 50 | + |
| 51 | +(defn reset-all-repls [] |
| 52 | + (a.run! |
| 53 | + (fn [filename] |
| 54 | + (tset repls filename nil)) |
| 55 | + (a.keys repls)) |
| 56 | + (log.append [(.. "; Reset all REPLs")] {:break? true})) |
| 57 | + |
| 58 | +(def default-module-name "conjure.user") |
| 59 | + |
| 60 | +(defn module-name [context file-path] |
| 61 | + (if |
| 62 | + context context |
| 63 | + file-path (or (fs.file-path->module-name file-path) default-module-name) |
| 64 | + default-module-name)) |
| 65 | + |
| 66 | +(defn repl [opts] |
| 67 | + (let [filename (a.get opts :filename)] |
| 68 | + (or ;; Reuse an existing REPL. |
| 69 | + (and (not (a.get opts :fresh?)) (a.get repls filename)) |
| 70 | + |
| 71 | + ;; Build a new REPL. |
| 72 | + (let [;; Shared between the error-handler function (created at the same time as the REPL). |
| 73 | + ;; And each individual eval call. This allows us to capture errors from different call stacks. |
| 74 | + ret {} |
| 75 | + |
| 76 | + ;; Set up the error-handler function on the creation of the REPL. |
| 77 | + ;; Will place any errors in the ret table. |
| 78 | + _ (tset opts :error-handler |
| 79 | + (fn [err] |
| 80 | + (set ret.ok? false) |
| 81 | + (set ret.results [err]))) |
| 82 | + |
| 83 | + ;; Instantiate the raw REPL, we'll wrap this a little first though. |
| 84 | + eval! (anic :eval :repl opts) |
| 85 | + |
| 86 | + ;; Build our REPL function. |
| 87 | + repl (fn [code] |
| 88 | + ;; Reset the ret table before running anything. |
| 89 | + (set ret.ok? nil) |
| 90 | + (set ret.results nil) |
| 91 | + |
| 92 | + ;; Run the code, either capturing a result or an error. |
| 93 | + ;; If there's no error in ret we can place the results in the ret table. |
| 94 | + (let [results (eval! code)] |
| 95 | + (when (a.nil? ret.ok?) |
| 96 | + (set ret.ok? true) |
| 97 | + (set ret.results results)) |
| 98 | + ;; Finally this good or bad result is returned. |
| 99 | + ret))] |
| 100 | + |
| 101 | + ;; Set up the REPL in the module context. |
| 102 | + (repl (.. "(module " (a.get opts :moduleName) ")")) |
| 103 | + |
| 104 | + ;; Store the REPL for future reuse. |
| 105 | + (tset repls filename repl) |
| 106 | + |
| 107 | + ;; Return the new REPL! |
| 108 | + repl)))) |
| 109 | + |
41 | 110 | (defn display-result [opts]
|
42 | 111 | (when opts
|
43 | 112 | (let [{: ok? : results} opts
|
44 |
| - result-str (if ok? |
45 |
| - (if (a.empty? results) |
46 |
| - "nil" |
47 |
| - (str.join "\n" (a.map view.serialise results))) |
48 |
| - (a.first results)) |
| 113 | + result-str (or |
| 114 | + (if ok? |
| 115 | + (when (not (a.empty? results)) |
| 116 | + (str.join "\n" (a.map view.serialise results))) |
| 117 | + (a.first results)) |
| 118 | + "nil") |
49 | 119 | result-lines (str.split result-str "\n")]
|
50 | 120 | (when (not opts.passive?)
|
51 | 121 | (log.append
|
|
60 | 130 | (defn eval-str [opts]
|
61 | 131 | ((client.wrap
|
62 | 132 | (fn []
|
63 |
| - (let [code (.. (.. "(module " (or opts.context "aniseed.user") ") ") |
64 |
| - opts.code "\n") |
65 |
| - out (anic :nu :with-out-str |
| 133 | + (let [out (anic :nu :with-out-str |
66 | 134 | (fn []
|
67 | 135 | (when (and (cfg [:use_metadata])
|
68 | 136 | (not package.loaded.fennel))
|
69 | 137 | (set package.loaded.fennel (anic :fennel :impl)))
|
70 | 138 |
|
71 |
| - (let [[ok? & results] |
72 |
| - [(anic :eval :str code |
73 |
| - {:filename opts.file-path |
74 |
| - :useMetadata (cfg [:use_metadata])})]] |
| 139 | + (let [eval! (repl {:filename opts.file-path |
| 140 | + :moduleName (module-name opts.context opts.file-path) |
| 141 | + :useMetadata (cfg [:use_metadata]) |
| 142 | + |
| 143 | + ;; Restart the REPL if... |
| 144 | + :fresh? (or ;; We eval an entire file or buffer. |
| 145 | + (= :file opts.origin) (= :buf opts.origin) |
| 146 | + |
| 147 | + ;; The user is evaluating the module form. |
| 148 | + (text.starts-with opts.code (.. "(module " (or opts.context ""))))}) |
| 149 | + {: ok? : results} (eval! (.. opts.code "\n"))] |
75 | 150 | (set opts.ok? ok?)
|
76 | 151 | (set opts.results results))))]
|
77 | 152 | (when (not (a.empty? out))
|
|
110 | 185 | (mapping.buf :n :FnlRunBufTests
|
111 | 186 | (cfg [:mapping :run_buf_tests]) *module-name* :run-buf-tests)
|
112 | 187 | (mapping.buf :n :FnlRunAllTests
|
113 |
| - (cfg [:mapping :run_all_tests]) *module-name* :run-all-tests)) |
| 188 | + (cfg [:mapping :run_all_tests]) *module-name* :run-all-tests) |
| 189 | + (mapping.buf :n :FnlResetREPL |
| 190 | + (cfg [:mapping :reset_repl]) *module-name* :reset-repl) |
| 191 | + (mapping.buf :n :FnlResetAllREPLs |
| 192 | + (cfg [:mapping :reset_all_repls]) *module-name* :reset-all-repls)) |
114 | 193 |
|
115 | 194 | (defn value->completions [x]
|
116 | 195 | (when (= :table (type x))
|
|
132 | 211 |
|
133 | 212 | (defn completions [opts]
|
134 | 213 | (let [code (when (not (str.blank? opts.prefix))
|
135 |
| - (.. "((. (require :" *module-name* ") :value->completions) " |
136 |
| - (opts.prefix:gsub ".$" "") ")")) |
| 214 | + (let [prefix (string.gsub opts.prefix ".$" "")] |
| 215 | + (.. "((. (require :" *module-name* ") :value->completions) " prefix ")"))) |
137 | 216 | mods (value->completions package.loaded)
|
138 |
| - locals (let [(ok? m) (and opts.context (pcall #(require opts.context)))] |
| 217 | + locals (let [(ok? m) (pcall #(require opts.context))] |
139 | 218 | (if ok?
|
140 | 219 | (a.concat
|
| 220 | + (value->completions m) |
141 | 221 | (value->completions (a.get m :aniseed/locals))
|
142 |
| - (value->completions (a.get-in m [:aniseed/local-fns :require])) |
143 |
| - (value->completions (a.get-in m [:aniseed/local-fns :autoload])) |
144 | 222 | mods)
|
145 | 223 | mods))
|
146 | 224 | result-fn
|
|
155 | 233 | xs)
|
156 | 234 | locals)
|
157 | 235 | locals))))
|
158 |
| - (_ ok?) |
| 236 | + (ok? err-or-res) |
159 | 237 | (when code
|
160 | 238 | (pcall
|
161 | 239 | (fn []
|
162 | 240 | (eval-str
|
163 |
| - {:context opts.context |
| 241 | + {:file-path opts.file-path |
| 242 | + :context opts.context |
164 | 243 | :code code
|
165 | 244 | :passive? true
|
166 | 245 | :on-result-raw result-fn}))))]
|
|
0 commit comments