Skip to content

Commit d824774

Browse files
authored
Merge pull request #3892 from clojure-emacs/decouple-nrepl-handler
Decouple nrepl-make-eval-handler from CIDER's UI layer
2 parents 8b65da7 + d330f5f commit d824774

8 files changed

Lines changed: 219 additions & 77 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- Introduce `cider-jack-in-tools` and `cider-register-jack-in-tool` so third-party packages can register new project tools for `cider-jack-in` and `cider-jack-in-universal`.
1010
- Cache the result of `cider--running-nrepl-paths` (used by `cider-locate-running-nrepl-ports`) for `cider-running-nrepl-paths-cache-ttl` seconds (default 5). Repeated `cider-connect` completions no longer re-spawn a fresh round of `ps`/`lsof` subprocesses each time. `cider-clear-running-nrepl-paths-cache` discards the cache on demand.
1111
- New `nrepl-make-eval-handler` with a keyword-arg API (`:on-value`, `:on-stdout`, `:on-stderr`, `:on-done`, `:on-eval-error`, `:on-content-type`, `:on-truncated`). Sub-handlers no longer take a buffer argument -- they close over whatever they need. `nrepl-make-response-handler`, the legacy 7-positional-arg form, is preserved as an obsolete shim that adapts the old (buffer x) lambdas to the new (x) lambdas, so existing extensions keep working.
12+
- Decouple the nREPL transport layer from CIDER's UI layer (closes [#1099](https://github.com/clojure-emacs/cider/issues/1099)). `nrepl-make-eval-handler` is now CIDER-agnostic: it no longer references `nrepl-namespace-handler-function`, `nrepl-err-handler-function`, `nrepl-need-input-handler-function`, or any hardcoded UI strings. New `:on-ns` and `:on-status` keyword slots let any consumer wire up their own namespace tracking and status handling. The editor-level `cider-make-eval-handler` wraps it with CIDER's UI behavior (ns tracking, default error handler, need-input prompt, "Evaluation interrupted." / "Namespace not found." messages); in-tree callers all use it.
1213

1314
### Bugs fixed
1415

lisp/cider-client.el

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
(require 'nrepl-client)
4141

4242
(declare-function cider--render-stacktrace-causes "cider-eval")
43+
(declare-function cider-make-eval-handler "cider-eval")
4344

4445

4546
;;; Spinner
@@ -493,7 +494,7 @@ itself is present."
493494

494495
(defun cider-interrupt-handler (buffer)
495496
"Create an interrupt response handler for BUFFER."
496-
(nrepl-make-eval-handler :buffer buffer))
497+
(cider-make-eval-handler :buffer buffer))
497498

498499
(defun cider-interrupt ()
499500
"Interrupt any pending evaluations."
@@ -905,7 +906,7 @@ FORMAT-OPTIONS is an optional configuration map for cljfmt."
905906
"Make a stdin response handler for _BUFFER.
906907
Intentionally swallows value/out/err so the response is consumed without
907908
side effects; only the global handlers fire (need-input, eval-error, ...)."
908-
(nrepl-make-eval-handler :buffer (current-buffer)))
909+
(cider-make-eval-handler :buffer (current-buffer)))
909910

