emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/eglot d0a657e81c 3/4: Per #681: Add support for "single


From: ELPA Syncer
Subject: [elpa] externals/eglot d0a657e81c 3/4: Per #681: Add support for "single server, multiple modes"
Date: Mon, 26 Sep 2022 06:57:34 -0400 (EDT)

branch: externals/eglot
commit d0a657e81c5b02529c4f32c2e51e00bdf4729a9e
Author: João Távora <joaotavora@gmail.com>
Commit: João Távora <joaotavora@gmail.com>

    Per #681: Add support for "single server, multiple modes"
    
    Previously, if an entry such as:
    
       ((c++-mode c-mode) . ("clangd)")
    
    were found in eglot-server-programs, it meant that opening a .cpp file
    and a .c file in the same project and enabling eglot for both would
    lead to two clangd instances.  Now only one instance is created to
    handle all buffers of those major modes, as long as they are in the
    same project.
    
    This change accomplishes this with minimal changes and NO modification
    to the already complicated syntax of eglot-server-programs.
    
    Naturally, this means that a subtle backward-incompatibility was
    introduced.  If, instead of "clangd", someone is using some kind
    "c++-or-c-but-not-both-at-once" server, this commit now breaks that
    person's configuration.
    
    After analysing the entries of this variable, an educated guess was
    made that this situation is rare.  If it's not rare, then some change
    to the syntax of eglot-server-programs will have to ensue.
    
    * eglot.el (eglot-server-programs): Update docstring.
    (eglot-lsp-server): Replace major-mode -> major-modes.
    (eglot--lookup-mode): Rework.
    (eglot--guess-contact): Rework.
    (eglot--connect): Reword first parameter.
    (eglot-reconnect): Use eglot--major-modes.
    (eglot--read-server): Rework.
    (eglot--ensure-list): New helper.
    (eglot-current-server): Rework.
    (eglot-handle-request workspace/configuration): Use first of
    managed major modes.
    
    * NEWS.md: Mention change.
---
 NEWS.md  |  9 +++++++++
 eglot.el | 70 +++++++++++++++++++++++++++++++++++++++++-----------------------
 2 files changed, 54 insertions(+), 25 deletions(-)

diff --git a/NEWS.md b/NEWS.md
index c439a2c1ca..58d14abcdc 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,5 +1,14 @@
 # (upcoming)
 
+#### Support for "single server, multiple modes" ([#681][github#681])
+
+Previously, if an entry such as `((c++-mode c-mode) . ("clangd)")` was
+found in `eglot-server-programs`, it meant that `.cpp` files `.c`
+files would lead to two `clangd` instances for managing them, even if
+these files were in the same project.  Now only one is created.  It is
+assumed that most, if not all, servers configured in
+`eglot-server-programs` handle this correctly.
+
 ##### `eglot-imenu` no longer uses problematic "special elements" 
([#758][github#758], [#536][github#536], [#535][github#535])
 
 Though Eglot's `eglot-imenu` returned a fully compliant `imenu`
diff --git a/eglot.el b/eglot.el
index fa29c60617..6a1eb1282c 100644
--- a/eglot.el
+++ b/eglot.el
@@ -218,7 +218,8 @@ MAJOR-MODE can be:
 
 * A list combining the previous two alternatives, meaning
   multiple major modes will be associated with a single server
-  program.
+  program.  This association is such that the same resulting
+  server process will manage buffers of different major modes.
 
 CONTACT can be:
 
@@ -760,9 +761,9 @@ treated as in `eglot-dbind'."
     :documentation "Short nickname for the associated project."
     :accessor eglot--project-nickname
     :reader eglot-project-nickname)
-   (major-mode
-    :documentation "Major mode symbol."
-    :accessor eglot--major-mode)
+   (major-modes
+    :documentation "Major modes server is responsible for in a given project."
+    :accessor eglot--major-modes)
    (language-id
     :documentation "Language ID string for the mode."
     :accessor eglot--language-id)
@@ -879,16 +880,31 @@ PRESERVE-BUFFERS as in `eglot-shutdown', which see."
 
 (defun eglot--lookup-mode (mode)
   "Lookup `eglot-server-programs' for MODE.
-Return (LANGUAGE-ID . CONTACT-PROXY).  If not specified,
-LANGUAGE-ID is determined from MODE."
+Return (MANAGED-MODES LANGUAGE-ID CONTACT-PROXY).
+
+MANAGED-MODES is a list with MODE as its first elements.
+Subsequent elements are other major modes also potentially
+managed by the server that is to manage MODE.
+
+If not specified in `eglot-server-programs' (which see),
+LANGUAGE-ID is determined from MODE's name.
+
+CONTACT-PROXY is the value of the corresponding
+`eglot-server-programs' entry."
   (cl-loop
    for (modes . contact) in eglot-server-programs
+   for mode-symbols = (cons mode
+                            (delete mode
+                                    (mapcar #'car
+                                            (mapcar #'eglot--ensure-list
+                                                    (eglot--ensure-list 
modes)))))
    thereis (cl-some
             (lambda (spec)
               (cl-destructuring-bind (probe &key language-id &allow-other-keys)
-                  (if (consp spec) spec (list spec))
+                  (eglot--ensure-list spec)
                 (and (provided-mode-derived-p mode probe)
-                     (cons
+                     (list
+                      mode-symbols
                       (or language-id
                           (or (get mode 'eglot-language-id)
                               (get spec 'eglot-language-id)
@@ -903,7 +919,7 @@ Return (MANAGED-MODE PROJECT CLASS CONTACT LANG-ID).  If 
INTERACTIVE is
 non-nil, maybe prompt user, else error as soon as something can't
 be guessed."
   (let* ((guessed-mode (if buffer-file-name major-mode))
-         (managed-mode
+         (main-mode
           (cond
            ((and interactive
                  (or (>= (prefix-numeric-value current-prefix-arg) 16)
@@ -916,10 +932,11 @@ be guessed."
            ((not guessed-mode)
             (eglot--error "Can't guess mode to manage for `%s'" 
(current-buffer)))
            (t guessed-mode)))
-         (lang-id-and-guess (eglot--lookup-mode guessed-mode))
-         (language-id (or (car lang-id-and-guess)
+         (triplet (eglot--lookup-mode main-mode))
+         (managed-modes (car triplet))
+         (language-id (or (cadr triplet)
                           (string-remove-suffix "-mode" (symbol-name 
guessed-mode))))
-         (guess (cdr lang-id-and-guess))
+         (guess (caddr triplet))
          (guess (if (functionp guess)
                     (funcall guess interactive)
                   guess))
@@ -945,7 +962,7 @@ be guessed."
                (cond (current-prefix-arg base-prompt)
                      ((null guess)
                       (format "[eglot] Sorry, couldn't guess for `%s'!\n%s"
-                              managed-mode base-prompt))
+                              main-mode base-prompt))
                      ((and program
                            (not (file-name-absolute-p program))
                            (not (eglot--executable-find program t)))
@@ -967,7 +984,7 @@ be guessed."
                      full-program-invocation
                      'eglot-command-history)))
               guess)))
-    (list managed-mode (eglot--current-project) class contact language-id)))
+    (list managed-modes (eglot--current-project) class contact language-id)))
 
 (defvar eglot-lsp-context)
 (put 'eglot-lsp-context 'variable-documentation
@@ -1038,7 +1055,7 @@ INTERACTIVE is t if called interactively."
   (interactive (list (eglot--current-server-or-lose) t))
   (when (jsonrpc-running-p server)
     (ignore-errors (eglot-shutdown server interactive nil 'preserve-buffers)))
-  (eglot--connect (eglot--major-mode server)
+  (eglot--connect (eglot--major-modes server)
                   (eglot--project server)
                   (eieio-object-class-name server)
                   (eglot--saved-initargs server)
@@ -1115,12 +1132,12 @@ Each function is passed the server as an argument")
 (defvar-local eglot--cached-server nil
   "A cached reference to the current EGLOT server.")
 
-(defun eglot--connect (managed-major-mode project class contact language-id)
-  "Connect to MANAGED-MAJOR-MODE, LANGUAGE-ID, PROJECT, CLASS and CONTACT.
+(defun eglot--connect (managed-modes project class contact language-id)
+  "Connect to MANAGED-MODES, LANGUAGE-ID, PROJECT, CLASS and CONTACT.
 This docstring appeases checkdoc, that's all."
   (let* ((default-directory (project-root project))
          (nickname (file-name-base (directory-file-name default-directory)))
-         (readable-name (format "EGLOT (%s/%s)" nickname managed-major-mode))
+         (readable-name (format "EGLOT (%s/%s)" nickname managed-modes))
          autostart-inferior-process
          server-info
          (contact (if (functionp contact) (funcall contact) contact))
@@ -1180,7 +1197,7 @@ This docstring appeases checkdoc, that's all."
     (setf (eglot--saved-initargs server) initargs)
     (setf (eglot--project server) project)
     (setf (eglot--project-nickname server) nickname)
-    (setf (eglot--major-mode server) managed-major-mode)
+    (setf (eglot--major-modes server) (eglot--ensure-list managed-modes))
     (setf (eglot--language-id server) language-id)
     (setf (eglot--inferior-process server) autostart-inferior-process)
     (run-hook-with-args 'eglot-server-initialized-hook server)
@@ -1236,7 +1253,7 @@ This docstring appeases checkdoc, that's all."
                                      (setf (eglot--inhibit-autoreconnect 
server)
                                            (null eglot-autoreconnect)))))))
                           (let ((default-directory (project-root project))
-                                (major-mode managed-major-mode))
+                                (major-mode (car managed-modes)))
                             (hack-dir-local-variables-non-file-buffer)
                             (run-hook-with-args 'eglot-connect-hook server))
                           (eglot--message
@@ -1244,7 +1261,7 @@ This docstring appeases checkdoc, that's all."
 in project `%s'."
                            (or (plist-get serverInfo :name)
                                (jsonrpc-name server))
-                           managed-major-mode
+                           managed-modes
                            (eglot-project-nickname server))
                           (when tag (throw tag t))))
                       :timeout eglot-connect-timeout
@@ -1545,8 +1562,8 @@ and just return it.  PROMPT shouldn't end with a question 
mark."
                           being hash-values of eglot--servers-by-project
                           append servers))
         (name (lambda (srv)
-                (format "%s/%s" (eglot-project-nickname srv)
-                        (eglot--major-mode srv)))))
+                (format "%s %s" (eglot-project-nickname srv)
+                        (eglot--major-modes srv)))))
     (cond ((null servers)
            (eglot--error "No servers!"))
           ((or (cdr servers) (not dont-if-just-the-one))
@@ -1570,6 +1587,8 @@ and just return it.  PROMPT shouldn't end with a question 
mark."
 (defun eglot--plist-keys (plist) "Get keys of a plist."
   (cl-loop for (k _v) on plist by #'cddr collect k))
 
+(defun eglot--ensure-list (x) (if (listp x) x (list x)))
+
 
 ;;; Minor modes
 ;;;
@@ -1700,7 +1719,8 @@ Use `eglot-managed-p' to determine if current buffer is 
managed.")
         (or eglot--cached-server
             (cl-find major-mode
                      (gethash (eglot--current-project) 
eglot--servers-by-project)
-                     :key #'eglot--major-mode)
+                     :key #'eglot--major-modes
+                     :test #'memq)
             (and eglot-extend-to-xref
                  buffer-file-name
                  (gethash (expand-file-name buffer-file-name)
@@ -2288,7 +2308,7 @@ When called interactively, use the currently active 
server"
                                 (file-directory-p uri-path))
                            (file-name-as-directory uri-path)
                          (project-root (eglot--project server)))))
-                (setq-local major-mode (eglot--major-mode server))
+                (setq-local major-mode (car (eglot--major-modes server)))
                 (hack-dir-local-variables-non-file-buffer)
                 (cl-loop for (wsection o)
                          on (eglot--workspace-configuration-plist server)



reply via email to

[Prev in Thread] Current Thread [Next in Thread]