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

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

-----
Auxiliary information:

- Emacs version: 30.0.50 (commit ea4c5fcd77257d7a7c050714b809b1507d93a6ef)
- Eglot version: 1.15 (comes with the Emacs repository of the commit above)
- Contents of `eglot-events-buffer':

[internal] Sun Nov 26 15:24:00 2023:
(:message
 "Running language server: npx typescript-language-server --stdio")
[client-request] (id:1) Sun Nov 26 15:24:00 2023:
(:jsonrpc "2.0" :id 1 :method "initialize" :params
 (:processId 26705 :clientInfo
     (:name "Eglot" :version "1.15") :rootPath
     "/tmp/eglot-bug/" :rootUri
     "file:///tmp/eglot-bug" :initializationOptions
     #s(hash-table size 1 test eql rehash-size 1.5
   rehash-threshold 0.8125 data ())
     :capabilities
     (:workspace
      (:applyEdit t :executeCommand
  (:dynamicRegistration :json-false)
  :workspaceEdit (:documentChanges t)
  :didChangeWatchedFiles
  (:dynamicRegistration t) :symbol
  (:dynamicRegistration :json-false)
  :configuration t :workspaceFolders
  t)
      :textDocument
      (:synchronization
(:dynamicRegistration :json-false :willSave t
     :willSaveWaitUntil t
     :didSave t)
:completion
(:dynamicRegistration :json-false
     :completionItem
     (:snippetSupport
      :json-false
      :deprecatedSupport t
      :resolveSupport
      (:properties
["documentation"
"details"
"additionalTextEdits"])
      :tagSupport
      (:valueSet [1]))
     :contextSupport t)
:hover
(:dynamicRegistration :json-false
     :contentFormat
     ["plaintext"])
:signatureHelp
(:dynamicRegistration :json-false
     :signatureInformation
     (:parameterInformation
      (:labelOffsetSupport t)
      :documentationFormat
      ["plaintext"]
      :activeParameterSupport
      t))
:references (:dynamicRegistration :json-false)
:definition
(:dynamicRegistration :json-false :linkSupport
     t)
:declaration
(:dynamicRegistration :json-false :linkSupport
     t)
:implementation
(:dynamicRegistration :json-false :linkSupport
     t)
:typeDefinition
(:dynamicRegistration :json-false :linkSupport
     t)
:documentSymbol
(:dynamicRegistration :json-false
     :hierarchicalDocumentSymbolSupport
     t :symbolKind
     (:valueSet
      [1 2 3 4 5 6 7 8 9 10
 11 12 13 14 15 16 17
 18 19 20 21 22 23 24
 25 26]))
:documentHighlight
(:dynamicRegistration :json-false) :codeAction
(:dynamicRegistration :json-false
     :resolveSupport
     (:properties
      ["edit" "command"])
     :dataSupport t
     :codeActionLiteralSupport
     (:codeActionKind
      (:valueSet
["quickfix" "refactor"
"refactor.extract"
"refactor.inline"
"refactor.rewrite"
"source"
"source.organizeImports"]))
     :isPreferredSupport t)
:formatting (:dynamicRegistration :json-false)
:rangeFormatting
(:dynamicRegistration :json-false) :rename
(:dynamicRegistration :json-false) :inlayHint
(:dynamicRegistration :json-false)
:publishDiagnostics
(:relatedInformation :json-false
    :codeDescriptionSupport
    :json-false :tagSupport
    (:valueSet [1 2])))
      :window
      (:showDocument (:support t) :workDoneProgress t)
      :general
      (:positionEncodings ["utf-32" "utf-8" "utf-16"])
      :experimental
      #s(hash-table size 1 test eql rehash-size 1.5
    rehash-threshold 0.8125 data ()))
     :workspaceFolders
     [(:uri "file:///tmp/eglot-bug" :name
    "/tmp/eglot-bug/")]))
[server-notification] Sun Nov 26 15:24:01 2023:
(:jsonrpc "2.0" :method "window/logMessage" :params
 (:type 3 :message
"Using Typescript version (workspace) 5.3.2 from path
\"/tmp/eglot-bug/node_modules/typescript/lib/tsserver.js\""))
[server-request] (id:0) Sun Nov 26 15:24:01 2023:
(:jsonrpc "2.0" :id 0 :method "window/workDoneProgress/create" :params
 (:token "3a0c42cc-0696-45e3-b763-564b150099bb"))
[client-reply] (id:0) Sun Nov 26 15:24:01 2023:
(:jsonrpc "2.0" :id 0 :result nil)
[server-reply] (id:1) Sun Nov 26 15:24:01 2023:
(:jsonrpc "2.0" :id 1 :result
 (:capabilities
  (:textDocumentSync 2 :completionProvider
     (:triggerCharacters
      ["." "\"" "'" "/" "@" "<"]
      :resolveProvider t)
     :codeActionProvider
     (:codeActionKinds
      ["source.fixAll.ts"
"source.removeUnused.ts"
"source.addMissingImports.ts"
"source.organizeImports.ts"
"source.removeUnusedImports.ts"
"source.sortImports.ts" "quickfix"
"refactor"])
     :codeLensProvider (:resolveProvider t)
     :definitionProvider t
     :documentFormattingProvider t
     :documentRangeFormattingProvider t
     :documentHighlightProvider t
     :documentSymbolProvider t
     :executeCommandProvider
     (:commands
      ["_typescript.applyWorkspaceEdit"
"_typescript.applyCodeAction"
"_typescript.applyRefactoring"
"_typescript.configurePlugin"
"_typescript.organizeImports"
"_typescript.applyRenameFile"
"_typescript.goToSourceDefinition"])
     :hoverProvider t :inlayHintProvider t
     :linkedEditingRangeProvider :json-false
     :renameProvider t :referencesProvider t
     :selectionRangeProvider t
     :signatureHelpProvider
     (:triggerCharacters ["(" "," "<"]
 :retriggerCharacters
 [")"])
     :workspaceSymbolProvider t
     :implementationProvider t
     :typeDefinitionProvider t
     :foldingRangeProvider t
     :semanticTokensProvider
     (:documentSelector nil :legend
(:tokenTypes
 ["class" "enum"
  "interface"
  "namespace"
  "typeParameter"
  "type" "parameter"
  "variable"
  "enumMember"
  "property"
  "function" "member"]
 :tokenModifiers
 ["declaration"
  "static" "async"
  "readonly"
  "defaultLibrary"
  "local"])
:full t :range t)
     :workspace
     (:fileOperations
      (:willRename
(:filters
[(:scheme "file" :pattern
  (:glob
   "**/*.{ts,js,jsx,tsx,mjs,mts,cjs,cts}"
   :matches "file"))]))))))
[client-notification] Sun Nov 26 15:24:01 2023:
(:jsonrpc "2.0" :method "initialized" :params
 #s(hash-table size 1 test eql rehash-size 1.5
rehash-threshold 0.8125 data ()))
[client-notification] Sun Nov 26 15:24:01 2023:
(:jsonrpc "2.0" :method "textDocument/didOpen" :params
 (:textDocument
  (:uri "file:///tmp/eglot-bug/main.js" :version 0
:languageId "javascript" :text "")))
[client-notification] Sun Nov 26 15:24:01 2023:
(:jsonrpc "2.0" :method "workspace/didChangeConfiguration" :params
 (:settings
  #s(hash-table size 1 test eql rehash-size 1.5
rehash-threshold 0.8125 data ())))
[server-notification] Sun Nov 26 15:24:01 2023:
(:jsonrpc "2.0" :method "$/progress" :params
 (:token "3a0c42cc-0696-45e3-b763-564b150099bb" :value
 (:kind "begin" :title
"Initializing JS/TS language featuresâ€¦")))
[server-notification] Sun Nov 26 15:24:01 2023:
(:jsonrpc "2.0" :method "$/typescriptVersion" :params
 (:version "5.3.2" :source "workspace"))
[client-request] (id:2) Sun Nov 26 15:24:01 2023:
(:jsonrpc "2.0" :id 2 :method "textDocument/hover" :params
 (:textDocument (:uri "file:///tmp/eglot-bug/main.js")
:position (:line 0 :character 0)))
[client-request] (id:3) Sun Nov 26 15:24:01 2023:
(:jsonrpc "2.0" :id 3 :method "textDocument/documentHighlight" :params
 (:textDocument (:uri "file:///tmp/eglot-bug/main.js")
:position (:line 0 :character 0)))
[client-request] (id:4) Sun Nov 26 15:24:01 2023:
(:jsonrpc "2.0" :id 4 :method "textDocument/signatureHelp" :params
 (:textDocument (:uri "file:///tmp/eglot-bug/main.js")
:position (:line 0 :character 0)))
[server-reply] (id:3) Sun Nov 26 15:24:02 2023:
(:jsonrpc "2.0" :id 3 :result [])
[server-reply] (id:4) Sun Nov 26 15:24:02 2023:
(:jsonrpc "2.0" :id 4 :result nil)
[server-reply] (id:2) Sun Nov 26 15:24:02 2023:
(:jsonrpc "2.0" :id 2 :result nil)
[server-notification] Sun Nov 26 15:24:02 2023:
(:jsonrpc "2.0" :method "$/progress" :params
 (:token "3a0c42cc-0696-45e3-b763-564b150099bb" :value
 (:kind "end")))
[server-notification] Sun Nov 26 15:24:02 2023:
(:jsonrpc "2.0" :method "textDocument/publishDiagnostics" :params
 (:uri "file:///tmp/eglot-bug/main.js" :diagnostics []))
[server-notification] Sun Nov 26 15:24:05 2023:
(:jsonrpc "2.0" :method "textDocument/publishDiagnostics" :params
 (:uri "file:///tmp/eglot-bug/main.js" :diagnostics []))
[server-notification] Sun Nov 26 15:24:05 2023:
(:jsonrpc "2.0" :method "textDocument/publishDiagnostics" :params
 (:uri "file:///tmp/eglot-bug/main.js" :diagnostics []))
[client-notification] Sun Nov 26 15:24:19 2023:
(:jsonrpc "2.0" :method "textDocument/didOpen" :params
 (:textDocument
  (:uri "file:///tmp/eglot-bug/package.json" :version 0
:languageId "javascript" :text
"{\n  \"dependencies\": {\n    \"typescript\": \"^5.3.2\",\n
\"typescript-language-server\": \"^4.1.2\"\n  }\n}\n")))
[client-request] (id:5) Sun Nov 26 15:24:19 2023:
(:jsonrpc "2.0" :id 5 :method "textDocument/inlayHint" :params
 (:textDocument (:uri "file:///tmp/eglot-bug/package.json")
:range
(:start (:line 0 :character 0) :end
(:line 6 :character 0))))
[server-reply] (id:5) Sun Nov 26 15:24:19 2023:
(:jsonrpc "2.0" :id 5 :result [])
[server-notification] Sun Nov 26 15:24:19 2023:
(:jsonrpc "2.0" :method "textDocument/publishDiagnostics" :params
 (:uri "file:///tmp/eglot-bug/package.json" :diagnostics
[(:range
 (:start (:line 1 :character 16) :end
 (:line 1 :character 17))
 :message "';' expected." :severity 1 :code 1005
 :source "typescript" :tags [])
(:range
 (:start (:line 2 :character 16) :end
 (:line 2 :character 17))
 :message "';' expected." :severity 1 :code 1005
 :source "typescript" :tags [])
(:range
 (:start (:line 3 :character 32) :end
 (:line 3 :character 33))
 :message "';' expected." :severity 1 :code 1005
 :source "typescript" :tags [])]))

In GNU Emacs 30.0.50 (build 11, x86_64-pc-linux-gnu)
Repository revision: ea4c5fcd77257d7a7c050714b809b1507d93a6ef
Repository branch: master

Configured using:
 'configure --prefix=/home/pengji/.local --without-x
 --program-transform-name=s/^ctags/ctags.emacs/ --disable-build-details
 PKG_CONFIG_PATH=/home/pengji/.local/share/pkgconfig:/home/pengji/.local/lib/pkgconfig:/home/pengji/.local/share/pkgconfig:/home/pengji/.local/lib/pkgconfig:'

Configured features:
GNUTLS LIBXML2 MODULES NOTIFY INOTIFY PDUMPER SOUND SQLITE3 THREADS
TREE_SITTER ZLIB

Important settings:
  value of $LANG: en_US.UTF-8
  locale-coding-system: utf-8-unix

Major mode: Info

Minor modes in effect:
  mouse-wheel-mode: t
  tooltip-mode: t
  global-eldoc-mode: t
  show-paren-mode: t
  electric-indent-mode: t
  menu-bar-mode: t
  file-name-shadow-mode: t
  isearch-fold-quotes-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  blink-cursor-mode: t
  minibuffer-regexp-mode: t
  buffer-read-only: t
  line-number-mode: t
  indent-tabs-mode: t
  transient-mark-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t

Load-path shadows:
None found.

Features:
(shadow sort mail-extr emacsbug message mailcap yank-media puny dired
dnd dired-loaddefs rfc822 mml mml-sec epa derived epg rfc6068 epg-config
gnus-util mm-decode mm-bodies mm-encode mail-parse rfc2231 mailabbrev
gmm-utils mailheader sendmail rfc2047 rfc2045 ietf-drums mm-util
mail-prsvr mail-utils mule-util info time-date cl-extra eglot
external-completion jsonrpc xref flymake mwheel thingatpt project diff
diff-mode easy-mmode ert pp ewoc debug backtrace help-mode find-func
filenotify warnings icons compile text-property-search tool-bar pcase
url-util url-parse auth-source cl-seq eieio eieio-core cl-macs
password-cache url-vars sh-script rx smie executable files-x shell
pcomplete comint ansi-osc ansi-color ring js c-ts-common treesit json
subr-x map imenu cc-mode cc-fonts cc-guess cc-menus cc-cmds cc-styles
cc-align cc-engine cc-vars cc-defs regexp-opt cl-loaddefs cl-lib
term/tmux term/xterm xterm byte-opt gv bytecomp byte-compile rmc
iso-transl tooltip cconv eldoc paren electric uniquify ediff-hook
vc-hooks lisp-float-type elisp-mode touch-screen tabulated-list replace
newcomment text-mode lisp-mode prog-mode register page tab-bar menu-bar
rfn-eshadow isearch easymenu timer select mouse jit-lock font-lock
syntax font-core term/tty-colors frame minibuffer nadvice seq simple
cl-generic indonesian philippine cham georgian utf-8-lang misc-lang
vietnamese tibetan thai tai-viet lao korean japanese eucjp-ms cp51932
hebrew greek romanian slovak czech european ethiopic indian cyrillic
chinese composite emoji-zwj charscript charprop case-table epa-hook
jka-cmpr-hook help abbrev obarray oclosure cl-preloaded button loaddefs
theme-loaddefs faces cus-face macroexp files window text-properties
overlay sha1 md5 base64 format env code-pages mule custom widget keymap
hashtable-print-readable backquote threads inotify multi-tty
make-network-process emacs)

Memory information:
((conses 16 134366 14563) (symbols 48 13684 0) (strings 32 42470 2356)
 (string-bytes 1 1456015) (vectors 16 24965)
 (vector-slots 8 306626 12688) (floats 8 58 9041)
 (intervals 56 548 1368) (buffers 992 20) (heap 1024 7552 769))