Skip to content

Commit 25d94dc

Browse files
committed
Resync the load-state indicators after namespace reloads
The per-form eval fringe and the namespace load-state marker only ever got refreshed by the load-file handler (C-c C-k). `cider-ns-reload`, `-reload-all` and `cider-ns-refresh` went around it, so after a reload a buffer's markers stayed stale even though its namespace was current again. Extract the load handler's resync into `cider--mark-loaded` (re-mark the fringe across the buffer + run `cider-file-loaded-hook`) and call it from the reload paths for the namespaces actually reloaded - but only for buffers with no unsaved edits, since `:reload` loads from disk and an unsaved buffer is genuinely out of sync. `cider-ns-refresh` threads the reloaded-namespace list through a per-request cell, so concurrent refreshes across REPLs can't clobber each other. Since this routes through `cider-file-loaded-hook', `cider-auto-test-mode' now re-runs tests on reload too, consistent with `cider-load-buffer'.
1 parent 4cb74cf commit 25d94dc

4 files changed

Lines changed: 108 additions & 21 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
### Changes
2323

24+
- [#3980](https://github.com/clojure-emacs/cider/pull/3980): Keep the load-state indicators in sync across `cider-ns-reload`/`cider-ns-reload-all` and `cider-ns-refresh`: the reloaded namespaces' (unsaved-edit-free) buffers get their fringe and mode-line markers refreshed, instead of staying stale until the next `cider-load-buffer`. These commands now run `cider-file-loaded-hook` for the reloaded buffers, so `cider-auto-test-mode` re-runs their tests too (matching what `cider-load-buffer` already does).
2425
- [#3967](https://github.com/clojure-emacs/cider/pull/3967): Refresh the embedded Clojure cheatsheet with functions added since Clojure 1.11 (`partitionv`, `partitionv-all`, `splitv-at`, `clojure.repl.deps`, `clojure.java.process`, the `clojure.core` Java-stream helpers, and more `clojure.math` members).
2526
- [#3967](https://github.com/clojure-emacs/cider/pull/3967): Give the cheatsheet buffer a dedicated `cider-cheatsheet-mode` with fontified section headers and `TAB`/`S-TAB` navigation between entries.
2627
- [#3971](https://github.com/clojure-emacs/cider/pull/3971): Add `n` and `t` keys in the macroexpansion buffer to cycle namespace display (`cider-macroexpansion-display-namespaces`) and toggle metadata (`cider-macroexpansion-print-metadata`), re-expanding in place.

lisp/cider-eval.el

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,16 @@ REPL buffer. This is controlled via
300300
(cider--make-fringe-overlay (point)))
301301
(scan-error nil)))))
302302

