summaryrefslogtreecommitdiff
path: root/cider-client.el
diff options
context:
space:
mode:
Diffstat (limited to 'cider-client.el')
-rw-r--r--cider-client.el906
1 files changed, 115 insertions, 791 deletions
diff --git a/cider-client.el b/cider-client.el
index df5b1431..1e09bae2 100644
--- a/cider-client.el
+++ b/cider-client.el
@@ -26,8 +26,8 @@
;;; Code:
(require 'spinner)
-(require 'ewoc)
(require 'nrepl-client)
+(require 'cider-connection)
(require 'cider-common)
(require 'cider-util)
(require 'clojure-mode)
@@ -36,535 +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.
-
-A good alternative to the default is `cider-random-tip'."
- :type 'function
- :group 'cider
- :package-version '(cider . "0.11.0"))
-
-(defvar cider-connections nil
- "A list of connections.")
-
-(defun cider-connected-p ()
- "Return t if CIDER is currently connected, nil otherwise."
- (not (null (cider-connections))))
-
-(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))))
-
-(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-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'.
-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))
- (when (buffer-live-p buffer)
- (with-current-buffer buffer
- (when spinner-current (spinner-stop))
- (when nrepl-tunnel-buffer
- (cider--close-buffer nrepl-tunnel-buffer)))
- ;; If this is the only (or last) REPL connected to its server, the
- ;; kill-process hook will kill the server.
- (cider--close-buffer buffer)
- (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.
-
-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)))))
-
-(defun cider-connection-type-for-buffer (&optional buffer)
- "Return the matching connection type (clj or cljs) for BUFFER.
-In cljc buffers return \"multi\". This function infers connection
-type based on the major mode. See `cider-project-connections-types' for a
-list of types of actual connections within a project. BUFFER defaults to
-the `current-buffer'."
- (with-current-buffer (or buffer (current-buffer))
- (cond
- ((derived-mode-p 'clojurescript-mode) "cljs")
- ((derived-mode-p 'clojurec-mode) "multi")
- ((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)))))
-
-(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-assoc-buffer-with-connection ()
- "Associate the current buffer with a connection.
-
-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: ")))
- (when conn
- (setq-local cider-connections (list conn)))))
-
-(defun cider-toggle-buffer-connection (&optional restore-all)
- "Toggle the current buffer's connection between Clojure and ClojureScript.
-
-Default behavior of a cljc buffer is to send eval commands to both Clojure
-and ClojureScript. This function sets a local buffer variable to hide one
-or the other. Optional argument RESTORE-ALL undo any toggled behavior by
-using the default list of connections."
- (interactive "P")
- (cider-ensure-connected)
- (if restore-all
- (progn
- (kill-local-variable 'cider-connections)
- (let ((types (mapcar #'cider--connection-type (cider-connections))))
- (message (format "CIDER connections available: %s" types))))
- (let ((current-conn (cider-current-connection))
- (was-local (local-variable-p 'cider-connections))
- (original-connections (cider-connections)))
- ;; we set the local variable to eclipse all connections in favor of the
- ;; toggled connection. to recover the full list we must remove the
- ;; obfuscation
- (kill-local-variable 'cider-connections)
- (if-let* ((other-conn (cider-other-connection current-conn)))
- (progn
- (setq-local cider-connections (list other-conn))
- (message "Connection set to %s" (cider--connection-type other-conn)))
- (progn
- (when was-local
- (setq-local cider-connections original-connections))
- (user-error "No other connection available"))))))
-
-(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-toggle-request-dispatch ()
- "Toggle the value of `cider-request-dispatch' between static and dynamic.
-
-Handy when you're using dynamic dispatch, but you want to quickly force all
-evaluation commands to use a particular connection."
- (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-tracker 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) (with-current-buffer c (equal cider-repl-type "cljs")))
- project-connections)
- (seq-find (lambda (c) (with-current-buffer c (equal cider-repl-type "cljs")))
- (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 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 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' 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-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))))))
-
-
-;;; 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)
- (define-key map "?" #'describe-mode)
- (define-key map "h" #'describe-mode)
- map))
-
-(declare-function cider-popup-buffer-mode "cider-popup")
-(define-derived-mode cider-connections-buffer-mode cider-popup-buffer-mode
- "CIDER Connections"
- "CIDER Connections Buffer Mode.
-\\{cider-connections-buffer-mode-map}
-\\{cider-popup-buffer-mode-map}"
- (when cider-special-mode-truncate-lines
- (setq-local truncate-lines t)))
-
-(defvar cider--connection-ewoc)
-(defconst cider--connection-browser-buffer-name "*cider-connections*")
-
-(defun cider-connection-browser ()
- "Open a browser buffer for nREPL connections."
- (interactive)
- (if-let* ((buffer (get-buffer cider--connection-browser-buffer-name)))
- (progn
- (cider--connections-refresh-buffer buffer)
- (unless (get-buffer-window buffer)
- (select-window (display-buffer buffer))))
- (cider--setup-connection-browser)))
-
-(defun cider--connections-refresh ()
- "Refresh the connections buffer, if the buffer exists.
-The connections buffer is determined by
-`cider--connection-browser-buffer-name'"
- (when-let* ((buffer (get-buffer cider--connection-browser-buffer-name)))
- (cider--connections-refresh-buffer buffer)))
-
-(add-hook 'nrepl-disconnected-hook #'cider--connections-refresh)
-
-(defun cider--connections-refresh-buffer (buffer)
- "Refresh the connections BUFFER."
- (cider--update-connections-display
- (buffer-local-value 'cider--connection-ewoc buffer)
- cider-connections))
-
-(defun cider--setup-connection-browser ()
- "Create a browser buffer for nREPL connections."
- (with-current-buffer (get-buffer-create cider--connection-browser-buffer-name)
- (let ((ewoc (ewoc-create
- 'cider--connection-pp
- " REPL Host Port Project Type\n")))
- (setq-local cider--connection-ewoc ewoc)
- (cider--update-connections-display ewoc cider-connections)
- (setq buffer-read-only t)
- (cider-connections-buffer-mode)
- (display-buffer (current-buffer)))))
-
-(defun cider-client-name-repl-type (type)
- "Return a human-readable name for a connection TYPE.
-TYPE can be any of the possible values of `cider-repl-type'."
- (pcase type
- ("clj" "Clojure")
- ("cljs" "ClojureScript")
- (_ "Unknown")))
-
-(defun cider-project-name (project-dir)
- "Extract the project name from PROJECT-DIR."
- (if (and project-dir (not (equal project-dir "")))
- (file-name-nondirectory (directory-file-name project-dir))
- "-"))
-
-(defun cider--connection-pp (connection)
- "Print an nREPL CONNECTION to the current buffer."
- (let* ((buffer-read-only nil)
- (buffer (get-buffer connection))
- (project-name (cider-project-name (buffer-local-value 'nrepl-project-dir buffer)))
- (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)) "*" " ")
- (buffer-name connection)
- (car endpoint)
- (prin1-to-string (cadr endpoint))
- project-name
- repl-type))))
-
-(defun cider--update-connections-display (ewoc connections)
- "Update the connections EWOC to show CONNECTIONS."
- (ewoc-filter ewoc (lambda (n) (member n connections)))
- (let ((existing))
- (ewoc-map (lambda (n) (setq existing (cons n existing))) ewoc)
- (let ((added (seq-difference connections existing)))
- (mapc (apply-partially 'ewoc-enter-last ewoc) added)
- (save-excursion (ewoc-refresh ewoc)))))
-
-(defun cider--ewoc-apply-at-point (f)
- "Apply function F to the ewoc node at point.
-F is a function of two arguments, the ewoc and the data at point."
- (let* ((ewoc cider--connection-ewoc)
- (node (and ewoc (ewoc-locate ewoc))))
- (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)
- (cider--ewoc-apply-at-point #'cider--connections-close-connection))
-
-(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))
-
-(defun cider-connections-goto-connection ()
- "Goto connection at point in the connection browser."
- (interactive)
- (cider--ewoc-apply-at-point #'cider--connections-goto-connection))
-
-(defun cider--connections-goto-connection (_ewoc data)
- "Goto the REPL for the connection in _EWOC specified by DATA."
- (when (buffer-live-p data)
- (select-window (display-buffer data))))
-
-
-(defun cider-display-connected-message ()
- "Message displayed on successful connection."
- (message
- (concat "Connected."
- (if cider-connection-message-fn
- (format " %s" (funcall cider-connection-message-fn))
- ""))))
-
-;; TODO: Replace direct usage of such hooks with CIDER hooks,
-;; that are connection type independent
-(add-hook 'nrepl-connected-hook 'cider-display-connected-message)
-
;;; Eval spinner
(defcustom cider-eval-spinner-type 'progress-bar
@@ -625,34 +96,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)))
- (buffer-local-value 'cider-buffer-ns repl-buf))
+ (when-let* ((repl (cider-current-repl)))
+ (buffer-local-value 'cider-buffer-ns repl))
(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 nREPL connection."
(if (cider-connected-p)
(let* ((path (or path (file-truename (buffer-file-name))))
(relpath (thread-last (cider-sync-request:classpath)
@@ -672,9 +136,9 @@ active nREPL connection."
(clojure-expected-ns path)))
(clojure-expected-ns path)))
-(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)))
+(defun cider-nrepl-op-supported-p (op &optional connection)
+ "Check whether the CONNECTION supports the nREPL middleware OP."
+ (nrepl-op-supported-p op (or connection (cider-current-repl))))
(defvar cider-version)
(defun cider-ensure-op-supported (op)
@@ -686,12 +150,10 @@ 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\" ... ).
+ \"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))))
+the current connection. Return the id of the sent message."
+ (nrepl-send-request request callback (or connection (cider-current-repl))))
(defun cider-nrepl-send-sync-request (request &optional connection abort-on-input)
"Send REQUEST to the nREPL server synchronously using CONNECTION.
@@ -701,14 +163,13 @@ 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-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))
+(defun cider-nrepl-send-unhandled-request (request &optional connection)
+ "Send REQUEST to the nREPL CONNECTION and ignore any responses.
+Immediately mark the REQUEST as done. Return the id of the sent message."
+ (let* ((conn (or connection (cider-current-repl)))
(id (nrepl-send-request request #'ignore conn)))
(with-current-buffer conn
(nrepl--mark-id-completed id))
@@ -719,8 +180,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-repl)."
+ (let ((connection (or connection (cider-current-repl))))
(nrepl-request:eval input
(if cider-show-eval-spinner
(cider-eval-spinner-handler connection callback)
@@ -732,22 +193,20 @@ buffer, defaults to (cider-current-connection)."
(defun cider-nrepl-sync-request:eval (input &optional connection ns)
"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))
- ns))
+ (nrepl-sync-request:eval input (or connection (cider-current-repl)) ns))
(defcustom cider-pprint-fn 'pprint
"Sets the function to use when pretty-printing evaluation results.
The value must be one of the following symbols:
- `pprint' - to use \\=`clojure.pprint/pprint\\=`
+`pprint' - to use \\=`clojure.pprint/pprint\\=`
- `fipp' - to use the Fast Idiomatic Pretty Printer, approximately 5-10x
- faster than \\=`clojure.core/pprint\\=` (this is the default)
+`fipp' - to use the Fast Idiomatic Pretty Printer, approximately 5-10x
+faster than \\=`clojure.core/pprint\\=` (this is the default)
- `puget' - to use Puget, which provides canonical serialization of data on
- top of fipp, but at a slight performance cost
+`puget' - to use Puget, which provides canonical serialization of data on
+top of fipp, but at a slight performance cost
Alternatively, can be the namespace-qualified name of a Clojure function of
one argument. If the function cannot be resolved, an exception will be
@@ -784,70 +243,82 @@ result, and is included in the request if non-nil."
"Plist to be appended to an eval request to make it use content-types."
'("content-type" "true"))
-(defun cider-tooling-eval (input callback &optional ns)
- "Send the request INPUT and register the CALLBACK as the response handler.
-NS specifies the namespace in which to evaluate the request.
-
-Requests evaluated in the tooling nREPL session don't affect the
-thread-local bindings of the primary eval nREPL session (e.g. this is not
-going to clobber *1/2/3)."
+(defun cider-tooling-eval (input callback &optional ns connection)
+ "Send the request INPUT to CONNECTION and register the CALLBACK.
+NS specifies the namespace in which to evaluate the request. Requests
+evaluated in the tooling nREPL session don't affect the thread-local
+bindings of the primary eval nREPL session (e.g. this is not going to
+clobber *1/2/3)."
;; namespace forms are always evaluated in the "user" namespace
(nrepl-request:eval input
callback
- (cider-current-connection)
- ns nil nil nil t ; tooling
- ))
-
-(defun cider-sync-tooling-eval (input &optional ns)
- "Send the request INPUT and evaluate in synchronously.
-NS specifies the namespace in which to evaluate the request.
-
-Requests evaluated in the tooling nREPL session don't affect the
-thread-local bindings of the primary eval nREPL session (e.g. this is not
-going to clobber *1/2/3)."
+ (or connection (cider-current-repl))
+ ns nil nil nil 'tooling))
+
+(defun cider-sync-tooling-eval (input &optional ns connection)
+ "Send the request INPUT to CONNECTION and evaluate in synchronously.
+NS specifies the namespace in which to evaluate the request. Requests
+evaluated in the tooling nREPL session don't affect the thread-local
+bindings of the primary eval nREPL session (e.g. this is not going to
+clobber *1/2/3)."
;; namespace forms are always evaluated in the "user" namespace
(nrepl-sync-request:eval input
- (cider-current-connection)
+ (or connection (cider-current-repl))
ns
- t ; tooling
- ))
+ 'tooling))
+
+;; TODO: Add some unit tests and pretty those two functions up.
+;; FIXME: Currently that's broken for group-id with multiple segments (e.g. org.clojure/clojure)
+(defun cider-classpath-libs ()
+ "Return a list of all libs on the classpath."
+ (let ((libs (seq-filter (lambda (cp-entry)
+ (string-suffix-p ".jar" cp-entry))
+ (cider-sync-request:classpath)))
+ (dir-sep (if (string-equal system-type "windows-nt") "\\\\" "/")))
+ (thread-last libs
+ (seq-map (lambda (s) (split-string s dir-sep)))
+ (seq-map #'reverse)
+ (seq-map (lambda (l) (reverse (seq-take l 4)))))))
(defun cider-library-present-p (lib)
- "Check whether LIB is present on the classpath."
- (seq-find (lambda (s) (string-match-p (concat lib ".*\\.jar") s)) (cider-sync-request:classpath)))
+ "Check whether LIB is present on the classpath.
+The library is a string of the format \"group-id/artifact-id\"."
+ (let* ((lib (split-string lib "/"))
+ (group-id (car lib))
+ (artifact-id (cadr lib)))
+ (seq-find (lambda (lib)
+ (let ((g (car lib))
+ (a (cadr lib)))
+ (and (equal group-id g) (equal artifact-id a))))
+ (cider-classpath-libs))))
+
+
+;;; Interrupt evaluation
-(defalias 'cider-current-repl-buffer #'cider-current-connection
- "The current REPL buffer.
-Return the REPL buffer given by `cider-current-connection'.")
+(defun cider-interrupt-handler (buffer)
+ "Create an interrupt response handler for BUFFER."
+ (nrepl-make-response-handler buffer nil nil nil nil))
-(declare-function cider-interrupt-handler "cider-interaction")
(defun cider-interrupt ()
"Interrupt any pending evaluations."
(interactive)
- (with-current-buffer (cider-current-connection)
+ ;; FIXME: does this work correctly in cljc files?
+ (with-current-buffer (cider-current-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-repl))))))
-(defun cider-current-session ()
+(defun cider-nrepl-eval-session ()
"Return the eval nREPL session id of the current connection."
- (cider-session-for-connection (cider-current-connection)))
-
-(defun cider-session-for-connection (connection)
- "Create a CIDER session for CONNECTION."
- (with-current-buffer connection
+ (with-current-buffer (cider-current-repl)
nrepl-session))
-(defun cider-current-messages-buffer ()
- "The nREPL messages buffer, matching the current connection."
- (nrepl-messages-buffer (cider-current-connection)))
-
-(defun cider-current-tooling-session ()
+(defun cider-nrepl-tooling-session ()
"Return the tooling nREPL session id of the current connection."
- (with-current-buffer (cider-current-connection)
+ (with-current-buffer (cider-current-repl)
nrepl-tooling-session))
(defun cider--var-choice (var-info)
@@ -876,53 +347,15 @@ unless ALL is truthy."
(when (and class member)
(cider-sync-request:info nil class member)))
-(defun cider--find-var-other-window (var &optional line)
- "Find the definition of VAR, optionally at a specific LINE.
-
-Display the results in a different window."
- (if-let* ((info (cider-var-info var)))
- (progn
- (if line (setq info (nrepl-dict-put info "line" line)))
- (cider--jump-to-loc-from-info info t))
- (user-error "Symbol `%s' not resolved" var)))
-
-(defun cider--find-var (var &optional line)
- "Find the definition of VAR, optionally at a specific LINE."
- (if-let* ((info (cider-var-info var)))
- (progn
- (if line (setq info (nrepl-dict-put info "line" line)))
- (cider--jump-to-loc-from-info info))
- (user-error "Symbol `%s' not resolved" var)))
-
-(defun cider-find-var (&optional arg var line)
- "Find definition for VAR at LINE.
-
-Prompt according to prefix ARG and `cider-prompt-for-symbol'.
-A single or double prefix argument inverts the meaning of
-`cider-prompt-for-symbol'. A prefix of `-` or a double prefix argument causes
-the results to be displayed in a different window. The default value is
-thing at point."
- (interactive "P")
- (cider-ensure-op-supported "info")
- (if var
- (cider--find-var var line)
- (funcall (cider-prompt-for-symbol-function arg)
- "Symbol"
- (if (cider--open-other-window-p arg)
- #'cider--find-var-other-window
- #'cider--find-var))))
-
;;; Requests
-(declare-function cider-load-file-handler "cider-interaction")
+(declare-function cider-load-file-handler "cider-eval")
(defun cider-request:load-file (file-contents file-path file-name &optional connection callback)
"Perform the nREPL \"load-file\" op.
FILE-CONTENTS, FILE-PATH and FILE-NAME are details of the file to be
-loaded.
-
-If CONNECTION is nil, use `cider-current-connection'.
-If CALLBACK is nil, use `cider-load-file-handler'."
+loaded. If CONNECTION is nil, use `cider-current-repl'. If CALLBACK
+is nil, use `cider-load-file-handler'."
(cider-nrepl-send-request `("op" "load-file"
"file" ,file-contents
"file-path" ,file-path
@@ -935,14 +368,14 @@ If CALLBACK is nil, use `cider-load-file-handler'."
;;; Sync Requests
(defcustom cider-filtered-namespaces-regexps
- '("^cider.nrepl" "^refactor-nrepl" "^clojure.tools.nrepl")
+ '("^cider.nrepl" "^refactor-nrepl" "^clojure.tools.nrepl" "^nrepl")
"List of regexps used to filter out some vars/symbols/namespaces.
When nil, nothing is filtered out. Otherwise, all namespaces matching any
-regexp from this list are dropped out of the \"ns-list\" op.
-Also, \"apropos\" won't include vars from such namespaces.
-This list is passed on to the nREPL middleware without any pre-processing.
-So the regexps have to be in Clojure format (with twice the number of
-backslashes) and not Emacs Lisp."
+regexp from this list are dropped out of the \"ns-list\" op. Also,
+\"apropos\" won't include vars from such namespaces. This list is passed
+on to the nREPL middleware without any pre-processing. So the regexps have
+to be in Clojure format (with twice the number of backslashes) and not
+Emacs Lisp."
:type '(repeat string)
:safe #'listp
:group 'cider
@@ -986,7 +419,7 @@ CONTEXT represents a completion context for compliment."
(defun cider-sync-request:complete-flush-caches ()
"Send \"complete-flush-caches\" op to flush Compliment's caches."
(cider-nrepl-send-sync-request (list "op" "complete-flush-caches"
- "session" (cider-current-session))
+ "session" (cider-nrepl-eval-session))
'abort-on-input))
(defun cider-sync-request:info (symbol &optional class member)
@@ -1025,7 +458,6 @@ CONTEXT represents a completion context for compliment."
(defun cider-sync-request:spec-list (&optional filter-regex)
"Get a list of the available specs in the registry.
-
Optional argument FILTER-REGEX filters specs. By default, all specs are
returned."
(setq filter-regex (or filter-regex ""))
@@ -1091,7 +523,6 @@ returned."
(defun cider-sync-request:resources-list ()
"Return a list of all resources on the classpath.
-
The result entries are relative to the classpath."
(when-let* ((resources (thread-first '("op" "resources-list")
(cider-nrepl-send-sync-request)
@@ -1118,135 +549,28 @@ The result entries are relative to the classpath."
(error (car (split-string err "\n"))))
(nrepl-dict-get response "formatted-edn")))
-
-;;; Connection info
-(defun cider--java-version ()
- "Retrieve the underlying connection's Java version."
- (with-current-buffer (cider-current-connection "clj")
- (when nrepl-versions
- (thread-first nrepl-versions
- (nrepl-dict-get "java")
- (nrepl-dict-get "version-string")))))
-
-(defun cider--clojure-version ()
- "Retrieve the underlying connection's Clojure version."
- (with-current-buffer (cider-current-connection "clj")
- (when nrepl-versions
- (thread-first nrepl-versions
- (nrepl-dict-get "clojure")
- (nrepl-dict-get "version-string")))))
-
-(defun cider--nrepl-version ()
- "Retrieve the underlying connection's nREPL version."
- (with-current-buffer (cider-current-connection "clj")
- (when nrepl-versions
- (thread-first nrepl-versions
- (nrepl-dict-get "nrepl")
- (nrepl-dict-get "version-string")))))
-
-(defun cider--connection-info (connection-buffer)
- "Return info about CONNECTION-BUFFER.
-
-Info contains project name, current REPL namespace, host:port
-endpoint and Clojure version."
- (with-current-buffer connection-buffer
- (format "%s%s@%s:%s (Java %s, Clojure %s, nREPL %s)"
- (upcase (concat cider-repl-type " "))
- (or (cider--project-name nrepl-project-dir) "<no project>")
- (car nrepl-endpoint)
- (cadr nrepl-endpoint)
- (cider--java-version)
- (cider--clojure-version)
- (cider--nrepl-version))))
-
-(defun cider--connection-properties (conn-buffer)
- "Extract the essential properties of CONN-BUFFER."
- (with-current-buffer conn-buffer
- (list
- :type cider-repl-type
- :host (car nrepl-endpoint)
- :port (cadr nrepl-endpoint)
- :project-dir nrepl-project-dir)))
-
-(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))
-
-(defun cider--connection-host (conn-buffer)
- "Get CONN-BUFFER's host."
- (plist-get (cider--connection-properties conn-buffer) :host))
-
-(defun cider--connection-port (conn-buffer)
- "Get CONN-BUFFER's port."
- (plist-get (cider--connection-properties conn-buffer) :port))
-
-(defun cider--connection-project-dir (conn-buffer)
- "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."
- (interactive)
- (cider-ensure-connected)
- (if (= (length (cider-connections)) 1)
- (user-error "There's just a single active nREPL connection")
- (setq cider-connections
- (append (cdr cider-connections)
- (list (car cider-connections))))
- (message "Default nREPL connection: %s"
- (cider--connection-info (car cider-connections)))))
-
-
-(declare-function cider-connect "cider")
-(defun cider-replicate-connection (&optional conn)
- "Establish a new connection based on an existing connection.
-The new connection will use the same host and port.
-If CONN is not provided the user will be prompted to select a connection."
- (interactive)
- (let* ((conn (or conn (cider-read-connection "Select connection to replicate: ")))
- (host (cider--connection-host conn))
- (port (cider--connection-port conn))
- (project-dir (cider--connection-project-dir conn)))
- (cider-connect host port project-dir)))
-
-(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)))
- (template (split-string nrepl-repl-buffer-name-template "%s")))
- (string-match (format "^%s\\(.*\\)%s"
- (regexp-quote (concat (car template) nrepl-buffer-name-separator))
- (regexp-quote (cadr template)))
- repl-buffer-name)
- (or (match-string 1 repl-buffer-name) "<no designation>")))
-
-(defun cider-change-buffers-designation (designation)
- "Change the DESIGNATION in cider buffer names.
-Buffer names changed are cider-repl and nrepl-server."
- (interactive (list (read-string (format "Change CIDER buffer designation from '%s': "
- (cider-extract-designation-from-current-repl-buffer)))))
- (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)
- (rename-buffer new-repl-buffer-name)
- (when nrepl-server-buffer
- (let ((new-server-buffer-name (nrepl-format-buffer-name-template
- nrepl-server-buffer-name-template designation)))
- (with-current-buffer nrepl-server-buffer
- (rename-buffer new-server-buffer-name)))))
- (message "CIDER buffer designation changed to: %s" designation)))
+;;; Dealing with input
+;; TODO: Replace this with some nil handler.
+(defun cider-stdin-handler (&optional _buffer)
+ "Make a stdin response handler for _BUFFER."
+ (nrepl-make-response-handler (current-buffer)
+ (lambda (_buffer _value))
+ (lambda (_buffer _out))
+ (lambda (_buffer _err))
+ nil))
+
+(defun cider-need-input (buffer)
+ "Handle an need-input request from BUFFER."
+ (with-current-buffer buffer
+ (let ((map (make-sparse-keymap)))
+ (set-keymap-parent map minibuffer-local-map)
+ (define-key map (kbd "C-c C-c") 'abort-recursive-edit)
+ (let ((stdin (condition-case nil
+ (concat (read-from-minibuffer "Stdin: " nil map) "\n")
+ (quit nil))))
+ (nrepl-request:stdin stdin
+ (cider-stdin-handler buffer)
+ (cider-current-repl))))))
(provide 'cider-client)