joaotavora / eglot

A client for Language Server Protocol servers
GNU General Public License v3.0
2.21k stars 203 forks source link

vscode-eslint-language-server not working #976

Open minikN opened 2 years ago

minikN commented 2 years ago

Hello.

This is a 2-in-1 issue report, but since there have been open issue regarding the first one, I thought it's unnecessary to create a new one again.

1) I'm trying to run the typescript-language-server (link) alongside the vscode-eslint-language-server (link). There already are issues regarding this:

After reading through the issues, I was under the impression that this could be done using eglot-alternatives, so I added this to my configuration:

(add-to-list
    'eglot-server-programs
    `((js-mode typescript-mode typescript-tsx-mode) .
      ,(eglot-alternatives
        '(("/gnu/store/q2fz3ccwf6ax8wd9ln011i1vln0wh1h4-node-typescript-language-server-0.11.0/bin/typescript-language-server"
           "--tsserver-path"
           "/gnu/store/s4smmc11wizcv2lzlgn9a0q7sxa7yvg3-node-typescript-4.7.3/lib/"
           "--stdio")
          ("/gnu/store/q050rzv1wqliqk7jvqfx872xa2b7cfxa-node-vscode-langservers-extracted-4.2.1/bin/vscode-eslint-language-server"
           "--stdio")))))

But I think I may have misunderstood it. It doesn't establish connections to all language servers, but only the first one it can execute. Is it therefore not possible to run multiple language servers at once for a given mode? In the javascript eco system this is kind of needed as typescript-language-server only reports syntax / type errors and so one whereas vscode-eslint-language-server reports errors in regards to the local/global eslint configuration. We need both language servers at once.

2) After that I tried using the vscode-eslint-language-server alone to test it. So I added

(add-to-list 'eglot-server-programs '(typescript-tsx-mode . ("vscode-eslint-language-server" "--stdio")))

to my config.

I have the eslint binary itself installed globally. So I init a test project with create-react-app, then cd into it and run eslint --init. I answer the question and end up with basic .eslint.js config file.

When loading the test project in emacs, I see two warnings in the Messages buffer:

[eglot] Connected! Server `EGLOT (ts-app/typescript-tsx-mode)' now managing `typescript-tsx-mode' buffers in project `ts-app'.
[eglot] (warning) Server tried to register unsupported capability `workspace/didChangeConfiguration'
[eglot] (warning) Server tried to register unsupported capability `workspace/didChangeWorkspaceFolders'

The connection seem to be established, but nothing is working as I expect it. If I try to execute eglot-code-actions I get

jsonrpc-error: "request id=2 failed:", (jsonrpc-error-code . -32603), (jsonrpc-error-message . "Request textDocument/codeAction failed with message: Cannot read property 'workingDirectory' of null"), (jsonrpc-error-data)

Looking through eglot's event log, I see (full log below):

[stderr] (node:1262) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'workingDirectory' of null
[stderr]     at /home/db/test/npm/lib/node_modules/vscode-langservers-extracted/lib/eslint-language-server/eslint.js:1089:60

This is the line it fails. So I'm not quite sure what to make of this as I'm not experienced with language servers. It seems like connection.workspace.getConfiguration on line 117 is not returning a proper configuration.

Now is this an internal problem with the eslint language server or something that eglot should provide but doesn't?

I'd love to get some input on these two issues. Thanks in advance!


LSP transcript - M-x eglot-events-buffer (mandatory unless Emacs inoperable)

[client-request] (id:1) Thu Jun 16 15:35:41 2022:
(:jsonrpc "2.0" :id 1 :method "initialize" :params
          (:processId 1008 :rootPath "/home/db/projects/ts-app/" :rootUri "file:///home/db/projects/ts-app" :initializationOptions #s(hash-table size 65 test eql rehash-size 1.5 rehash-threshold 0.8125 data
                                                                                                                                                 ())
                      :capabilities
                      (:workspace
                       (:applyEdit t :executeCommand
                                   (:dynamicRegistration :json-false)
                                   :workspaceEdit
                                   (:documentChanges :json-false)
                                   :didChangeWatchedFiles
                                   (:dynamicRegistration t)
                                   :symbol
                                   (:dynamicRegistration :json-false)
                                   :configuration t)
                       :textDocument
                       (:synchronization
                        (:dynamicRegistration :json-false :willSave t :willSaveWaitUntil t :didSave t)
                        :completion
                        (:dynamicRegistration :json-false :completionItem
                                              (:snippetSupport :json-false)
                                              :contextSupport t)
                        :hover
                        (:dynamicRegistration :json-false :contentFormat
                                              ["markdown" "plaintext"])
                        :signatureHelp
                        (:dynamicRegistration :json-false :signatureInformation
                                              (:parameterInformation
                                               (:labelOffsetSupport t)
                                               :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 :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)
                        :publishDiagnostics
                        (:relatedInformation :json-false :codeDescriptionSupport :json-false))
                       :experimental #s(hash-table size 65 test eql rehash-size 1.5 rehash-threshold 0.8125 data
                                                   ()))))
[server-notification] Thu Jun 16 15:35:41 2022:
(:jsonrpc "2.0" :method "window/logMessage" :params
          (:type 3 :message "ESLint server running in node v14.18.3"))
[server-reply] (id:1) Thu Jun 16 15:35:41 2022:
(:jsonrpc "2.0" :id 1 :result
          (:capabilities
           (:textDocumentSync
            (:openClose t :change 2 :willSaveWaitUntil :json-false :save
                        (:includeText :json-false))
            :workspace
            (:workspaceFolders
             (:supported t))
            :codeActionProvider
            (:codeActionKinds
             ["quickfix" "source.fixAll.eslint"])
            :executeCommandProvider
            (:commands
             ["eslint.applySingleFix" "eslint.applySuggestion" "eslint.applySameFixes" "eslint.applyAllFixes" "eslint.applyDisableLine" "eslint.applyDisableFile" "eslint.openRuleDoc"]))))
[client-notification] Thu Jun 16 15:35:41 2022:
(:jsonrpc "2.0" :method "initialized" :params #s(hash-table size 65 test eql rehash-size 1.5 rehash-threshold 0.8125 data
                                                            ()))
[client-notification] Thu Jun 16 15:35:41 2022:
(:jsonrpc "2.0" :method "textDocument/didOpen" :params
          (:textDocument
           (:uri "file:///home/db/projects/ts-app/src/App.tsx" :version 0 :languageId "typescript-tsx" :text "import React from 'react';\nimport logo from './logo.svg';\nimport './App.css';\n\nfunction App() {\n  return (\n    <div className=\"App\">\n      <header className=\"App-header\">\n        <img src={logo} className=\"App-logo\" alt=\"logo\" />\n        <p>\n          Edit <code>src/App.tsx</code> and save to reload.\n        </p>\n        <a\n          className=\"App-link\"\n          href=\"https://reactjs.org\"\n          target=\"_blank\"\n          rel=\"noopener noreferrer\"\n        >\n          Learn React\n        </a>\n      </header>\n    </div>\n  );\n}\n\nexport default App;\n")))
[client-notification] Thu Jun 16 15:35:41 2022:
(:jsonrpc "2.0" :method "workspace/didChangeConfiguration" :params
          (:settings nil))
[server-request] (id:0) Thu Jun 16 15:35:41 2022:
(:jsonrpc "2.0" :id 0 :method "client/registerCapability" :params
          (:registrations
           [(:id "dcfa99a0-12bc-4d8d-b2da-14ed6d106182" :method "workspace/didChangeConfiguration" :registerOptions nil)]))
[client-reply] (id:0) Thu Jun 16 15:35:41 2022:
(:jsonrpc "2.0" :id 0 :result nil)
[server-request] (id:1) Thu Jun 16 15:35:41 2022:
(:jsonrpc "2.0" :id 1 :method "client/registerCapability" :params
          (:registrations
           [(:id "234a266a-0363-4598-ba68-392b41dd2801" :method "workspace/didChangeWorkspaceFolders" :registerOptions nil)]))
[client-reply] (id:1) Thu Jun 16 15:35:41 2022:
(:jsonrpc "2.0" :id 1 :result nil)
[server-request] (id:2) Thu Jun 16 15:35:41 2022:
(:jsonrpc "2.0" :id 2 :method "workspace/configuration" :params
          (:items
           [(:scopeUri "file:///home/db/projects/ts-app/src/App.tsx" :section "")]))
[client-reply] (id:2) Thu Jun 16 15:35:41 2022:
(:jsonrpc "2.0" :id 2 :result
          [nil])
[server-request] (id:3) Thu Jun 16 15:35:41 2022:
(:jsonrpc "2.0" :id 3 :method "workspace/configuration" :params
          (:items
           [(:scopeUri "file:///home/db/projects/ts-app/src/App.tsx" :section "")]))
[client-reply] (id:3) Thu Jun 16 15:35:41 2022:
(:jsonrpc "2.0" :id 3 :result
          [nil])
[stderr] (node:1262) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'workingDirectory' of null
[stderr]     at /home/db/test/npm/lib/node_modules/vscode-langservers-extracted/lib/eslint-language-server/eslint.js:1089:60
[stderr] (Use `node --trace-warnings ...` to show where the warning was created)
[stderr] (node:1262) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
[stderr] (node:1262) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
[stderr] (node:1262) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'workingDirectory' of null
[stderr]     at /home/db/test/npm/lib/node_modules/vscode-langservers-extracted/lib/eslint-language-server/eslint.js:1089:60
[stderr] (node:1262) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
[stderr] (node:1262) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'workingDirectory' of null
[stderr]     at /home/db/test/npm/lib/node_modules/vscode-langservers-extracted/lib/eslint-language-server/eslint.js:1089:60
[stderr] (node:1262) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 3)

Minimum Reproducible Example (mandatory)

  1. Install vscode-eslint-language-server: npm i -g vscode-langservers-extracted
  2. Make sure npm bin path is part of PATH
  3. Test if lsp server is available with which vscode-eslint-language-server
  4. Start emacs and evaluate
    (add-to-list 'eglot-server-programs '(js-mode . ("vscode-eslint-language-server" "--stdio")))
  5. Setup test js/ts project, for example create-react-app: npx create-react-app my-app
  6. Open /path/to/my-app/src/App.js in Emacs
  7. Start eglot
  8. vscode-eslint-language-server connection should be established
joaotavora commented 2 years ago

Is it therefore not possible to run multiple language servers at once for a given mode?

That's correct. It's currently impossible in Eglot.

In the javascript eco system this is kind of needed as

For what it's worth, I used to program a lot of JS/TS until recently and I used a language server and a separate eslint Flymake backend. https://github.com/orzechowskid/flymake-eslint To manually configure other Flymake backends alongside Eglot's LSP-based backend, refer to the Flymake documentation and make sure you add flymake to eglot-stay-out-of

joaotavora commented 2 years ago

As to the rest of your issue, it could be the server has a bug, which is it doesn't respond well to null "workspace configurations". I can't confirm right now, but I don't know if responding with [nil] if actually wrong on Eglot's side. If you know it to be wrong, point me to some reference and I'll fix it.

[server-request] (id:3) Thu Jun 16 15:35:41 2022:
(:jsonrpc "2.0" :id 3 :method "workspace/configuration" :params
          (:items
           [(:scopeUri "file:///home/db/projects/ts-app/src/App.tsx" :section "")]))
[client-reply] (id:3) Thu Jun 16 15:35:41 2022:
(:jsonrpc "2.0" :id 3 :result
          [nil])

Regardless, Eglot just doesn't have anything to provide to the server. So if you want it to, please read up on eglot-workspace-configuration (look in the README/issue/discussion tracker) and try to set it up just as vscode-eslint-language-server expects it.

On a side note, I don't understand why all these language servers don't assume sensible defaults when presented with no information. That's what "defaults" are for. I guess the VScode folks like to WETWET (write everything twice, waste everyone's time) A probable sensible default for workingDirectory of a given language server is the rootPath and rootUri given by Eglot to the server in

 (:processId 1008 :rootPath "/home/db/projects/ts-app/" :rootUri "file:///home/db/projects/ts-app" :initializationOptions #s(hash-table size 65 test eql rehash-size 1.5 rehash-threshold 0.8125 data
minikN commented 2 years ago

First of all, thanks for the fast response!

For what it's worth, I used to program a lot of JS/TS until recently and I used a language server and a separate eslint Flymake backend. https://github.com/orzechowskid/flymake-eslint To manually configure other Flymake backends alongside Eglot's LSP-based backend, refer to the Flymake documentation and make sure you add flymake to eglot-stay-out-of

I actually didn't know about this at all. On first sight, this seems like a suitable replacement. I'll check it out! In case it doesn't work or doesn't do what I want I'll look into workspace configuration for the lsp server. Actually, looking into it may be worth anyway.

I'll report back. Thanks.

minikN commented 2 years ago

flymake-eslint works very well together with eglot. I really like it and it's what I'm going to use, at least for now. Thanks for pointing it out.

However, I think it's still worth setting up vscode-eslint-language-server properly. At the moment, I use eslint-fix to fix the errors reported by eslint. It works, but it's pretty rudimentary. With vscode-eslint-language-server, one (to just name one example) has the option to add a disable rule for the error at point. This is not possible using the flymake approach.

Let's see how far I can get configuring vscode-eslint-language-server.

P.S.: But then again, this only really makes sense once it's possible to run multiple language servers. This is feature planned for the foreseeable future @joaotavora ?

merrickluo commented 1 year ago

Hi @joaotavora , I'm not sure if this is the best place to start the discussion again.

I think allowing multiple language servers in the same buffer is essential for the "frontend" development flow. The vscode-eslint-language-server is a great example here, and there is the tailwindcss-intellisense I want to use in jsx, tsx, or leex files.

For comparison, vscode supports this out of the box, lsp-mode can achieve this by setting the server to "add-on" mode, I haven't tried neovim, but a quick search shows that it can also be achieved.

I was hoping that I can switch to eglot since it is now in the emacs core, and I really hope that this feature I use every day can be implemented in eglot, thanks :pray:

stevemolitor commented 1 year ago

I'd like to second @merrickluo's comment above. I'm really excited that eglot is in Emacs core now, and it seems really fast and integrates well with built-in emacs features.

But I just can't use it because I need both the typescript and eslint servers. The eslint server isn't just for highlighting errors; it's also for fixing those issues from a menu of options. By running both servers it all works seamlessly, with a consistent UX for identifying and fixing issues wherever the source (typescript or eslint).

My concern is that if this is not addressed in eglot, and since eglot is in emacs core now, Emacs will be perceived as a second class LSP citizen for a huge swath of LSP users.

joaotavora commented 1 year ago

I just want to say here that multi-server support is indeed number one on my Eglot/LSP/OSS todo list right now. But life keeps getting in the way, unfortunately. Let's see maybe over the holidays i can get something rolling.

gdanov commented 1 year ago

was trying to solve the same problem and saw ts lang server supports plug-ins. Configured the eslint and gql plugins in my tsconfig, but this makes no difference? https://www.typescriptlang.org/tsconfig#plugins

laduke commented 1 year ago

hey thanks for the flymake-eslint tip. It works good.


edit: i was calling eglot multiple times on accident, creating chaos.

stevemolitor commented 9 months ago

@gdanov

was trying to solve the same problem and saw ts lang server supports plug-ins. Configured the eslint and gql plugins in my tsconfig, but this makes no difference? https://www.typescriptlang.org/tsconfig#plugins

I had slightly better luck using this approach:

https://notes.alexkehayias.com/setting-up-typescript-and-eslint-with-eglot/

Pretty invasive though, and eslint code actions / fixes weren't working.

Anyone else have any luck with typescript language server plugins?

gdanov commented 9 months ago

HAHAHA this is unexpectedly informative reply, thanks. By the way...

stevemolitor commented 9 months ago

Yeah it's getting closer. The ability to auto-fix lint errors is pretty important to me though for FE dev. For some reason, when using the eslint plugin as described above, eglot will show the errors but eglot-code-actions won't show the option to fix it. It about more than just fixing niggling issues - eslint can catch and fix React bugs for example. It's a big productivity boost.

According to this page you can configure vscode to use a plugin without having to modify your project's tsconfig.json:

VS Code has the ability for a extension to automatically include language service plugins, and so you may have some running in your editor without needing to define them in your tsconfig.json.

Maybe there's a way to get eglot to support that as well. Still need auto-fix though.

gdanov commented 9 months ago

followed the steps and I think I got lang server with the eslint plugin working. I see now the code action "remove unused imports" which I'm pretty sure is eslint feature.

AlexanderArvidsson commented 6 months ago

I'm struggling with the same problem here, specifically Eslint code actions not available in Eglot.

I'm gonna come with a slightly different idea: I don't know too much about flymake, but is there any reason Flymake couldn't report code actions to Eglot? Unless there's anything other than code actions that we're after here, I think it makes sense if Eglot could ask Flymake for code actions, since Flymake already handles the syntax checking and such and is the recommended checker with Eglot. This way, flymake-eslint would report available code actions to Eglot, and any of Flymake's multiple backends could report code actions and Eglot would combine them into a single list.

Thoughts?

joaotavora commented 6 months ago

Until I find time to get some multi-server support going, the best way I know of is to use a language server that does this coalescing of various sources itself. clangd is a fine eaxmple, where clang-tidy actions are easily available, along with some other non-clang-tidy ones. pylsp is another example of a server who joins many sources into a single one.

To make a generic server joiner for any language, but I must admit it's harder than I thought. Every server combination must be clearly understood, what notifications go where, how to communicate with this beast, what to do when one of the subservers times out, etc, etc. Where to send requests? Both servers and merge responses? What about specific requests like rename? Who renames? Where is are all these decisions configured?

It's easier when these decisions are taken by a single server already existing (like the clangd example above). How hard can it be to make typescript-language server call into eslint libraries and join its diagnostics and code actions with theirs? I know, "famous last words", but is there any technical impediment?

Alternatively, maybe someone make this server joiner just for the typescript-language-server and vscode-eslint-language-server with opinionated decisions on what things each server is responsible for.

Thoughts?

That doesn't work, can never work cleanly. Flymake works independently with Eglot as one of its backends. Inverting that relation for something Flymake wasn't designed is madness. Flymake diagnostics can however have clickable buttons that run arbitrary functions, but you'd be repeating a lot of the logic Eglot has for code actions, and bumping into the same problems, and they would never be available through M-x eglot-code-actions and friends.

AlexanderArvidsson commented 6 months ago

Thanks for the quick response @joaotavora!

Perhaps I'll take a look at the specific case of TypeScript + Eslint as some kind of server joiner. I haven't had any success using typescript-eslint-language-service plugin, but perhaps it contains some useful code which could be ported as is into a server joined.

Makes sense about the flymake stuff, it was a weird idea I thought perhaps could be easier than running eslint multiple times (once for language server, once for flymake eslint). As I said, I have not read much about the internals of eglot and flymake. The small code I've read of Eglot seems to indicate that Eglot does call certain things from flymake, so I thought it was already communicating specifically with flymake about some things.

lephyrus commented 6 months ago

It's easier when these decisions are taken by a single server already existing (like the clangd example above). How hard can it be to make typescript-language server call into eslint libraries and join its diagnostics and code actions with theirs? I know, "famous last words", but is there any technical impediment?

This is keeping me from fully switching from lsp-mode at the moment, so I just want to try and add some more context to this from a "Javscript" (in the broadest sense) perspective. Unfortunately, it's not as simple as "joining" the Typescript and ESLint language servers. This may seem a bit mad (and maybe rightly so) from the perspective of other ecosystems, but the following combination of language servers is not unusual at all for an Angular project:

With Svelte, as another example, you could easily have a combo of Svelte + ESLint + Tailwind language servers for a .svelte file. I'm sure there's many other combinations people use.

Being able to "compose" language servers in an arbitrary way seems very useful in this ecosystem. Unfortunately, I see the complexity in your questions and the need for configurability, @joaotavora. And lsp-mode doesn't necessarily get this right at the moment: for example, both the Angular and the Typescript server have something to say about renaming a symbol, and last I checked, it does not work out of the box.

ovistoica commented 4 months ago

Just came here to say that sure eslint integration for typescript would be nice but my current blocker is with tailwindcss lsp server running in the same buffer as typescript lsp

denisw commented 1 week ago

Adding to @ovistoica, the use case for multiple LSP servers per buffer goes beyond JavaScript / TypeScript. For instance, in Python it's common to want to use ruff-lsp together with a core Python LSP server like Pyright, which is preferable to integration via Flymake for the same reasons (e.g., code actions).

@joaotavora Perhaps sensible answers to the tricky questions can be found in the LSP implementation of Neovim, which has quite seamless multi-server support. From a first glance at the docs, it seems like most LSP requests are indeed sent to all servers, but it's possible to turn off particular capabilities for particular servers. (The official nvim-lspconfig repo has such configurations for dozens of LSP servers, so this configuration is nothing a Neovim user has to deal with usually.)

Another inspiration could be lsp-mode, which supports multiple servers as well.

joaotavora commented 1 week ago

Another inspiration could be lsp-mode, which supports multiple servers as well.

According to one reader here, not extremely well. https://github.com/joaotavora/eglot/issues/976#issuecomment-1878970192

But OK. I guess it's a source.

From a first glance at the docs, it seems like most LSP requests are indeed sent to all servers, but it's possible to turn off particular capabilities for particular servers.

It would be helpful if someone could dig beyond the "first glance" and really investigate down to the details, with practical scenarios. Are "rename" requests really sent to all servers? Or are these the exception in your "most"? And if they are sent to all servers, are some of the responses ignored. Which one is?

Ideally some Nvim or lsp-mode user would produce LSP annotations transcripts of small typical LSP interactions. That transcript would clearly indicate where the client messages are, where server A's messages are and where server B's messages are. It would also indicate in plain text what happened in the UI as the cause or consequence of such interactions.

denisw commented 1 week ago

I‘m not sure whether I‘ll have time for creating proper traces, but regarding the rename question: yup, seems like Neovim sends the request to every attached server that advertises the capability.

https://github.com/neovim/neovim/discussions/24121

While I cannot say I know, I strongly suspect that this is Neovim‘s general approach for all LSP commands:

1.Filter servers by which one advertises the capability

  1. Send the request to each of them
  2. (Combine results if needed - e.g., for code actions)

Sometimes, this is not ideal (see link above). But it works surprisingly well for the server combinations one would realistically have, as e.g. the ESLint server will most likely not advertise support for renames, for instance.

ovistoica commented 1 week ago

I agree with @denisw

If eglot exposed a configuration option to filter what server receives the specific event, that would be enough to have a good experience.

I'm not sure what this entails, but I cannot use eglot on Javascript/web buffers currently because of the lack of multi-server support.