From fa4568163509a018b89715ecce43748b480e205d Mon Sep 17 00:00:00 2001 From: Vitalie Spinu Date: Thu, 27 Nov 2014 21:58:22 -0800 Subject: [PATCH] Isolate `nrepl-client` connection logic from CIDER Allow `nrepl-client` to be used by non CIDER frontends. - Frontends can control the creation of the client buffer through `nrepl-create-client-buffer-function`. - Each frontend has it's own `xxx-connected-hook` which is run by `xxx--connected-handler`. The latter function is placed in the local `nrepl-connected-hook` during the client buffer initialization. Similarly for `xxx-disconnected-hook`. - Rename `nrepl--init-connection-buffer` -> nrepl--init-capabilities --- cider-interaction.el | 4 --- cider-repl.el | 13 +++++---- cider.el | 35 ++++++++++++++++++++++-- nrepl-client.el | 63 +++++++++++++++++++++++--------------------- 4 files changed, 74 insertions(+), 41 deletions(-) diff --git a/cider-interaction.el b/cider-interaction.el index 7751543b7..cdf1950c0 100644 --- a/cider-interaction.el +++ b/cider-interaction.el @@ -1776,10 +1776,6 @@ restart the server." (sleep-for 2) (cider-jack-in prompt-project)) -(add-hook 'nrepl-connected-hook 'cider-enable-on-existing-clojure-buffers) -(add-hook 'nrepl-disconnected-hook - 'cider-possibly-disable-on-existing-clojure-buffers) - (provide 'cider-interaction) ;;; cider-interaction.el ends here diff --git a/cider-repl.el b/cider-repl.el index 7beeaa675..14fccd3f1 100644 --- a/cider-repl.el +++ b/cider-repl.el @@ -162,16 +162,19 @@ PROJECT-DIR, PORT and HOST are as in `nrepl-make-buffer-name'." (current-buffer)) (nrepl-make-buffer-name nrepl-repl-buffer-name-template project-dir host port))) -(defun cider-repl-create (&optional project-dir host port) +(defun cider-repl-create (endpoint) "Create a REPL buffer and install `cider-repl-mode'. -PROJECT-DIR, PORT and HOST are as in `nrepl-make-buffer-name'." +ENDPOINT is a plist as returned by `nrepl-connect'." ;; Connection might not have been set as yet. Please don't send requests here. - (let ((buf (nrepl-make-buffer-name nrepl-repl-buffer-name-template - project-dir host port))) + (let ((buf (nrepl-make-buffer-name nrepl-repl-buffer-name-template nil + (plist-get endpoint :host) + (plist-get endpoint :port)))) (with-current-buffer (get-buffer-create buf) (unless (derived-mode-p 'cider-repl-mode) (cider-repl-mode)) - (cider-repl-reset-markers)) + (cider-repl-reset-markers) + (add-hook 'nrepl-connected-hook 'cider--connected-handler nil 'local) + (add-hook 'nrepl-disconnected-hook 'cider--disconnected-handler nil 'local)) buf)) (defun cider-repl-require-repl-utils () diff --git a/cider.el b/cider.el index 21014fb82..e94e56320 100644 --- a/cider.el +++ b/cider.el @@ -92,6 +92,16 @@ This variable is used by `cider-connect'." :type 'list :group 'cider) +(defcustom cider-connected-hook nil + "List of functions to call when connected to Clojure nREPL server." + :type 'hook + :group 'cider) + +(defcustom cider-disconnected-hook nil + "List of functions to call when disconnected from the Clojure nREPL server." + :type 'hook + :group 'cider) + (defvar cider-ps-running-nrepls-command "ps u | grep leiningen" "Process snapshot command used in `cider-locate-running-nrepl-ports'.") @@ -118,7 +128,8 @@ start the server." (interactive "P") (setq cider-current-clojure-buffer (current-buffer)) (if (cider--lein-present-p) - (let* ((project (when prompt-project + (let* ((nrepl-create-client-buffer-function #'cider-repl-create) + (project (when prompt-project (read-directory-name "Project: "))) (project-dir (nrepl-project-directory-for (or project (nrepl-current-dir)))) @@ -140,7 +151,8 @@ Create REPL buffer and start an nREPL client connection." (interactive (cider-select-endpoint)) (setq cider-current-clojure-buffer (current-buffer)) (when (nrepl-check-for-repl-buffer `(,host ,port) nil) - (nrepl-start-client-process host port t))) + (let ((nrepl-create-client-buffer-function #'cider-repl-create)) + (nrepl-start-client-process host port)))) (defun cider-select-endpoint () "Interactively select the host and port to connect to." @@ -234,6 +246,25 @@ In case `default-directory' is non-local we assume the command is available." (executable-find cider-lein-command) (executable-find (concat cider-lein-command ".bat")))) +(defun cider--connected-handler () + "Handle cider initialization after nREPL connection has been established. +This function is appended to `nrepl-connected-hook' in the client process +buffer." + ;; `nrepl-connected-hook' is run in connection buffer + (cider-repl-init (current-buffer)) + (cider--check-required-nrepl-ops) + (cider--check-middleware-compatibility) + (cider-enable-on-existing-clojure-buffers) + (run-hooks 'cider-connected-hook)) + +(defun cider--disconnected-handler () + "Cleanup after nREPL connection has been lost or closed. +This function is appended to `nrepl-disconnected-hook' in the client +process buffer." + ;; `nrepl-connected-hook' is run in connection buffer + (cider-possibly-disable-on-existing-clojure-buffers) + (run-hooks 'cider-disconnected-hook)) + ;;;###autoload (eval-after-load 'clojure-mode '(progn diff --git a/nrepl-client.el b/nrepl-client.el index e1afe26c3..47c8e1ca2 100644 --- a/nrepl-client.el +++ b/nrepl-client.el @@ -135,6 +135,11 @@ buffer will be hidden." :type 'boolean :group 'nrepl) +(defvar nrepl-create-client-buffer-function 'nrepl-create-client-buffer-default + "Name of a function that returns a client process buffer. +It is called with one argument, a plist containing :host, :port and :proc +as returned by `nrepl-connect'. ") + ;;; nREPL Buffer Names @@ -686,20 +691,18 @@ If NO-ERROR is non-nil, show messages instead of throwing an error." ;; `nrepl-start-client-process' is called from `nrepl-server-filter'. It ;; starts the client process described by `nrepl-client-filter' and ;; `nrepl-client-sentinel'. -(defun nrepl-start-client-process (&optional host port replp server-proc) +(defun nrepl-start-client-process (&optional host port server-proc) "Create new client process identified by HOST and PORT. -If eitehr HOST or PORT are nil, pick them from the value returned by -`nrepl-connection-endpoint'. If REPLP is non-nil create a client -connection which is associated with a repl buffer. When non-nil, -SERVER-PROC must be a running nrepl server process within Emacs. Return -the newly created client connection process." +In remote buffers, HOST and PORT is taken from current tramp +connection. SERVER-PROC must be a running nREPL server process within +Emacs. This function creates connection buffer by a call to +`nrepl-create-client-buffer-function'. Return newly created client +process." (let* ((endpoint (nrepl-connect host port)) (client-proc (plist-get endpoint :proc)) (host (plist-get endpoint :host)) (port (plist-get endpoint :port)) - (client-buf (if replp - (cider-repl-create default-directory host port) - (nrepl-create-connection-buffer default-directory host port)))) + (client-buf (funcall nrepl-create-client-buffer-function endpoint))) (set-process-buffer client-proc (get-buffer client-buf)) @@ -715,9 +718,9 @@ the newly created client connection process." (setq nrepl-project-dir (buffer-local-value 'nrepl-project-dir server-buf) nrepl-server-buffer server-buf)) (setq nrepl-endpoint `(,host ,port) - ;; FIXME: REPL and connection buffers are the same thing nrepl-connection-buffer client-buf - nrepl-repl-buffer (when replp client-buf) + ;; FIXME: In Cider, REPL and client buffers are the same thing + nrepl-repl-buffer client-buf nrepl-buffer-ns "user" nrepl-tunnel-buffer (-when-let (tunnel (plist-get endpoint :tunnel)) (process-buffer tunnel)) @@ -726,10 +729,10 @@ the newly created client connection process." (nrepl-make-connection-default client-buf) (nrepl--init-client-sessions client-proc) - (nrepl--init-connection-buffer client-buf replp) - (cider--check-required-nrepl-ops) - (cider--check-middleware-compatibility) - (run-hooks 'nrepl-connected-hook) + (nrepl--init-capabilities client-buf) + + (with-current-buffer client-buf + (run-hooks 'nrepl-connected-hook)) client-proc)) @@ -754,19 +757,13 @@ for functionality like pretty-printing won't clobber the values of *1, *2, etc." (setq nrepl-tooling-session new-session)) (error "Could not create new tooling session (%s)" err))))) -(defun nrepl--init-connection-buffer (conn-buffer replp) - "Initialize CONN-BUFFER as a connection buffer. -If REPLP is non-nil, initialize as a REPL buffer. - -Here we determine the main session's capabilities using the \"describe\" op -and store that information as buffer-local data in the connection buffer." +(defun nrepl--init-capabilities (conn-buffer) + "Store locally in CONN-BUFFER the capabilities of nREPL server." (let ((description (nrepl-sync-request:describe))) (nrepl-dbind-response description (ops versions) (with-current-buffer conn-buffer (setq nrepl-ops ops) - (setq nrepl-versions versions))) - (when replp - (cider-repl-init conn-buffer)))) + (setq nrepl-versions versions))))) (defun nrepl-close-client-sessions () "Close the nREPL sessions for the active connection." @@ -997,7 +994,10 @@ Return a newly created process." (set-process-sentinel serv-proc 'nrepl-server-sentinel) (set-process-coding-system serv-proc 'utf-8-unix 'utf-8-unix) (with-current-buffer serv-buf - (setq nrepl-project-dir directory)) + (setq nrepl-project-dir directory) + ;; ensure that `nrepl-start-client-process' sees right function + (setq-local nrepl-create-client-buffer-function + nrepl-create-client-buffer-function)) (message "Starting nREPL server via %s..." (propertize cmd 'face 'font-lock-keyword-face)) serv-proc)) @@ -1012,7 +1012,7 @@ Return a newly created process." (let ((port (string-to-number (match-string 1 output)))) (message (format "nREPL server started on %s" port)) (with-current-buffer (process-buffer process) - (let ((client-proc (nrepl-start-client-process nil port t process))) + (let ((client-proc (nrepl-start-client-process nil port process))) ;; FIXME: Bad connection tracking system. There can be multiple client ;; connections per server (setq nrepl-connection-buffer (buffer-name (process-buffer client-proc)))))))) @@ -1142,10 +1142,13 @@ The default buffer name is *nrepl-messages*." tramp-current-host nrepl-host)) -(defun nrepl-create-connection-buffer (&optional project-dir host port) - "Create an nREPL connection buffer. -PROJECT-DIR, HOST and PORT are as in `nrepl-make-buffer-name'." - (let ((buffer (generate-new-buffer (nrepl-connection-buffer-name project-dir host port)))) +(defun nrepl-create-client-buffer-default (endpoint) + "Create an nREPL client process buffer. +ENDPOINT is a plist returned by `nrepl-connect'." + (let ((buffer (generate-new-buffer + (nrepl-connection-buffer-name default-directory + (plist-get endpoint :host) + (plist-get endpoint :port))))) (with-current-buffer buffer (buffer-disable-undo) (setq-local kill-buffer-query-functions nil))