branch: elpa/cider
commit 9950962788e76b4f38cf5629da254391e9e73734
Author: Bozhidar Batsov <[email protected]>
Commit: Bozhidar Batsov <[email protected]>

    Retire the async eldoc request when it completes
    
    `cider-eldoc--request-async' accumulated responses and called back on the
    `done' status but never marked the request id completed, so every eldoc
    lookup left an entry in `nrepl-pending-requests'. Eldoc fires on nearly
    every cursor move, so the table grew unbounded for the whole session and
    a late/duplicate response could re-invoke the callback. Mark the id
    completed on `done', matching every other async handler.
---
 lisp/cider-eldoc.el       | 15 ++++++++++-----
 test/cider-eldoc-tests.el | 22 ++++++++++++++++++++++
 2 files changed, 32 insertions(+), 5 deletions(-)

diff --git a/lisp/cider-eldoc.el b/lisp/cider-eldoc.el
index 18ce199b6e..d51925dde9 100644
--- a/lisp/cider-eldoc.el
+++ b/lisp/cider-eldoc.el
@@ -505,11 +505,16 @@ Unlike the synchronous path this never blocks Emacs, so 
it needs no
            "sym" ,thing
            "context" ,(cider-completion-get-context t))
          (lambda (response)
-           (setq accumulated (if accumulated
-                                 (nrepl-dict-merge accumulated response)
-                               response))
-           (when (member "done" (nrepl-dict-get response "status"))
-             (funcall k accumulated)))
+           (nrepl-dbind-response response (status id)
+             (setq accumulated (if accumulated
+                                   (nrepl-dict-merge accumulated response)
+                                 response))
+             (when (member "done" status)
+               ;; Eldoc fires on nearly every cursor move, so the request must
+               ;; be retired from `nrepl-pending-requests' once done - 
otherwise
+               ;; the table grows unbounded for the whole session.
+               (nrepl--mark-id-completed id)
+               (funcall k accumulated))))
          connection))
     (funcall k nil)))
 
diff --git a/test/cider-eldoc-tests.el b/test/cider-eldoc-tests.el
index 27cee22d6a..1ca10729c7 100644
--- a/test/cider-eldoc-tests.el
+++ b/test/cider-eldoc-tests.el
@@ -388,3 +388,25 @@ Relies on the spied `cider-eldoc-info-async' invoking its 
callback synchronously
             :and-return-value '(dict "inputs" (("$" "?first-name"))))
     (expect (cider--eldoc-add-datomic-query-inputs-to-arglists '(("query" "&" 
"inputs")))
             :to-equal '(("query" "$" "?first-name")))))
+
+(describe "cider-eldoc--request-async"
+  (it "retires the request id and calls back once done"
+    (spy-on 'cider-current-repl :and-return-value 'conn)
+    (spy-on 'cider-current-ns :and-return-value "user")
+    (spy-on 'cider-completion-get-context :and-return-value "nil")
+    (spy-on 'nrepl--mark-id-completed)
+    (spy-on 'cider-nrepl-send-request :and-call-fake
+            (lambda (_request callback &rest _)
+              (funcall callback '(dict "status" ("done") "id" "7" "eldoc" 
(("x"))))))
+    (let (result)
+      (cider-eldoc--request-async "foo" (lambda (r) (setq result r)))
+      ;; the pending request is retired so the table doesn't grow every lookup
+      (expect 'nrepl--mark-id-completed :to-have-been-called-with "7")
+      ;; and the accumulated dict is handed to the continuation
+      (expect (nrepl-dict-get result "eldoc") :to-equal '(("x")))))
+
+  (it "calls back with nil when there is no connection"
+    (spy-on 'cider-current-repl :and-return-value nil)
+    (let ((called 'unset))
+      (cider-eldoc--request-async "foo" (lambda (r) (setq called r)))
+      (expect called :to-be nil))))

Reply via email to