branch: elpa/cider
commit 75dc57aebed59212952595684b9aae60f95c94a6
Author: Bozhidar Batsov <[email protected]>
Commit: Bozhidar Batsov <[email protected]>
Add default session feature to bypass sesman's project-based dispatch
Some users want a simpler model: designate one session as the "default"
and have all evaluations go there, regardless of project context. This
is useful for monorepo setups, scratch buffers, or when working across
multiple projects that share a single REPL.
New commands:
- `cider-set-default-session` - pick a session to use for all lookups
- `cider-clear-default-session` - revert to normal sesman dispatch
When set, `cider-repls` returns REPLs from the default session (with
type filtering still applied). Stale sessions produce a warning and
yield no REPLs. `cider-describe-connection` shows the default session
in its output. Both commands are available from the CIDER menu.
---
CHANGELOG.md | 4 +
.../ROOT/pages/usage/managing_connections.adoc | 16 +++
lisp/cider-connection.el | 114 +++++++++++++--------
lisp/cider-mode.el | 4 +
test/cider-connection-tests.el | 90 ++++++++++++++++
5 files changed, 186 insertions(+), 42 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5850a518c1..eebe5d3547 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
## master (unreleased)
+### New features
+
+- [#3865](https://github.com/clojure-emacs/cider/pull/3865): Add default
session feature to bypass sesman's project-based dispatch
(`cider-set-default-session`, `cider-clear-default-session`).
+
## 1.21.0 (2026-02-07)
### Changes
diff --git a/doc/modules/ROOT/pages/usage/managing_connections.adoc
b/doc/modules/ROOT/pages/usage/managing_connections.adoc
index fa3007e297..c938095280 100644
--- a/doc/modules/ROOT/pages/usage/managing_connections.adoc
+++ b/doc/modules/ROOT/pages/usage/managing_connections.adoc
@@ -126,6 +126,22 @@ By default https://github.com/vspinu/sesman[Sesman] allows
multiple simultaneous
directories, but only one link per buffer. See `sesman-single-link-contexts` if
you would like to change that.
+== Default Session
+
+If you'd rather bypass sesman's context-based session inference and always use
a
+specific session, you can designate one as the *default session*. When a
default
+session is set, all REPL lookups (evaluation, completion, etc.) use it
regardless
+of project context. This is useful for monorepo setups, scratch buffers, or
when
+working across multiple projects that share a single REPL.
+
+* `cider-set-default-session` -- prompts you to pick from the active sessions
and sets it as the default.
+* `cider-clear-default-session` -- clears the default session, reverting to
normal project-based session association.
+
+Both commands are also available from the CIDER menu.
+
+If the default session no longer exists (e.g. it was closed), CIDER will warn
you
+and fall through to the normal session lookup.
+
== Current REPL
The current REPL is the most relevant REPL from the current session. REPL
relevance
diff --git a/lisp/cider-connection.el b/lisp/cider-connection.el
index 68ff044c55..963dfd2b0c 100644
--- a/lisp/cider-connection.el
+++ b/lisp/cider-connection.el
@@ -82,6 +82,10 @@ All other values do not combine any sessions."
:safe #'symbolp
:package-version '(cider . "1.5"))
+(defvar cider-default-session nil
+ "When non-nil, bypass sesman and use this session for all REPL lookups.
+Set interactively with `cider-set-default-session'.")
+
(defcustom cider-reuse-dead-repls 'prompt
"How to deal with existing dead REPL buffers when initializing a connection.
@@ -481,30 +485,33 @@ Info contains project name, current REPL namespace,
host:port endpoint and
runtime details. When GENERICP is non-nil, don't provide specific info
about this buffer (like variable `cider-repl-type')."
(with-current-buffer connection-buffer
- (cond
- ((cider--clojure-version)
- (format "%s%s@%s:%s (Java %s, Clojure %s, nREPL %s)"
- (if genericp "" (upcase (concat (symbol-name cider-repl-type) "
")))
- (or (cider--project-name nrepl-project-dir) "<no project>")
- (plist-get nrepl-endpoint :host)
- (plist-get nrepl-endpoint :port)
- (cider--java-version)
- (cider--clojure-version)
- (cider--nrepl-version)))
- ((cider--babashka-version)
- (format "%s%s@%s:%s (Babashka %s, babashka.nrepl %s)"
- (if genericp "" (upcase (concat (symbol-name cider-repl-type) "
")))
- (or (cider--project-name nrepl-project-dir) "<no project>")
- (plist-get nrepl-endpoint :host)
- (plist-get nrepl-endpoint :port)
- (cider--babashka-version)
- (cider--babashka-nrepl-version)))
- (t
- (format "%s%s@%s:%s"
- (if genericp "" (upcase (concat (symbol-name cider-repl-type) "
")))
- (or (cider--project-name nrepl-project-dir) "<no project>")
- (plist-get nrepl-endpoint :host)
- (plist-get nrepl-endpoint :port))))))
+ (let ((info (cond
+ ((cider--clojure-version)
+ (format "%s%s@%s:%s (Java %s, Clojure %s, nREPL %s)"
+ (if genericp "" (upcase (concat (symbol-name
cider-repl-type) " ")))
+ (or (cider--project-name nrepl-project-dir) "<no
project>")
+ (plist-get nrepl-endpoint :host)
+ (plist-get nrepl-endpoint :port)
+ (cider--java-version)
+ (cider--clojure-version)
+ (cider--nrepl-version)))
+ ((cider--babashka-version)
+ (format "%s%s@%s:%s (Babashka %s, babashka.nrepl %s)"
+ (if genericp "" (upcase (concat (symbol-name
cider-repl-type) " ")))
+ (or (cider--project-name nrepl-project-dir) "<no
project>")
+ (plist-get nrepl-endpoint :host)
+ (plist-get nrepl-endpoint :port)
+ (cider--babashka-version)
+ (cider--babashka-nrepl-version)))
+ (t
+ (format "%s%s@%s:%s"
+ (if genericp "" (upcase (concat (symbol-name
cider-repl-type) " ")))
+ (or (cider--project-name nrepl-project-dir) "<no
project>")
+ (plist-get nrepl-endpoint :host)
+ (plist-get nrepl-endpoint :port))))))
+ (if cider-default-session
+ (format "%s [default session: %s]" info cider-default-session)
+ info))))
(defvar-local cider-connection-capabilities '()
"A list of some of the capabilities of this connection buffer.
@@ -608,6 +615,24 @@ REPL defaults to the current REPL."
(mapc (lambda (mw) (insert (format " * %s\n" mw))) middleware))
(display-buffer "*cider-nrepl-middleware*")))
+(defun cider-set-default-session ()
+ "Set the default session for all REPL lookups.
+When a default session is set, all evaluations use it
+regardless of project context."
+ (interactive)
+ (let* ((sessions (cider-sessions))
+ (session-names (mapcar #'car sessions))
+ (name (completing-read "Set default CIDER session: " session-names
nil t)))
+ (setq cider-default-session name)
+ (message "Default CIDER session set to '%s'" name)))
+
+(defun cider-clear-default-session ()
+ "Clear the default CIDER session.
+Reverts to normal project-based session association."
+ (interactive)
+ (setq cider-default-session nil)
+ (message "Default CIDER session cleared"))
+
;;; Sesman's Session-Wise Management UI
@@ -1021,24 +1046,29 @@ filters out all the REPLs that do not support the
designated ops."
((listp type)
(mapcar #'cider-maybe-intern type))
((cider-maybe-intern type))))
- (repls (pcase cider-merge-sessions
- ('host
- (if ensure
- (or (cider--extract-connections
(cider--get-sessions-with-same-host
- (sesman-current-session
'CIDER)
-
(sesman-current-sessions 'CIDER)))
- (user-error "No linked %s sessions" 'CIDER))
- (cider--extract-connections
(cider--get-sessions-with-same-host
- (sesman-current-session
'CIDER)
- (sesman-current-sessions
'CIDER)))))
- ('project
- (if ensure
- (or (cider--extract-connections (sesman-current-sessions
'CIDER))
- (user-error "No linked %s sessions" 'CIDER))
- (cider--extract-connections (sesman-current-sessions
'CIDER))))
- (_ (cdr (if ensure
- (sesman-ensure-session 'CIDER)
- (sesman-current-session 'CIDER)))))))
+ (repls (if cider-default-session
+ (if-let* ((session (sesman-session 'CIDER
cider-default-session)))
+ (cdr session)
+ (message "Default CIDER session '%s' no longer exists,
ignoring" cider-default-session)
+ nil)
+ (pcase cider-merge-sessions
+ ('host
+ (if ensure
+ (or (cider--extract-connections
(cider--get-sessions-with-same-host
+
(sesman-current-session 'CIDER)
+
(sesman-current-sessions 'CIDER)))
+ (user-error "No linked %s sessions" 'CIDER))
+ (cider--extract-connections
(cider--get-sessions-with-same-host
+ (sesman-current-session
'CIDER)
+ (sesman-current-sessions
'CIDER)))))
+ ('project
+ (if ensure
+ (or (cider--extract-connections
(sesman-current-sessions 'CIDER))
+ (user-error "No linked %s sessions" 'CIDER))
+ (cider--extract-connections (sesman-current-sessions
'CIDER))))
+ (_ (cdr (if ensure
+ (sesman-ensure-session 'CIDER)
+ (sesman-current-session 'CIDER))))))))
(or (seq-filter (lambda (b)
(unless
(cider-cljs-pending-p b)
diff --git a/lisp/cider-mode.el b/lisp/cider-mode.el
index 7576f90178..1c5ce529e5 100644
--- a/lisp/cider-mode.el
+++ b/lisp/cider-mode.el
@@ -296,6 +296,10 @@ If invoked with a prefix ARG eval the expression after
inserting it."
"--"
["Connection info" cider-describe-connection
:active (cider-connected-p)]
+ ["Set default session" cider-set-default-session
+ :active (cider-connected-p)]
+ ["Clear default session" cider-clear-default-session
+ :active cider-default-session]
["Select any CIDER buffer" cider-selector]
"--"
["Configure CIDER" (customize-group 'cider)]
diff --git a/test/cider-connection-tests.el b/test/cider-connection-tests.el
index 4a2293e05e..ae4073fdb3 100644
--- a/test/cider-connection-tests.el
+++ b/test/cider-connection-tests.el
@@ -543,3 +543,93 @@
:project-dir "/Users/me/myproject"
:repl-type cider-connection-tests-dummy-function))
:to-equal "*cider-repl
me/myproject:localhost:12345(cider-connection-tests-dummy-function)*")))
+
+(describe "cider-default-session"
+
+ :var (sesman-sessions-hashmap sesman-links-alist ses-name ses-name2)
+
+ (before-each
+ (setq sesman-sessions-hashmap (make-hash-table :test #'equal)
+ sesman-links-alist nil
+ cider-default-session nil
+ ses-name "a-session"
+ ses-name2 "b-session"))
+
+ (after-each
+ (setq cider-default-session nil))
+
+ (describe "cider-repls with default session"
+ (it "returns REPLs from the default session regardless of project context"
+ (let ((a-dir (expand-file-name "/tmp/a-dir"))
+ (b-dir (expand-file-name "/tmp/b-dir")))
+ (let ((default-directory a-dir))
+ (with-repl-buffer ses-name 'clj b1
+ (with-repl-buffer ses-name 'cljs b2
+ (let ((default-directory b-dir))
+ (with-repl-buffer ses-name2 'clj b3
+ (with-repl-buffer ses-name2 'cljs b4
+ ;; Without default session, we get b-dir's session
+ (expect (cider-repls) :to-equal (list b4 b3))
+
+ ;; Set default session to a-session
+ (setq cider-default-session ses-name)
+
+ ;; Now we get a-session's REPLs even though we're in b-dir
+ (expect (cider-repls) :to-have-same-items-as (list b1
b2))))))))))
+
+ (it "still filters by type when default session is set"
+ (let ((a-dir (expand-file-name "/tmp/a-dir"))
+ (b-dir (expand-file-name "/tmp/b-dir")))
+ (let ((default-directory a-dir))
+ (with-repl-buffer ses-name 'clj b1
+ (with-repl-buffer ses-name 'cljs b2
+ (let ((default-directory b-dir))
+ (with-repl-buffer ses-name2 'clj b3
+ (setq cider-default-session ses-name)
+ (expect (cider-repls 'clj) :to-equal (list b1))
+ (expect (cider-repls 'cljs) :to-equal (list b2)))))))))
+
+ (it "returns nil and warns when default session no longer exists"
+ (let ((default-directory (expand-file-name "/tmp/a-dir")))
+ (with-repl-buffer ses-name 'clj _b1
+ (setq cider-default-session "nonexistent-session")
+ ;; Should warn and return nil (stale default session yields no REPLs)
+ (expect (cider-repls) :to-equal nil)))))
+
+ (describe "cider-set-default-session"
+ (it "sets the default session from active sessions"
+ (let ((default-directory (expand-file-name "/tmp/a-dir")))
+ (with-repl-buffer ses-name 'clj b1
+ (spy-on 'completing-read :and-return-value ses-name)
+ (cider-set-default-session)
+ (expect cider-default-session :to-equal ses-name)))))
+
+ (describe "cider-clear-default-session"
+ (it "clears the default session"
+ (setq cider-default-session "some-session")
+ (cider-clear-default-session)
+ (expect cider-default-session :to-equal nil)))
+
+ (describe "cider--connection-info with default session"
+ (before-each
+ (spy-on 'cider--java-version :and-return-value "1.7")
+ (spy-on 'cider--clojure-version :and-return-value "1.7.0")
+ (spy-on 'cider--nrepl-version :and-return-value "0.2.1"))
+
+ (it "appends default session info when set"
+ (with-temp-buffer
+ (setq-local nrepl-endpoint '(:host "localhost" :port 4005))
+ (setq-local nrepl-project-dir "proj")
+ (setq-local cider-repl-type 'clj)
+ (setq cider-default-session "my-session")
+ (expect (cider--connection-info (current-buffer))
+ :to-equal "CLJ proj@localhost:4005 (Java 1.7, Clojure 1.7.0,
nREPL 0.2.1) [default session: my-session]")))
+
+ (it "does not append default session info when not set"
+ (with-temp-buffer
+ (setq-local nrepl-endpoint '(:host "localhost" :port 4005))
+ (setq-local nrepl-project-dir "proj")
+ (setq-local cider-repl-type 'clj)
+ (setq cider-default-session nil)
+ (expect (cider--connection-info (current-buffer))
+ :to-equal "CLJ proj@localhost:4005 (Java 1.7, Clojure 1.7.0,
nREPL 0.2.1)")))))