910911
(defun cider-need-input (buffer)
911912
"Handle an need-input request from BUFFER."

lisp/cider-eval.el

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,49 @@ parsed location."
617617

618618

619619
;;; Interactive evaluation handlers
620+
621+
(declare-function cider--update-buffer-ns "cider-connection" (buffer ns))
622+
623+
(cl-defun cider-make-eval-handler (&key buffer
624+
on-value on-stdout on-stderr on-done
625+
on-eval-error on-content-type on-truncated)
626+
"Build an eval response handler with CIDER's UI behavior wired in.
627+
628+
This is the editor-level wrapper around `nrepl-make-eval-handler': it
629+
forwards the standard slots (`:on-value', `:on-stdout', `:on-stderr',
630+
`:on-done', `:on-content-type', `:on-truncated', `:on-eval-error') and
631+
additionally:
632+
633+
- updates BUFFER's `cider-buffer-ns' from the response's `ns' slot
634+
(only for non-source buffers, via `cider--update-buffer-ns');
635+
- prompts the user via `cider-need-input' on \"need-input\" status;
636+
- prints \"Evaluation interrupted.\" on \"interrupted\" status;
637+
- prints \"Namespace `X' not found.\" on \"namespace-not-found\" status;
638+
- when no `:on-eval-error' is given, defaults to a thunk that calls
639+
`cider-default-err-handler' with BUFFER.
640+
641+
BUFFER is the editor buffer the response is associated with (typically
642+
the one that issued the eval). All other slots have the same semantics
643+
as in `nrepl-make-eval-handler'."
644+
(nrepl-make-eval-handler
645+
:on-value on-value
646+
:on-stdout on-stdout
647+
:on-stderr on-stderr
648+
:on-done on-done
649+
:on-content-type on-content-type
650+
:on-truncated on-truncated
651+
:on-ns (lambda (ns) (cider--update-buffer-ns buffer ns))
652+
:on-status (lambda (status response)
653+
(when (member "interrupted" status)
654+
(message "Evaluation interrupted."))
655+
(when (member "namespace-not-found" status)
656+
(nrepl-dbind-response response (ns)
657+
(message "Namespace `%s' not found." ns)))
658+
(when (member "need-input" status)
659+
(cider-need-input buffer)))
660+
:on-eval-error (or on-eval-error
661+
(lambda () (cider-default-err-handler buffer)))))
662+
620663
(defun cider-insert-eval-handler (&optional buffer _bounds source-buffer on-success-callback)
621664
"Make an nREPL evaluation handler for the BUFFER,
622665
_BOUNDS representing the buffer bounds of the evaled input,
@@ -627,7 +670,7 @@ The handler simply inserts the result value in BUFFER."
627670
(let ((eval-buffer (current-buffer))
628671
(res "")
629672
(failed nil))
630-
(nrepl-make-eval-handler
673+
(cider-make-eval-handler
631674
:buffer (or buffer eval-buffer)
632675
:on-value (lambda (value)
633676
(with-current-buffer buffer
@@ -727,7 +770,7 @@ when `cider-auto-inspect-after-eval' is non-nil."
727770
(end (when end (copy-marker end)))
728771
(fringed nil)
729772
(res ""))
730-
(nrepl-make-eval-handler
773+
(cider-make-eval-handler
731774
:buffer (or buffer eval-buffer)
732775
:on-value (lambda (value)
733776
(setq res (concat res value))
@@ -763,7 +806,7 @@ Optional argument DONE-HANDLER lambda will be run once load is complete."
763806
(let* ((eval-buffer (current-buffer))
764807
(target (or buffer eval-buffer))
765808
(res ""))
766-
(nrepl-make-eval-handler
809+
(cider-make-eval-handler
767810
:buffer target
768811
:on-value (lambda (value)
769812
(cider--display-interactive-eval-result value 'value)
@@ -788,7 +831,7 @@ Optional argument DONE-HANDLER lambda will be run once load is complete."
788831
;; NOTE: cider-eval-register behavior is not implemented here for performance reasons.
789832
;; See https://github.com/clojure-emacs/cider/pull/3162
790833
(let ((target (or buffer (current-buffer))))
791-
(nrepl-make-eval-handler
834+
(cider-make-eval-handler
792835
:buffer target
793836
:on-value (lambda (value)
794837
(with-current-buffer target
@@ -803,7 +846,7 @@ Optional argument DONE-HANDLER lambda will be run once load is complete."
803846
LOCATION is the location marker at which to insert. COMMENT-PREFIX is the
804847
comment prefix to use."
805848
(let ((res ""))
806-
(nrepl-make-eval-handler
849+
(cider-make-eval-handler
807850
:buffer buffer
808851
:on-value (lambda (value) (setq res (concat res value)))
809852
:on-stdout #'cider-emit-interactive-eval-output
@@ -842,7 +885,7 @@ COMMENT-PREFIX is the comment prefix for the first line of output.
842885
CONTINUED-PREFIX is the comment prefix to use for the remaining lines.
843886
COMMENT-POSTFIX is the text to output after the last line."
844887
(let ((res ""))
845-
(nrepl-make-eval-handler
888+
(cider-make-eval-handler
846889
:buffer buffer
847890
:on-value (lambda (value) (setq res (concat res value)))
848891
:on-stderr (lambda (err) (setq res (concat res err)))
@@ -870,7 +913,7 @@ This is used by pretty-printing commands."
870913
;; NOTE: cider-eval-register behavior is not implemented here for performance reasons.
871914
;; See https://github.com/clojure-emacs/cider/pull/3162
872915
(let ((chosen-buffer (or buffer (current-buffer))))
873-
(nrepl-make-eval-handler
916+
(cider-make-eval-handler
874917
:buffer chosen-buffer
875918
:on-value (lambda (value)
876919
(cider-emit-into-popup-buffer chosen-buffer (ansi-color-apply value) nil t))

lisp/cider-profile.el

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
"Make a response handler that calls HANDLER with the response value.
5757
HANDLER takes one argument (the value). BUFFER, defaulting to the
5858
current buffer, is used by the global nREPL handlers (e.g. error)."
59-
(nrepl-make-eval-handler
59+
(cider-make-eval-handler
6060
:buffer (or buffer (current-buffer))
6161
:on-value handler))
6262

lisp/cider-repl.el

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
(require 'cider-resolve)
5353

5454
(declare-function cider-inspect "cider-inspector")
55+
(declare-function cider-make-eval-handler "cider-eval")
5556
(declare-function cider-quit "cider-connection")
5657
(declare-function cider-restart "cider-connection")
5758
(declare-function cider-describe-connection "cider-connection")
@@ -272,7 +273,7 @@ This cache is stored in the connection buffer.")
272273
"Make an nREPL evaluation handler for use during REPL init.
273274
Run CALLBACK once the evaluation is complete."
274275
(let ((buffer (current-buffer)))
275-
(nrepl-make-eval-handler
276+
(cider-make-eval-handler
276277
:buffer buffer
277278
:on-stdout (apply-partially #'cider-repl-emit-stdout buffer)
278279
:on-stderr (apply-partially #'cider-repl-emit-stderr buffer)
@@ -1042,7 +1043,7 @@ and responding to them.")
10421043
(defun cider-repl-handler (buffer)
10431044
"Make an nREPL evaluation handler for the REPL BUFFER."
10441045
(let ((show-prompt t))
1045-
(nrepl-make-eval-handler
1046+
(cider-make-eval-handler
10461047
:buffer buffer
10471048
:on-value (lambda (value)
10481049
(cider-repl-emit-result buffer value t))
@@ -1282,7 +1283,7 @@ With a prefix argument CLEAR-REPL it will clear the entire REPL buffer instead."
12821283

12831284
(defun cider-repl-switch-ns-handler (buffer)
12841285
"Make an nREPL evaluation handler for the REPL BUFFER's ns switching."
1285-
(nrepl-make-eval-handler
1286+
(cider-make-eval-handler
12861287
:buffer buffer
12871288
:on-stdout (apply-partially #'cider-repl-emit-stdout buffer)
12881289
:on-stderr (apply-partially #'cider-repl-emit-stderr buffer)

lisp/nrepl-client.el

Lines changed: 54 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -656,70 +656,63 @@ Displays the notification via `message'."
656656
(format "%s: %s" (upcase type) msg))))
657657
(message msg)))
658658

659-
(cl-defun nrepl-make-eval-handler (&key buffer
660-
on-value on-stdout on-stderr on-done
659+
(cl-defun nrepl-make-eval-handler (&key on-value on-stdout on-stderr on-done
660+
on-ns on-status
661661
on-eval-error on-content-type on-truncated)
662662
"Build a callback for an nREPL `eval'-style response stream.
663663
664664
Returns a function of one argument (the decoded response dict). The
665-
handler dispatches on the standard eval response slots and invokes
666-
whichever of the keyword sub-handlers has been provided:
665+
handler is a transport-layer building block: it dispatches on the
666+
standard eval response slots and invokes whichever of the keyword
667+
sub-handlers has been provided. All UI concerns (namespace tracking,
668+
default error handling, need-input prompting, status messages) live in
669+
higher layers -- see `cider-make-eval-handler' for the editor wrapper.
670+
671+
Sub-handlers, all optional:
667672
668673
:on-value called with VALUE when the response carries one.
669674
:on-stdout called with the OUT string for stdout chunks.
670675
:on-stderr called with the ERR string for stderr chunks.
671676
:on-done called with no arguments on the final \"done\" status.
677+
:on-ns called with NS whenever the response carries an `ns'
678+
slot, regardless of which other slots are present.
679+
:on-status called with (STATUS RESPONSE) when the response
680+
carries a `status' slot. STATUS is the list of
681+
status flags; RESPONSE is the full response dict
682+
so the handler can read sibling slots if needed.
672683
:on-eval-error called with no arguments on \"eval-error\" status.
673-
If omitted, falls back to `nrepl-err-handler-function'
674-
(which is then invoked with BUFFER).
675684
:on-content-type called with (BODY CONTENT-TYPE) when the response
676685
carries a `content-type' slot. BODY has been
677686
base64-decoded when the response indicates so.
678687
:on-truncated called with no arguments when the response is flagged
679-
as truncated by `nrepl.middleware.print'.
680-
681-
Sub-handlers do not receive BUFFER -- close over whatever buffer or other
682-
context you need. BUFFER is only passed to the global handlers
683-
configured via `nrepl-namespace-handler-function',
684-
`nrepl-err-handler-function', and `nrepl-need-input-handler-function'."
688+
as truncated by `nrepl.middleware.print'."
685689
(lambda (response)
686690
(nrepl-dbind-response response (content-type content-transfer-encoding body
687691
value ns out err status id)
688-
(when (and ns nrepl-namespace-handler-function)
689-
(funcall nrepl-namespace-handler-function buffer ns))
692+
(when (and ns on-ns)
693+
(funcall on-ns ns))
690694
(cond ((and content-type on-content-type)
691695
(funcall on-content-type
692696
(if (string= content-transfer-encoding "base64")
693697
(base64-decode-string body)
694698
body)
695699
content-type))
696700
(value
697-
(when on-value
698-
(funcall on-value value)))
701+
(when on-value (funcall on-value value)))
699702
(out
700-
(when on-stdout
701-
(funcall on-stdout out)))
703+
(when on-stdout (funcall on-stdout out)))
702704
(err
703-
(when on-stderr
704-
(funcall on-stderr err)))
705+
(when on-stderr (funcall on-stderr err)))
705706
(status
706-
(when (and on-truncated (member "nrepl.middleware.print/truncated" status))
707+
(when on-status (funcall on-status status response))
708+
(when (and on-truncated
709+
(member "nrepl.middleware.print/truncated" status))
707710
(funcall on-truncated))
708-
(when (member "interrupted" status)
709-
(message "Evaluation interrupted."))
710-
(when (member "eval-error" status)
711-
(cond (on-eval-error (funcall on-eval-error))
712-
(nrepl-err-handler-function
713-
(funcall nrepl-err-handler-function buffer))))
714-
(when (member "namespace-not-found" status)
715-
(message "Namespace `%s' not found." ns))
716-
(when (and (member "need-input" status)
717-
nrepl-need-input-handler-function)
718-
(funcall nrepl-need-input-handler-function buffer))
711+
(when (and on-eval-error (member "eval-error" status))
712+
(funcall on-eval-error))
719713
(when (member "done" status)
720714
(nrepl--mark-id-completed id)
721-
(when on-done
722-
(funcall on-done))))))))
715+
(when on-done (funcall on-done))))))))
723716

