branch: elpa/cider
commit e1a1350d4fdca123d856f5bc294e338f6521dec8
Author: Bozhidar Batsov <[email protected]>
Commit: Bozhidar Batsov <[email protected]>
Add keyword-arg forms of the nREPL request senders
The eval and sync request senders carried long tails of positional
optional arguments, forcing callers to pad with nil just to reach a
later one (e.g. ". . . nil nil nil 'tooling").
Introduce cl-defun keyword-argument primaries, nrepl-send-eval-request
and nrepl-sync-request, and keep the old positional nrepl-request:eval
and nrepl-send-sync-request as thin shims that delegate to them. No
obsolete markers, so existing positional callers (including third-party
packages) keep working without warnings. Same-file callers are routed
through the new forms.
---
lisp/nrepl-client.el | 75 ++++++++++++++++++++++++++++++++++++----------
test/nrepl-client-tests.el | 43 ++++++++++++++++++++++++++
2 files changed, 102 insertions(+), 16 deletions(-)
diff --git a/lisp/nrepl-client.el b/lisp/nrepl-client.el
index 0405d777c7..f7933bf217 100644
--- a/lisp/nrepl-client.el
+++ b/lisp/nrepl-client.el
@@ -866,8 +866,8 @@ the standard session."
(defvar nrepl-ongoing-sync-request nil
"Dynamically bound to t while a sync request is ongoing.")
-(defun nrepl-send-sync-request (request connection &optional abort-on-input
- tooling callback)
+(cl-defun nrepl-sync-request (request connection &key abort-on-input
+ tooling callback)
"Send REQUEST to the nREPL server synchronously using CONNECTION.
Hold till final \"done\" message has arrived and join all response messages
of the same \"op\" that came along.
@@ -879,7 +879,10 @@ If CALLBACK is non-nil, it will additionally be called on
all received
messages. This shouldn't be used this for any control logic - use the
asynchronous `nrepl-send-request' directly for that. CALLBACK here should
be used to react to some intermediate events in an otherwise synchronous
-command and e.g. notify the user about them."
+command and e.g. notify the user about them.
+
+This is the keyword-argument form; `nrepl-send-sync-request' is the legacy
+positional shim retained for backward compatibility."
(let* ((time0 (current-time))
(response (cons 'dict nil))
(nrepl-ongoing-sync-request t)
@@ -921,6 +924,28 @@ command and e.g. notify the user about them."
(nrepl--mark-id-completed id)))
response))))
+(defun nrepl-send-sync-request (request connection &optional abort-on-input
+ tooling callback)
+ "Send REQUEST to the nREPL server synchronously using CONNECTION.
+Hold till final \"done\" message has arrived and join all response messages
+of the same \"op\" that came along.
+If ABORT-ON-INPUT is non-nil, the function will return nil at the first
+sign of user input, so as not to hang the interface.
+If TOOLING, use the tooling session rather than the standard session.
+
+If CALLBACK is non-nil, it will additionally be called on all received
+messages. This shouldn't be used this for any control logic - use the
+asynchronous `nrepl-send-request' directly for that. CALLBACK here should
+be used to react to some intermediate events in an otherwise synchronous
+command and e.g. notify the user about them.
+
+This positional form is kept for backward compatibility; new code should
+prefer the keyword-argument `nrepl-sync-request'."
+ (nrepl-sync-request request connection
+ :abort-on-input abort-on-input
+ :tooling tooling
+ :callback callback))
+
(defun nrepl-request:stdin (input callback connection)
"Send a :stdin request with INPUT using CONNECTION.
Register CALLBACK as the response handler."
@@ -959,19 +984,38 @@ If LINE and COLUMN are non-nil and current buffer is a
file buffer, \"line\",
"line" ,line
"column" ,column)))))
-(defun nrepl-request:eval (input callback connection &optional ns line column
additional-params tooling)
+(cl-defun nrepl-send-eval-request (input callback connection
+ &key ns line column additional-params
tooling)
"Send the request INPUT and register the CALLBACK as the response handler.
The request is dispatched via CONNECTION. If NS is non-nil,
include it in the request. LINE and COLUMN, if non-nil, define the position
of INPUT in its buffer. A CONNECTION uniquely determines two connections
available: the standard interaction one and the tooling session. If the
tooling is desired, set TOOLING to true.
-ADDITIONAL-PARAMS is a plist to be appended to the request message."
+ADDITIONAL-PARAMS is a plist to be appended to the request message.
+
+This is the keyword-argument form; `nrepl-request:eval' is the legacy
+positional shim retained for backward compatibility."
(nrepl-send-request (append (nrepl--eval-request input ns line column)
additional-params)
callback
connection
tooling))
+(defun nrepl-request:eval (input callback connection &optional ns line column
additional-params tooling)
+ "Send the request INPUT and register the CALLBACK as the response handler.
+The request is dispatched via CONNECTION. If NS is non-nil,
+include it in the request. LINE and COLUMN, if non-nil, define the position
+of INPUT in its buffer. A CONNECTION uniquely determines two connections
+available: the standard interaction one and the tooling session. If the
+tooling is desired, set TOOLING to true.
+ADDITIONAL-PARAMS is a plist to be appended to the request message.
+
+This positional form is kept for backward compatibility; new code should
+prefer the keyword-argument `nrepl-send-eval-request'."
+ (nrepl-send-eval-request input callback connection
+ :ns ns :line line :column column
+ :additional-params additional-params :tooling
tooling))
+
(defvar nrepl-client-name "nrepl.el"
"Name of the nREPL client, sent in clone requests.")
@@ -983,17 +1027,17 @@ ADDITIONAL-PARAMS is a plist to be appended to the
request message."
The request is dispatched via CONNECTION.
Optional argument TOOLING Tooling is set to t if wanting the tooling session
from CONNECTION."
- (nrepl-send-sync-request `("op" "clone"
- "client-name" ,nrepl-client-name
- ,@(when nrepl-client-version
- `("client-version" ,nrepl-client-version)))
- connection
- nil tooling))
+ (nrepl-sync-request `("op" "clone"
+ "client-name" ,nrepl-client-name
+ ,@(when nrepl-client-version
+ `("client-version" ,nrepl-client-version)))
+ connection
+ :tooling tooling))
(defun nrepl-sync-request:close (connection)
"Sent a :close request to close CONNECTION's SESSION."
- (nrepl-send-sync-request '("op" "close") connection)
- (nrepl-send-sync-request '("op" "close") connection nil t)) ;; close tooling
session
+ (nrepl-sync-request '("op" "close") connection)
+ (nrepl-sync-request '("op" "close") connection :tooling t)) ;; close tooling
session
(defun nrepl-sync-request:describe (connection)
"Perform :describe request for CONNECTION and SESSION."
@@ -1014,11 +1058,10 @@ The request is dispatched via CONNECTION.
If NS is non-nil, include it in the request
If TOOLING is non-nil the evaluation is done using the tooling nREPL
session."
- (nrepl-send-sync-request
+ (nrepl-sync-request
(nrepl--eval-request input ns)
connection
- nil
- tooling))
+ :tooling tooling))
(defun nrepl-sessions (connection)
"Get a list of active sessions on the nREPL server using CONNECTION."
diff --git a/test/nrepl-client-tests.el b/test/nrepl-client-tests.el
index 5202a85643..1f9b0d0b1e 100644
--- a/test/nrepl-client-tests.el
+++ b/test/nrepl-client-tests.el
@@ -267,6 +267,49 @@
(expect received-body :to-equal "hello")
(expect received-type :to-equal '("text/plain" ())))))
+(describe "nrepl-send-eval-request"
+ (it "passes code, callback, connection and tooling through to
nrepl-send-request"
+ (let (captured)
+ (spy-on 'nrepl-send-request :and-call-fake
+ (lambda (request callback connection &optional tooling)
+ (setq captured (list request callback connection tooling))))
+ (let ((cb (lambda (_))))
+ (nrepl-send-eval-request "(+ 1 1)" cb :fake-conn
+ :ns "user" :tooling 'tooling)
+ (cl-destructuring-bind (request callback connection tooling) captured
+ (expect callback :to-be cb)
+ (expect connection :to-be :fake-conn)
+ (expect tooling :to-be 'tooling)
+ (expect (nrepl-dict-get (cons 'dict request) "code") :to-equal "(+ 1
1)")
+ (expect (nrepl-dict-get (cons 'dict request) "ns") :to-equal
"user")))))
+
+ (it "produces the same request as the positional nrepl-request:eval shim"
+ (let (calls)
+ (spy-on 'nrepl-send-request :and-call-fake
+ (lambda (&rest args) (push args calls)))
+ (let ((cb (lambda (_))))
+ (nrepl-send-eval-request "(+ 1 1)" cb :fake-conn
+ :ns "user" :line 1 :column 2
+ :additional-params '("foo" "bar") :tooling
'tooling)
+ (nrepl-request:eval "(+ 1 1)" cb :fake-conn
+ "user" 1 2 '("foo" "bar") 'tooling))
+ (expect (length calls) :to-equal 2)
+ (expect (nth 0 calls) :to-equal (nth 1 calls)))))
+
+(describe "nrepl-send-sync-request"
+ (it "delegates to nrepl-sync-request with positional args mapped to keywords"
+ (let (captured)
+ (spy-on 'nrepl-sync-request :and-call-fake
+ (lambda (request connection &rest kwargs)
+ (setq captured (list request connection kwargs))))
+ (nrepl-send-sync-request '("op" "x") :fake-conn 'abort 'tooling #'ignore)
+ (cl-destructuring-bind (request connection kwargs) captured
+ (expect request :to-equal '("op" "x"))
+ (expect connection :to-be :fake-conn)
+ (expect (plist-get kwargs :abort-on-input) :to-be 'abort)
+ (expect (plist-get kwargs :tooling) :to-be 'tooling)
+ (expect (plist-get kwargs :callback) :to-be #'ignore)))))
+
(describe "nrepl--dispatch-response"
:var (nrepl-pending-requests nrepl-completed-requests)
(before-each