From 5807f2b1d28a9a5295fb28012e8024963171852e Mon Sep 17 00:00:00 2001 From: Vitalie Spinu Date: Tue, 1 Aug 2017 14:16:14 +0200 Subject: [PATCH] New connection API and user level connection commands New variables: cider-connection-alist, cider-connection-name, cider-connected-directories New connection API functions: cider-delete-connection-repl, cider-add-connection-repl, cider-get-connection, cider-project-connections-repls, cider-current-connection-repls, cider-current-connection-repl New commands for connection association: cider-assoc-buffer-with-connection cider-assoc-directory-with-connection cider-assoc-project-with-connection cider-assoc-with-connection New semantics: cider-assoc-project-with-connection, cider-connections, cider-current-connection, cider-project-connections Removed: cider-request-dispatch, cider-connections, cider-find-connection-buffer-for-project-directory, cider-toggle-buffer-connection, cider-clear-buffer-local-connection, cider-toggle-request-dispatch, cider-current-connection, cider--in-connection-buffer-p, cider-default-connection, cider-rotate-default-connection cider--guess-cljs-connection, cider--has-warned-about-bad-repl-type, cider-map-connections, cider-connections-make-default, cider--connections-make-default, nrepl-use-this-as-repl-buffer Work, but need to be re-factored/adjusted: cider-change-buffers-designation cider-connection-browser --- cider-client.el | 690 ++++++++++------------ cider-debug.el | 2 +- cider-interaction.el | 213 +++---- cider-mode.el | 42 +- cider-repl.el | 43 +- cider-resolve.el | 6 +- cider-scratch.el | 4 +- cider-selector.el | 2 +- cider-test.el | 88 ++- cider.el | 75 ++- doc/cider-refcard.tex | 1 - doc/interactive_programming.md | 3 +- doc/managing_connections.md | 20 +- nrepl-client.el | 8 +- test/cider-client-tests.el | 392 ++++++------ test/cider-interaction-tests.el | 26 +- test/cider-selector-tests.el | 8 +- test/cider-tests.el | 72 ++- test/utils/cider-connection-test-utils.el | 28 +- 19 files changed, 790 insertions(+), 933 deletions(-) diff --git a/cider-client.el b/cider-client.el index b07954ebc..bbd515729 100644 --- a/cider-client.el +++ b/cider-client.el @@ -36,21 +36,6 @@ (require 'cider-compat) (require 'seq) -;;; Connection Buffer Management - -(defcustom cider-request-dispatch 'dynamic - "Controls the request dispatch mechanism when several connections are present. -Dynamic dispatch tries to infer the connection based on the current project -& currently visited file, while static dispatch simply uses the default -connection. - -Project metadata is attached to connections when they are created with commands -like `cider-jack-in' and `cider-connect'." - :type '(choice (const :tag "dynamic" dynamic) - (const :tag "static" static)) - :group 'cider - :package-version '(cider . "0.10.0")) - (defcustom cider-connection-message-fn #'cider-random-words-of-inspiration "The function to use to generate the message displayed on connect. When set to nil no additional message will be displayed. @@ -60,70 +45,115 @@ A good alternative to the default is `cider-random-tip'." :group 'cider :package-version '(cider . "0.11.0")) -(defvar cider-connections nil - "A list of connections.") +(defvar-local cider-repl-type nil + "The type of this REPL buffer, usually either \"clj\" or \"cljs\".") + + +;;; Connection Basics + +(defvar-local cider-connection-name nil + "Name of the connection of which the current buffer is part of.") + +(defvar cider-connected-directories (make-hash-table :test 'equal) + "A hash-table of directory -> connection associations.") + +(defvar cider-connection-alist nil + "An alist of connections. +Each element is of the form (NAME BUF1 ...), where NAME is a user visible +name of the connection, and BUF1 ... are connection endpoints which +constitute a connection (currently REPL buffers).") + +(defun cider-connections () + "Return the `cider-connection-alist' free of dead buffers." + (setq cider-connection-alist + (delq nil + (mapcar (lambda (el) + (when-let ((buffers (seq-filter #'buffer-live-p (cdr el)))) + (cons (car el) buffers))) + cider-connection-alist)))) (defun cider-connected-p () "Return t if CIDER is currently connected, nil otherwise." - (not (null (cider-connections)))) + (not (null (cider-current-connection)))) (defun cider-ensure-connected () "Ensure there is a cider connection present. An error is signaled in the absence of a connection." - (unless (cider-connected-p) - (user-error "`%s' needs an active nREPL connection" this-command))) - -(defsubst cider--in-connection-buffer-p () - "Return non-nil if current buffer is connected to a server." - (and (derived-mode-p 'cider-repl-mode) - (process-live-p - (get-buffer-process (current-buffer))))) - -(defun cider-default-connection (&optional no-error) - "The default (fallback) connection to use for nREPL interaction. -When NO-ERROR is non-nil, don't throw an error when no connection has been -found." - (or (car (cider-connections)) - (unless no-error - (error "No nREPL connection buffer")))) - -(defun cider-connections () - "Return the list of connection buffers. -If the list is empty and buffer-local, return the global value." - (or (setq cider-connections - (seq-filter #'buffer-live-p cider-connections)) - (when (local-variable-p 'cider-connect) - (kill-local-variable 'cider-connections) - (seq-filter #'buffer-live-p cider-connections)))) + (unless (cider-current-connection :ask-user) + (user-error "`%s' needs current connection" this-command))) + +(defun cider-delete-connection-repl (conn buffer) + "Delete CONN's BUFFER from `cider-connection-alist' and cleanup associations." + (let ((name (if (stringp conn) conn (car conn)))) + ;; remove buffer associations + (dolist (b (cider-util--clojure-buffers)) + (with-current-buffer b + (when (equal cider-connection-name name) + (kill-local-variable 'cider-connection-name)))) + ;; remove directory associations + (maphash (lambda (k v) + (when (equal v name) + (remhash k cider-connected-directories))) + cider-connected-directories) + ;; destructively delete conn from cider-connection-alist + (delq buffer (cider-get-connection conn)))) + +(defun cider-add-connection-repl (conn buffer) + "Add a CONS's BUFFER to `cider-connection-alist'." + (if-let ((conn1 (cider-get-connection conn))) + (setcdr conn1 (cons buffer (delq buffer (cdr conn1)))) + (setq cider-connection-alist + (cons (list conn buffer) + cider-connection-alist)))) + +(defun cider-get-connection (conn) + "Retrieve connection CONN from `cider-connection-alist'. +If CONN is not a string, just return it." + (if (stringp conn) + (assoc conn cider-connection-alist) + conn)) + +(defun cider-make-connection-name (&optional project-dir) + (let* ((dir (or project-dir + (clojure-project-dir (cider-current-dir)) + default-directory)) + (name0 (file-name-nondirectory (directory-file-name dir))) + (name name0) + (names (mapcar #'car cider-connection-alist)) + (i 2)) + (while (member name names) + (setq name (concat name0 "#" (number-to-string i)) + i (+ i 1))) + name)) + +(defun cider-close-ancillary-buffers () + "Close buffers that are shared across connections." + (interactive) + (dolist (buf-name cider-ancillary-buffers) + (when (get-buffer buf-name) + (kill-buffer buf-name)))) -(defun cider-repl-buffers () - "Return the list of REPL buffers." - (seq-filter - (lambda (buffer) - (with-current-buffer buffer (derived-mode-p 'cider-repl-mode))) - (buffer-list))) +(defun cider--close-buffer (buffer) + "Close the BUFFER and kill its associated process (if any)." + (when (buffer-live-p buffer) + (with-current-buffer buffer + (when-let ((proc (get-buffer-process buffer))) + (when (process-live-p proc) + (when (or (not nrepl-server-buffer) + ;; Sync request will hang if the server is dead. + (process-live-p (get-buffer-process nrepl-server-buffer))) + (when (or nrepl-session nrepl-tooling-session) + (nrepl-sync-request:close buffer))) + (when proc (delete-process proc))))) + (kill-buffer buffer))) -(defun cider-make-connection-default (connection-buffer) - "Make the nREPL CONNECTION-BUFFER the default connection. -Moves CONNECTION-BUFFER to the front of variable `cider-connections'." - (interactive (list (if (cider--in-connection-buffer-p) - (current-buffer) - (user-error "Not in a REPL buffer")))) - ;; maintain the connection list in most recently used order - (let ((buf (get-buffer connection-buffer))) - (setq cider-connections - (cons buf (delq buf cider-connections)))) - (cider--connections-refresh)) - -(declare-function cider--close-buffer "cider-interaction") (defun cider--close-connection-buffer (conn-buffer) - "Close CONN-BUFFER, removing it from variable `cider-connections'. + "Close CONN-BUFFER, removing it from variable `cider-connection-alist'. Also close associated REPL and server buffers." (let ((buffer (get-buffer conn-buffer)) (nrepl-messages-buffer (and nrepl-log-messages (nrepl-messages-buffer conn-buffer)))) - (setq cider-connections - (delq buffer cider-connections)) + (cider-delete-connection-repl cider-connection-name conn-buffer) (when (buffer-live-p buffer) (with-current-buffer buffer (when spinner-current (spinner-stop)) @@ -135,48 +165,41 @@ Also close associated REPL and server buffers." (when nrepl-messages-buffer (kill-buffer nrepl-messages-buffer))))) - -;;; Current connection logic -(defvar-local cider-repl-type nil - "The type of this REPL buffer, usually either \"clj\" or \"cljs\".") - -(defun cider-find-connection-buffer-for-project-directory (&optional project-directory all-connections) - "Return the most appropriate connection-buffer for the current project. - -By order of preference, this is any connection whose directory matches -`clojure-project-dir', followed by any connection whose directory is nil, -followed by any connection at all. - -If PROJECT-DIRECTORY is provided act on that project instead. - -Only return nil if variable `cider-connections' is empty, -i.e there are no connections. - -If more than one connection satisfy a given level of preference, return the -connection buffer closer to the start of variable `cider-connections'. This is -usally the connection that was more recently created, but the order can be -changed. For instance, the function `cider-make-connection-default' can be -used to move a connection to the head of the list, so that it will take -precedence over other connections associated with the same project. +(defun cider--quit-connection (conn) + "Quit the connection CONN." + (when conn + (mapcar #'cider--close-connection-buffer + (cdr (cider-get-connection conn))))) + +(defun cider--restart-connection-buffer (buff) + "Restart connection's buffer BUFF." + (let ((project-dir (with-current-buffer buff nrepl-project-dir)) + (buf-name (buffer-name conn)) + ;; save these variables before we kill the connection + (conn-creation-method (with-current-buffer buff cider-connection-created-with)) + (conn-endpoint (with-current-buffer buff nrepl-endpoint))) + (cider--close-connection-buffer buff) + ;; Workaround for a nasty race condition https://github.com/clojure-emacs/cider/issues/439 + ;; TODO: Find a better way to ensure `cider-quit' has finished + (message "Waiting for CIDER connection %s to quit..." + (cider-propertize buf-name 'bold)) + (sleep-for 2) + (pcase conn-creation-method + (`connect (apply #'cider-connect conn-endpoint)) + (`jack-in (if project-dir + (let ((default-directory project-dir)) + (cider-jack-in)) + (error "Can't restart CIDER connection for unknown project"))) + (_ (error "Unexpected value %S for `cider-connection-created-with'" + conn-creation-method))))) + +(defun cider--restart-connection (conn) + "Restart the connection CONN." + (mapcar #'cider--restart-connection-buffer + (cdr (cider-get-connection conn)))) -If ALL-CONNECTIONS is non-nil, the return value is a list and all matching -connections are returned, instead of just the most recent." - (when-let ((project-directory (or project-directory - (clojure-project-dir (cider-current-dir)))) - (fn (if all-connections #'seq-filter #'seq-find))) - (or (funcall fn (lambda (conn) - (when-let ((conn-proj-dir (with-current-buffer conn - nrepl-project-dir))) - (equal (file-truename project-directory) - (file-truename conn-proj-dir)))) - cider-connections) - (funcall fn (lambda (conn) - (with-current-buffer conn - (not nrepl-project-dir))) - cider-connections) - (if all-connections - cider-connections - (car cider-connections))))) + +;;; Connection Accessors (defun cider-connection-type-for-buffer () "Return the matching connection type (clj or cljs) for the current buffer. @@ -190,211 +213,169 @@ list of types of actual connections within a project." ((derived-mode-p 'clojure-mode) "clj") (cider-repl-type))) -(defun cider-project-connections-types () - "Return a list of types of connections within current project." - (let ((connections (cider-find-connection-buffer-for-project-directory nil :all-connections))) - (seq-uniq (seq-map #'cider--connection-type connections)))) - (defun cider-read-connection (prompt) "Completing read for connections using PROMPT." - (get-buffer (completing-read prompt (mapcar #'buffer-name (cider-connections))))) + (completing-read prompt (mapcar #'car cider-connection-alist))) -(defun cider-assoc-project-with-connection (&optional project connection) - "Associate a Clojure PROJECT with an nREPL CONNECTION. - -Useful for connections created using `cider-connect', as for them -such a link cannot be established automatically." - (interactive) - (cider-ensure-connected) - (let ((conn-buf (or connection (cider-read-connection "Connection: "))) - (project-dir (or project (read-directory-name "Project directory: " (clojure-project-dir))))) - (when conn-buf - (with-current-buffer conn-buf - (setq nrepl-project-dir project-dir))))) +(defun cider-project-connections (&optional project-directory) + "Return a list of connections for PROJECT-DIRECTORY. +When nil, PROJECT-DIRECTORY defaults to the current Clojure project +directory. When not in a project, return nil." + (when-let ((project-directory (or project-directory + (clojure-project-dir (cider-current-dir))))) + (let ((project-directory (file-truename project-directory))) + (seq-filter (lambda (conn) + (when-let ((buff (cadr conn))) + (when-let ((conn-proj-dir (with-current-buffer buff + nrepl-project-dir))) + (equal project-directory (file-truename conn-proj-dir))))) + cider-connection-alist)))) -(defun cider-assoc-buffer-with-connection () - "Associate the current buffer with a connection. +(defun cider-project-connections-repls (&optional project-directory) + "Return all REPL buffers for PROJECT-DIRECTORY." + (apply #'append (mapcar #'cdr (cider-project-connections project-directory)))) -Useful for connections created using `cider-connect', as for them -such a link cannot be established automatically." - (interactive) - (cider-ensure-connected) - (let ((conn (cider-read-connection "Connection: "))) +(defun cider-project-connections-types (&optional project-directory) + "Return a list of types of connections within current project." + (let ((buffers (cider-project-connections-repls project-directory))) + (seq-uniq (seq-map #'cider--connection-type buffers)))) + +(defun cider--connected-dir (&optional dir) + "Return connected sub-directory of DIR." + (let ((dir (file-truename (or dir default-directory))) + (match "") + (conn nil)) + (maphash (lambda (k v) + (when (and (> (length k) (length match)) + (<= (length k) (length dir)) + (string= k (substring dir 0 (length k)))) + (setq match k) + (setq conn v))) + cider-connected-directories) (when conn - (setq-local cider-connections (list conn))))) + (cons match conn)))) + +(defun cider-current-connection (&optional ask-user) + "Get current connection. +Ask the user for association in ambiguous cases." + (when-let ((connections (cider-connections))) + (or + ;; 1) Buffer Local Association + (and cider-connection-name + (cider-get-connection cider-connection-name)) + + ;; 2) Directory Associations + (when-let ((dirconn (cider--connected-dir))) + (cider-get-connection (cdr dirconn))) + + ;; 3) Current Project + ;; when-let* is broken in emacs 24.5 + (when-let ((buffs (cider-project-connections-repls))) + (when-let ((most-recent (seq-find (lambda (b) (member b buffs)) (buffer-list)))) + (let ((conn-name (buffer-local-value 'cider-connection-name most-recent))) + (cider-get-connection conn-name)))) + + ;; 4) Ask User for Association + (when ask-user + (progn + (cider-assoc-with-connection) + (cider-current-connection)))))) -(defun cider-toggle-buffer-connection () - "Toggle the current buffer's connection between Clojure and ClojureScript." - (interactive) - (cider-ensure-connected) - (let ((other-conn (cider-other-connection))) - (if other-conn - (setq-local cider-connections (list other-conn)) - (user-error "No other connection available")))) +(defun cider-current-connection-repls (&optional type) + "Get all REPL buffers of the current connection. +If TYPE is non-nil, return only REPL buffers of TYPE. TYPE can be either +\"clj\" or \"cljs\"." + (let ((conn (cider-current-connection)) + (right-type-p (lambda (b) + (and (buffer-live-p b) + (equal (cider--connection-type b) type))))) + (if type + (seq-filter right-type-p (cdr conn)) + (cdr conn)))) + +(defun cider-current-connection-repl (&optional type) + "Return the most recent REPL buffer from current connection. +TYPE is either \"clj\" or \"cljs\". When nil, infer the REPL from current +buffer type. When multiple REPLs match the criteria, return the most +recently used one." + (let* ((type (or type + (let ((inferred-type (cider-connection-type-for-buffer))) + (unless (string= inferred-type "multi") + inferred-type)))) + (buffs (cider-current-connection-repls type))) + (seq-find (lambda (b) (member b buffs)) (buffer-list)))) + +(defun cider-other-repl (&optional repl) + "Return the first connection of another type than CONNECTION." + (when-let ((repl (or repl (cider-current-connection-repl))) + (type (cider--connection-type repl))) + ;; FIXME: this logic beaks with more than 2 REPLs per connection + (with-current-buffer repl + (cider-current-connection-repl + (if (equal type "clj") "cljs" "clj"))))) -(defun cider-clear-buffer-local-connection () - "Remove association between the current buffer and a connection." - (interactive) - (cider-ensure-connected) - (kill-local-variable 'cider-connections)) +(defun cider-repl-buffers () + "Return the list of REPL buffers." + (seq-filter + (lambda (buffer) + (with-current-buffer buffer (derived-mode-p 'cider-repl-mode))) + (buffer-list))) -(defun cider-toggle-request-dispatch () - "Toggle the value of `cider-request-dispatch' between static and dynamic. + +;;; Connection Associations + +(defun cider-assoc-buffer-with-connection (&optional buffer connection) + "Associate the current buffer with CONNECTION. +Useful for connections created using `cider-connect', as for them such a +link cannot be established automatically." + (interactive) + (when-let ((connection (or connection (cider-read-connection + (format "Assoc \"%s\" with connection: " + (buffer-name buffer)))))) + (setq cider-connection-name connection))) -Handy when you're using dynamic dispatch, but you want to quickly force all -evaluation commands to use a particular connection." +(defun cider-assoc-directory-with-connection (&optional dir connection) + "Associate a DIR with CONNECTION." + (interactive) + (let* ((dir (or dir (read-directory-name "Directory: " default-directory))) + (prompt (format "Assoc dir \"%s\" with connection: " + (file-name-nondirectory (directory-file-name dir)))) + (connection (or connection (cider-read-connection prompt)))) + (puthash (file-truename dir) connection cider-connected-directories))) + +(defun cider-assoc-project-with-connection (&optional project-dir connection) + "Associate a Clojure PROJECT-DIR with CONNECTION." + (interactive) + (let* ((project-dir (or project-dir (read-directory-name "Project directory: " (clojure-project-dir)))) + (prompt (format "Assoc project \"%s\" with connection: " + (file-name-nondirectory (directory-file-name project-dir)))) + (connection (or connection (cider-read-connection prompt)))) + (puthash (file-truename project-dir) connection cider-connected-directories))) + +(defun cider-assoc-with-connection (&optional connection) + "Contextually associate buffer, dir or project with CONNECTION. +This is a user friendly command aimed at the most common patterns. When +current file is part of the project assoc connection with the project. +Else, if current buffer has a physical file, associate with current +directory. Else, associate with current buffer." (interactive) - (let ((new-value (if (eq cider-request-dispatch 'static) 'dynamic 'static))) - (setq cider-request-dispatch new-value) - (message "Toggled CIDER request dispatch to %s." new-value))) - -(defun cider-current-connection (&optional type) - "Return the REPL buffer relevant for the current Clojure source buffer. -A REPL is relevant if its `nrepl-project-dir' is compatible with the -current directory (see `cider-find-connection-buffer-for-project-directory'). - -When there are multiple relevant connections of the same TYPE, return the -most recently used one. - -If TYPE is provided, it is either \"clj\" or \"cljs\", and only a -connection of that type is returned. If no connections of that TYPE exist, -return nil. - -If TYPE is nil, then connections whose type matches the current file -extension are given preference, but if none exist, any connection is -returned. In this case, only return nil if there are no active connections -at all." - ;; If TYPE was specified, we only return that type (or nil). OW, we prefer - ;; that TYPE, but ultimately allow any type. - (cl-labels ((right-type-p (c type) - (when (or (not type) - (equal type "multi") - (and (buffer-live-p c) - (equal (cider--connection-type c) type))) - c)) - (most-recent-buf (connections type) - (when connections - (seq-find (lambda (c) - (and (member c connections) - (right-type-p c type))) - (buffer-list))))) - (let ((connections (cider-connections))) - (cond - ((not connections) nil) - ;; if you're in a REPL buffer, it's the connection buffer - ((and (derived-mode-p 'cider-repl-mode) (right-type-p (current-buffer) type))) - ((eq cider-request-dispatch 'static) (car connections)) - ((= 1 (length connections)) (right-type-p (car connections) type)) - (t (let ((project-connections (cider-find-connection-buffer-for-project-directory - nil :all-connections)) - (guessed-type (or type (cider-connection-type-for-buffer)))) - (or - ;; cljc - (and (equal guessed-type "multi") - (most-recent-buf project-connections nil)) - ;; clj or cljs - (and guessed-type - (or (most-recent-buf project-connections guessed-type) - (most-recent-buf connections guessed-type))) - ;; when type was not specified or guessed - (most-recent-buf project-connections type) - (most-recent-buf connections type)))))))) - -(defun cider-other-connection (&optional connection) - "Return the first connection of another type than CONNECTION. -Only return connections in the same project or nil. -CONNECTION defaults to `cider-current-connection'." - (when-let ((connection (or connection (cider-current-connection))) - (connection-type (cider--connection-type connection))) - (cider-current-connection (pcase connection-type - (`"clj" "cljs") - (_ "clj"))))) - -(defvar cider--has-warned-about-bad-repl-type nil) - -(defun cider--guess-cljs-connection () - "Hacky way to find a ClojureScript REPL. -DO NOT USE THIS FUNCTION. -It was written only to be used in `cider-map-connections', as a workaround -to a still-undetermined bug in the state-stracker backend." - (when-let ((project-connections (cider-find-connection-buffer-for-project-directory - nil :all-connections)) - (cljs-conn - ;; So we have multiple connections. Look for the connection type we - ;; want, prioritizing the current project. - (or (seq-find (lambda (c) (string-match "\\bCLJS\\b" (buffer-name c))) - project-connections) - (seq-find (lambda (c) (string-match "\\bCLJS\\b" (buffer-name c))) - (cider-connections))))) - (unless cider--has-warned-about-bad-repl-type - (setq cider--has-warned-about-bad-repl-type t) - (read-key - (concat "The ClojureScript REPL seems to be is misbehaving." - (substitute-command-keys - "\nWe have applied a workaround, but please also file a bug report with `\\[cider-report-bug]'.") - "\nPress any key to continue."))) - cljs-conn)) - -(defun cider-map-connections (function which &optional any-mode) - "Call FUNCTION once for each appropriate connection. -The function is called with one argument, the connection buffer. -The appropriate connections are found by inspecting the current buffer. If -the buffer is associated with a .cljc or .cljx file, BODY will be executed -multiple times. - -WHICH is one of the following keywords identifying which connections to map -over. - :any - Act the connection whose type matches the current buffer. - :clj - Like :any, but signal a `user-error' in `clojurescript-mode' or if - there is no Clojure connection (use this for commands only - supported in Clojure). - :cljs - Like :clj, but demands a ClojureScript connection instead. - :both - In `clojurec-mode' or `clojurex-mode' act on both connections, - otherwise function like :any. Obviously, this option might run - FUNCTION twice. - -If ANY-MODE is non-nil, :clj and :cljs don't signal errors due to being in -the wrong major mode (they still signal if the desired connection type -doesn't exist). Use this for commands that only apply to a specific -connection but can be invoked from any buffer (like `cider-refresh')." - (cl-labels ((err (msg) (user-error (concat "`%s' " msg) this-command))) - ;; :both in a clj or cljs buffer just means :any. - (let* ((which (if (and (eq which :both) - (not (cider--cljc-or-cljx-buffer-p))) - :any - which)) - (curr - (pcase which - (`:any (let ((type (cider-connection-type-for-buffer))) - (or (cider-current-connection type) - (when (equal type "cljs") - (cider--guess-cljs-connection)) - (err (substitute-command-keys - (format "needs a Clojure%s REPL.\nIf you don't know what that means, you probably need to jack-in (%s)." - (if (equal type "cljs") "Script" "") - (if (equal type "cljs") "`\\[cider-jack-in-clojurescript]'" "`\\[cider-jack-in]'"))))))) - (`:both (or (cider-current-connection) - (err "needs an active REPL connection"))) - (`:clj (cond ((and (not any-mode) - (derived-mode-p 'clojurescript-mode)) - (err "doesn't support ClojureScript")) - ((cider-current-connection "clj")) - ((err "needs a Clojure REPL")))) - (`:cljs (cond ((and (not any-mode) - (eq major-mode 'clojure-mode)) - (err "doesn't support Clojure")) - ((cider-current-connection "cljs")) - ((err "needs a ClojureScript REPL"))))))) - (funcall function curr) - (when (eq which :both) - (when-let ((other-connection (cider-other-connection curr))) - (funcall function other-connection)))))) + (if cider-connection-name + (cider-assoc-buffer-with-connection) + (if-let ((project-dir (clojure-project-dir (cider-current-dir)))) + (if-let ((dir (car (cider--connected-dir)))) + (if (not (string= dir project-dir)) + (cider-assoc-directory-with-connection default-directory) + (cider-assoc-project-with-connection project-dir)) + (cider-assoc-project-with-connection project-dir)) + (if (buffer-file-name) + (cider-assoc-directory-with-connection default-directory) + (cider-assoc-buffer-with-connection))))) ;;; Connection Browser + (defvar cider-connections-buffer-mode-map (let ((map (make-sparse-keymap))) - (define-key map "d" #'cider-connections-make-default) (define-key map "g" #'cider-connection-browser) (define-key map "k" #'cider-connections-close-connection) (define-key map (kbd "RET") #'cider-connections-goto-connection) @@ -437,7 +418,7 @@ The connections buffer is determined by "Refresh the connections BUFFER." (cider--update-connections-display (buffer-local-value 'cider--connection-ewoc buffer) - cider-connections)) + (cider-repl-buffers))) (defun cider--setup-connection-browser () "Create a browser buffer for nREPL connections." @@ -446,7 +427,7 @@ The connections buffer is determined by 'cider--connection-pp " REPL Host Port Project Type\n"))) (setq-local cider--connection-ewoc ewoc) - (cider--update-connections-display ewoc cider-connections) + (cider--update-connections-display ewoc (cider-repl-buffers)) (setq buffer-read-only t) (cider-connections-buffer-mode) (display-buffer (current-buffer))))) @@ -473,8 +454,7 @@ TYPE can be any of the possible values of `cider-repl-type'." (repl-type (cider-client-name-repl-type (buffer-local-value 'cider-repl-type buffer))) (endpoint (buffer-local-value 'nrepl-endpoint buffer))) (insert - (format "%s %-30s %-16s %5s %-16s %s" - (if (equal connection (car cider-connections)) "*" " ") + (format " %-30s %-16s %5s %-16s %s" (buffer-name connection) (car endpoint) (prin1-to-string (cadr endpoint)) @@ -498,19 +478,6 @@ F is a function of two arguments, the ewoc and the data at point." (when node (funcall f ewoc (ewoc-data node))))) -(defun cider-connections-make-default () - "Make default the connection at point in the connection browser." - (interactive) - (save-excursion - (cider--ewoc-apply-at-point #'cider--connections-make-default))) - -(defun cider--connections-make-default (ewoc data) - "Make the connection in EWOC specified by DATA default. -Refreshes EWOC." - (interactive) - (cider-make-connection-default data) - (ewoc-refresh ewoc)) - (defun cider-connections-close-connection () "Close connection at point in the connection browser." (interactive) @@ -519,7 +486,7 @@ Refreshes EWOC." (defun cider--connections-close-connection (ewoc data) "Close the connection in EWOC specified by DATA." (cider--close-connection-buffer (get-buffer data)) - (cider--update-connections-display ewoc cider-connections)) + (cider--update-connections-display ewoc (cider-repl-buffers))) (defun cider-connections-goto-connection () "Goto connection at point in the connection browser." @@ -604,34 +571,27 @@ EVAL-BUFFER is the buffer where the spinner was started." (defvar-local cider-buffer-ns nil "Current Clojure namespace of some buffer. - -Useful for special buffers (e.g. REPL, doc buffers) that have to -keep track of a namespace. - -This should never be set in Clojure buffers, as there the namespace -should be extracted from the buffer's ns form.") +Useful for special buffers (e.g. REPL, doc buffers) that have to keep track +of a namespace. This should never be set in Clojure buffers, as there the +namespace should be extracted from the buffer's ns form.") (defun cider-current-ns (&optional no-default) "Return the current ns. The ns is extracted from the ns form for Clojure buffers and from `cider-buffer-ns' for all other buffers. If it's missing, use the current -REPL's ns, otherwise fall back to \"user\". - -When NO-DEFAULT is non-nil, it will return nil instead of \"user\"." +REPL's ns, otherwise fall back to \"user\". When NO-DEFAULT is non-nil, it +will return nil instead of \"user\"." (or cider-buffer-ns (clojure-find-ns) - (when-let ((repl-buf (cider-current-connection))) + (when-let ((repl-buf (cider-current-connection-repl))) (buffer-local-value 'cider-buffer-ns repl-buf)) (if no-default nil "user"))) (defun cider-expected-ns (&optional path) "Return the namespace string matching PATH, or nil if not found. - -PATH is expected to be an absolute file path. -If PATH is nil, use the path to the file backing the current buffer. - -The command falls back to `clojure-expected-ns' in the absence of an -active nREPL connection." +PATH is expected to be an absolute file path. If PATH is nil, use the path +to the file backing the current buffer. The command falls back to +`clojure-expected-ns' in the absence of an active connection." (if (cider-connected-p) (let* ((path (or path (file-truename (buffer-file-name)))) (relpath (thread-last (cider-sync-request:classpath) @@ -653,7 +613,8 @@ active nREPL connection." (defun cider-nrepl-op-supported-p (op) "Check whether the current connection supports the nREPL middleware OP." - (nrepl-op-supported-p op (cider-current-connection))) + (when-let ((buff (cider-current-connection-repl))) + (nrepl-op-supported-p op buff))) (defvar cider-version) (defun cider-ensure-op-supported (op) @@ -665,12 +626,9 @@ Signal an error if it is not supported." (defun cider-nrepl-send-request (request callback &optional connection) "Send REQUEST and register response handler CALLBACK. REQUEST is a pair list of the form (\"op\" \"operation\" \"par1-name\" -\"par1\" ... ). -If CONNECTION is provided dispatch to that connection instead of -the current connection. - -Return the id of the sent message." - (nrepl-send-request request callback (or connection (cider-current-connection)))) +\"par1\" ... ). If CONNECTION is provided dispatch to that connection +instead of the current connection. Return the id of the sent message." + (nrepl-send-request request callback (or connection (cider-current-connection-repl)))) (defun cider-nrepl-send-sync-request (request &optional connection abort-on-input) "Send REQUEST to the nREPL server synchronously using CONNECTION. @@ -680,14 +638,14 @@ 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." (nrepl-send-sync-request request - (or connection (cider-current-connection)) + (or connection (cider-current-connection-repl)) abort-on-input)) (defun cider-nrepl-send-unhandled-request (request) "Send REQUEST to the nREPL server and ignore any responses. Immediately mark the REQUEST as done. Return the id of the sent message." - (let* ((conn (cider-current-connection)) + (let* ((conn (cider-current-connection-repl)) (id (nrepl-send-request request #'ignore conn))) (with-current-buffer conn (nrepl--mark-id-completed id)) @@ -698,8 +656,8 @@ Return the id of the sent message." If NS is non-nil, include it in the request. LINE and COLUMN, if non-nil, define the position of INPUT in its buffer. ADDITIONAL-PARAMS is a plist to be appended to the request message. CONNECTION is the connection -buffer, defaults to (cider-current-connection)." - (let ((connection (or connection (cider-current-connection)))) +buffer, defaults to (cider-current-connection-repl)." + (let ((connection (or connection (cider-current-connection-repl)))) (nrepl-request:eval input (if cider-show-eval-spinner (cider-eval-spinner-handler connection callback) @@ -712,7 +670,7 @@ buffer, defaults to (cider-current-connection)." "Send the INPUT to the nREPL CONNECTION synchronously. If NS is non-nil, include it in the eval request." (nrepl-sync-request:eval input - (or connection (cider-current-connection)) + (or connection (cider-current-connection-repl)) ns)) (defcustom cider-pprint-fn 'pprint @@ -765,29 +723,25 @@ NS specifies the namespace in which to evaluate the request." ;; namespace forms are always evaluated in the "user" namespace (nrepl-request:eval input callback - (cider-current-connection) + (cider-current-connection-repl) ns nil nil nil t ; tooling )) -(defalias 'cider-current-repl-buffer #'cider-current-connection - "The current REPL buffer. -Return the REPL buffer given by `cider-current-connection'.") - (declare-function cider-interrupt-handler "cider-interaction") (defun cider-interrupt () "Interrupt any pending evaluations." (interactive) - (with-current-buffer (cider-current-connection) + (with-current-buffer (cider-current-connection-repl) (let ((pending-request-ids (cider-util--hash-keys nrepl-pending-requests))) (dolist (request-id pending-request-ids) (nrepl-request:interrupt request-id (cider-interrupt-handler (current-buffer)) - (cider-current-connection)))))) + (cider-current-connection-repl)))))) (defun cider-current-session () "Return the eval nREPL session id of the current connection." - (cider-session-for-connection (cider-current-connection))) + (cider-session-for-connection (cider-current-connection-repl))) (defun cider-session-for-connection (connection) "Create a CIDER session for CONNECTION." @@ -796,11 +750,11 @@ Return the REPL buffer given by `cider-current-connection'.") (defun cider-current-messages-buffer () "The nREPL messages buffer, matching the current connection." - (nrepl-messages-buffer (cider-current-connection))) + (nrepl-messages-buffer (cider-current-connection-repl))) (defun cider-current-tooling-session () "Return the tooling nREPL session id of the current connection." - (with-current-buffer (cider-current-connection) + (with-current-buffer (cider-current-connection-repl) nrepl-tooling-session)) (defun cider--var-choice (var-info) @@ -874,7 +828,7 @@ thing at point." FILE-CONTENTS, FILE-PATH and FILE-NAME are details of the file to be loaded. -If CONNECTION is nil, use `cider-current-connection'. +If CONNECTION is nil, use `cider-current-connection-repl'. If CALLBACK is nil, use `cider-load-file-handler'." (cider-nrepl-send-request `("op" "load-file" "file" ,file-contents @@ -1072,7 +1026,7 @@ returned." ;;; Connection info (defun cider--java-version () "Retrieve the underlying connection's Java version." - (with-current-buffer (cider-current-connection "clj") + (with-current-buffer (cider-current-connection-repl "clj") (when nrepl-versions (thread-first nrepl-versions (nrepl-dict-get "java") @@ -1080,7 +1034,7 @@ returned." (defun cider--clojure-version () "Retrieve the underlying connection's Clojure version." - (with-current-buffer (cider-current-connection "clj") + (with-current-buffer (cider-current-connection-repl "clj") (when nrepl-versions (thread-first nrepl-versions (nrepl-dict-get "clojure") @@ -1088,7 +1042,7 @@ returned." (defun cider--nrepl-version () "Retrieve the underlying connection's nREPL version." - (with-current-buffer (cider-current-connection "clj") + (with-current-buffer (cider-current-connection-repl "clj") (when nrepl-versions (thread-first nrepl-versions (nrepl-dict-get "nrepl") @@ -1120,7 +1074,6 @@ endpoint and Clojure version." (defun cider--connection-type (conn-buffer) "Get CONN-BUFFER's type. - Return value matches `cider-repl-type'." (plist-get (cider--connection-properties conn-buffer) :type)) @@ -1136,25 +1089,10 @@ Return value matches `cider-repl-type'." "Get CONN-BUFFER's project dir." (plist-get (cider--connection-properties conn-buffer) :project-dir)) -(defun cider-display-connection-info (&optional show-default) - "Display information about the current connection. - -With a prefix argument SHOW-DEFAULT it will display info about the -default connection." - (interactive "P") - (message "%s" (cider--connection-info (if show-default - (cider-default-connection) - (cider-current-connection))))) - -(defun cider-rotate-default-connection () - "Rotate and display the default nREPL connection." +(defun cider-display-connection-info () + "Display information about the current connection." (interactive) - (cider-ensure-connected) - (setq cider-connections - (append (cdr cider-connections) - (list (car cider-connections)))) - (message "Default nREPL connection: %s" - (cider--connection-info (car cider-connections)))) + (message "%s" (cider--connection-info (cider-current-connection-repl)))) (defun cider-replicate-connection (&optional conn) "Establish a new connection based on an existing connection. @@ -1169,7 +1107,7 @@ If CONN is not provided the user will be prompted to select a connection." (defun cider-extract-designation-from-current-repl-buffer () "Extract the designation from the cider repl buffer name." - (let ((repl-buffer-name (buffer-name (cider-current-repl-buffer))) + (let ((repl-buffer-name (buffer-name (cider-current-connection-repl))) (template (split-string nrepl-repl-buffer-name-template "%s"))) (string-match (format "^%s\\(.*\\)%s" (regexp-quote (concat (car template) nrepl-buffer-name-separator)) @@ -1185,7 +1123,7 @@ Buffer names changed are cider-repl and nrepl-server." (cider-ensure-connected) (let ((new-repl-buffer-name (nrepl-format-buffer-name-template nrepl-repl-buffer-name-template designation))) - (with-current-buffer (cider-current-repl-buffer) + (with-current-buffer (cider-current-connection-repl) (rename-buffer new-repl-buffer-name) (when nrepl-server-buffer (let ((new-server-buffer-name (nrepl-format-buffer-name-template diff --git a/cider-debug.el b/cider-debug.el index 3bb9f784e..09328e20d 100644 --- a/cider-debug.el +++ b/cider-debug.el @@ -344,7 +344,7 @@ In order to work properly, this mode must be activated by ;; cider-nrepl has a chance to send the next message, and so that the user ;; doesn't accidentally hit `n' between two messages (thus editing the code). (when-let ((proc (unless nrepl-ongoing-sync-request - (get-buffer-process (cider-current-connection))))) + (get-buffer-process (cider-current-connection-repl))))) (accept-process-output proc 1)) (unless cider--debug-mode (setq buffer-read-only nil) diff --git a/cider-interaction.el b/cider-interaction.el index 909eb0ec9..6a0eae7d6 100644 --- a/cider-interaction.el +++ b/cider-interaction.el @@ -1040,10 +1040,11 @@ evaluation command. Honor `cider-auto-jump-to-error'." (defun cider-need-input (buffer) "Handle an need-input request from BUFFER." + ;; FIXME: breaks with cljc (with-current-buffer buffer (nrepl-request:stdin (concat (read-from-minibuffer "Stdin: ") "\n") (cider-stdin-handler buffer) - (cider-current-connection)))) + (cider-current-connection-repl)))) (defun cider-emit-into-color-buffer (buffer value) "Emit into color BUFFER the provided VALUE." @@ -1103,6 +1104,7 @@ ADDITIONAL-PARAMS is a plist to be appended to the request message. If `cider-interactive-eval-override' is a function, call it with the same arguments and only proceed with evaluation if it returns nil." + (cider-ensure-connected) (let ((form (or form (apply #'buffer-substring-no-properties bounds))) (start (car-safe bounds)) (end (car-safe (cdr-safe bounds)))) @@ -1111,21 +1113,18 @@ arguments and only proceed with evaluation if it returns nil." (unless (and cider-interactive-eval-override (functionp cider-interactive-eval-override) (funcall cider-interactive-eval-override form callback bounds)) - (cider-map-connections #'ignore :any) - (cider-map-connections - (lambda (connection) - (cider--prep-interactive-eval form connection) - (cider-nrepl-request:eval - form - (or callback (cider-interactive-eval-handler nil bounds)) - ;; always eval ns forms in the user namespace - ;; otherwise trying to eval ns form for the first time will produce an error - (if (cider-ns-form-p form) "user" (cider-current-ns)) - (when start (line-number-at-pos start)) - (when start (cider-column-number-at-pos start)) - additional-params - connection)) - :both)))) + (dolist (repl (cider-current-connection-repls)) + (cider--prep-interactive-eval form repl) + (cider-nrepl-request:eval + form + (or callback (cider-interactive-eval-handler nil bounds)) + ;; always eval ns forms in the user namespace + ;; otherwise trying to eval ns form for the first time will produce an error + (if (cider-ns-form-p form) "user" (cider-current-ns)) + (when start (line-number-at-pos start)) + (when start (cider-column-number-at-pos start)) + additional-params + repl))))) (defun cider-eval-region (start end) "Evaluate the region between START and END." @@ -1176,8 +1175,9 @@ With a prefix arg, LOC, insert before the form, otherwise afterwards." "Evaluate the expression preceding point and insert its result in the REPL. If invoked with a PREFIX argument, switch to the REPL buffer." (interactive "P") + ;; FIXME: handler doesn't consider cljc (cider-interactive-eval nil - (cider-insert-eval-handler (cider-current-connection)) + (cider-insert-eval-handler (cider-current-connection-repl)) (cider-last-sexp 'bounds)) (when prefix (cider-switch-to-repl-buffer))) @@ -1186,7 +1186,8 @@ If invoked with a PREFIX argument, switch to the REPL buffer." "Evaluate expr before point and insert its pretty-printed result in the REPL. If invoked with a PREFIX argument, switch to the REPL buffer." (interactive "P") - (let* ((conn-buffer (cider-current-connection))) + ;; FIME: breaks with cljc + (let* ((conn-buffer (cider-current-connection-repl))) (cider-interactive-eval nil (cider-insert-eval-handler conn-buffer) (cider-last-sexp 'bounds) @@ -1321,7 +1322,8 @@ passing arguments." If EVAL is non-nil the form will also be evaluated." (while (string-match "\\`[ \t\n\r]+\\|[ \t\n\r]+\\'" form) (setq form (replace-match "" t t form))) - (with-current-buffer (cider-current-connection) + ;; FIXME: breaks with cljc + (with-current-buffer (cider-current-connection-repl) (goto-char (point-max)) (let ((beg (point))) (insert form) @@ -1371,7 +1373,8 @@ See command `cider-mode'." (add-hook 'clojure-mode-hook #'cider-mode) (dolist (buffer (cider-util--clojure-buffers)) (with-current-buffer buffer - (cider-mode +1)))) + (when (cider-current-connection) + (cider-mode +1))))) (defun cider-disable-on-existing-clojure-buffers () "Disable command `cider-mode' on existing Clojure buffers." @@ -1427,20 +1430,18 @@ opposite of what that option dictates." "Toggle ns tracing. Defaults to the current ns. With prefix arg QUERY, prompts for a ns." (interactive "P") - (cider-map-connections - (lambda (conn) - (with-current-buffer conn - (cider-ensure-op-supported "toggle-trace-ns") - (let ((ns (if query - (completing-read "Toggle trace for ns: " - (cider-sync-request:ns-list)) - (cider-current-ns)))) - (let* ((trace-response (cider-sync-request:toggle-trace-ns ns)) - (ns-status (nrepl-dict-get trace-response "ns-status"))) - (pcase ns-status - ("not-found" (error "Namespace %s not found" (cider-propertize ns 'ns))) - (_ (message "Namespace %s %s" (cider-propertize ns 'ns) ns-status))))))) - :clj)) + (dolist (repl (cider-current-connection-repls "clj")) + (with-current-buffer repl + (cider-ensure-op-supported "toggle-trace-ns") + (let ((ns (if query + (completing-read "Toggle trace for ns: " + (cider-sync-request:ns-list)) + (cider-current-ns)))) + (let* ((trace-response (cider-sync-request:toggle-trace-ns ns)) + (ns-status (nrepl-dict-get trace-response "ns-status"))) + (pcase ns-status + ("not-found" (error "Namespace %s not found" (cider-propertize ns 'ns))) + (_ (message "Namespace %s %s" (cider-propertize ns 'ns) ns-status)))))))) (defun cider-undef () "Undefine a symbol from the current ns." @@ -1545,34 +1546,32 @@ refresh functions (defined in `cider-refresh-before-fn' and (let ((clear? (member mode '(clear 16))) (refresh-all? (member mode '(refresh-all 4))) (inhibit-refresh-fns (member mode '(inhibit-fns -1)))) - (cider-map-connections - (lambda (conn) - ;; Inside the lambda, so the buffer is not created if we error out. - (let ((log-buffer (or (get-buffer cider-refresh-log-buffer) - (cider-make-popup-buffer cider-refresh-log-buffer)))) - (when cider-refresh-show-log-buffer - (cider-popup-buffer-display log-buffer)) - (when inhibit-refresh-fns - (cider-emit-into-popup-buffer log-buffer - "inhibiting refresh functions\n" - nil - t)) - (when clear? - (cider-nrepl-send-sync-request '("op" "refresh-clear") conn)) - (cider-nrepl-send-request - (nconc `("op" ,(if refresh-all? "refresh-all" "refresh") - "print-length" ,cider-stacktrace-print-length - "print-level" ,cider-stacktrace-print-level) - (when (cider--pprint-fn) - `("pprint-fn" ,(cider--pprint-fn))) - (when (and (not inhibit-refresh-fns) cider-refresh-before-fn) - `("before" ,cider-refresh-before-fn)) - (when (and (not inhibit-refresh-fns) cider-refresh-after-fn) - `("after" ,cider-refresh-after-fn))) - (lambda (response) - (cider-refresh--handle-response response log-buffer)) - conn))) - :clj 'any-mode))) + (dolist (conn (cider-current-connection-repls "clj")) + ;; Inside the lambda, so the buffer is not created if we error out. + (let ((log-buffer (or (get-buffer cider-refresh-log-buffer) + (cider-make-popup-buffer cider-refresh-log-buffer)))) + (when cider-refresh-show-log-buffer + (cider-popup-buffer-display log-buffer)) + (when inhibit-refresh-fns + (cider-emit-into-popup-buffer log-buffer + "inhibiting refresh functions\n" + nil + t)) + (when clear? + (cider-nrepl-send-sync-request '("op" "refresh-clear") conn)) + (cider-nrepl-send-request + (nconc `("op" ,(if refresh-all? "refresh-all" "refresh") + "print-length" ,cider-stacktrace-print-length + "print-level" ,cider-stacktrace-print-level) + (when (cider--pprint-fn) + `("pprint-fn" ,(cider--pprint-fn))) + (when (and (not inhibit-refresh-fns) cider-refresh-before-fn) + `("before" ,cider-refresh-before-fn)) + (when (and (not inhibit-refresh-fns) cider-refresh-after-fn) + `("after" ,cider-refresh-after-fn))) + (lambda (response) + (cider-refresh--handle-response response log-buffer)) + conn))))) (defun cider-file-string (file) "Read the contents of a FILE and return as a string." @@ -1602,16 +1601,14 @@ ClojureScript REPL exists for the project, it is evaluated in both REPLs." (cider--quit-error-window) (let ((filename (buffer-file-name buffer)) (ns-form (cider-ns-form))) - (cider-map-connections - (lambda (connection) - (when ns-form - (cider-repl--cache-ns-form ns-form connection)) - (cider-request:load-file (cider-file-string filename) - (funcall cider-to-nrepl-filename-function - (cider--server-filename filename)) - (file-name-nondirectory filename) - connection)) - :both) + (dolist (repl (cider-current-connection-repls)) + (when ns-form + (cider-repl--cache-ns-form ns-form repl)) + (cider-request:load-file (cider-file-string filename) + (funcall cider-to-nrepl-filename-function + (cider--server-filename filename)) + (file-name-nondirectory filename) + repl)) (message "Loading %s..." filename)))) (defun cider-load-file (filename) @@ -1724,9 +1721,9 @@ START and END represent the region's boundaries." "Describe an nREPL session." (interactive) (cider-ensure-connected) - (let ((selected-session (completing-read "Describe nREPL session: " (nrepl-sessions (cider-current-connection))))) + (let ((selected-session (completing-read "Describe nREPL session: " (nrepl-sessions (cider-current-connection-repl))))) (when (and selected-session (not (equal selected-session ""))) - (let* ((session-info (nrepl-sync-request:describe (cider-current-connection))) + (let* ((session-info (nrepl-sync-request:describe (cider-current-connection-repl))) (ops (nrepl-dict-keys (nrepl-dict-get session-info "ops"))) (session-id (nrepl-dict-get session-info "session")) (session-type (cond @@ -1745,87 +1742,37 @@ START and END represent the region's boundaries." "Close an nREPL session for the current connection." (interactive) (cider-ensure-connected) - (nrepl-sync-request:close (cider-current-connection)) - (message "Closed nREPL session")) - -;;; quiting -(defun cider--close-buffer (buffer) - "Close the BUFFER and kill its associated process (if any)." - (when (buffer-live-p buffer) - (with-current-buffer buffer - (when-let ((proc (get-buffer-process buffer))) - (when (process-live-p proc) - (when (or (not nrepl-server-buffer) - ;; Sync request will hang if the server is dead. - (process-live-p (get-buffer-process nrepl-server-buffer))) - (when (or nrepl-session nrepl-tooling-session) - (nrepl-sync-request:close buffer))) - (when proc (delete-process proc))))) - (kill-buffer buffer))) - -(defun cider-close-ancillary-buffers () - "Close buffers that are shared across connections." - (interactive) - (dolist (buf-name cider-ancillary-buffers) - (when (get-buffer buf-name) - (kill-buffer buf-name)))) - -(defun cider--quit-connection (conn) - "Quit the connection CONN." - (when conn - (cider--close-connection-buffer conn))) + (nrepl-sync-request:close (cider-current-connection-repl)) + (message "Closed nREPL session")) (defun cider-quit (&optional quit-all) "Quit the currently active CIDER connection. - With a prefix argument QUIT-ALL the command will kill all connections and all ancillary CIDER buffers." (interactive "P") (cider-ensure-connected) (if (and quit-all (y-or-n-p "Are you sure you want to quit all CIDER connections? ")) (progn - (dolist (connection cider-connections) - (cider--quit-connection connection)) + (mapcar #'cider--quit-connection cider-connection-alist) (message "All active nREPL connections were closed")) - (let ((connection (cider-current-connection))) + (let ((conn (cider-current-connection))) (when (y-or-n-p (format "Are you sure you want to quit the current CIDER connection %s? " - (cider-propertize (buffer-name connection) 'bold))) - (cider--quit-connection connection)))) + (cider-propertize (car conn) 'bold))) + (cider--quit-connection conn)))) ;; if there are no more connections we can kill all ancillary buffers (unless (cider-connected-p) (cider-close-ancillary-buffers))) -(defun cider--restart-connection (conn) - "Restart the connection CONN." - (let ((project-dir (with-current-buffer conn nrepl-project-dir)) - (buf-name (buffer-name conn)) - ;; save these variables before we kill the connection - (conn-creation-method (with-current-buffer conn cider-connection-created-with)) - (conn-endpoint (with-current-buffer conn nrepl-endpoint))) - (cider--quit-connection conn) - ;; Workaround for a nasty race condition https://github.com/clojure-emacs/cider/issues/439 - ;; TODO: Find a better way to ensure `cider-quit' has finished - (message "Waiting for CIDER connection %s to quit..." - (cider-propertize buf-name 'bold)) - (sleep-for 2) - (pcase conn-creation-method - (`connect (apply #'cider-connect conn-endpoint)) - (`jack-in (if project-dir - (let ((default-directory project-dir)) - (cider-jack-in)) - (error "Can't restart CIDER connection for unknown project"))) - (_ (error "Unexpected value %S for `cider-connection-created-with'" - conn-creation-method))))) - (defun cider-restart (&optional restart-all) "Restart the currently active CIDER connection. If RESTART-ALL is t, then restarts all connections." (interactive "P") (cider-ensure-connected) (if restart-all - (dolist (conn cider-connections) + (dolist (conn cider-connection-alist) (cider--restart-connection conn)) - (cider--restart-connection (cider-current-connection)))) + (dolist (conn (cider-current-connection-repls)) + (cider--restart-connection conn)))) (defvar cider--namespace-history nil "History of user input for namespace prompts.") diff --git a/cider-mode.el b/cider-mode.el index 5d0c6115c..140592270 100644 --- a/cider-mode.el +++ b/cider-mode.el @@ -50,7 +50,7 @@ "Return info for the `cider-mode' modeline. Info contains project name and host:port endpoint." - (if-let ((current-connection (ignore-errors (cider-current-connection)))) + (if-let ((current-connection (ignore-errors (cider-current-connection-repl)))) (with-current-buffer current-connection (concat cider-repl-type @@ -137,8 +137,9 @@ the buffer should appear. With a prefix arg SET-NAMESPACE sets the namespace in the REPL buffer to that of the namespace in the Clojure source buffer." (interactive "P") - (let* ((connections (cider-connections)) - (buffer (seq-find (lambda (b) (member b connections)) + (cider-ensure-connected) + (let* ((repls (cider-current-connection-repls)) + (buffer (seq-find (lambda (b) (member b repls)) (buffer-list)))) (cider--switch-to-repl-buffer buffer set-namespace))) @@ -172,7 +173,7 @@ With a prefix argument CLEAR-REPL the command clears the entire REPL buffer. Returns to the buffer in which the command was invoked." (interactive "P") (let ((origin-buffer (current-buffer))) - (switch-to-buffer (cider-current-repl-buffer)) + (switch-to-buffer (cider-current-connection-repls)) (if clear-repl (cider-repl-clear-buffer) (cider-repl-clear-output)) @@ -186,8 +187,8 @@ Returns to the buffer in which the command was invoked." :help "Starts an nREPL server (with lein, boot, or maven) and connects a REPL to it."] ["Connect to a REPL" cider-connect :help "Connects to a REPL that's already running."] - ["Quit" cider-quit :active cider-connections] - ["Restart" cider-restart :active cider-connections] + ["Quit" cider-quit :active cider-connection-alist] + ["Restart" cider-restart :active cider-connection-alist] ("Clojurescript" ["Start a Clojure REPL, and a ClojureScript REPL" cider-jack-in-clojurescript :help "Starts an nREPL server, connects a Clojure REPL to it, and then a ClojureScript REPL. @@ -198,9 +199,11 @@ Configure `cider-cljs-*-repl' to change the ClojureScript REPL to use for your b ["Form for launching a ClojureScript REPL via Gradle" (customize-variable 'cider-cljs-gradle-repl)]) "--" ["Connection info" cider-display-connection-info - :active cider-connections] - ["Rotate default connection" cider-rotate-default-connection - :active (cdr cider-connections)] + :active cider-connection-alist] + ("Connection assoc" :active cider-connection-alist + ["Assoc buffer" cider-assoc-buffer-with-connection] + ["Assoc directory" cider-assoc-directory-with-connection] + ["Assoc project" cider-assoc-project-with-connection]) ["Select any CIDER buffer" cider-selector] "--" ["Configure CIDER" (customize-group 'cider)] @@ -213,14 +216,14 @@ Configure `cider-cljs-*-repl' to change the ClojureScript REPL to use for your b "--" ["Close ancillary buffers" cider-close-ancillary-buffers :active (seq-remove #'null cider-ancillary-buffers)] - ("nREPL" :active cider-connections + ("nREPL" :active cider-connection-alist ["Describe session" cider-describe-nrepl-session] ["Close session" cider-close-nrepl-session] ["Toggle message logging" nrepl-toggle-message-logging])) "Menu for CIDER mode.") (defconst cider-mode-eval-menu - '("CIDER Eval" :visible cider-connections + '("CIDER Eval" :visible cider-connection-alist ["Eval top-level sexp" cider-eval-defun-at-point] ["Eval current sexp" cider-eval-sexp-at-point] ["Eval last sexp" cider-eval-last-sexp] @@ -247,7 +250,7 @@ Configure `cider-cljs-*-repl' to change the ClojureScript REPL to use for your b "Menu for CIDER mode eval commands.") (defconst cider-mode-interactions-menu - `("CIDER Interactions" :visible cider-connections + `("CIDER Interactions" :visible cider-connection-alist ["Complete symbol" complete-symbol] "--" ("REPL" @@ -291,6 +294,19 @@ Configure `cider-cljs-*-repl' to change the ClojureScript REPL to use for your b ["Flush completion cache" cider-completion-flush-caches])) "Menu for CIDER interactions.") +(defconst cider-connection-map + (let ((map (define-prefix-command 'cider-connection-map))) + (define-key map (kbd "a") #'cider-assoc-with-connection) + (define-key map (kbd "C-a") #'cider-assoc-with-connection) + (define-key map (kbd "b") #'cider-assoc-buffer-with-connection) + (define-key map (kbd "C-b") #'cider-assoc-buffer-with-connection) + (define-key map (kbd "d") #'cider-assoc-directory-with-connection) + (define-key map (kbd "C-d") #'cider-assoc-directory-with-connection) + (define-key map (kbd "p") #'cider-assoc-project-with-connection) + (define-key map (kbd "C-p") #'cider-assoc-project-with-connection) + (define-key map (kbd "i") #'cider-display-connection-info) + (define-key map (kbd "C-i") #'cider-display-connection-info))) + (defconst cider-mode-map (let ((map (make-sparse-keymap))) (define-key map (kbd "C-c C-d") 'cider-doc-map) @@ -326,8 +342,8 @@ Configure `cider-cljs-*-repl' to change the ClojureScript REPL to use for your b (define-key map (kbd "C-c C-b") #'cider-interrupt) (define-key map (kbd "C-c ,") 'cider-test-commands-map) (define-key map (kbd "C-c C-t") 'cider-test-commands-map) + (define-key map (kbd "C-c C-a") 'cider-connection-map) (define-key map (kbd "C-c M-s") #'cider-selector) - (define-key map (kbd "C-c M-r") #'cider-rotate-default-connection) (define-key map (kbd "C-c M-d") #'cider-display-connection-info) (define-key map (kbd "C-c C-x") #'cider-refresh) (define-key map (kbd "C-c C-q") #'cider-quit) diff --git a/cider-repl.el b/cider-repl.el index a22e82920..eacbb421f 100644 --- a/cider-repl.el +++ b/cider-repl.el @@ -200,7 +200,7 @@ Currently its only purpose is to facilitate `cider-repl-clear-buffer'.") (defvar-local cider-repl-ns-cache nil "A dict holding information about all currently loaded namespaces. This cache is stored in the connection buffer. Other buffer's access it -via `cider-current-connection'.") +via `cider-current-connection-repl'.") (defvar cider-mode) (declare-function cider-refresh-dynamic-font-lock "cider-mode") @@ -228,31 +228,35 @@ via `cider-current-connection'.") (declare-function cider-default-err-handler "cider-interaction") -(defun cider-repl-create (endpoint) +(defun cider-repl-create (endpoint repl-buffer connection-name) "Create a REPL buffer and install `cider-repl-mode'. ENDPOINT is a plist as returned by `nrepl-connect'." ;; Connection might not have been set as yet. Please don't send requests here. - (let* ((reuse-buff (not (eq 'new nrepl-use-this-as-repl-buffer))) - (buff-name (nrepl-make-buffer-name nrepl-repl-buffer-name-template nil + (let* ((dup-ok (and repl-buffer + (not (eq 'new repl-buffer)))) + (buff-name (nrepl-make-buffer-name nrepl-repl-buffer-name-template + connection-name (plist-get endpoint :host) (plist-get endpoint :port) - reuse-buff))) + dup-ok))) ;; when reusing, rename the buffer accordingly - (when (and reuse-buff - (not (equal buff-name nrepl-use-this-as-repl-buffer))) + (when (and dup-ok + (not (equal buff-name (buffer-name repl-buffer)))) ;; uniquify as it might be Nth connection to the same endpoint (setq buff-name (generate-new-buffer-name buff-name)) - (with-current-buffer nrepl-use-this-as-repl-buffer + (with-current-buffer repl-buffer (rename-buffer buff-name))) (with-current-buffer (get-buffer-create buff-name) (unless (derived-mode-p 'cider-repl-mode) (cider-repl-mode) (setq cider-repl-type "clj")) - (setq nrepl-err-handler #'cider-default-err-handler) + (setq cider-connection-name connection-name + nrepl-err-handler #'cider-default-err-handler) (cider-repl-reset-markers) (add-hook 'nrepl-response-handler-functions #'cider-repl--state-handler nil 'local) (add-hook 'nrepl-connected-hook 'cider--connected-handler nil 'local) (add-hook 'nrepl-disconnected-hook 'cider--disconnected-handler nil 'local) + (cider-add-connection-repl connection-name (current-buffer)) (current-buffer)))) (defun cider-repl-require-repl-utils () @@ -631,7 +635,7 @@ If BOL is non-nil insert at the beginning of line." (defun cider-repl--emit-interactive-output (string face) "Emit STRING as interactive output using FACE." - (with-current-buffer (cider-current-repl-buffer) + (with-current-buffer (cider-current-connection-repl) (let ((pos (cider-repl--end-of-line-before-input-start)) (string (replace-regexp-in-string "\n\\'" "" string))) (cider-repl--emit-output-at-pos (current-buffer) string face pos t)))) @@ -871,7 +875,7 @@ text property `cider-old-input'." (defun cider-repl-switch-to-other () "Switch between the Clojure and ClojureScript REPLs for the current project." (interactive) - (if-let (other-connection (cider-other-connection)) + (if-let (other-connection (cider-other-repl)) (switch-to-buffer other-connection) (message "There's no other REPL for the current project"))) @@ -968,12 +972,10 @@ With a prefix argument CLEAR-REPL it will clear the entire REPL buffer instead." (defun cider-repl-set-ns (ns) "Switch the namespace of the REPL buffer to NS. - If called from a cljc or cljx buffer act on both the Clojure and -ClojureScript REPL if there are more than one REPL present. - -If invoked in a REPL buffer the command will prompt for the name of the -namespace to switch to." +ClojureScript REPL if there are more than one REPL present. If invoked in a +REPL buffer the command will prompt for the name of the namespace to switch +to." (interactive (list (if (or (derived-mode-p 'cider-repl-mode) (null (cider-ns-form))) (completing-read "Switch to namespace: " @@ -981,11 +983,9 @@ namespace to switch to." (cider-current-ns)))) (when (or (not ns) (equal ns "")) (user-error "No namespace selected")) - (cider-map-connections - (lambda (connection) - (cider-nrepl-request:eval (format "(in-ns '%s)" ns) - (cider-repl-switch-ns-handler connection))) - :both)) + (dolist (repl (cider-current-connection-repls)) + (cider-nrepl-request:eval (format "(in-ns '%s)" ns) + (cider-repl-switch-ns-handler repl)))) ;;; Location References @@ -1341,7 +1341,6 @@ constructs." (cider-repl-add-shortcut "test-report" #'cider-test-show-report) (cider-repl-add-shortcut "run" #'cider-run) (cider-repl-add-shortcut "conn-info" #'cider-display-connection-info) -(cider-repl-add-shortcut "conn-rotate" #'cider-rotate-default-connection) (cider-repl-add-shortcut "hasta la vista" #'cider-quit) (cider-repl-add-shortcut "adios" #'cider-quit) (cider-repl-add-shortcut "sayonara" #'cider-quit) diff --git a/cider-resolve.el b/cider-resolve.el index 6f9f8ebfc..7aade0614 100644 --- a/cider-resolve.el +++ b/cider-resolve.el @@ -72,8 +72,8 @@ (defun cider-resolve--get-in (&rest keys) "Return (nrepl-dict-get-in cider-repl-ns-cache KEYS)." - (when cider-connections - (with-current-buffer (cider-current-connection) + (when-let ((buff (cider-current-connection-repl))) + (with-current-buffer buff (nrepl-dict-get-in cider-repl-ns-cache keys)))) (defun cider-resolve-alias (ns alias) @@ -105,7 +105,7 @@ Return nil only if VAR cannot be resolved." "Return a dict of the core namespace for current connection. This will be clojure.core or cljs.core depending on `cider-repl-type'." (when (cider-connected-p) - (with-current-buffer (cider-current-connection) + (with-current-buffer (cider-current-connection-repl) (cider-resolve--get-in (if (equal cider-repl-type "cljs") "cljs.core" "clojure.core"))))) diff --git a/cider-scratch.el b/cider-scratch.el index 4e500847d..1c5ec5e29 100644 --- a/cider-scratch.el +++ b/cider-scratch.el @@ -46,9 +46,7 @@ "--" ["Reset" #'cider-scratch-reset] "--" - ["Set buffer connection" #'cider-assoc-buffer-with-connection] - ["Toggle buffer connection" #'cider-toggle-buffer-connection] - ["Reset buffer connection" #'cider-clear-buffer-local-connection]))) + ["Set buffer connection" #'cider-assoc-buffer-with-connection]))) map)) (defconst cider-scratch-buffer-name "*cider-scratch*") diff --git a/cider-selector.el b/cider-selector.el index ba85f7031..4335a7666 100644 --- a/cider-selector.el +++ b/cider-selector.el @@ -138,7 +138,7 @@ is chosen. The returned buffer is selected with (def-cider-selector-method ?r "Current REPL buffer." - (cider-current-repl-buffer)) + (cider-current-connection-repl)) (def-cider-selector-method ?n "Connections browser buffer." diff --git a/cider-test.el b/cider-test.el index a23a9bf88..ff76c2279 100644 --- a/cider-test.el +++ b/cider-test.el @@ -584,51 +584,49 @@ report is optionally displayed. When test failures/errors occur, their sources are highlighted. If SILENT is non-nil, suppress all messages other then test results." (cider-test-clear-highlights) - (cider-map-connections - (lambda (conn) - (unless silent - (if (and tests (= (length tests) 1)) - ;; we generate a different message when running individual tests - (cider-test-echo-running ns (car tests)) - (cider-test-echo-running ns))) - (cider-nrepl-send-request - `("op" ,(cond ((stringp ns) "test") - ((eq :project ns) "test-all") - ((eq :loaded ns) "test-all") - ((eq :non-passing ns) "retest")) - "ns" ,(when (stringp ns) ns) - "tests" ,(when (stringp ns) tests) - "load?" ,(when (or (stringp ns) - (eq :project ns)) - "true")) - (lambda (response) - (nrepl-dbind-response response (summary results status out err) - (cond ((member "namespace-not-found" status) - (unless silent - (message "No test namespace: %s" (cider-propertize ns 'ns)))) - (out (cider-emit-interactive-eval-output out)) - (err (cider-emit-interactive-eval-err-output err)) - (results - (nrepl-dbind-response summary (error fail) - (setq cider-test-last-summary summary) - (setq cider-test-last-results results) - (cider-test-highlight-problems results) - (cider-test-echo-summary summary results) - (if (or (not (zerop (+ error fail))) - cider-test-show-report-on-success) - (cider-test-render-report - (cider-popup-buffer cider-test-report-buffer - cider-auto-select-test-report-buffer) - summary results) - (when (get-buffer cider-test-report-buffer) - (with-current-buffer cider-test-report-buffer - (let ((inhibit-read-only t)) - (erase-buffer))) - (cider-test-render-report - cider-test-report-buffer - summary results)))))))) - conn)) - :clj)) + (dolist (repl (cider-current-connection-repls "clj")) + (unless silent + (if (and tests (= (length tests) 1)) + ;; we generate a different message when running individual tests + (cider-test-echo-running ns (car tests)) + (cider-test-echo-running ns))) + (cider-nrepl-send-request + `("op" ,(cond ((stringp ns) "test") + ((eq :project ns) "test-all") + ((eq :loaded ns) "test-all") + ((eq :non-passing ns) "retest")) + "ns" ,(when (stringp ns) ns) + "tests" ,(when (stringp ns) tests) + "load?" ,(when (or (stringp ns) + (eq :project ns)) + "true")) + (lambda (response) + (nrepl-dbind-response response (summary results status out err) + (cond ((member "namespace-not-found" status) + (unless silent + (message "No test namespace: %s" (cider-propertize ns 'ns)))) + (out (cider-emit-interactive-eval-output out)) + (err (cider-emit-interactive-eval-err-output err)) + (results + (nrepl-dbind-response summary (error fail) + (setq cider-test-last-summary summary) + (setq cider-test-last-results results) + (cider-test-highlight-problems results) + (cider-test-echo-summary summary results) + (if (or (not (zerop (+ error fail))) + cider-test-show-report-on-success) + (cider-test-render-report + (cider-popup-buffer cider-test-report-buffer + cider-auto-select-test-report-buffer) + summary results) + (when (get-buffer cider-test-report-buffer) + (with-current-buffer cider-test-report-buffer + (let ((inhibit-read-only t)) + (erase-buffer))) + (cider-test-render-report + cider-test-report-buffer + summary results)))))))) + repl))) (defun cider-test-rerun-failed-tests () "Rerun failed and erring tests from the last test run." diff --git a/cider.el b/cider.el index 1b21fd707..f18f1d311 100644 --- a/cider.el +++ b/cider.el @@ -521,13 +521,15 @@ it should start a ClojureScript REPL." "Create a ClojureScript REPL with the same server as CLIENT-BUFFER. The new buffer will correspond to the same project as CLIENT-BUFFER, which should be the regular Clojure REPL started by the server process filter." - (interactive (list (cider-current-connection))) + (interactive (list (cider-current-connection-repl))) ;; Load variables in .dir-locals.el into the server process buffer, so ;; cider-cljs-*-repl can be set for each project individually. (hack-local-variables) (let* ((nrepl-repl-buffer-name-template "*cider-repl CLJS%s*") - (nrepl-create-client-buffer-function #'cider-repl-create) - (nrepl-use-this-as-repl-buffer 'new) + (conn-name (buffer-local-value 'cider-connection-name client-buffer)) + (nrepl-create-client-buffer-function + (lambda (endpoint) + (cider-repl-create endpoint 'new conn-name))) (client-process-args (with-current-buffer client-buffer (unless (or nrepl-server-buffer nrepl-endpoint) (error "This is not a REPL buffer, is there a REPL active?")) @@ -539,14 +541,8 @@ should be the regular Clojure REPL started by the server process filter." (cljs-buffer (process-buffer cljs-proc)) (cljs-repl-form (cider-cljs-repl-form (cider-project-type)))) (with-current-buffer cljs-buffer - ;; The new connection has now been bumped to the top, but it's still a - ;; Clojure REPL! Additionally, some ClojureScript REPLs can actually take - ;; a while to start (some even depend on the user opening a browser). - ;; Meanwhile, this REPL will gladly receive requests in place of the - ;; original Clojure REPL. Our solution is to bump the original REPL back - ;; up the list, so it takes priority on Clojure requests. - (cider-make-connection-default client-buffer) - (setq cider-repl-type "cljs") + (setq cider-repl-type "cljs" + cider-connection-name conn-name) (pcase (assoc cljs-repl-form cider--cljs-repl-types) (`(,_ ,name ,info) (message "Starting a %s REPL%s" name (or info ""))) @@ -615,6 +611,14 @@ own buffer." (read-directory-name "Project: "))) (project-dir (clojure-project-dir (or project (cider-current-dir)))) + (project-dir (cond + (project-dir project-dir) + ((eq cider-allow-jack-in-without-project t) default-directory) + ((eq cider-allow-jack-in-without-project 'warn) + (if (y-or-n-p "Are you sure you want to run `cider-jack-in' without a Clojure project? ") + default-directory + (signal 'quit nil))) + (user-error "`cider-jack-in' is not allowed without a Clojure project"))) (params (if prompt-project (read-string (format "nREPL server command: %s " command-params) @@ -623,22 +627,14 @@ own buffer." (params (if cider-inject-dependencies-at-jack-in (cider-inject-jack-in-dependencies command-global-opts params project-type) params)) - (cmd (format "%s %s" command-resolved params))) - (if (or project-dir cider-allow-jack-in-without-project) - (progn - (when (or project-dir - (eq cider-allow-jack-in-without-project t) - (and (null project-dir) - (eq cider-allow-jack-in-without-project 'warn) - (y-or-n-p "Are you sure you want to run `cider-jack-in' without a Clojure project? "))) - (when-let ((repl-buff (cider-find-reusable-repl-buffer nil project-dir))) - (let ((nrepl-create-client-buffer-function #'cider-repl-create) - (nrepl-use-this-as-repl-buffer repl-buff)) - (nrepl-start-server-process - project-dir cmd - (when cljs-too #'cider-create-sibling-cljs-repl)))))) - (user-error "`cider-jack-in' is not allowed without a Clojure project"))) + (when-let ((repl-buff (cider-find-reusable-repl-buffer nil project-dir))) + (let ((nrepl-create-client-buffer-function + (lambda (endpoint) + (cider-repl-create endpoint repl-buff + (cider-make-connection-name project-dir)))) + (callback (when cljs-too #'cider-create-sibling-cljs-repl))) + (nrepl-start-server-process project-dir cmd callback)))) (user-error "The %s executable isn't on your `exec-path'" command)))) ;;;###autoload @@ -652,15 +648,15 @@ start the server." ;;;###autoload (defun cider-connect (host port &optional project-dir) "Connect to an nREPL server identified by HOST and PORT. -Create REPL buffer and start an nREPL client connection. - -When the optional param PROJECT-DIR is present, the connection -gets associated with it." +Create REPL buffer and start an nREPL client connection. When the optional +param PROJECT-DIR is present, the connection gets associated with it." (interactive (cider-select-endpoint)) (setq cider-current-clojure-buffer (current-buffer)) (when-let ((repl-buff (cider-find-reusable-repl-buffer `(,host ,port) nil))) - (let* ((nrepl-create-client-buffer-function #'cider-repl-create) - (nrepl-use-this-as-repl-buffer repl-buff) + (let* ((nrepl-create-client-buffer-function + (lambda (endpoint) + (cider-repl-create endpoint repl-buff + (cider-make-connection-name project-dir)))) (conn (process-buffer (nrepl-start-client-process host port)))) (with-current-buffer conn (setq cider-connection-created-with 'connect)) @@ -669,9 +665,11 @@ gets associated with it." (let ((project-dir (clojure-project-dir))) (cond ;; associate only if we're in a project - ((and project-dir (null cider-prompt-for-project-on-connect)) (cider-assoc-project-with-connection project-dir conn)) + ((and project-dir (null cider-prompt-for-project-on-connect)) + (cider-assoc-project-with-connection project-dir conn)) ;; associate if we're in a project, prompt otherwise - ((eq cider-prompt-for-project-on-connect 'when-needed) (cider-assoc-project-with-connection project-dir conn)) + ((eq cider-prompt-for-project-on-connect 'when-needed) + (cider-assoc-project-with-connection project-dir conn)) ;; always prompt (t (cider-assoc-project-with-connection nil conn)))))))) @@ -804,7 +802,6 @@ choose." (car choices)) (t cider-default-repl-command)))) - ;; TODO: Implement a check for `cider-lein-command' over tramp (defun cider--lein-resolve-command () "Find `cider-lein-command' on `exec-path' if possible, or return nil. @@ -866,7 +863,7 @@ In case `default-directory' is non-local we assume the command is available." Retrieve the underlying connection's CIDER-nREPL version and checks if the middleware used is compatible with CIDER. If not, will display a warning message in the REPL area." - (let* ((version-dict (nrepl-aux-info "cider-version" (cider-current-connection))) + (let* ((version-dict (nrepl-aux-info "cider-version" (cider-current-connection-repl))) (middleware-version (nrepl-dict-get version-dict "version-string" "not installed"))) (unless (equal cider-version middleware-version) (cider-repl-manual-warning "troubleshooting/#cider-complains-of-the-cider-nrepl-version" @@ -883,11 +880,9 @@ message in the REPL area." This function is appended to `nrepl-connected-hook' in the client process buffer." ;; `nrepl-connected-hook' is run in the connection buffer - ;; `cider-enlighten-mode' changes eval to include the debugger, so we inhibit ;; it here as the debugger isn't necessarily initialized yet (let ((cider-enlighten-mode nil)) - (cider-make-connection-default (current-buffer)) (cider-repl-init (current-buffer)) (cider--check-required-nrepl-version) (cider--check-clojure-version-supported) @@ -911,7 +906,9 @@ process buffer." '(progn (define-key clojure-mode-map (kbd "C-c M-j") #'cider-jack-in) (define-key clojure-mode-map (kbd "C-c M-J") #'cider-jack-in-clojurescript) - (define-key clojure-mode-map (kbd "C-c M-c") #'cider-connect))) + (define-key clojure-mode-map (kbd "C-c M-c") #'cider-connect) + (define-key clojure-mode-map (kbd "C-c C-z") #'cider-switch-to-repl-buffer) + (define-key clojure-mode-map (kbd "C-c C-a") 'cider-connection-map))) (provide 'cider) diff --git a/doc/cider-refcard.tex b/doc/cider-refcard.tex index 7f09e249c..35eb52c68 100644 --- a/doc/cider-refcard.tex +++ b/doc/cider-refcard.tex @@ -126,7 +126,6 @@ \section{REPL control} \item[C-c C-z] cider-switch-to-repl-buffer \item[C-c M-o] cider-find-and-clear-repl-buffer \item[C-c M-d] cider-display-connection-info - \item[C-c M-r] cider-rotate-default-connection \item[C-c M-n] cider-repl-set-ns \item[C-c C-b] cider-interrupt \item[C-c C-x] cider-refresh diff --git a/doc/interactive_programming.md b/doc/interactive_programming.md index d9716e32a..3d71eb6a0 100644 --- a/doc/interactive_programming.md +++ b/doc/interactive_programming.md @@ -36,8 +36,7 @@ Here's a list of `cider-mode`'s keybindings: `cider-switch-to-repl-buffer` |C-c C-z | Switch to the relevant REPL buffer. Use a prefix argument to change the namespace of the REPL buffer to match the currently visited source file. `cider-switch-to-repl-buffer` |C-u C-u C-c C-z | Switch to the REPL buffer based on a user prompt for a directory. `cider-load-buffer-and-switch-to-repl-buffer` |C-c M-z | Load (eval) the current buffer and switch to the relevant REPL buffer. Use a prefix argument to change the namespace of the REPL buffer to match the currently visited source file. -`cider-display-connection-info` |C-c M-d | Display default REPL connection details, including project directory name, buffer namespace, host and port. -`cider-rotate-default-connection` |C-c M-r | Rotate and display the default nREPL connection. +`cider-display-connection-info` |C-c M-d | Display REPL connection details, including project directory name, buffer namespace, host and port. `cider-find-and-clear-repl-output` |C-c C-o | Clear the last output in the REPL buffer. With a prefix argument it will clear the entire REPL buffer, leaving only a prompt. Useful if you're running the REPL buffer in a side by side buffer. `cider-load-buffer` |C-c C-k | Load (eval) the current buffer. `cider-load-file` |C-c C-l | Load (eval) a Clojure file. diff --git a/doc/managing_connections.md b/doc/managing_connections.md index d425ad27b..0931c343b 100644 --- a/doc/managing_connections.md +++ b/doc/managing_connections.md @@ -8,22 +8,9 @@ You can connect to multiple nREPL servers using M-x `cider-jack-in` CIDER maintains a list of nREPL connections and a single 'default' connection. When you execute CIDER commands in a Clojure editing buffer such as to compile a namespace, these commands are executed against a specific -connection. This is controlled by the variable `cider-request-dispatch` - when -it's set to `'dynamic` (the default), CIDER will try to infer which connection -to use from the current project and currently visited file; when `'static` -dispatch is used all requests will always be routed to the default connection -(this was the default behavior in CIDER before 0.10). - -There's a handy command called `cider-toggle-request-dispatch`. You can use it -to quickly switch between dynamic and static request dispatch. A common use-case -for it would be to force temporary all evaluation commands to be using a -particular (the default) connection. - -You can display the current nREPL connection using C-c M-d -and rotate the default connection using C-c M-r. Another -option for setting the default connection is to execute the command -M-x `cider-make-connection-default` in the appropriate -REPL buffer. +connection. + +You can display info on current nREPL connection using C-c M-d. ## Connection browser @@ -32,7 +19,6 @@ You can obtain a list of all active connections using M-x Command |Keyboard shortcut | Description -------------------------------------|--------------------------------|------------------------------- -`cider-connections-make-default` |d | Make connection at point default. `cider-connections-close-connection` |k | Close connection at point. `cider-connection-browser` |g | Refresh connection browser. `cider-connections-goto-connection` |RET | Visit connection buffer. diff --git a/nrepl-client.el b/nrepl-client.el index 706e4719a..dabbd280e 100644 --- a/nrepl-client.el +++ b/nrepl-client.el @@ -142,10 +142,6 @@ When true some special buffers like the server buffer will be hidden." It is called with one argument, a plist containing :host, :port and :proc as returned by `nrepl-connect'.") -(defvar nrepl-use-this-as-repl-buffer 'new - "Name of the buffer to use as REPL buffer. -In case of a special value 'new, a new buffer is created.") - ;;; Buffer Local Declarations @@ -1022,9 +1018,7 @@ client process is started, the function is called with the client buffer." ;; as long as `serv-buf' is not the buffer where the let-binding was ;; started. http://www.gnu.org/software/emacs/manual/html_node/elisp/Creating-Buffer_002dLocal.html (setq-local nrepl-create-client-buffer-function - nrepl-create-client-buffer-function) - (setq-local nrepl-use-this-as-repl-buffer - nrepl-use-this-as-repl-buffer)) + nrepl-create-client-buffer-function)) (message "Starting nREPL server via %s..." (propertize cmd 'face 'font-lock-keyword-face)) serv-proc)) diff --git a/test/cider-client-tests.el b/test/cider-client-tests.el index 2206dc477..de6564aef 100644 --- a/test/cider-client-tests.el +++ b/test/cider-client-tests.el @@ -35,183 +35,226 @@ ;;; cider-client tests -(describe "cider-current-connection" +(describe "cider-current-connection-repl" + + :var (project-dir) + + (before-all + (setq project-dir "~/dummy/project/") + (spy-on 'clojure-project-dir :and-return-value project-dir)) (describe "when there are no active connections" - :var (cider-connections) + :var (cider-connection-alist) (it "returns nil" - (setq cider-connections nil) - (expect (cider-current-connection) :not :to-be-truthy) - (expect (cider-current-connection "clj") :not :to-be-truthy) - (expect (cider-current-connection "cljs") :not :to-be-truthy))) + (setq cider-connection-alist nil) + (expect (cider-current-connection-repl) :not :to-be-truthy) + (expect (cider-current-connection-repl "clj") :not :to-be-truthy) + (expect (cider-current-connection-repl "cljs") :not :to-be-truthy))) (describe "when active connections are available" (it "always returns the latest connection" - (with-connection-buffer "clj" bb1 - (with-connection-buffer "cljs" bb2 - (with-connection-buffer "clj" b1 - (with-connection-buffer "cljs" b2 - (expect (cider-current-connection) :to-equal b2) + (with-connection-buffer "bb" project-dir "clj" bb1 + (with-connection-buffer "bb" project-dir "cljs" bb2 + (with-connection-buffer "b" project-dir "clj" b1 + (with-connection-buffer "b" project-dir "cljs" b2 + (expect (cider-current-connection-repl) :to-equal b2) ;; follows type arguments - (expect (cider-current-connection "clj") :to-equal b1) - (expect (cider-current-connection "cljs") :to-equal b2) + (expect (cider-current-connection-repl "clj") :to-equal b1) + (expect (cider-current-connection-repl "cljs") :to-equal b2) ;; follows file type (with-temp-buffer (setq major-mode 'clojure-mode) - (expect (cider-current-connection) :to-equal b1)) + (expect (cider-current-connection-repl) :to-equal b1)) (with-temp-buffer (setq major-mode 'clojurescript-mode) - (expect (cider-current-connection) :to-equal b2))))))) + (expect (cider-current-connection-repl) :to-equal b2))))))) (it "always returns the most recently used connection" - (with-connection-buffer "clj" bb1 - (with-connection-buffer "cljs" bb2 - (with-connection-buffer "clj" b1 - (with-connection-buffer "cljs" b2 + (with-connection-buffer "bb" project-dir "clj" bb1 + (with-connection-buffer "bb" project-dir "cljs" bb2 + (with-connection-buffer "b" project-dir "clj" b1 + (with-connection-buffer "b" project-dir "cljs" b2 (switch-to-buffer bb2) (switch-to-buffer bb1) - (expect (cider-current-connection) :to-equal bb1) + (expect (cider-current-connection-repl) :to-equal bb1) ;; follows type arguments - (expect (cider-current-connection "clj") :to-equal bb1) - (expect (cider-current-connection "cljs") :to-equal bb2) + (expect (cider-current-connection-repl "clj") :to-equal bb1) + (expect (cider-current-connection-repl "cljs") :to-equal bb2) ;; follows file type (with-temp-buffer (setq major-mode 'clojure-mode) - (expect (cider-current-connection) :to-equal bb1)) + (expect (cider-current-connection-repl) :to-equal bb1)) (with-temp-buffer (setq major-mode 'clojurescript-mode) - (expect (cider-current-connection) :to-equal bb2))))))) + (expect (cider-current-connection-repl) :to-equal bb2))))))) (describe "when current buffer is a 'multi' buffer" (describe "when there is only one connection available" - (it "returns the only connection" - (with-connection-buffer "clj" b - (with-temp-buffer - (clojure-mode) - (expect (cider-current-connection "clj") :to-equal b)) - (with-temp-buffer - (clojurec-mode) - (expect (cider-current-connection "clj") :to-equal b)) - (with-temp-buffer - (clojurex-mode) - (expect (cider-current-connection "clj") :to-equal b)))))) + (it "returns the only connection" + (with-connection-buffer "tmp" project-dir "clj" b + (with-temp-buffer + (clojure-mode) + (expect (cider-current-connection-repl "clj") :to-equal b)) + (with-temp-buffer + (clojurec-mode) + (expect (cider-current-connection-repl "clj") :to-equal b)) + (with-temp-buffer + (clojurex-mode) + (expect (cider-current-connection-repl "clj") :to-equal b)))))) (describe "when type argument is given" (describe "when connection of that type exists" (it "returns that connection buffer" ;; for clj - (with-connection-buffer "clj" b1 - (with-connection-buffer "cljs" b2 - (expect (cider-current-connection "clj") :to-equal b1))) + (with-connection-buffer "tmp" project-dir "clj" b1 + (with-connection-buffer "tmp" project-dir "cljs" b2 + (expect (cider-current-connection-repl "clj") :to-equal b1))) ;; for cljs - (with-connection-buffer "cljs" b1 - (with-connection-buffer "clj" b2 - (expect (cider-current-connection "cljs") :to-equal b1))))) + (with-connection-buffer "tmp" project-dir "cljs" b1 + (with-connection-buffer "tmp" project-dir "clj" b2 + (expect (cider-current-connection-repl "cljs") :to-equal b1))))) (describe "when connection of that type doesn't exists" (it "returns nil" ;; for clj - (with-connection-buffer "cljs" b1 - (expect (cider-current-connection "clj") :to-equal nil)) + (with-connection-buffer "tmp" project-dir "cljs" b1 + (expect (cider-current-connection-repl "clj") :to-equal nil)) ;; for cljs - (with-connection-buffer "clj" b2 - (expect (cider-current-connection "cljs") :to-equal nil))))) + (with-connection-buffer "tmp" project-dir "clj" b2 + (expect (cider-current-connection-repl "cljs") :to-equal nil))))) (describe "when type argument is not given" (describe "when a connection matching current file extension exists" (it "returns that connection buffer" ;; for clj - (with-connection-buffer "clj" b1 - (with-connection-buffer "cljs" b2 + (with-connection-buffer "tmp" project-dir "clj" b1 + (with-connection-buffer "tmp" project-dir "cljs" b2 (with-temp-buffer (setq major-mode 'clojure-mode) - (expect (cider-current-connection) :to-equal b1)))) + (expect (cider-current-connection-repl) :to-equal b1)))) ;; for cljs - (with-connection-buffer "cljs" b1 - (with-connection-buffer "clj" b2 + (with-connection-buffer "tmp" project-dir "cljs" b1 + (with-connection-buffer "tmp" project-dir "clj" b2 (with-temp-buffer (setq major-mode 'clojurescript-mode) - (expect (cider-current-connection) :to-equal b1)))))) + (expect (cider-current-connection-repl) :to-equal b1)))))) (describe "when a connection matching current file extension doesn't exist" - (it "returns the latest connection buffer" + (it "returns nil" ;; for clj - (with-connection-buffer "clj" b1 + (with-connection-buffer "tmp" project-dir "clj" b1 (with-temp-buffer (setq major-mode 'clojurescript-mode) - (expect (cider-current-connection) :to-equal b1))) + (expect (cider-current-connection-repl) :to-equal nil))) ;; for cljs - (with-connection-buffer "cljs" b2 + (with-connection-buffer "tmp" project-dir "cljs" b2 (with-temp-buffer (setq major-mode 'clojure-mode) - (expect (cider-current-connection) :to-equal b2)))))))) + (expect (cider-current-connection-repl) :to-equal nil)))))))) + +(describe "cider-other-repl" + + :var (project-dir) + + (before-all + (setq project-dir "~/dummy/project/") + (spy-on 'clojure-project-dir :and-return-value project-dir)) + + (describe "when within different connections" + (it "returns nil" + ;; for clj + (with-connection-buffer "tmp1" project-dir "clj" b1 + (with-connection-buffer "tmp2" project-dir "cljs" b2 + (expect (cider-other-repl) :to-equal nil) + (expect (cider-other-repl b1) :to-equal nil) + (expect (cider-other-repl b2) :to-equal nil))) + ;; for cljs + (with-connection-buffer "tmp1" project-dir "cljs" b1 + (with-connection-buffer "tmp2" project-dir "cljc" b2 + (expect (cider-other-repl) :to-equal nil) + (expect (cider-other-repl b1) :to-equal nil) + (expect (cider-other-repl b2) :to-equal nil))))) -(describe "cider-other-connection" (describe "when there are no active connections" - :var (cider-connections) + :var (cider-connection-alist) (it "returns nil" - (setq cider-connections nil) - (expect (cider-other-connection) :to-equal nil))) + (setq cider-connection-alist nil) + (expect (cider-other-repl) :to-equal nil))) (describe "when there is only 1 active connection" (it "returns nil" ;; for clj - (with-connection-buffer "clj" b1 - (expect (cider-other-connection) :to-equal nil) - (expect (cider-other-connection b1) :to-equal nil)) + (with-connection-buffer "tmp" project-dir "clj" b1 + (expect (cider-other-repl) :to-equal nil) + (expect (cider-other-repl b1) :to-equal nil)) ;; for cljs - (with-connection-buffer "cljs" b1 - (expect (cider-other-connection) :to-equal nil) - (expect (cider-other-connection b1) :to-equal nil)))) + (with-connection-buffer "tmp" project-dir "cljs" b1 + (expect (cider-other-repl) :to-equal nil) + (expect (cider-other-repl b1) :to-equal nil)))) (describe "when active connections are available" (describe "when a connection of other type doesn't exist" - (it "returns nil" - ;; for clj - (with-connection-buffer "clj" b1 - (with-connection-buffer "clj" b2 - (expect (cider-other-connection) :to-equal nil) - (expect (cider-other-connection b1) :to-equal nil) - (expect (cider-other-connection b2) :to-equal nil))) - ;; for cljs - (with-connection-buffer "cljs" b1 - (with-connection-buffer "cljs" b2 - (expect (cider-other-connection) :to-equal nil) - (expect (cider-other-connection b1) :to-equal nil) - (expect (cider-other-connection b2) :to-equal nil))))) + (describe "and within same connection" + (it "returns nil" + ;; for clj + (with-connection-buffer "tmp" project-dir "clj" b1 + (with-connection-buffer "tmp" project-dir "clj" b2 + (expect (cider-other-repl) :to-equal nil) + (expect (cider-other-repl b1) :to-equal nil) + (expect (cider-other-repl b2) :to-equal nil))) + ;; for cljs + (with-connection-buffer "tmp" project-dir "cljs" b1 + (with-connection-buffer "tmp" project-dir "cljs" b2 + (expect (cider-other-repl) :to-equal nil) + (expect (cider-other-repl b1) :to-equal nil) + (expect (cider-other-repl b2) :to-equal nil))))) + + (describe "when within different connections" + (it "returns nil" + ;; for clj + (with-connection-buffer "tmp1" project-dir "clj" b1 + (with-connection-buffer "tmp2" project-dir "cljs" b2 + (expect (cider-other-repl) :to-equal nil) + (expect (cider-other-repl b1) :to-equal nil) + (expect (cider-other-repl b2) :to-equal nil))) + ;; for cljs + (with-connection-buffer "tmp1" project-dir "cljs" b1 + (with-connection-buffer "tmp2" project-dir "cljc" b2 + (expect (cider-other-repl) :to-equal nil) + (expect (cider-other-repl b1) :to-equal nil) + (expect (cider-other-repl b2) :to-equal nil)))))) (describe "when a connection of other type exists" (it "returns that connection" - (with-connection-buffer "clj" b1 - (with-connection-buffer "cljs" b2 - (expect (cider-other-connection) :to-equal b1) - (expect (cider-other-connection b1) :to-equal b2) - (expect (cider-other-connection b2) :to-equal b1))))) + (with-connection-buffer "tmp" project-dir "clj" b1 + (with-connection-buffer "tmp" project-dir "cljs" b2 + (expect (cider-other-repl) :to-equal b1) + (expect (cider-other-repl b1) :to-equal b2) + (expect (cider-other-repl b2) :to-equal b1))))) (describe "when there are multiple active connections" (it "always returns the latest connection" - - (with-connection-buffer "clj" bb1 - (with-connection-buffer "cljs" bb2 - (with-connection-buffer "clj" b1 - (with-connection-buffer "cljs" b2 - (expect (cider-other-connection) :to-equal b1) - (expect (cider-other-connection b1) :to-equal b2) - (expect (cider-other-connection b2) :to-equal b1) + (with-connection-buffer "bb" project-dir "clj" bb1 + (with-connection-buffer "bb" project-dir "cljs" bb2 + (with-connection-buffer "b" project-dir "clj" b1 + (with-connection-buffer "b" project-dir "cljs" b2 + (expect (cider-other-repl) :to-equal b1) + (expect (cider-other-repl b1) :to-equal b2) + (expect (cider-other-repl b2) :to-equal b1) ;; older connections still work - (expect (cider-other-connection bb1) :to-equal b2) - (expect (cider-other-connection bb2) :to-equal b1))))))))) + (expect (cider-other-repl bb1) :to-equal bb2) + (expect (cider-other-repl bb2) :to-equal bb1))))))))) (describe "cider-var-info" (it "returns vars info as an alist" @@ -235,64 +278,15 @@ :to-equal "stub") (expect (cider-var-info "") :to-equal nil))) -(describe "cider-make-connection-default" - :var (connections) - - (it "makes the nrepl connection buffer, the default connection" - (cider-test-with-buffers - (a b) - ;; Add one connection - (cider-make-connection-default a) - (expect (cider-default-connection) :to-equal a) - ;; Add second connection - (cider-make-connection-default b) - (expect (cider-default-connection) :to-equal b) - ;; Re-add first connection - (cider-make-connection-default a) - (expect (cider-default-connection) :to-equal a))) - - (it "moves the connection buffer to the front of `cider-connections'" - (setq connections (cider-connections)) - (cider-test-with-buffers - (a b) - ;; Add one connection - (cider-make-connection-default a) - (expect (cider-connections) :to-equal (append (list a) connections)) - ;; Add second connection - (cider-make-connection-default b) - (expect (cider-connections) :to-equal (append (list b a) connections)) - ;; Re-add first connection - (cider-make-connection-default a) - (expect (cider-connections) :to-equal (append (list a b) connections))))) - -(describe "cider-connections" - :var (connections) - (it "removes a connection buffer from connections list, when it is killed" - (setq connections (cider-connections)) - (cider-test-with-buffers - (a b) - (cider-make-connection-default a) - (cider-make-connection-default b) - (kill-buffer a) - (expect (cider-default-connection) :to-equal b) - (expect (cider-connections) :to-equal (append (list b) connections))))) - -(describe "cider-rotate-default-connection" - (it "rotates the default nREPL connections in `cider-connections'" - ;; to mute the output on stdout - (spy-on 'message :and-return-value nil) - (cider-test-with-buffers - (a b c) - (cider-make-connection-default c) - (cider-make-connection-default b) - (cider-make-connection-default a) - (expect (cider-default-connection) :to-equal a) - (cider-rotate-default-connection) - (expect (cider-default-connection) :to-equal b) - (cider-rotate-default-connection) - (expect (cider-default-connection) :to-equal c) - (cider-rotate-default-connection) - (expect (cider-default-connection) :to-equal a)))) +;; (describe "cider-connections" +;; :var (connections) +;; (it "removes a connection buffer from connections list, when it is killed" +;; (setq connections (cider-connections)) +;; (cider-test-with-buffers +;; (a b) +;; (kill-buffer a) +;; (expect (cider-current-connection-repl) :to-equal b) +;; (expect (cider-connections) :to-equal (append (list b) connections))))) (describe "cider--connection-info" (spy-on 'cider--java-version :and-return-value "1.7") @@ -318,17 +312,19 @@ (describe "cider--close-connection-buffer" :var (connections) - (it "removes the connection from `cider-connections'" + (it "removes the connection from `cider-connection-alist'" (setq connections (cider-connections)) - (cider-test-with-buffers - (a b) - (cider-make-connection-default a) - (cider-make-connection-default b) - ;; closing a buffer should see it removed from the connection list - (cider--close-connection-buffer a) - (expect (buffer-live-p a) :not :to-be-truthy) - (expect (cider-connections) :to-equal (cons b connections)) - (expect (cider-default-connection) :to-equal b)))) + (cider-test-with-buffers (a b) + (let ((conn-name (make-temp-name "conn"))) + (cider-add-connection-repl conn-name a) + (cider-add-connection-repl conn-name b) + ;; closing a buffer should see it removed from the connection list + (cider--close-connection-buffer a) + (expect (buffer-live-p a) :not :to-be-truthy) + (expect (cider-connections) :to-equal (cons (list conn-name b) connections)) + (cider--close-connection-buffer b) + (expect (buffer-live-p b) :not :to-be-truthy) + (expect (cider-connections) :to-equal connections))))) (describe "cider-connection-type-for-buffer" :var (cider-repl-type) @@ -355,19 +351,15 @@ (setq cider-repl-type nil) (expect (cider-connection-type-for-buffer) :to-equal nil))) - (describe "cider-nrepl-send-unhandled-request" (it "returns the id of the request sent to nREPL server and ignores the response" (spy-on 'process-send-string :and-return-value nil) - (with-temp-buffer + (with-connection-buffer "dummy" "/dummy/project/" "clj" repl (setq-local nrepl-pending-requests (make-hash-table :test 'equal)) (setq-local nrepl-completed-requests (make-hash-table :test 'equal)) - (let* ((cider-connections (list (current-buffer))) - (id (cider-nrepl-send-unhandled-request '("op" "t" "extra" "me")))) - + (let ((id (cider-nrepl-send-unhandled-request '("op" "t" "extra" "me")))) ;; the request should never be marked as pending (expect (gethash id nrepl-pending-requests) :not :to-be-truthy) - ;; the request should be marked completed immediately (expect (gethash id nrepl-completed-requests) :to-be-truthy) (expect (gethash id nrepl-completed-requests) :to-equal #'ignore))) @@ -378,51 +370,51 @@ (it "changes designation in all cider buffer names" (with-temp-buffer (let ((server-buffer (current-buffer))) - (with-temp-buffer - (let* ((connection-buffer (current-buffer)) - (cider-connections (list connection-buffer))) + (with-connection-buffer "dummy" "/dummy/" "clj" repl + (let* ((connection-buffer (current-buffer))) (setq-local nrepl-server-buffer server-buffer) + (setq-local cider-connection-name "dummy") (cider-change-buffers-designation "bob") (expect (buffer-name connection-buffer) :to-equal "*cider-repl bob*") (expect (buffer-name server-buffer) :to-equal "*nrepl-server bob*") (with-current-buffer connection-buffer (expect (buffer-name) :to-equal "*cider-repl bob*")))))))) - (describe "cider-extract-designation-from-current-repl-buffer" + :var (cider-connection-alist) + (before-all + (spy-on 'clojure-project-dir :and-return-value "/dummy/") + (setq cider-connection-alist nil)) + (describe "when the buffers have a designation" (it "returns that designation string" - (with-temp-buffer - (let ((cider-connections (list (current-buffer))) - (nrepl-repl-buffer-name-template "*cider-repl%s*")) + (with-connection-buffer "dummy" "/dummy/" "clj" repl + (let ((nrepl-repl-buffer-name-template "*cider-repl%s*")) (rename-buffer "*cider-repl bob*") (switch-to-buffer (current-buffer)) (with-temp-buffer (switch-to-buffer (current-buffer)) (expect (cider-extract-designation-from-current-repl-buffer) :to-equal "bob") - (rename-buffer "*cider-repl apa*") - (push (current-buffer) cider-connections) - (expect (cider-extract-designation-from-current-repl-buffer) - :to-equal "apa") - (setq-local cider-connections (list (current-buffer))) - (expect (cider-extract-designation-from-current-repl-buffer) - :to-equal "apa")))))) - - (describe "when the buffers don't have a designation" - (it "returns " - (with-temp-buffer - (let* ((connection-buffer (current-buffer)) - (cider-connections (list connection-buffer))) - (with-temp-buffer - (let ((repl-buffer (current-buffer))) - (rename-buffer "*cider-repl*") - (with-temp-buffer - (with-current-buffer connection-buffer - (setq-local nrepl-repl-buffer repl-buffer)) - (expect (cider-extract-designation-from-current-repl-buffer) - :to-equal ""))))))))) + (with-connection-buffer "dummy2" "/dummy/" "clj" repl2 + (rename-buffer "*cider-repl apa*") + (expect (cider-extract-designation-from-current-repl-buffer) + :to-equal "apa"))))))) + + ;; (describe "when the buffers don't have a designation" + ;; (it "returns " + ;; (with-connection-buffer "dummy" "/dummy/" "clj" repl + ;; (let* ((connection-buffer (current-buffer))) + ;; (with-temp-buffer + ;; (let ((repl-buffer (current-buffer))) + ;; (rename-buffer "*cider-repl*") + ;; (with-temp-buffer + ;; (with-current-buffer connection-buffer + ;; (setq-local nrepl-repl-buffer repl-buffer)) + ;; (expect (cider-extract-designation-from-current-repl-buffer) + ;; :to-equal "")))))))) + ) (describe "cider-project-name" @@ -432,14 +424,6 @@ (expect (cider-project-name "path/to/project") :to-equal "project") (expect (cider-project-name "path/to/project/") :to-equal "project"))) -(describe "cider-ensure-connected" - (it "returns nil when a cider connection is available" - (spy-on 'cider-connected-p :and-return-value t) - (expect (cider-ensure-connected) :to-equal nil)) - (it "raises a user-error in the absence of a connection" - (spy-on 'cider-connected-p :and-return-value nil) - (expect (lambda () (cider-ensure-connected)) :to-throw 'user-error))) - (describe "cider-ensure-op-supported" (it "returns nil when the op is supported" (spy-on 'cider-nrepl-op-supported-p :and-return-value t) diff --git a/test/cider-interaction-tests.el b/test/cider-interaction-tests.el index 0681861c7..21c0c1673 100644 --- a/test/cider-interaction-tests.el +++ b/test/cider-interaction-tests.el @@ -87,18 +87,18 @@ (expect (lambda () (cider-load-all-project-ns)) :to-throw 'user-error))) (describe "cider-load-file" - (it "works as expected in empty Clojure buffers" - (spy-on 'cider-request:load-file :and-return-value nil) - (with-connection-buffer "clj" b - (with-temp-buffer - (clojure-mode) - (setq buffer-file-name (make-temp-name "tmp.clj")) - (expect (lambda () (cider-load-buffer)) :not :to-throw))))) + (it "works as expected in empty Clojure buffers" + (spy-on 'cider-request:load-file :and-return-value nil) + (with-connection-buffer "dummy" "/dummy/" "clj" b + (with-temp-buffer + (clojure-mode) + (setq buffer-file-name (make-temp-name "tmp.clj")) + (expect (lambda () (cider-load-buffer)) :not :to-throw))))) (describe "cider-interactive-eval" - (it "works as expected in empty Clojure buffers" - (spy-on 'cider-nrepl-request:eval :and-return-value nil) - (with-connection-buffer "clj" b - (with-temp-buffer - (clojure-mode) - (expect (lambda () (cider-interactive-eval "(+ 1)")) :not :to-throw))))) + (it "works as expected in empty Clojure buffers" + (spy-on 'cider-nrepl-request:eval :and-return-value nil) + (with-connection-buffer "dummy" "/dummy/" "clj" b + (with-temp-buffer + (clojure-mode) + (expect (lambda () (cider-interactive-eval "(+ 1)")) :not :to-throw))))) diff --git a/test/cider-selector-tests.el b/test/cider-selector-tests.el index 9a2d680e6..ee5bd7f60 100644 --- a/test/cider-selector-tests.el +++ b/test/cider-selector-tests.el @@ -47,11 +47,11 @@ (expect (current-buffer) :to-equal expected-buffer))))) (describe "cider-selector-n" - :var (cider-endpoint cider-connections) + :var (cider-endpoint cider-connection-alist) (it "switches to the connection browser buffer" (with-temp-buffer (setq cider-endpoint '("123.123.123.123" 4006) - cider-connections (list (current-buffer))) + cider-connection-alist (list (current-buffer))) (with-temp-buffer ;; switch to another buffer (cider-invoke-selector-method-by-key ?n) @@ -68,9 +68,9 @@ (cider--test-selector-method ?e 'emacs-lisp-mode "*testfile*.el"))) (describe "cider-seletor-method-r" - :var (cider-current-repl-buffer) + :var (cider-current-connection-repl) (it "switches to current REPL buffer" - (spy-on 'cider-current-repl-buffer :and-return-value "*cider-repl xyz*") + (spy-on 'cider-current-connection-repl :and-return-value "*cider-repl xyz*") (cider--test-selector-method ?r 'cider-repl-mode "*cider-repl xyz*"))) (describe "cider-selector-method-m" diff --git a/test/cider-tests.el b/test/cider-tests.el index 910901731..8f8e1edc3 100644 --- a/test/cider-tests.el +++ b/test/cider-tests.el @@ -36,44 +36,40 @@ ;;; connection browser -(describe "cider-connections-buffer" - (it "lists all the active connections" - (with-temp-buffer - (rename-buffer "*cider-repl test1*") - (let ((b1 (current-buffer))) - (setq-local nrepl-endpoint '("localhost" 4005)) - (setq-local nrepl-project-dir "proj") - (setq-local cider-repl-type "clj") - (with-temp-buffer - (rename-buffer "*cider-repl test2*") - (let ((b2 (current-buffer))) - (setq-local nrepl-endpoint '("123.123.123.123" 4006)) - (setq-local cider-repl-type "clj") - (let ((cider-connections (list b1 b2))) - (cider-connection-browser) - (with-current-buffer "*cider-connections*" - (expect (buffer-string) :to-equal " REPL Host Port Project Type - -* *cider-repl test1* localhost 4005 proj Clojure - *cider-repl test2* 123.123.123.123 4006 - Clojure\n\n") - - (goto-line 4) ; somewhere in the second connection listed - (cider-connections-make-default) - (expect (car cider-connections) :to-equal b2) - (message "%s" (cider-connections)) - (expect (buffer-string) :to-equal " REPL Host Port Project Type - - *cider-repl test1* localhost 4005 proj Clojure -* *cider-repl test2* 123.123.123.123 4006 - Clojure\n\n") - (goto-line 4) ; somewhere in the second connection listed - (cider-connections-close-connection) - (expect cider-connections :to-equal (list b1)) - (expect (buffer-string) :to-equal " REPL Host Port Project Type - -* *cider-repl test1* localhost 4005 proj Clojure\n\n") - (cider-connections-goto-connection) - (expect (current-buffer) :to-equal b1) - (kill-buffer "*cider-connections*"))))))))) +;; (describe "cider-connections-buffer" +;; (it "lists all the active connections" +;; (with-temp-buffer +;; (rename-buffer "*cider-repl test1*") +;; (let ((b1 (current-buffer))) +;; (setq-local nrepl-endpoint '("localhost" 4005)) +;; (setq-local nrepl-project-dir "proj") +;; (setq-local cider-repl-type "clj") +;; (with-temp-buffer +;; (rename-buffer "*cider-repl test2*") +;; (let ((b2 (current-buffer))) +;; (setq-local nrepl-endpoint '("123.123.123.123" 4006)) +;; (setq-local cider-repl-type "clj") +;; (let ((cider-connection-alist (list b1 b2))) +;; (cider-connection-browser) +;; (with-current-buffer "*cider-connections*" +;; (expect (buffer-string) :to-equal " REPL Host Port Project Type + +;; *cider-repl test1* localhost 4005 proj Clojure +;; *cider-repl test2* 123.123.123.123 4006 - Clojure\n\n") + +;; (goto-line 4) ; somewhere in the second connection listed +;; (expect (buffer-string) :to-equal " REPL Host Port Project Type + +;; *cider-repl test1* localhost 4005 proj Clojure +;; *cider-repl test2* 123.123.123.123 4006 - Clojure\n\n") +;; (goto-line 4) ; somewhere in the second connection listed +;; (cider-connections-close-connection) +;; (expect (buffer-string) :to-equal " REPL Host Port Project Type + +;; *cider-repl test1* localhost 4005 proj Clojure\n\n") +;; (cider-connections-goto-connection) +;; (expect (current-buffer) :to-equal b1) +;; (kill-buffer "*cider-connections*"))))))))) (describe "cider-inject-jack-in-dependencies" :var (cider-jack-in-dependencies cider-jack-in-nrepl-middlewares cider-jack-in-lein-plugins cider-jack-in-dependencies-exclusions) diff --git a/test/utils/cider-connection-test-utils.el b/test/utils/cider-connection-test-utils.el index b51f090ec..cfe856ddb 100644 --- a/test/utils/cider-connection-test-utils.el +++ b/test/utils/cider-connection-test-utils.el @@ -30,22 +30,28 @@ (require 'cider) (require 'cider-client) -(defmacro with-connection-buffer (type symbol &rest body) - "Run BODY in a temp buffer, with the given repl TYPE. +(defmacro with-connection-buffer (connection-name project-dir type symbol &rest body) + "Run BODY in a temp buffer, within CONNECTION-NAME, PROJECT-DIR and REPL TYPE. SYMBOL is locally let-bound to the current buffer." - (declare (indent 2) + (declare (indent 4) (debug (sexp sexp &rest form))) `(with-temp-buffer - (setq major-mode 'cider-repl-mode) - (setq cider-repl-type ,type) - ;; `with-current-buffer' doesn't bump the buffer up the list. - (switch-to-buffer (current-buffer)) - (rename-buffer (format "*cider-repl %s-%s*" ,type (random 10000)) t) - (let ((cider-connections (cons (current-buffer) cider-connections)) - (,symbol (current-buffer))) - ,@body))) + (let ((CURBUFF (current-buffer))) + (setq major-mode 'cider-repl-mode + nrepl-project-dir ,project-dir + cider-repl-type ,type + cider-connection-name ,connection-name) + ;; `with-current-buffer' doesn't bump the buffer up the list. + (switch-to-buffer (current-buffer)) + (rename-buffer (format "*cider-repl %s-%s*" ,type (random 10000)) t) + (cider-add-connection-repl ,connection-name CURBUFF) + (let ((,symbol CURBUFF)) + ,@body) + (cider-delete-connection-repl ,connection-name CURBUFF)))) (defmacro cider-test-with-buffers (buffer-names &rest body) + "Create buffers in BUFFER-NAMES, execute BODY and kill buffers." + (declare (indent 1)) (let ((create (lambda (b) (list b `(generate-new-buffer " *temp*"))))) `(let (,@(mapcar create buffer-names)) (unwind-protect