branch: externals/eglot commit 95187cfcecbfafb090c530808e2f6932cf9d860e Author: João Távora <joaotav...@gmail.com> Commit: João Távora <joaotav...@gmail.com>
Connect to LSP server via TCP * eglot.el (eglot--make-process): Rename from eglot-make-local-process. (eglot): Fix docstring and rework. (eglot--bootstrap-fn): Remove (eglot--contact): New process-local var. (eglot--connect): Take CONTACT arg. (eglot--reconnect): Rework. --- eglot.el | 137 ++++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 73 insertions(+), 64 deletions(-) diff --git a/eglot.el b/eglot.el index ac52b9a..c5a0c0f 100644 --- a/eglot.el +++ b/eglot.el @@ -133,30 +133,40 @@ A list (ID WHAT DONE-P)." t) "Status as declared by the server. A list (WHAT SERIOUS-P)." t) -(eglot--define-process-var eglot--bootstrap-fn nil - "Function for returning processes/connetions to LSP servers. -Must be a function of one arg, a name, returning a process -object.") +(eglot--define-process-var eglot--contact nil + "Method used to contact a server. +Either a list of strings (a shell command and arguments), or a +list of a single string of the form <host>:<port>") (eglot--define-process-var eglot--buffer-open-count (make-hash-table) "Keeps track of didOpen/didClose notifs for each buffer.") -(defun eglot-make-local-process (name command) - "Make a local LSP process from COMMAND. +(defun eglot--make-process (name contact) + "Make a process from CONTACT. NAME is a name to give the inferior process or connection. -Returns a process object." +CONTACT is as `eglot--contact'. Returns a process object." (let* ((readable-name (format "EGLOT server (%s)" name)) + (buffer (get-buffer-create + (format "*%s inferior*" readable-name))) + (singleton (and (null (cdr contact)) (car contact))) (proc - (make-process - :name readable-name - :buffer (get-buffer-create - (format "*%s inferior*" readable-name)) - :command command - :connection-type 'pipe - :filter 'eglot--process-filter - :sentinel 'eglot--process-sentinel - :stderr (get-buffer-create (format "*%s stderr*" - name))))) + (if (and + singleton + (string-match "^[\s\t]*\\(.*\\):\\([[:digit:]]+\\)[\s\t]*$" + singleton)) + (open-network-stream readable-name + buffer + (match-string 1 singleton) + (string-to-number (match-string 2 singleton))) + (make-process + :name readable-name + :buffer buffer + :command contact + :connection-type 'pipe + :stderr (get-buffer-create (format "*%s stderr*" + name)))))) + (set-process-filter proc #'eglot--process-filter) + (set-process-sentinel proc #'eglot--process-sentinel) proc)) (defmacro eglot--obj (&rest what) @@ -180,13 +190,12 @@ Returns a process object." retval)) (defun eglot--connect (project managed-major-mode - short-name bootstrap-fn &optional success-fn) - "Make a connection for PROJECT, SHORT-NAME and MANAGED-MAJOR-MODE. -Use BOOTSTRAP-FN to make the actual process object. Call + short-name contact &optional success-fn) + "Connect for PROJECT, MANAGED-MAJOR-MODE, SHORT-NAME and CONTACT. SUCCESS-FN with no args if all goes well." - (let* ((proc (funcall bootstrap-fn short-name)) + (let* ((proc (eglot--make-process short-name contact)) (buffer (process-buffer proc))) - (setf (eglot--bootstrap-fn proc) bootstrap-fn + (setf (eglot--contact proc) contact (eglot--project proc) project (eglot--major-mode proc) managed-major-mode) (with-current-buffer buffer @@ -226,15 +235,17 @@ SUCCESS-FN with no args if all goes well." "History of COMMAND arguments to `eglot'.") (defun eglot (managed-major-mode command &optional interactive) - ;; FIXME: Later make this function also connect to TCP servers by - ;; overloading semantics on COMMAND. "Start a Language Server Protocol server. Server is started with COMMAND and manages buffers of MANAGED-MAJOR-MODE for the current project. COMMAND is a list of strings, an executable program and -optionally its arguments. MANAGED-MAJOR-MODE is an Emacs major -mode. +optionally its arguments. If the first and only string in the +list is of the form \"<host>:<port>\" it is taken as an +indication to connect to a server instead of starting one. This +is also know as the server's \"contact\". + +MANAGED-MAJOR-MODE is an Emacs major mode. With a prefix arg, prompt for MANAGED-MAJOR-MODE and COMMAND, else guess them from current context and `eglot-executables'. @@ -257,47 +268,45 @@ INTERACTIVE is t if called interactively." (cdr (assoc managed-major-mode eglot-executables)))) (list managed-major-mode - (if current-prefix-arg - (split-string-and-unquote - (read-shell-command "[eglot] Run program: " - (combine-and-quote-strings guessed-command) - 'eglot-command-history)) - guessed-command) + (let ((prompt + (cond (current-prefix-arg + "[eglot] Execute program (or connect to <host>:<port>) ") + ((null guessed-command) + (format "[eglot] Sorry, couldn't guess for `%s'!\n\ +Execute program (or connect to <host>:<port>) " + managed-major-mode))))) + (if prompt + (split-string-and-unquote + (read-shell-command prompt + (combine-and-quote-strings guessed-command) + 'eglot-command-history)) + guessed-command)) t))) (let* ((project (project-current)) (short-name (eglot--project-short-name project))) - (unless project (eglot--error - "Cannot work without a current project!")) - (let ((current-process (eglot--current-process)) - (command - (or command - (eglot--error "Don't know how to start EGLOT for %s buffers" - major-mode)))) - (cond - ((and current-process - (process-live-p current-process)) - (when (and - interactive - (y-or-n-p "[eglot] Live process found, reconnect instead? ")) - (eglot-reconnect current-process interactive))) - (t - (eglot--connect - project - managed-major-mode - short-name - (lambda (name) - (eglot-make-local-process - name - command)) - (lambda (proc) - (eglot--message "Connected! Process `%s' now managing `%s'\ + (unless project (eglot--error "Cannot work without a current project!")) + (unless command (eglot--error "Don't know how to start EGLOT for %s buffers" + major-mode)) + (let ((current-process (eglot--current-process))) + (cond ((and (process-live-p current-process) + interactive + (y-or-n-p "[eglot] Live process found, reconnect instead? ")) + (eglot-reconnect current-process interactive)) + (t + (eglot--connect + project + managed-major-mode + short-name + command + (lambda (proc) + (eglot--message "Connected! Process `%s' now managing `%s'\ buffers in project %s." - proc - managed-major-mode - short-name) - (dolist (buffer (buffer-list)) - (with-current-buffer buffer - (eglot--maybe-activate-editing-mode proc)))))))))) + proc + managed-major-mode + short-name) + (dolist (buffer (buffer-list)) + (with-current-buffer buffer + (eglot--maybe-activate-editing-mode proc)))))))))) (defun eglot-reconnect (process &optional interactive) "Reconnect to PROCESS. @@ -309,7 +318,7 @@ INTERACTIVE is t if called interactively." (eglot--project process) (eglot--major-mode process) (eglot--short-name process) - (eglot--bootstrap-fn process) + (eglot--contact process) (lambda (proc) (eglot--message "Reconnected!") (dolist (buffer (buffer-list))