Open justinmk opened 4 months ago
We need a general mechanism that makes it easy for users and scripts (such as nvim-lspconfig) to:
- iterate through discovered server capabilities / commands
- define aliases to customer server commands and capabilities, in a non-verbose way
I think there's a bit of a misunderstanding here.
LSP defines two types of commands:
server commands. These are advertised within server_capabilities.executeCommandProvider.commands and they (usually) work without any special client logic. The workflow is usually something like:
We cannot provide a generic way for users to call these commands directly, because many of them require arguments or context (e.g given via the code actions). The arguments required are command specific
client commands. The specification doesn't specify any concrete client commands, but it acknowledges that clients can implement custom client commands. These are not advertised by the server in server_capabilities
. Typically the client informs the server that it supports them via settings
or init_options
in the initialize request. These always require some sort of custom client logic, and there's no way to have a general mechanism to use them.
The current situation with nvim-lspconfig is that 10+ lines of code are needed to define each command or custom capability. That's unsustainable, we can't maintain that for 100s of LSP servers.
Some of the client commands could become part of the specification. But for some of them there are issues with no clear path forward (E.g. https://github.com/microsoft/language-server-protocol/issues/1641)
Other than that, I don't see what else neovim core could do. Other than delegating more of the work to language specific plugins.
I think there's a bit of a misunderstanding here.
Yes, I'm just feeling around here. I updated the title + description.
LSP defines two types of commands ...
- server commands
server_capabilities.executeCommandProvider.commands
... user triggers code actions. We cannot provide a generic way for users to call these commands directly
✅ Those are discovered and presented via "code actions" menu, correct? So that's already covered.
- client commands. ... These are not advertised by the server in
server_capabilities
. Typically the client informs the server that it supports them viasettings
orinit_options
in the initialize request.
Using rust-analyzer
experimental/externalDocs as an example: is there any way to list all of its experimental/xx
methods?
Are these goals tractable:
Code required to add custom rust-analyzer commands:
Assuming we provide a util function like:
vim.lsp.command(name:string, on_done:function)
-- Works like buf_request_all ?
vim.lsp.command_all(name:string, on_done:function)
The code would now look like:
TODO
@justinmk other lsp clients, such as coc or vscode, use something similar to a command palette (other ides also have similar interfaces which group actions in an easily filterable/discoverable pick-list). What i suggest is we provide a way (e.g. an interface for attaching commands, and another interface on top of ui.select for the actual ui presentation) for other plugins (such as for example nvim-jdtls @mfussenegger, which has quite a few user commands exposing some of the internal offspec jdtls capabilities) to easily attach their own custom commands, instead of creating UserCommands as they do atm. It should look/feel similar, and it is pretty much an analogue to code_actions.
Here is how coc nvim exposes all the commands that the different extensions register. For nvim those should maybe be filtered by filetype, currently active clients etc etc, but this is the general idea. If we provide this, it will be easy for plugin users to discover custom commands that the lsp extension plugins expose and they will not need to learn new user commands, if they can fuzzy search them or their description.
There are others, which have a well defined behavior, which we can expose/add from nvim too, eventually, such as workspace related actions - displaying currently attached workspace folders, or inspecting workspace edits and so on.
Are these goals tractable:
reduce the amount of boilerplate needed to define commands, by at least 2x. reduce the amount of documentation needed to explain how users can create custom commands provide concise (but fully-working) examples
We could document some examples, but I don't think you can reduce the amount of boilerplate by 2x.
Using rust-analyzer experimental/externalDocs as an example: is there any way to list all of its experimental/xx methods?
This isn't a client command. It's just a extra LSP method. There is no discovery mechanism for these. Just the documentation of the language server - or in many cases not even that, but reverse engineering of vscode extensions.
The main problems here are:
This is all specific to the method.
And the open_docs
example is also kinda broken, it should use client.request
like the reload_workspace
example. vim.lsp.buf_request
would make a request using all clients attached to a buffer, which means other servers not supporting it would receive the request too, and very likely fail.
Client commands on the other hand can be used instead of server commands, and are part of some other operations like code-actions. The difference to server commands is that they usually have more round-trips.
For example nvim-jdtls defines a java.action.generateToStringPrompt
command (using vim.lsp.commands
, which is documented), and advertises it in the settings of the initiailize request. The workflow is then like this:
java/checkToStringStatus
), server responds with the properties that can be included in a toString
implementation.java/generateToString
), using the properties the user selectedWorkspaceEdit
, that the client appliesThis is all completely custom
Also note that what lspconfig does (or did?) with commands
was to provide sugar for neovim user commands definitions. That's not really specific to the LSP module at all.
Here is how coc nvim exposes all the commands that the different extensions register
@asmodeus812 but how are those commands discovered? Where is that info coming from?
Using rust-analyzer experimental/externalDocs as an example: is there any way to list all of its experimental/xx methods?
This isn't a client command. It's just a extra LSP method. There is no discovery mechanism for these. Just the documentation of the language server - or in many cases not even that, but reverse engineering of vscode extensions.
That isn't part of the "capabilities" response? It's strange that servers can't list their custom methods.
The main problems here are:
- You need to know the arguments required, and logic to create them.
- You need to know the response type
- You need custom logic to process the response.
Understood. But even just listing the names, and point to the upstream docs, would be very helpful, and avoids support requests. Some UX tweaks may be enough.
nvim-lspconfig has a CI job that pulls package.json files to get various bits of info. But custom methods aren't declared in package.json files, they are just loosely documented as you mentioned.
Here is how coc nvim exposes all the commands that the different extensions register.
@justinmk they are registered through/using the coc-lsp client's api, by each extension, i think that was obvious from my reply. This is also what vs code does, the commands are registered by the extension, but the api to interact/add/register/use those commands is provided by the vs code lsp client as a library itself.
This is also what vs code does, the commands are registered.
We're not VS Code, though, on purpose; neither do we plan on replacing coc.nvim. We implement the LSP specification and nothing but the specification. Anything beyond that is intentionally left for server-specific plugins (which correspond to VS Code extensions) built on top of the base LSP API (as well as other methods that compose nicely, which this may or may not be relevant to).
@clason in that case if nvim only adheres to the spec and only the spec then this issue can be closed as resolved / not planned.
Not quite; the last part about "other methods that compose nicely" may still be actionable here. (We do want to add general API methods that makes it easier to write such custom plugins -- given a positive, individual, cost-benefit analysis, of course.)
That isn't part of the "capabilities" response? It's strange that servers can't list their custom methods.
No, the capabilities only contain the capabilities defined in the specification and there is also no OpenAPI style dynamic introspection of methods.
We could suggest to add that, but even if it were added, due to the BWC nature of the protocol, it would remain optional.
Understood. But even just listing the names, and point to the upstream docs, would be very helpful, and avoids support requests. Some UX tweaks may be enough.
We could add a lsp-offspec-extensions
doc section, that lists the two common scenarios for plugin devs, including examples and some prose on how to best implement them:
the commands are registered by the extension, but the api to interact/add/register/use those commands is provided by the client.
We do want to add general API methods that makes it easier to write such custom plugins
@clason Well yes then i agree and it does match up with i said those clients do already at the moment as a solution to this very problem
Problem
LSP servers have various custom capabilities and commands that are not part of the LSP spec, e.g.:
The current situation with nvim-lspconfig is that 10+ lines of code are needed to define each command or custom capability. That's unsustainable, we can't maintain that for 100s of LSP servers.
See also:
Expected behavior
Reduce friction for users to use custom (off-spec) server capabilities/commands:
server capabilitiescustom (off-spec) commands.commands
interface and/or provide util functions so that defining commands/aliases to call custom (off-spec) server commands requires 2x less code.Questions
vim.lsp.get_active_clients()[1].server_capabilities
andvim.lsp.get_active_clients()[1].commands
already exist. What is the remaining friction? Why do we get requests like this? Do we simply need some hints in the documentation that guide users? Do we need a util function that makes it easy for users to define mappings around server commands/capabilities?Possible interface
From https://github.com/neovim/nvim-lspconfig/issues/1937#issuecomment-1481033025 (the names don't matter right now, they are just pseudo-code; the point is that we need a generic interface for (1) iterating custom shit from the LSP server and (2) enabling users to create their own mappings to this stuff):