Subject: bug#76423: Major-mode provided LSP server for Eglot

From: Stefan Monnier <[email-masked]>
Date: Wed, 19 Feb 2025 15:33:07 -0500

Tags: patch

Tags: patch

Eglot should make it easier for major modes to suggest which LSP
server to use.
Currently major modes end up doing funny dances like:

    branch: externals/bicep-ts-mode
    commit 8192a400fae45cdde32526f96bb7ed5158d98008
    Author: Kevin Brubeck Unhammer <[email-masked]>
    Commit: Kevin Brubeck Unhammer <[email-masked]>
    
        Wait with altering eglot-server-programs until eglot loaded
    ---
     bicep-ts-mode.el | 10 +++++-----
     1 file changed, 5 insertions(+), 5 deletions(-)
    
    diff --git a/bicep-ts-mode.el b/bicep-ts-mode.el
    index 204629b4c6..f047bccae0 100644
    --- a/bicep-ts-mode.el
    +++ b/bicep-ts-mode.el
    @@ -205,11 +205,11 @@ Return nil if there is no name or if NODE is not a defun node."
                                            . bicep-ts-mode))))
     
     ;;;###autoload
    -(and (boundp 'eglot-server-programs)
    -     (file-exists-p (bicep-langserver-path))
    -     (progn
    -       (add-to-list 'eglot-server-programs
    -                    `(bicep-ts-mode . ("dotnet" ,(bicep-langserver-path))))))
    +(eval-after-load 'eglot
    +  '(and (file-exists-p (bicep-langserver-path))
    +        (progn
    +          (add-to-list 'eglot-server-programs
    +                       `(bicep-ts-mode . ("dotnet" ,(bicep-langserver-path)))))))
     
     (provide 'bicep-ts-mode)

The patch below would let this major modes do it with a simple:

      (setq-local eglot-server-programs
                  `(bicep-ts-mode . ("dotnet" ,(bicep-langserver-path))))

- Stefan

diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index bd28174e7da..3aff922be1d 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -235,6 +235,10 @@ eglot-alternatives
                       when probe return (cons probe args)
                       finally (funcall err)))))))
 
+(defvar-local eglot-server-program nil
+  "Description of the server to use.
+Follows the same syntax as any element of `eglot-server-programs', ")
+
 (defvar eglot-server-programs
   ;; FIXME: Maybe this info should be distributed into the major modes
   ;; themselves where they could set a buffer-local `eglot-server-program'
@@ -354,7 +358,7 @@ eglot-server-programs
 
 * In the most common case, a symbol such as `c-mode';
 
-* A list (MAJOR-MODE-SYMBOL :LANGUAGE-ID ID) where
+* A list (MAJOR-MODE-SYMBOL :language-id ID) where
   MAJOR-MODE-SYMBOL is the aforementioned symbol and ID is a
   string identifying the language to the server;
 
@@ -1331,7 +1335,9 @@ eglot--lookup-mode
                                         (replace-regexp-in-string
                                          "\\(?:-ts\\)?-mode$" ""
                                          (symbol-name sym))))))
-   for (modes . contact) in eglot-server-programs
+   for (modes . contact) in (if (null eglot-server-program)
+                                eglot-server-programs
+                              (cons eglot-server-program eglot-server-programs))
    for llists = (mapcar #'eglot--ensure-list
                         (if (or (symbolp modes) (keywordp (cadr modes)))
                             (list modes) modes))