303+
(defun cider--mark-loaded (&optional buffer)
304+
"Mark BUFFER's content as loaded into the REPL and in sync.
305+
Refreshes the evaluation fringe indicators across BUFFER and runs
306+
`cider-file-loaded-hook' (which the namespace load-state indicator hooks
307+
into). BUFFER defaults to the current buffer. Used by the load-file flow
308+
and by the namespace reloading commands once they finish."
309+
(with-current-buffer (or buffer (current-buffer))
310+
(cider--make-fringe-overlays-for-region (point-min) (point-max))
311+
(run-hooks 'cider-file-loaded-hook)))
312+
303313

304314
(declare-function cider-inspect-last-result "cider-inspector")
305315
(defun cider-interactive-eval-handler (&optional buffer place)
@@ -359,9 +369,7 @@ Optional argument DONE-HANDLER lambda will be run once load is complete."
359369
(when cider-eval-register
360370
(setq res (concat res value)))
361371
(when (buffer-live-p target)
362-
(with-current-buffer target
363-
(cider--make-fringe-overlays-for-region (point-min) (point-max))
364-
(run-hooks 'cider-file-loaded-hook))))
372+
(cider--mark-loaded target)))
365373
:on-stdout #'cider-emit-interactive-eval-output
366374
:on-stderr (lambda (err)
367375
(cider-emit-interactive-eval-err-output err)

lisp/cider-ns.el

Lines changed: 61 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,31 @@ Present the error message as an overlay."
161161
(when buf
162162
(select-window (get-buffer-window buf)))))
163163

164-
(defun cider-ns-refresh--handle-response (response log-buffer)
165-
"Refresh LOG-BUFFER with RESPONSE."
164+
(defun cider-ns--mark-reloaded (namespaces)
165+
"Resync the load-state indicators of open buffers in NAMESPACES.
166+
For each live Clojure buffer whose namespace is in NAMESPACES (a list of
167+
name strings) and that has no unsaved edits, refresh the evaluation fringe
168+
indicators and the namespace load-state marker. Modified buffers are left
169+
alone, since reloading happens from disk and an unsaved buffer is genuinely
170+
out of sync with what was loaded.
171+
172+
This goes through `cider-file-loaded-hook' (like `cider-load-buffer' does),
173+
so its other subscribers also run for the reloaded buffers - notably
174+
`cider-auto-test-mode' will re-run their tests."
175+
(when namespaces
176+
(dolist (buf (buffer-list))
177+
(with-current-buffer buf
178+
(when (and (derived-mode-p 'clojure-mode 'clojure-ts-mode)
179+
(not (buffer-modified-p))
180+
(member (cider-current-ns 'no-default) namespaces))
181+
(cider--mark-loaded))))))
182+
183+
(defun cider-ns-refresh--handle-response (response log-buffer &optional reloading-cell)
184+
"Refresh LOG-BUFFER with RESPONSE.
185+
RELOADING-CELL, when given, is a one-element list (a mutable cell, private to
186+
a single refresh request) used to carry the set of namespaces being reloaded
187+
from the `reloading' response to the final `ok' response, so that concurrent
188+
refreshes across REPLs don't clobber each other."
166189
(nrepl-dbind-response response (out err reloading progress status error error-ns after before)
167190
(cl-flet* ((log (message &optional face)
168191
(cider-emit-into-popup-buffer log-buffer message face t))
@@ -189,6 +212,7 @@ Present the error message as an overlay."
189212
(log-echo "Could not resolve refresh function\n" 'font-lock-string-face))
190213

191214
(reloading
215+
(when reloading-cell (setcar reloading-cell reloading))
192216
(log-echo (format "Reloading %s\n" reloading) 'font-lock-string-face))
193217

194218
(progress
@@ -198,7 +222,8 @@ Present the error message as an overlay."
198222
(log-echo "Nothing to reload\n" 'font-lock-string-face))
199223

200224
((member "ok" status)
201-
(log-echo "Reloading successful\n" 'font-lock-string-face))
225+
(log-echo "Reloading successful\n" 'font-lock-string-face)
226+
(cider-ns--mark-reloaded (car reloading-cell)))
202227

203228
(error-ns
204229
(log-echo (format "Error reloading %s\n" error-ns) 'font-lock-warning-face))
@@ -245,6 +270,21 @@ Based on OP-NAME and the value of `cider-ns-code-reload-tool'."
245270
((string= op-name "reload-all") "cider.clj-reload/reload-all")
246271
((string= op-name "reload-clear") "cider.clj-reload/reload-clear"))))
247272

273+
(defun cider-ns--reload-callback (ns)
274+
"Return an eval handler that resyncs NS's buffers after a successful reload.
275+
Wraps the standard interactive-eval handler so results and errors display as
276+
usual, and additionally marks NS's buffers loaded once evaluation finishes
277+
without an error."
278+
(let ((base (cider-interactive-eval-handler))
279+
(errored nil))
280+
(lambda (response)
281+
(funcall base response)
282+
(let ((status (nrepl-dict-get response "status")))
283+
(when (member "eval-error" status)
284+
(setq errored t))
285+
(when (and (not errored) (member "done" status))
286+
(cider-ns--mark-reloaded (list ns)))))))
287+
248288
;;;###autoload
249289
(defun cider-ns-reload (&optional prompt)
250290
"Send a (require \\='ns :reload) to the REPL.
@@ -258,7 +298,8 @@ identified libs even if they are already loaded\"."
258298
(when-let* ((ns (if prompt
259299
(string-remove-prefix "'" (read-from-minibuffer "Namespace: " (cider-get-ns-name)))
260300
(cider-get-ns-name))))
261-
(cider-interactive-eval (format "(require '%s :reload)" ns))))
301+
(cider-interactive-eval (format "(require '%s :reload)" ns)
302+
(cider-ns--reload-callback ns))))
262303

263304
;;;###autoload
264305
(defun cider-ns-reload-all (&optional prompt)
@@ -274,7 +315,8 @@ indirectly load via require\"."
274315
(when-let* ((ns (if prompt
275316
(string-remove-prefix "'" (read-from-minibuffer "Namespace: " (cider-get-ns-name)))
276317
(cider-get-ns-name))))
277-
(cider-interactive-eval (format "(require '%s :reload-all)" ns))))
318+
(cider-interactive-eval (format "(require '%s :reload-all)" ns)
319+
(cider-ns--reload-callback ns))))
278320

279321
;;;###autoload
280322
(defun cider-ns-refresh (&optional mode)
@@ -314,19 +356,20 @@ refresh functions (defined in `cider-ns-refresh-before-fn' and
314356
t))
315357
(when clear?
316358
(cider-nrepl-send-sync-request `("op" ,(cider-ns--reload-op "reload-clear")) conn))
317-
(cider-nrepl-send-request
318-
`("op" ,(cider-ns--reload-op (if all? "reload-all" "reload"))
319-
,@(cider--nrepl-print-request-plist fill-column)
320-
,@(when (and (not inhibit-refresh-fns) cider-ns-refresh-before-fn)
321-
`("before" ,cider-ns-refresh-before-fn))
322-
,@(when (and (not inhibit-refresh-fns) cider-ns-refresh-after-fn)
323-
`("after" ,cider-ns-refresh-after-fn)))
324-
(lambda (response)
325-
(cider-ns-refresh--handle-response response log-buffer)
326-
(nrepl-dbind-response response (status id)
327-
(when (member "done" status)
328-
(nrepl--mark-id-completed id))))
329-
conn))))))
359+
(let ((reloading (list nil)))
360+
(cider-nrepl-send-request
361+
`("op" ,(cider-ns--reload-op (if all? "reload-all" "reload"))
362+
,@(cider--nrepl-print-request-plist fill-column)
363+
,@(when (and (not inhibit-refresh-fns) cider-ns-refresh-before-fn)
364+
`("before" ,cider-ns-refresh-before-fn))
365+
,@(when (and (not inhibit-refresh-fns) cider-ns-refresh-after-fn)
366+
`("after" ,cider-ns-refresh-after-fn)))
367+
(lambda (response)
368+
(cider-ns-refresh--handle-response response log-buffer reloading)
369+
(nrepl-dbind-response response (status id)
370+
(when (member "done" status)
371+
(nrepl--mark-id-completed id))))
372+
conn)))))))
330373

331374
(provide 'cider-ns)
332375
;;; cider-ns.el ends here

test/cider-ns-tests.el

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,3 +204,38 @@
204204
(cider-ns--present-error cider-ns-tests--sample-causes)
205205
(when-let ((b (get-buffer "*cider-error*"))) ;; Clean it up for other tests
206206
(kill-buffer b)))))
207+
208+
(describe "cider-ns--mark-reloaded"
209+
(it "resyncs an unmodified buffer whose namespace was reloaded"
210+
(spy-on 'cider--mark-loaded)
211+
(with-clojure-buffer "(ns foo)"
212+
(set-buffer-modified-p nil)
213+
(cider-ns--mark-reloaded '("foo"))
214+
(expect 'cider--mark-loaded :to-have-been-called)))
215+
(it "skips a buffer whose namespace was not reloaded"
216+
(spy-on 'cider--mark-loaded)
217+
(with-clojure-buffer "(ns bar)"
218+
(set-buffer-modified-p nil)
219+
(cider-ns--mark-reloaded '("foo"))
220+
(expect 'cider--mark-loaded :not :to-have-been-called)))
221+
(it "skips a buffer with unsaved edits (out of sync with what was reloaded)"
222+
(spy-on 'cider--mark-loaded)
223+
(with-clojure-buffer "(ns foo)"
224+
(set-buffer-modified-p t)
225+
(cider-ns--mark-reloaded '("foo"))
226+
(expect 'cider--mark-loaded :not :to-have-been-called))))
227+
228+
(describe "cider-ns--reload-callback"
229+
(it "resyncs the namespace once evaluation finishes without error"
230+
(spy-on 'cider-interactive-eval-handler :and-return-value #'ignore)
231+
(spy-on 'cider-ns--mark-reloaded)
232+
(let ((cb (cider-ns--reload-callback "foo")))
233+
(funcall cb (nrepl-dict "status" '("done")))
234+
(expect 'cider-ns--mark-reloaded :to-have-been-called-with '("foo"))))
235+
(it "does not resync when evaluation errored"
236+
(spy-on 'cider-interactive-eval-handler :and-return-value #'ignore)
237+
(spy-on 'cider-ns--mark-reloaded)
238+
(let ((cb (cider-ns--reload-callback "foo")))
239+
(funcall cb (nrepl-dict "status" '("eval-error")))
240+
(funcall cb (nrepl-dict "status" '("done")))
241+
(expect 'cider-ns--mark-reloaded :not :to-have-been-called))))

0 commit comments

Comments
 (0)