diff options
author | Vitalie Spinu <spinuvit@gmail.com> | 2014-08-13 13:50:25 -0700 |
---|---|---|
committer | Vitalie Spinu <spinuvit@gmail.com> | 2014-08-25 12:02:51 -0700 |
commit | 09dcec951125300274eea6e0b5f591be1d57bb5e (patch) | |
tree | 41f678af60d204b3172b88a430eae05416c57397 | |
parent | b58ba0ac546fc6a4c9228cb18f552067741a060d (diff) |
Use repl buffer as connection buffer
Ancillary changes:
- new functions `nrepl-start-client-process' and `nrepl-start-server-process'.
- `cider-init-repl-buffer' and `cider-create-repl-buffer' have clear cut
separation of functionality
- `nrepl-make-buffer-name` and all `cider-create-xxx-buffer-name` take explicit
dir, port and host variables.
- increase `nrepl-decode-timeout` to 0.025 for greater reliability
- merge `nrepl--handle-process-output` into client process filter
- remove `nrepl-connect' in favor of `nrepl-start-client-process'
-rw-r--r-- | CHANGELOG.md | 4 | ||||
-rw-r--r-- | cider-interaction.el | 4 | ||||
-rw-r--r-- | cider-repl.el | 98 | ||||
-rw-r--r-- | cider-selector.el | 6 | ||||
-rw-r--r-- | cider.el | 36 | ||||
-rw-r--r-- | nrepl-client.el | 257 | ||||
-rw-r--r-- | test/cider-tests.el | 14 |
7 files changed, 229 insertions, 190 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fcd27fb..611a030a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,10 @@ are used to translate filenames from/to the nREPL server (default Cygwin impleme ### Changes +* REPL buffers are now connection buffers for REPL client connections. +* Server and client cranking were isolated into `nrepl-start-server-process` and + `nrepl-start-client-process`. + * nrepl-client.el refactoring: - `nrepl-send-request-sync` was renamed into `nrepl-send-sync-request` to comply diff --git a/cider-interaction.el b/cider-interaction.el index 0c1cda15..9a1ae8e8 100644 --- a/cider-interaction.el +++ b/cider-interaction.el @@ -156,7 +156,7 @@ Signal an error if it is not supported." (unless (nrepl-op-supported-p op) (error "Can't find nREPL middleware providing op \"%s\". Please, install (or update) cider-nrepl %s and restart CIDER" op cider-version))) -(defun cider-verify-required-nrepl-ops () +(defun cider--check-required-nrepl-ops () "Check whether all required nREPL ops are present." (let ((missing-ops (-remove 'nrepl-op-supported-p cider-required-nrepl-ops))) (when missing-ops @@ -317,7 +317,7 @@ of the namespace in the Clojure source buffer." (let ((buffer (current-buffer))) (when (eq 4 arg) (cider-repl-set-ns (cider-current-ns))) - (pop-to-buffer (cider-find-or-create-repl-buffer)) + (pop-to-buffer (cider-get-repl-buffer)) (cider-remember-clojure-buffer buffer) (goto-char (point-max)))) diff --git a/cider-repl.el b/cider-repl.el index 07139b58..03c8eaac 100644 --- a/cider-repl.el +++ b/cider-repl.el @@ -43,6 +43,7 @@ (defvar paredit-version) (defvar paredit-space-for-delimiter-predicates)) + (defgroup cider-repl nil "Interaction with the REPL." :prefix "cider-repl-" @@ -115,6 +116,7 @@ you'd like to use the default Emacs behavior use :type 'symbol :group 'cider-repl) + ;;;; REPL buffer local variables (defvar-local cider-repl-input-start-mark nil) @@ -151,31 +153,42 @@ joined together.") (set markname (make-marker)) (set-marker (symbol-value markname) (point)))) + ;;; REPL init -(defun cider-repl-buffer-name () - "Generate a REPL buffer name based on current connection buffer." - (with-current-buffer (get-buffer (nrepl-current-connection-buffer)) - (nrepl-make-buffer-name nrepl-repl-buffer-name-template))) - -(defun cider-create-repl-buffer () - "Create a REPL buffer." - (cider-init-repl-buffer - (let ((buffer-name (cider-repl-buffer-name))) - (if cider-repl-display-in-current-window - (add-to-list 'same-window-buffer-names buffer-name)) - (if cider-repl-pop-to-buffer-on-connect - (pop-to-buffer buffer-name) - (generate-new-buffer buffer-name)) - buffer-name))) - -(defun cider-make-repl (process) - "Make a REPL for the connection PROCESS." - (let ((connection-buffer (process-buffer process)) - (repl-buffer (cider-create-repl-buffer))) - (with-current-buffer repl-buffer - (setq nrepl-connection-buffer (buffer-name connection-buffer))) - (with-current-buffer connection-buffer - (setq nrepl-repl-buffer (buffer-name repl-buffer))))) +(defun cider-repl-buffer-name (&optional project-dir host port) + "Generate a REPL buffer name based on current connection buffer. +PROJECT-DIR, PORT and HOST are as in `nrepl-make-buffer-name'." + (with-current-buffer (or (get-buffer (nrepl-current-connection-buffer)) + (current-buffer)) + (nrepl-make-buffer-name nrepl-repl-buffer-name-template project-dir host port))) + +(defun cider-repl-create (&optional project-dir host port) + "Create a REPL buffer and install `cider-repl-mode'. +PROJECT-DIR, PORT and HOST are as in `nrepl-make-buffer-name'." + ;; 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))) + (with-current-buffer (get-buffer-create buf) + (unless (derived-mode-p 'cider-repl-mode) + (cider-repl-mode)) + (cider-repl-reset-markers)) + buf)) + +(defun cider-repl-init (buffer &optional no-banner) + "Initialize the REPL in BUFFER. +BUFFER must be a REPL buffer with `cider-repl-mode' and a running +clienprocessL connection. Unless NO-BANNER is non-nil, insert a banner." + (with-current-buffer buffer + ;; honor :init-ns from lein's :repl-options on startup + (setq nrepl-buffer-ns (cider-eval-and-get-value "(str *ns*)")) + (unless no-banner + (cider-repl--insert-banner-and-prompt nrepl-buffer-ns)) + (when cider-repl-display-in-current-window + (add-to-list 'same-window-buffer-names buf)) + (when cider-repl-pop-to-buffer-on-connect + (pop-to-buffer buffer)) + (cider-remember-clojure-buffer cider-current-clojure-buffer) + buffer)) (defun cider-repl--banner () "Generate the welcome REPL buffer banner." @@ -194,33 +207,14 @@ joined together.") (cider-repl--mark-input-start) (cider-repl--insert-prompt ns)) -(defun cider-init-repl-buffer (buffer &optional noprompt) - "Initialize the REPL in BUFFER. -Insert a banner, unless NOPROMPT is non-nil." - (with-current-buffer buffer - (unless (eq major-mode 'cider-repl-mode) - (cider-repl-mode)) - (cider-repl-reset-markers) - (unless noprompt - (cider-repl--insert-banner-and-prompt nrepl-buffer-ns)) - (cider-remember-clojure-buffer cider-current-clojure-buffer) - (current-buffer))) - -(defun cider-find-or-create-repl-buffer () - "Return the REPL buffer, create it if necessary." - (let ((buffer (cider-current-repl-buffer))) - (if (null buffer) - (error "No active nREPL connection") - (let ((buffer (get-buffer buffer))) - (or (when (buffer-live-p buffer) buffer) - (let ((buffer (nrepl-current-connection-buffer))) - (if (null buffer) - (error "No active nREPL connection") - (cider-init-repl-buffer - (get-process buffer) - (get-buffer-create - (cider-repl-buffer-name)))))))))) +(defun cider-get-repl-buffer () + "Return the REPL buffer for current connection." + (let ((buffer (get-buffer-create (cider-current-repl-buffer)))) + (if (buffer-live-p buffer) + buffer + (error "No active REPL")))) + ;;; REPL interaction (defun cider-repl--in-input-area-p () @@ -685,6 +679,7 @@ namespace to switch to." (cider-repl-handler (current-buffer)))) (error "Cannot determine the current namespace"))) + ;;;;; History (defcustom cider-repl-wrap-history nil @@ -904,6 +899,7 @@ constructs." (append (cl-subseq session-hist 0 n-added-items) file-hist)) + ;;; REPL shortcuts (defcustom cider-repl-shortcut-dispatch-char ?\, "Character used to distinguish REPL commands from Lisp forms." @@ -952,7 +948,7 @@ constructs." (call-interactively (gethash command cider-repl-shortcuts)) (error "No command selected"))))) - + ;;;;; CIDER REPL mode ;;; Prevent paredit from inserting some inappropriate spaces. diff --git a/cider-selector.el b/cider-selector.el index 308e8ccc..070f1b4f 100644 --- a/cider-selector.el +++ b/cider-selector.el @@ -32,7 +32,7 @@ (require 'cider-client) (require 'cider-interaction) -(require 'cider-repl) ; for cider-find-or-create-repl-buffer +(require 'cider-repl) ; for cider-get-repl-buffer (defconst cider-selector-help-buffer "*Selector Help*" "The name of the selector's help buffer.") @@ -135,7 +135,7 @@ is chosen. The returned buffer is selected with (def-cider-selector-method ?r "Current REPL buffer." - (cider-find-or-create-repl-buffer)) + (cider-get-repl-buffer)) (def-cider-selector-method ?n "Connections browser buffer." @@ -153,7 +153,7 @@ is chosen. The returned buffer is selected with (def-cider-selector-method ?s "Cycle to the next CIDER connection's REPL." (cider-rotate-connection) - (cider-find-or-create-repl-buffer)) + (cider-get-repl-buffer)) (provide 'cider-selector) @@ -117,28 +117,17 @@ start the server." (let* ((project (when prompt-project (read-directory-name "Project: "))) (project-dir (nrepl-project-directory-for - (or project (nrepl-current-dir))))) + (or project (nrepl-current-dir)))) + (lein-params (if prompt-project + (read-string (format "nREPL server command: %s " + cider-lein-command) + cider-lein-parameters) + cider-lein-parameters)) + (cmd (format "%s %s" cider-lein-command lein-params))) (when (nrepl-check-for-repl-buffer nil project-dir) - (let* ((nrepl-project-dir project-dir) - (lein-params (if prompt-project - (read-string (format "nREPL server command: %s " cider-lein-command) cider-lein-parameters) - cider-lein-parameters)) - (cmd (format "%s %s" cider-lein-command lein-params)) - (default-directory (or project-dir default-directory)) - (serv-buf-name (generate-new-buffer-name (nrepl-server-buffer-name))) - (process - (progn - ;; the buffer has to be created before the proc: - (get-buffer-create serv-buf-name) - (start-file-process-shell-command "nrepl-server" serv-buf-name cmd)))) - (set-process-filter process 'nrepl-server-filter) - (set-process-sentinel process 'nrepl-server-sentinel) - (set-process-coding-system process 'utf-8-unix 'utf-8-unix) - (with-current-buffer (process-buffer process) - (setq nrepl-project-dir project-dir)) - (message "Starting nREPL server via %s..." - (propertize cmd 'face 'font-lock-keyword-face))))) - (message "The %s executable (specified by `cider-lein-command') isn't on your exec-path" cider-lein-command))) + (nrepl-start-server-process project-dir cmd))) + (message "The %s executable (specified by `cider-lein-command') isn't on your exec-path" + cider-lein-command))) (defun cider-known-endpoint-candidates () "Known endpoint candidates for establishing an nREPL connection. @@ -162,7 +151,8 @@ The returned endpoint has the label removed." ;;;###autoload (defun cider-connect (host port) - "Connect to an nREPL server identified by HOST and PORT." + "Connect to an nREPL server identified by HOST and PORT. +Create REPL buffer and start an nREPL client connection." (interactive (let ((known-endpoint (when cider-known-endpoints (cider-select-known-endpoint)))) (list (or (car known-endpoint) @@ -171,7 +161,7 @@ The returned endpoint has the label removed." (read-string "Port: " port nil port)))))) (setq cider-current-clojure-buffer (current-buffer)) (when (nrepl-check-for-repl-buffer `(,host ,port) nil) - (nrepl-connect host port))) + (nrepl-start-client-process default-directory host port t))) (define-obsolete-function-alias 'cider diff --git a/nrepl-client.el b/nrepl-client.el index 104c0892..9ac2ea64 100644 --- a/nrepl-client.el +++ b/nrepl-client.el @@ -148,6 +148,7 @@ buffer will be hidden." (defconst nrepl-connection-buffer-name-template "*nrepl-connection%s*") (defconst nrepl-server-buffer-name-template "*nrepl-server%s*") (defconst nrepl-on-connection-buffer-name-template "*nrepl-on-connection%s*") +(defconst nrepl-decoder-buffer-name-template "*nrepl-decoder%s*") (defun nrepl-format-buffer-name-template (buffer-name-template designation) "Apply the DESIGNATION to the corresponding BUFFER-NAME-TEMPLATE." @@ -156,18 +157,21 @@ buffer will be hidden." (concat nrepl-buffer-name-separator designation) ""))) -(defun nrepl-make-buffer-name (buffer-name-template) +(defun nrepl-make-buffer-name (buffer-name-template &optional project-dir host port) "Generate a buffer name using BUFFER-NAME-TEMPLATE. -The name will include the project name if available or the -endpoint host if it is not. The name will also include the -connection port if `nrepl-buffer-name-show-port' is true." +If not supplied PROJECT-DIR, PORT and HOST default to the buffer local value of the +`nrepl-project-dir' and `nrepl-endpoint'. + +The name will include the project name if available or the endpoint host if +it is not. The name will also include the connection port if +`nrepl-buffer-name-show-port' is true." (generate-new-buffer-name - (let ((project-name (nrepl--project-name nrepl-project-dir)) - (nrepl-proj-port (cadr nrepl-endpoint))) + (let ((project-name (nrepl--project-name (or project-dir nrepl-project-dir))) + (nrepl-proj-port (or port (cadr nrepl-endpoint)))) (nrepl-format-buffer-name-template buffer-name-template - (concat (if project-name project-name (car nrepl-endpoint)) + (concat (if project-name project-name (or host (car nrepl-endpoint))) (if (and nrepl-proj-port nrepl-buffer-name-show-port) (format ":%s" nrepl-proj-port) "")))))) @@ -175,21 +179,34 @@ connection port if `nrepl-buffer-name-show-port' is true." "Apply a prefix to BUFFER-NAME that will hide the buffer." (concat (if nrepl-hide-special-buffers " " "") buffer-name)) -(defun nrepl-connection-buffer-name () - "Return the name of the connection buffer." +(defun nrepl-connection-buffer-name (&optional project-dir host port) + "Return the name of the connection buffer. +PROJECT-DIR, PORT and HOST are as in `/nrepl-make-buffer-name'." (nrepl--make-hidden-name - (nrepl-make-buffer-name nrepl-connection-buffer-name-template))) + (nrepl-make-buffer-name nrepl-connection-buffer-name-template + project-dir host port))) -(defun nrepl-server-buffer-name () - "Return the name of the server buffer." +(defun nrepl-server-buffer-name (&optional project-dir host port) + "Return the name of the server buffer. +PROJECT-DIR, PORT and HOST are as in `nrepl-make-buffer-name'." (nrepl--make-hidden-name - (nrepl-make-buffer-name nrepl-server-buffer-name-template))) + (nrepl-make-buffer-name nrepl-server-buffer-name-template + project-dir host port))) -(defun nrepl-on-connection-buffer-name () - "Return the name of the on-connection buffer." +(defun nrepl-on-connection-buffer-name (&optional project-dir host port) + "Return the name of the on-connection buffer. +PROJECT-DIR, PORT and HOST are as in `nrepl-make-buffer-name'." (nrepl--make-hidden-name - (nrepl-make-buffer-name nrepl-on-connection-buffer-name-template))) + (nrepl-make-buffer-name nrepl-on-connection-buffer-name-template + project-dir host port))) +(defun nrepl-decoder-buffer-name (&optional project-dir host port) + "Return the name of the buffer used for bencode decoding. +PROJECT-DIR, PORT and HOST are as in `nrepl-make-buffer-name'." + ;; this might go away if bdecode is rewriten by direct decoding of the sring + (nrepl--make-hidden-name + (nrepl-make-buffer-name nrepl-decoder-buffer-name-template + project-dir host port))) ;;; Buffer Local Declarations @@ -198,6 +215,7 @@ connection port if `nrepl-buffer-name-show-port' is true." (defvar-local nrepl-connection-buffer nil) (defvar-local nrepl-server-buffer nil) (defvar-local nrepl-repl-buffer nil) +(defvar-local nrepl-decoder-buffer nil) (defvar-local nrepl-endpoint nil) (defvar-local nrepl-project-dir nil) (defvar-local nrepl-on-connection-buffer nil) @@ -315,29 +333,25 @@ Remove the processed data from the buffer if the decode successful." ;; Decoding and dispatching of the server responses happens in ;; `nrepl-client-filter'. -(defvar nrepl-decode-timeout 0.01 +(defvar nrepl-decode-timeout 0.025 "Seconds to wait before decoding nREPL output.") (defun nrepl-client-filter (process string) "Decode the message(s) from PROCESS contained in STRING and dispatch." - (with-current-buffer (process-buffer process) + (with-current-buffer (get-buffer-create + (buffer-local-value 'nrepl-decoder-buffer + (process-buffer process))) (goto-char (point-max)) - (insert string)) - ;; end of the dict maybe? - (when (eq ?e (aref string (1- (length string)))) - ;; wait a bit to make sure we are at the real end - (unless (accept-process-output process nrepl-decode-timeout) - (nrepl--handle-process-output process)))) - -(defun nrepl--handle-process-output (process) - "Handle all complete messages from PROCESS." - (with-current-buffer (process-buffer process) - (let ((nrepl-connection-dispatch (current-buffer))) - ;; FIXME: An ugly fix for https://github.com/clojure-emacs/cider/issues/583 - (while (and (not (derived-mode-p 'cider-repl-mode)) (> (buffer-size) 1)) + (insert string) + ;; at the end of the dict? + (when (eq ?e (aref string (1- (length string)))) + ;; we are good to go if no new output came within the timeout + (unless (accept-process-output process nrepl-decode-timeout) (let ((responses (nrepl-decode-current-buffer))) - (dolist (r responses) - (nrepl--dispatch-response r))))))) + ;; callbacks are executed in the connection buffer + (with-current-buffer (process-buffer process) + (dolist (r responses) + (nrepl--dispatch-response r)))))))) (defun nrepl--dispatch-response (response) "Dispatch the RESPONSE to associated callback. @@ -360,7 +374,8 @@ process buffer and run the hook `nrepl-disconnected-hook'." (if (equal (process-status process) 'closed) (progn (with-current-buffer (process-buffer process) - (when (get-buffer nrepl-repl-buffer) + (when (and nrepl-repl-buffer + (get-buffer nrepl-repl-buffer)) (kill-buffer nrepl-repl-buffer)) (kill-buffer (current-buffer))) (run-hooks 'nrepl-disconnected-hook)))) @@ -368,60 +383,87 @@ process buffer and run the hook `nrepl-disconnected-hook'." ;;; Client: Initialization -;; `nrepl-connect' is called from `nrepl-server-filter' and starts the client -;; process described by `nrepl-client-filter' and `nrepl-client-sentinel'. -(defun nrepl-connect (host port) - "Connect to a running nREPL server running on HOST and PORT." - (message "Connecting to nREPL server on %s:%s..." host port) - (let* ((nrepl-endpoint `(,host ,port)) - (process (open-network-stream "nrepl" - (nrepl-make-connection-buffer) - host - port))) - (set-process-filter process 'nrepl-client-filter) - (set-process-sentinel process 'nrepl-client-sentinel) - (set-process-coding-system process 'utf-8-unix 'utf-8-unix) - (with-current-buffer (process-buffer process) - (setq nrepl-endpoint `(,host ,port))) - (let ((nrepl-connection-dispatch (buffer-name (process-buffer process)))) - (nrepl-init-client-sessions process) - (nrepl-request:describe (nrepl--make-describe-handler (process-buffer process)))) - process)) - -(defun nrepl--make-describe-handler (process-buffer) - "Return a handler to describe into PROCESS-BUFFER." +;; `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 directory host port replp server-proc) + "Create new client process identified by DIRECTORY, HOST and PORT. +If DIRECTORY is nil, use `default-directory'. 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." + (let* ((endpoint (if (functionp nrepl-connection-endpoint) + (funcall nrepl-connection-endpoint directory port) + (nrepl--default-endpoint directory port))) + (directory (or directory default-directory)) + (host (or host (plist-get endpoint :hostname))) + (port (or port (plist-get endpoint :port))) + (proc-buffer-name (plist-get endpoint :proc-buffer-name)) + (server-buf (and server-proc + (buffer-name (process-buffer server-proc)))) + (client-buf (if replp + (cider-repl-create directory host port) + (nrepl-create-connection-buffer directory host port))) + (client-proc (open-network-stream "nrepl" client-buf host port)) + (nrepl-connection-dispatch client-buf)) + + (set-process-filter client-proc 'nrepl-client-filter) + (set-process-sentinel client-proc 'nrepl-client-sentinel) + (set-process-coding-system client-proc 'utf-8-unix 'utf-8-unix) + + (with-current-buffer client-buf + (when server-buf + (setq nrepl-project-dir (buffer-local-value 'nrepl-project-dir + (get-buffer 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) + nrepl-on-connection-buffer proc-buffer-name + nrepl-decoder-buffer (nrepl-decoder-buffer-name directory host port))) + + ;; Everything is set. We are ready to send requests. + (nrepl-request:clone (nrepl--new-tooling-session-handler client-proc)) + (nrepl-request:clone (nrepl--new-session-handler client-proc)) + (when replp + (nrepl-request:describe (nrepl--init-repl-handler client-buf t))) + client-proc)) + +(defun nrepl--init-repl-handler (connection-buffer replp) + "Return a handler to describe into CONNECTION-BUFFER." (lambda (response) (nrepl-dbind-response response (ops versions) - (with-current-buffer process-buffer + (with-current-buffer connection-buffer (setq nrepl-ops ops) (setq nrepl-versions versions))) - (cider-make-repl (get-buffer-process process-buffer)) - (nrepl-make-repl-connection-default process-buffer) - (cider-verify-required-nrepl-ops) + (when replp + (cider-repl-init connection-buffer)) + (nrepl-make-connection-default connection-buffer) + (cider--check-required-nrepl-ops) (cider--check-middleware-compatibility))) (defun nrepl--new-session-handler (process) "Create a new session handler for PROCESS." (lambda (response) - (nrepl-dbind-response response (id new-session) + (nrepl-dbind-response response (id new-session err) (remhash id nrepl-pending-requests) - (let ((connection-buffer (process-buffer process))) - (setq nrepl-session new-session - nrepl-connection-buffer connection-buffer) - (run-hooks 'nrepl-connected-hook))))) + (if new-session + (with-current-buffer (process-buffer process) + (setq nrepl-session new-session)) + (error "Could not create new session (%s)" err)) + (run-hooks 'nrepl-connected-hook)))) (defun nrepl--new-tooling-session-handler (process) "Create a new tooling session handler for PROCESS." (lambda (response) - (nrepl-dbind-response response (id new-session) - (with-current-buffer (process-buffer process) - (setq nrepl-tooling-session new-session) - (remhash id nrepl-pending-requests))))) - -(defun nrepl-init-client-sessions (process) - "Initialize client sessions for PROCESS." - (nrepl-request:clone (nrepl--new-session-handler process)) - (nrepl-request:clone (nrepl--new-tooling-session-handler process))) + (nrepl-dbind-response response (id new-session err) + (if new-session + (with-current-buffer (process-buffer process) + (remhash id nrepl-pending-requests) + (setq nrepl-tooling-session new-session)) + (error "Could not create new tooling session (%s)"))))) (defun nrepl--port-from-file (file) "Attempts to read port from a file named by FILE." @@ -628,13 +670,12 @@ The result is a plist with keys :value, :stderr and :stdout." (nrepl-send-request request (nrepl-sync-request-handler (current-buffer))) (while (or (null nrepl-last-sync-response) (null (plist-get nrepl-last-sync-response :done))) - (accept-process-output nil 0.005) + (accept-process-output nil 0.01) ;; break out in case we don't receive a response for a while (when nrepl-sync-request-timeout (let ((seconds-ellapsed (cadr (time-subtract (current-time) nrepl-last-sync-request-timestamp)))) (when (> seconds-ellapsed nrepl-sync-request-timeout) - (error "Sync nREPL request %s timed out" request) - (keyboard-quit))))) + (error "nREPL sync request timed out %s" request))))) nrepl-last-sync-response)) (defun nrepl-sync-request:eval (input &optional ns session) @@ -646,11 +687,29 @@ If NS is non-nil, include it in the request. SESSION defaults to current session ;;; Server -;; The server side process is started by `cider-jack-in' and has a very simple -;; filter that pipes its output directly into its process buffer +;; The server side process is started by `nrepl-start-server-process' and has a +;; very simple filter that pipes its output directly into its process buffer ;; (*nrepl-server*). The main purpose of this process is to start the actual -;; nrepl communication client (`nrepl-client-filter') once the message "nREPL +;; nrepl communication client (`nrepl-client-filter') when the message "nREPL ;; server started on port ..." is detected. + +(defun nrepl-start-server-process (directory cmd) + "Start nREPL server process in DIRECTORY using shell command CMD. +Return a newly created process." + (let* ((default-directory (or directory default-directory)) + (serv-buf (get-buffer-create (generate-new-buffer-name + (nrepl-server-buffer-name directory)))) + (serv-proc (start-file-process-shell-command + "nrepl-server" serv-buf cmd))) + (set-process-filter serv-proc 'nrepl-server-filter) + (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)) + (message "Starting nREPL server via %s..." + (propertize cmd 'face 'font-lock-keyword-face)) + serv-proc)) + (defun nrepl-server-filter (process output) "Process nREPL server output from PROCESS contained in OUTPUT." (with-current-buffer (process-buffer process) @@ -661,21 +720,11 @@ If NS is non-nil, include it in the request. SESSION defaults to current session (let ((port (string-to-number (match-string 1 output)))) (message (format "nREPL server started on %s" port)) (with-current-buffer (process-buffer process) - (let* ((endpoint (if (functionp nrepl-connection-endpoint) - (funcall nrepl-connection-endpoint - default-directory port) - (nrepl--default-endpoint default-directory port))) - (hostname (plist-get endpoint :hostname)) - (port (plist-get endpoint :port)) - (proc-buffer-name (plist-get endpoint :proc-buffer-name))) - (let ((nrepl-process (nrepl-connect hostname port))) - (setq nrepl-connection-buffer - (buffer-name (process-buffer nrepl-process))) - (with-current-buffer (process-buffer nrepl-process) - (setq nrepl-server-buffer (buffer-name (process-buffer process)) - nrepl-project-dir (buffer-local-value - 'nrepl-project-dir (process-buffer process)) - nrepl-on-connection-buffer proc-buffer-name)))))))) + (let ((client-proc (nrepl-start-client-process + default-directory nil port t process))) + ;; fixme: Bad connection tracking system. There can be multiple client + ;; connections per server + (setq nrepl-connection-buffer (buffer-name (process-buffer client-proc)))))))) (defun nrepl-server-sentinel (process event) "Handle nREPL server PROCESS EVENT." @@ -877,9 +926,10 @@ This is bound for the duration of the handling of that message") tramp-current-host nrepl-host)) -(defun nrepl-make-connection-buffer () - "Create an nREPL connection buffer." - (let ((buffer (generate-new-buffer (nrepl-connection-buffer-name)))) +(defun nrepl-create-connection-buffer (&optional project-dir host port) + "Create an nREPL connection buffer. +PROJECT-DIR, PORT and HOST are as in `nrepl-make-buffer-name'." + (let ((buffer (generate-new-buffer (nrepl-connection-buffer-name project-dir host port)))) (with-current-buffer buffer (buffer-disable-undo) (setq-local kill-buffer-query-functions nil)) @@ -902,7 +952,7 @@ Purge the dead buffers from the `nrepl-connection-list' beforehand." ;; VS[06-08-2014]: FIXME: Bad user api; don't burden users with management of ;; the connection list, same holds for `cider-rotate-connection'. -(defun nrepl-make-repl-connection-default (connection-buffer) +(defun nrepl-make-connection-default (connection-buffer) "Make the nREPL CONNECTION-BUFFER the default connection. Moves CONNECITON-BUFFER to the front of `nrepl-connection-list'." (interactive (list nrepl-connection-buffer)) @@ -922,10 +972,9 @@ Also closes associated REPL and server buffers." (setq nrepl-connection-list (delq (buffer-name buffer) nrepl-connection-list)) (when (buffer-live-p buffer) - (dolist (buf-name `(,(buffer-local-value 'nrepl-repl-buffer buffer) - ,(buffer-local-value 'nrepl-server-buffer buffer) - ,(buffer-local-value - 'nrepl-on-connection-buffer buffer) + (dolist (buf-name `(,(buffer-local-value 'nrepl-server-buffer buffer) + ,(buffer-local-value 'nrepl-on-connection-buffer buffer) + ,(buffer-local-value 'nrepl-decoder-buffer buffer) ,buffer)) (when buf-name (cider--close-buffer buf-name))))))) @@ -1039,7 +1088,7 @@ F is a function of two arguments, the ewoc and the data at point." "Make the connection in EWOC specified by DATA default. Refreshes EWOC." (interactive) - (nrepl-make-repl-connection-default data) + (nrepl-make-connection-default data) (ewoc-refresh ewoc)) (defun nrepl-connections-close-connection () diff --git a/test/cider-tests.el b/test/cider-tests.el index d85272b2..b7fc131f 100644 --- a/test/cider-tests.el +++ b/test/cider-tests.el @@ -96,19 +96,19 @@ ,@body (mapc 'kill-buffer (list ,@buffer-names)))))) -(ert-deftest test-nrepl-make-repl-connection-default () +(ert-deftest test-nrepl-make-connection-default () (let ((connections (nrepl-connection-buffers))) (cider-test-with-buffers (a b) (should (get-buffer a)) (should (get-buffer b)) ;; Add one connection - (nrepl-make-repl-connection-default a) + (nrepl-make-connection-default a) (should (equal (append (list (buffer-name a)) connections) (nrepl-connection-buffers))) (should (equal (buffer-name a) (nrepl-current-connection-buffer))) ;; Add second connection - (nrepl-make-repl-connection-default b) + (nrepl-make-connection-default b) (should (equal (append (list (buffer-name b) (buffer-name a)) connections) (nrepl-connection-buffers))) (should (equal (buffer-name b) (nrepl-current-connection-buffer)))))) @@ -117,8 +117,8 @@ (let ((connections (nrepl-connection-buffers))) (cider-test-with-buffers (a b) - (nrepl-make-repl-connection-default a) - (nrepl-make-repl-connection-default b) + (nrepl-make-connection-default a) + (nrepl-make-connection-default b) ;; killing a buffer should see it purged from the connection list (kill-buffer a) (should (equal (append (list (buffer-name b)) connections) @@ -168,8 +168,8 @@ (let ((connections (nrepl-connection-buffers))) (cider-test-with-buffers (a b) - (nrepl-make-repl-connection-default a) - (nrepl-make-repl-connection-default b) + (nrepl-make-connection-default a) + (nrepl-make-connection-default b) ;; closing a buffer should see it removed from the connection list (nrepl-close a) (should (not (buffer-live-p a))) |