724717
(defun nrepl-make-response-handler (buffer value-handler stdout-handler
725718
stderr-handler done-handler
@@ -729,9 +722,18 @@ configured via `nrepl-namespace-handler-function',
729722
"Make a response handler for connection BUFFER.
730723
731724
This is the legacy positional-arg form retained for backward
732-
compatibility with extensions. It adapts the (BUFFER VALUE)-style
733-
sub-handlers expected here to the simpler (VALUE)-style sub-handlers of
734-
`nrepl-make-eval-handler', which is the form new code should use.
725+
compatibility with extensions. New code should use
726+
`nrepl-make-eval-handler' (transport-only) or, in CIDER, the higher-
727+
level `cider-make-eval-handler' which layers UI concerns on top.
728+
729+
The shim adapts the (BUFFER VALUE)-style sub-handlers expected here to
730+
the simpler (VALUE)-style sub-handlers of `nrepl-make-eval-handler',
731+
and additionally wires up the global handler hooks
732+
(`nrepl-namespace-handler-function', `nrepl-err-handler-function',
733+
`nrepl-need-input-handler-function') and the legacy status messages
734+
that used to live inside `nrepl-make-eval-handler' itself. Anything
735+
that was visible behavior of the original 7-positional-arg API stays
736+
visible behavior here.
735737
736738
VALUE-HANDLER, STDOUT-HANDLER, STDERR-HANDLER, DONE-HANDLER and
737739
EVAL-ERROR-HANDLER each receive BUFFER as their first argument; the
@@ -740,7 +742,6 @@ type; TRUNCATED-HANDLER receives BUFFER alone. Any handler given as nil
740742
results in the corresponding response branch being a no-op."
741743
(declare (obsolete nrepl-make-eval-handler "1.22"))
742744
(nrepl-make-eval-handler
743-
:buffer buffer
744745
:on-value (when value-handler
745746
(lambda (value) (funcall value-handler buffer value)))
746747
:on-stdout (when stdout-handler
@@ -749,8 +750,21 @@ results in the corresponding response branch being a no-op."
749750
(lambda (err) (funcall stderr-handler buffer err)))
750751
:on-done (when done-handler
751752
(lambda () (funcall done-handler buffer)))
752-
:on-eval-error (when eval-error-handler
753-
(lambda () (funcall eval-error-handler buffer)))
753+
:on-ns (when nrepl-namespace-handler-function
754+
(lambda (ns) (funcall nrepl-namespace-handler-function buffer ns)))
755+
:on-status (lambda (status response)
756+
(when (member "interrupted" status)
757+
(message "Evaluation interrupted."))
758+
(when (member "namespace-not-found" status)
759+
(nrepl-dbind-response response (ns)
760+
(message "Namespace `%s' not found." ns)))
761+
(when (and (member "need-input" status)
762+
nrepl-need-input-handler-function)
763+
(funcall nrepl-need-input-handler-function buffer)))
764+
:on-eval-error (cond (eval-error-handler
765+
(lambda () (funcall eval-error-handler buffer)))
766+
(nrepl-err-handler-function
767+
(lambda () (funcall nrepl-err-handler-function buffer))))
754768
:on-content-type (when content-type-handler
755769
(lambda (decoded type)
756770
(funcall content-type-handler buffer decoded type)))

0 commit comments

Comments
 (0)