Subject: Re: bug#67463: 30.0.50; Eglot may manage js-json-mode buffers with wrong server

From: João Távora <[email-masked]>
Date: Sun, 3 Dec 2023 20:51:02 +0000

On Sun, Dec 3, 2023 at 6:14 PM João Távora <[email-masked]> wrote:
>
> On Sat, Dec 2, 2023 at 12:51 PM Eli Zaretskii <[email-masked]> wrote:
> >
> > > From: Pengji Zhang <[email-masked]>
> > > Date: Sun, 26 Nov 2023 16:15:54 -0500
> > >
> > > It seems that buffers in `js-json-mode', the default mode for JSON
> > > files, could be wrongly managed by JavaScript language servers of Eglot,
> > > perhaps because:
> > >
> > >   - `js-json-mode' is derived from `js-mode';
> > >   - Eglot uses `provided-mode-derived-p' to determine whether a buffer
> > >     should be managed by a server.
> > >
> > > Here is a recipe to reproduce the problem I encountered:
> > >
> > >   1. Install Node.js[0] and NPM[1]. (Sorry, but I could not find an
> > >      easier way to set up the environment. They may be installable with
> > >      your system's package manager.)
> > >   2. mkdir /tmp/repro && cd /tmp/repro
> > >   3. npm install typescript typescript-language-server (This will install
> > >      the language server locally in the directory.)
> > >   4. emacs -Q
> > >   5. C-x C-f main.js RET
> > >   6. C-u M-x eglot; enter "npx typescript-language-server --stdio"
> > >   7. C-x C-f package.json RET (This file is automatically created by
> > >      `npm install'.)
> > >
> > > I expect that the 'package.json' buffer is not managed by Eglot, but it
> > > is actually managed by typescript-language-server, which does not really
> > > understand JSON and is reporting some invalid errors.
> > >
> > > Thanks!
> > >
> > > [0] https://nodejs.org/en
> > > [1] https://docs.npmjs.com/downloading-and-installing-node-js-and-npm
> >
> > João, any suggestions?
>
>
> Thanks Eli.  Yes, some notes:
>
> 1. JSON is technically Javascript (but only really technically, in actual
>    programming practice  it's not much use editing JSON as Javascript)
>
> 2. For the above reason the derivation of `json-json-mode` from `js-mode`
>    is technically correct.  When looking at a JSON file you are technically
>    looking at some minimalistic Javascript code.
>
> 3. It wouldn't be a bad idea for Emacs to do whatever behaviour-reuse it
>    is doing for 'js-json-mode' with inheritance from 'js-mode' with some
>    other mechanism like composition.  This would immediately solve Pengji's
>    problem.  But it is not trivial to do so.
>
> 4. It follows from 1 and 2 that this problem can be viewed as a bug in
>    typescript-language-server.  It's complaining we're not giving it
>    a Javascript file, but it has no reason to complain: we are!  Not sure
>    if it's worth arguing with the server devs like so.  Maybe.
>
> 5. A more promising change that can be tried today is to make use
>    of Eglot's :language-id feature, which is already in place for this
>    family of modes.  The current one looks like this:
>
>   (((js-mode :language-id "javascript")
>     (js-ts-mode :language-id "javascript")
>     (tsx-ts-mode :language-id "typescriptreact")
>     (typescript-ts-mode :language-id "typescript")
>     (typescript-mode :language-id "typescript"))
>   . ("typescript-language-server" "--stdio"))
>
> Which means that any Javascript file is reported as javascript.
> Maybe just adding
>
>      (js-json-mode :language-id "json")
>
> there would work.

Nope this doesn't work.  Even the slightly more involved patch after
my sig  doesn't work, and it correctly reports the :languageId as "json"

So I think the best courses of action are 3 and 4, in that order.

Another workaround is to first start M-x eglot in some json file
in your project.  If you have one of:

. ,(eglot-alternatives '(("vscode-json-language-server" "--stdio")
                         ("vscode-json-languageserver" "--stdio")
                         ("json-languageserver" "--stdio"))))

installed, then these json-specific servers should start to manage
js-json-mode files in your project.  If afterwards you start M-x
eglot in a plain js file, that server won't be (erroneously) used
to manage JSON files.

João

diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index d410367f902..5cfd5baa225 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -222,6 +222,7 @@ eglot-server-programs

("vscode-json-languageserver" "--stdio")

("json-languageserver" "--stdio"))))
                                 (((js-mode :language-id "javascript")
+                                  (js-json-mode :language-id "json")
;; bug#67463
                                   (js-ts-mode :language-id "javascript")
                                   (tsx-ts-mode :language-id "typescriptreact")
                                   (typescript-ts-mode :language-id
"typescript")
@@ -2493,10 +2494,12 @@ eglot--languageId
   "Compute LSP \\='languageId\\=' string for current buffer.
 Doubles as an predicate telling if SERVER can manage current
 buffer."
-  (cl-loop for (mode . languageid) in
-           (eglot--languages server)
-           when (provided-mode-derived-p major-mode mode)
-           return languageid))
+  ;; bug#67463
+  (cl-loop for pred in (list #'equal #'provided-mode-derived-p)
+           thereis (cl-loop for (mode . languageid) in
+                            (eglot--languages server)
+                            when (funcall pred major-mode mode)
+                            return languageid)))

 (defun eglot--TextDocumentItem ()
   "Compute TextDocumentItem object for current buffer."