Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@

## master (unreleased)

### New features

- [#3354](https://github.com/clojure-emacs/cider/issues/3354): Add new customization variable `cider-reuse-dead-repls` to control how dead REPL buffers are reused on new connections.

### Bugs fixed

- [#3341](https://github.com/clojure-emacs/cider/issues/3341): Escape clojure-cli args on MS-Windows on non powershell invocations.
- [#3353](https://github.com/clojure-emacs/cider/issues/3353): Fix regression which caused new connections to prompt for reusing dead REPLs.

## 1.7.0 (2023-03-23)

Expand Down
69 changes: 45 additions & 24 deletions cider-connection.el
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,40 @@ available) and the matching REPL buffer."
:safe #'booleanp
:package-version '(cider . "0.9.0"))

;;;###autoload
(defcustom cider-merge-sessions nil
"Controls session combination behaviour.

Symbol `host' combines all sessions of a project associated with the same host.
Symbol `project' combines all sessions of a project.

All other values do not combine any sessions."
:type 'symbol
:type '(choice (const :tag "Combine all sessions with the same host" host)
(const :tag "Combine all sessions from the same project" project)
(other :tag "Do not combine any sessions"))
:group 'cider
:safe #'symbolp
:package-version '(cider . "1.5"))

(defcustom cider-reuse-dead-repls 'prompt
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could a nil default introduce less friction?

I think I've seen 2-3 times user feedback like this:

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mind it either way, but it seems like @bbatsov prefered to keep the prompt behaviour as a default, from the discussion in #3076.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, it's so good to have that discussion in the archives :)

Alright then. Perhaps we can change...?

-A dead REPL %s exists.  Reuse buffer?
+A dead REPL %s exists.  Reuse buffer? (see also: `cider-reuse-dead-repls' defcustom)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems a bit too much to mention the defcustom, as we never do this normally. And it this change probably doesn't affect people much anyways.

"How to deal with existing dead REPL buffers when initializing a connection.

Possible choices are `prompt', `auto', `any', and nil.
- `prompt' means to always ask the user for a decision.
- `auto' means to automatically reuse a dead REPL without prompting the user
if it is the only available option. When there are multiple buffers to
choose from, the user is is prompted for a choice.
- `any' (or any other non-nil value) means to reuse any dead REPL buffer
available, by default the most relevant according to various heuristics,
and never prompt the user.
- nil means to start a new REPL each time, ignoring existing buffers."
:type '(choice (const :tag "Always prompt for what to do with dead REPLs" prompt)
(const :tag "Reuse dead REPL, prompting only for multiple choice" auto)
(const :tag "Reuse any available dead REPL and never prompt" any)
(const :tag "Never reuse dead REPLs" nil))
:group 'cider
:safe #'symbolp
:package-version '(cider . "1.8"))

(defconst cider-required-nrepl-version "0.6.0"
"The minimum nREPL version that's known to work properly with CIDER.")

Expand Down Expand Up @@ -819,28 +840,27 @@ PARAMS is a plist as received by `cider-repl-create'."
(let* ((proj-dir (plist-get params :project-dir))
(host (plist-get params :host))
(port (plist-get params :port))
(cljsp (eq (plist-get params :repl-type) 'cljs))
(type (plist-get params :repl-type))
(scored-repls
(delq nil
(mapcar (lambda (b)
(let ((bparams (cider--gather-connect-params nil b)))
(when (and cljsp
(eq (plist-get bparams :repl-type)
'cljs))
(cons (buffer-name b)
(+
(if (equal proj-dir (plist-get bparams :project-dir)) 8 0)
(if (equal host (plist-get bparams :host)) 4 0)
(if (equal port (plist-get bparams :port)) 2 0))))))
repls))))
(when scored-repls
(if (> (length scored-repls) 1)
(when (y-or-n-p "Dead REPLs exist. Reuse? ")
(let ((sorted-repls (seq-sort (lambda (a b) (> (cdr a) (cdr b))) scored-repls)))
(get-buffer (completing-read "REPL to reuse: "
(mapcar #'car sorted-repls) nil t nil nil (caar sorted-repls)))))
(when (y-or-n-p (format "A dead REPL %s exists. Reuse? " (caar scored-repls)))
(get-buffer (caar scored-repls))))))))
(mapcar (lambda (b)
(let ((bparams (cider--gather-connect-params nil b)))
(when (eq type (plist-get bparams :repl-type))
(cons b (+
(if (equal proj-dir (plist-get bparams :project-dir)) 8 0)
(if (equal host (plist-get bparams :host)) 4 0)
(if (equal port (plist-get bparams :port)) 2 0))))))
repls))
(sorted-repls (mapcar #'car (seq-sort-by #'cdr #'> (delq nil scored-repls)))))
(when sorted-repls
(cond ((eq 'any cider-reuse-dead-repls)
(car sorted-repls))
((= 1 (length sorted-repls))
(when (or (eq 'auto cider-reuse-dead-repls)
(y-or-n-p (format "A dead REPL %s exists. Reuse buffer? " (car sorted-repls))))
(car sorted-repls)))
((y-or-n-p "Dead REPL buffers exist. Select one to reuse? ")
(get-buffer (completing-read "REPL buffer to reuse: " (mapcar #'buffer-name sorted-repls)
nil t nil nil (car sorted-repls)))))))))

(declare-function cider-default-err-handler "cider-eval")
(declare-function cider-repl-mode "cider-repl")
Expand All @@ -858,7 +878,8 @@ function with the repl buffer set as current."
;; Connection might not have been set as yet. Please don't send requests in
;; this function, but use cider--connected-handler instead.
(let ((buffer (or (plist-get params :repl-buffer)
(cider--choose-reusable-repl-buffer params)
(and cider-reuse-dead-repls
(cider--choose-reusable-repl-buffer params))
(get-buffer-create (generate-new-buffer-name "*cider-uninitialized-repl*"))))
(ses-name (or (plist-get params :session-name)
(cider-make-session-name params))))
Expand Down
15 changes: 15 additions & 0 deletions doc/modules/ROOT/pages/usage/managing_connections.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,18 @@ name and the REPL type specification post-fix
You can customize session names with `cider-session-name-template` and REPL
names with `nrepl-repl-buffer-name-template`. See also
`cider-format-connection-params` for available formats.

== Reusing dead REPLs

Under normal circumstances, CIDER automatically kills the REPL buffers and cleans up after itself when ending a session.
However, when a session is terminated unexpectedly, e.g. when it crashes or is disconnected from an external server process, the REPL buffer is left without an active connection and outputs a log:

[source]
----
*** Closed on < date/time > ***
----

Upon starting a new connection, CIDER can detect these "dead REPLs" and offer to reuse the buffer for the new connection.
By default, it prompts for confirmation whenever a dead REPL buffer is available for reuse, but you can customize this behaviour via the variable `cider-reuse-dead-repls`.
Setting it to `auto` reuses a dead REPL buffer automatically, and only displays a prompt when there are multiple options to choose from.
To suppress the prompt entirely, set it to `nil` to always start a new REPL buffer, or `any` to reuse the most relevant dead REPL.