Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

What's the correct way for an LSP server to know whether a command is available on the client? #642

Open
DanTup opened this issue Dec 17, 2018 · 43 comments
Labels
commands feature-request Request for new features or functionality help wanted Issues identified as good community contribution opportunities
Milestone

Comments

@DanTup
Copy link
Contributor

DanTup commented Dec 17, 2018

I'm implementing some refactors where the server needs some input from the user. In order to get input from the user I'm going to need to trigger a command on the client that can ask the user for input (for example, by having a command in the client extension that can call window.showInputBox or some equiv).

However, I can't find a way for the client to advertise (or server to know) which commands are supported on the client.

What's the expected way to handle this? Although I can document the commands that a client needs to implement for specific functionality in the server, that's not very compatible - if I wanted to add new requirements in the future, I'd need to know if the connected client supports them.

@dbaeumer
Copy link
Member

Asking for input is not supported in the LSP right now and using commands for this is IMO the wrong way to approach this.

If we thing a server should be able to ask for user input we should add corresponding requests send from the client to the server.

However in general I am more a fan of having more refactoring requests that flow from the client to the server and the client queries the input parameters upfront.

@dbaeumer dbaeumer added the feature-request Request for new features or functionality label Dec 18, 2018
@dbaeumer dbaeumer added this to the Backlog milestone Dec 18, 2018
@dbaeumer dbaeumer changed the title What's the correct way for an LSP server to know whether a command is available on the client? Support that servers can ask for user input Dec 18, 2018
@mickaelistria
Copy link

mickaelistria commented Dec 18, 2018 via email

@DanTup
Copy link
Contributor Author

DanTup commented Dec 18, 2018

However in general I am more a fan of having more refactoring requests that flow from the client to the server and the client queries the input parameters upfront.

I don't think this changes the problem. The server needs to know whether it can provide a code action that relies on getting user input, and that means the server needs to know if the command that it'll assign to the code action is available.

For example, in v1 of my LSP server, I have 0 code actions. In v2, I add a code action for "Extract Widget" which requires some input from the user. Even if I implement that user input in the client extension (which I think increases the burden on the client extension dev, but that aside..) I need to know whether the connected client has the implementation for this, and therefore I need to know if I can send the code action that references that command?

(So I think the rename of this issue may be misleading - I think it's valuable for a server to know whether a client extension implements/supports a command, whether it's for asking for input or not?).

@DanTup
Copy link
Contributor Author

DanTup commented Dec 18, 2018

Do you think you could spec the refactoring you're working on as LSP
operations?

In this case, the one I'm trying to implement is "Extract Widget". It's basically similar to behaviour of Extract Method, however it needs to exist alongside it (because it creates a special class, not just a method, and creating a method is also valid). This is very specific to Flutter so I don't think it's likely the LSP spec would ever have a specific request for it.

Of course, we can implement a lot of this by just adding more code to the client, but then we lose some of the benefit of LSP (making it simple to get the language into other editors).

I understand the concerns with exposing things like input to the server (since it could lead to a crappy experience if I request 5 pieces of input individually in an editor like VS where the user would expect a dialog with them all) but without this, I think there would need to be some fairly flexible API that allows for custom refactors that are fairly specific to a server.

@DanTup
Copy link
Contributor Author

DanTup commented Dec 18, 2018

However in general I am more a fan of having more refactoring requests that flow from the client to the server and the client queries the input parameters upfront.

Sorry, I think I misunderstood this as something that we should do ourselves, but I guess you mean extending LSP to be able to query the server for this metadata and then let the client (editor, not extension) handle collecting the input?

@mickaelistria
Copy link

Of course, we can implement a lot of this by just adding more code to the client, but then we lose some of the benefit of LSP (making it simple to get the language into other editors).

Indeed, protocol extensions loose most benefits of using LSP.
But many SDK make it relatively easy to add protocol extensions, so while the operation wouldn't be portable out of the box, it would still be "cheap" to implement in some IDEs of choice.

In this case, the one I'm trying to implement is "Extract Widget". It's basically similar to behaviour of Extract Method,

Can't you instead of requesting user input upfront, deal with parameter capturing in a similar way as a CompletionItem, allowing Snippet mode which allows to modify parameters after the refactoring?
Note that I'm not sure how this fits in current protocol, but I think I've already reported in some other issues that parameter capturing (after edit) could be useful in WorkspaceEdit as well so they could be used in almost all operations; so you use-case could help improving that.

@mickaelistria
Copy link

@DanTup : basically, could #592 be the solution here?

@DanTup
Copy link
Contributor Author

DanTup commented Dec 18, 2018

Can't you instead of requesting user input upfront, deal with parameter capturing in a similar way as a CompletionItem, allowing Snippet mode which allows to modify parameters after the refactoring?

In this specific case (Extract Widget) it probably would, but it'd require workspace/applyEdit to support snippets. I don't think it'd help if the options weren't directly tied to symbols in the code though (for ex. if there were non-string options for a refactor) or if it spanned multiple files (I presume we can't do snippet mode across multiple files?).

That said - I still think the original request is very valid. A client should be able to communicate which commands it supports to the server. Servers can return identifiers of commands against code actions - why shouldn't they be able to know whether or not the client can actually handle it?

If I want a code action that requires a lot of custom code running in the client extension, I'd want to do something like:

  • Server sends code action with a client command identifier
  • Use selects the code action, which invokes the local command in the extension
  • Extension does stuff, collects user input, whatever
  • Extension then calls executeCommand with the data for the server to process the action

The first step here should be able to read a capability from the client to know what the client command is available. I'm not saying the client should transmit all its internal commands, it's fine for the client extension to have to declare them (like the server declares which it can handle, so the client knows when to send them on to it).

@dbaeumer
Copy link
Member

A server at the end should only trigger those commands it implements or has documented as something the client extension needs to provide.

I think it will not be enough if a client lists these commands. It would require specing their behavior as well. This is why I think we are better off specing a corresponding request or notification.

@DanTup
Copy link
Contributor Author

DanTup commented Dec 19, 2018

A server at the end should only trigger those commands it implements or has documented as something the client extension needs to provide.

But then how do I add a new code action that requires a command from the client extension that wasn't in the first shipping version? Isn't the purpose of client capabilities to communicate this sort of thing? The server already advertises which commands it supports, so it seems natural (to me) for the client to do the same - they they both know which commands are available on the other.

@dbaeumer
Copy link
Member

The reason why a server advertises its commands is that a client knows about them and delegates the execution of the commands back to the client. If is more like a function dispatching.

As said I am not a fan of starting to spec client commands for the above reasons. Even if clients advertise them we would need to specify what they are support to do. Or am I misunderstanding you?

@alanz
Copy link

alanz commented Dec 19, 2018

I think requiring a client to be code specifically for a given feature in a server is a bad idea, and goes against the whole point of LSP.

For the haskell refactorer I eventually realized you can go a long way using just code actions and rename, if you apply default names to say a newly extracted widget.

@DanTup
Copy link
Contributor Author

DanTup commented Dec 19, 2018

As said I am not a fan of starting to spec client commands for the above reasons. Even if clients advertise them we would need to specify what they are support to do. Or am I misunderstanding you?

No commands need specifying, this is just a mechanism for the server to know if a client extension implements some command that it can use in a code action.

Imagine some code action that needs some work on both the server and the client. The server wants to include the code action in its list with a command ID that exists on the client - but it wants to know the client has that command implemented, else it should exclude it.

For example, let's say my server gains the ability to identify when a referenced symbol is from a first-party package that is not imported in the users workspace. It wants to give a code fix for "install package xxx" but that action needs to be executed on the client (it's like nuget install, npm install, whatever). The LSP server can document that a client extension should implement the "myext.packages.install" command that takes a string argument of the package name, however there's no guarantee it exists because client extensions may have shipped before it existed.

If the client extension (when setting up LSP, or dynamically) can tell the server (in its capabilities) "Hey, I have a myext.packages.install command" then the server knows it's valid for it to include code actions for importing the packages.

(note: I'm not doing package install, it just seemed like a convenient example that wasn't refactors, to avoid any confusion with whether the refactor APIs can/should be improved..)

Is that any clearer? Is there a better way you think this should be handled?

I think requiring a client to be code specifically for a given feature in a server is a bad idea, and goes against the whole point of LSP.

I somewhat agree - that's why I originally asked for being able to get input directly from the server. However, in hindsight I don't think the user experience would be great across editors if all requests for input had to be encoded into LSP messages. It'd be nice if an editor that has big fancy dialogs (eg. Visual Studio) can implement a nice dialog for the user, then send all the info in one go, while VS Code can use it's input boxes/quick picks/etc..

To get the best experience, I think it will sometimes be necessary for the client extension to implement some local functionality (and that's what my request for including commands in capabilities is for - to allow the server to be able to detect client functionality it can call).

I eventually realized you can go a long way using just code actions and rename, if you apply default names to say a newly extracted widget.

Extract Widget may fit into this, but many of our refactors have options that it'd be nice to expose to the user - which will require some UI.

@kdvolder
Copy link

Even if clients advertise them we would need to specify what they are support to do

I.m.o. that would actually be a good idea. I mean I always thought there was every intention to eventually standardize a list of client-side commands in the spec. As it is now client-side commands have to be implemented by server providers as custom client-side extension for every client they support. Getting started on a growing list of 'standard' commands that clients (may optionally) implement would not be bad idea at all.

@mickaelistria
Copy link

Getting started on a growing list of 'standard' commands that clients (may optionally) implement would not be bad idea at all.

Isn't this list of standard comands the LSP operations themselves? ie if a command is standard enough, why not creating a typed LSP request/response for it? Just like rename.
The case for commands are exactly for things that are not "standard" enough to be specified IMO, and we shouldn't try to standardize poorly things that either could be standardized nicely, nor try to standardize things that are not standard.

As for user input, a nice-ish and generic way could be to have a dialog operation (from server to client) that would contain a dialog description in an existing format (like XUL -or maybe there is something similar called JUL in those JSon days-) and when receiving this event, client should render the dialog and return the form values to the server that could then process them.
Would that work for @DanTup 's use-case? Would it be a not-too-bad fit for the LSP?

@kdvolder
Copy link

I never thought of it that way, but I think @mickaelistria is right. If some command is 'standard enough' to merrit detailed description, then it could probably rise to the level of being a protocol message.

The only potential issue I see is that you can't put 'protocol messages' as a 'command id' for something like a code action. But I suppose you can still have the server implement a server-side command, register that as a 'server capability' and then have the command implementation call the protocol message.

@DanTup
Copy link
Contributor Author

DanTup commented Dec 20, 2018

@kdvolder

Getting started on a growing list of 'standard' commands that clients (may optionally) implement would not be bad idea at all.

@mickaelistria

Isn't this list of standard comands the LSP operations themselves? ie if a command is standard enough, why not creating a typed LSP request/response for it?

To be clear, I'm asking for non-standard commands here. Something agreed between my LSP server and the client extensions, which they need to communicate capabilities for. I agree that if something is common, it should be a real LSP request/response. I gave an example above for a code fix for installing a package locally - this is going to need the client to handle it (for ex. in Visual Studio it might open the NuGet package manager, in VS Code it would be handled differently). In this case, the server needs to know if the client has that capability, and since it's invoking it by returning a code action with a command, it makes sense to do it by advertising that the client supports that command.

As for user input, a nice-ish and generic way could be to have a dialog operation (from server to client) that would contain a dialog description in an existing format

This doesn't solve the general case - for example the example given above - Visual Studio has the NuGet Package Manager they might want to open when you invoke the "Install package xxx" quick-fix (note: I'm making this quick-fix up as an example).

But also, I think trying to encode dialogs into LSP won't result in the best UI/UX. Client extensions should be able to choose the most appropriate way of requesting input, whether it's from multi-step inputs like VS Code, a dialog (Visual Studio) or even text inputs for terminal editors 😄

@mickaelistria
Copy link

But also, I think trying to encode dialogs into LSP won't result in the best UI/UX. Client extensions should be able to choose the most appropriate way of requesting input, whether it's from multi-step inputs like VS Code, a dialog (Visual Studio) or even text inputs for terminal editors 😄

You're right.
So another possibility would be to send a json schema and let client build whatever interaction with user to create an instance of this schema. But that would probably not be dynamic enough.
Or an.operation like «prepare command» that could be mutliple steps of interactions (ie mutliple user input requests) resulting in a «prepared command» that client could then invoke on server.

That said, while we're brainstorming about general case, #592 still seems to be a more affordable solution to the initial trigger.

@DanTup
Copy link
Contributor Author

DanTup commented Dec 20, 2018

So another possibility would be to send a json schema and let client build whatever interaction with user to create an instance of this schema

This seems to be getting awfully complicated for something that could be so simple. If the client can tell the server what commands it supports, the server could just use them in the code action. The client is then free to call whatever APIs it wants on the server, get whatever info it needs, show the user whatever it wants, etc.. It's very extensible and allows the client extension to implement things in the best way without being restricted by what LSP has deemed common enough to add to the spec.

#592 still seems to be a more affordable solution to the initial trigger

It may solve Extract Widget, but not all of the others (the ones that have options for the user).

@mickaelistria
Copy link

If the client can tell the server what commands it supports, the server could just use them in the code action.

The thing here is that it means LS needs to be somehow aware of client-specific support; and IDEs are supposed to implement commands that are not standardized in the protocol. That's making the LSP re-enable the N*M integration effort it's been trying to shutdown.
I'd hate to have to implement specific code for the Dart Language Server in the Eclipse IDE for instance, I would either consider it as a failure of LSP spec or Dart LS to keep focused on the value of being a language server: no specific integration beyond starting the LS.

@matklad
Copy link
Contributor

matklad commented Dec 20, 2018

However, I can't find a way for the client to advertise (or server to know) which commands are supported on the client.

@DanTup I think you can [ab]use initializationOptions for that? I do it for my hacky version of syntax highlighting.

client side, server side.

In general, I think that complex IntelliJ-style refactorings are really sub-protocols between the client and the server, which require several rounds of back and forth, and which could use language specific messages and language specific UI. As usual, I am a big fan of the Dart protocol, and there they have refactoring-specific Options (client -> server JSON) and refactoring-specific Feedback (server -> client JSON). Refactoring action then is a repeated back-and forth of options/feedback which continues until the user is satisfied with results. Rendering of options & feedback is refactoring-specific.

@DanTup
Copy link
Contributor Author

DanTup commented Dec 20, 2018

The thing here is that it means LSP needs to be somehow aware of client-specific support; and IDEs are supposed to implement commands that are not standardized in the protocol. That's making the LSP re-enable the N*M integration effort it's been trying to shutdown.

Right, but that's already the case. This is from the LSP spec:

Command

Represents a reference to a command. Provides a title which will be used to represent a command in the UI. Commands are identified by a string identifier. The protocol currently doesn’t specify a set of well-known commands. So executing a command requires some tool extension code.

Yes it's more work, but it'll necessary to provide some functionality. LSP can't cover everything, so some experiences will need custom extension code. Without this, all experiences will be limited to the set of functionality that every editor can provide out of the box.

I'd hate to have to implement specific code for the Dart Language Server in the Eclipse IDE for instance,

This is going to be unavoidable for some experiences - the LSP server/API isn't going to include a UI framework and client extensions will want to provide a UI that fits in with their application. Of course, LSP should minimise the amount of work done this way, but it shouldn't shut the door on it.

@DanTup I think you can [ab]use initializationOptions for that? I do it for my hacky version of syntax highlighting.

Right, but it'd be nice for there to be a standard way. The quoted text above already says that LSP client extensions may need to implement commands that the server will call - so why don't the capabilities allow for communicating which are supported? It seems strange for the spec to explicitly support something, but require some custom hack in order for compatibility.

In general, I think that complex IntelliJ-style refactorings are really sub-protocols between the client and the server, which require several rounds of back and forth, and which could use language specific messages and language specific UI.

I agree with this - and my proposal here (which is clients advertising which commands they support, not what's in the title - @dbaeumer would you object to renaming it again?) is to support that - it's letter the server know that it can include a quick-fix that can kick off this flow with a client command. The client can't decide when to show the refactors because it doesn't know when they should be available.

@dbaeumer dbaeumer changed the title Support that servers can ask for user input What's the correct way for an LSP server to know whether a command is available on the client? Dec 20, 2018
@dbaeumer
Copy link
Member

@DanTup I renamed it back.

Regarding

Command

Represents a reference to a command. Provides a title which will be used to represent a command in the UI. Commands are identified by a string identifier. The protocol currently doesn’t specify a set of well-known commands. So executing a command requires some tool extension code.

I need to update this. It is in there from the days where a server couldn't handle command execution. This is why it said So executing a command requires some tool extension code.

@dbaeumer
Copy link
Member

If the commands are not standardize why do we need a generic way to advertise them. If I understand @DanTup now correctly he wants to advertise commands that need to be implemented by the tool extension anyways. So to make this work you need specific extensions for every tool. Given that the extension could simply send a custom message right after the initialize request to let the server know about this?

@mickaelistria
Copy link

Indeed, given the level of flexibility required by @DanTup, protocol extensions seem actually to be a good way to implement it already.
I don't think there is need for advertising as clients won't dynamically care about it (since it requires a static implementation upfront in the client anyway). Getting the list of commands to implement at runtime is too late for execution.

@dbaeumer
Copy link
Member

I'm not sure I understand - why have them in the server capabilities at all if they should always be called on the server?

I was not clear: they are called by the client as a side effect of the server putting them into a code action or completion item. But the client doesn't do any interpretation of the command id value.

IMO providing locally executed commands requires to spec them in terms of their command id, the params they take and the result they deliver. This is like specifying a request :-). So I rather prefer adding requests.

Given this is it OK for you to close the issue ?

@dbaeumer dbaeumer added the info-needed Issue requires more information from poster label Jan 21, 2019
@DanTup
Copy link
Contributor Author

DanTup commented Jan 21, 2019

IMO providing locally executed commands requires to spec them in terms of their command id, the params they take and the result they deliver

I don't really agree with this... In VS Code, I can enumerate a list of commands (getCommands()) which gives me their IDs without any information on what they accept or return. That API presumably exists because it's useful, so why is the same not true here?

The LSP spec says that servers can provide command IDs to the client and "the tool extension code could handle the command.". If this is valid, why is the server not allowed to know in advance which commands the client could actually handle? If the server gains new functionality that relies on a local command, it needs to know if the client extension supports it. Yes it's possible for us to build our own way of communicating these capabilities, but it's extra work and seems to go against the idea of keeping things within the spec'd protocol.

So I rather prefer adding requests.

For functionality that's useful to multiple languages, this makes perfect sense. However there are going to be many features that are "too specific" and will just be rejected from the spec so it should be easy to extend.

(I don't want to flog a dead horse so if you really don't think it makes sense, feel free to close it - there are other ways around this with custom requests - it just seemed to fit that this could be symmetrical between client and server, since server already advertises the command IDs it supports).

@dbaeumer
Copy link
Member

The LSP spec says that servers can provide command IDs to the client and "the tool extension code could handle the command.".

What this tries to say is that the extension code is allowed to define a command that the server can invoke. In this case the code parts must match. However this is still not a built in editor command. However this forces every integrator of the language server to provide that command as well.

I am against allowing clients to advertise the commands. I think this make programming against the LSP more complicated except we would standardize these commands which I would do through requests.

I will close the issue as suggested since I don't see the benefit of standardizing commands over defining requests.

@DanTup
Copy link
Contributor Author

DanTup commented Jan 21, 2019

What this tries to say is that the extension code is allowed to define a command that the server can invoke. In this case the code parts must match. However this is still not a built in editor command.

There may have been some misunderstanding.. I was not requesting that built-in editor commands would be transmitted the server, I was specifically asking for a client extension to be able to provide some custom commands it has that the server is allowed to invoke, exactly as you detail there.

However this forces every integrator of the language server to provide that command as well.

It doesn't, if this feature was implemented :) The purpose of this request was specifically to allow the server to know if the client supported the feature, so it would know when it can use it.

I don't see the benefit of standardizing commands over defining requests.

I agree with that, but I think you may have misunderstood what was being asked for. I wanted the server to know that the client supports "dart.someCustomCommand" so it may include additional code actions (or completions, or whatever) that would call it. There are many things the server cannot do and will require some custom code locally - without this they'll need to have custom requests even if they could've been encoded into a simple command.

@dbaeumer
Copy link
Member

There may have been some misunderstanding.

Definitelly :-)

Reopening the issue then.

@dbaeumer dbaeumer reopened this Jan 21, 2019
@dbaeumer dbaeumer added help wanted Issues identified as good community contribution opportunities and removed info-needed Issue requires more information from poster labels Jan 21, 2019
@alanz
Copy link

alanz commented Jan 21, 2019

@dbaeumer said

However this forces every integrator of the language server to provide that command as well.

@DanTup said

I wanted the server to know that the client supports "dart.someCustomCommand" so it may include additional code actions (or completions, or whatever) that would call it.

It is not clear to me that these two cases are different, and it does mean that client developers will be forced to add features driven by the language server, to provide a decent experience. And over time the server becomes specialised to only a handful or only one client.

@DanTup
Copy link
Contributor Author

DanTup commented Jan 21, 2019

It is not clear to me that these two cases are different

I think one (calling editor-specific commands from the server) would start down the path of trying to standardise the commands across editors (which in some ways seems attractive, but I think will also result in poor experiences (for example there's a world of difference between Vim and Visual Studio - trying to encode things like UIs into commands will suck).

The other, is to provide an extension point for features that just won't ever make it into LSP (things that are very specific to a particular integration). For complicated cases, we'd add custom requests, but in the simple case it may be sufficient to just have the client execute a command.

The big difference here is that the second one is language specific and makes not attempt at being standardised. If you're integrating with my LSP server and you'd like to support SomeCoolFeature then you can do it by implementing this command, and advertising that you can handle it in the ClientCapabilities. That makes it ok for me to return code actions/completions/whatever that have this command ID.

Yes, it's limited - you can't pass arguments or receive results - but sometimes that's enough.

@DanTup
Copy link
Contributor Author

DanTup commented Feb 5, 2019

FWIW I found another place where I think this would help. We have some CodeLens links like this in the Dart plugin:

screenshot 2019-02-05 at 9 22 03 am

The server provides the info on where the tests are but the client has to handle the command to launch a debug session. We could build some custom way for the client to request the test locations from the server and then have a client-side CodeLens provider, but that feels weird when the server can already provide them. It would be much nicer if we could spec a client-command for launching of tests and then have the server know if the client supports it.

@DanTup
Copy link
Contributor Author

DanTup commented Mar 25, 2019

Another place where I think this would be useful... We're adding URLs for diagnostics that load documentation that might help a user understand/fix some errors. It'd be nice if we could attach code actions so that when they right-click the error in the Problems view, the context menu has an entry to open the docs to help with that error. Spawning the browser would require a client-side command be implemented, so it'd be nice if the server could conditionally include those code actions if it knows the client implements (a custom - provided by the client extension) "open docs in browser" command.

@DanTup
Copy link
Contributor Author

DanTup commented Jun 5, 2019

I came to try and implement "extract method" without using any client command as discussed above (just use a code action, apply the edit with a default name and then rename it). However, as far as I can tell, there's no way to invoke the rename, so this doesn't work.

So if I understand correctly, some work is required to support even just a simple "extract method" code action?

I've opened #764 as a more specific request about this. I'm currently blocked unless I implement my own mechanism for this (which doesn't seem like a good solution, since "extract method" is not a concept specific to my language).

@kdvolder
Copy link

kdvolder commented Oct 22, 2020

Just a late reaction/insight around why a client may need/want to advertise commands it can execute even if there is not a 'standardized' list of commands enshrined in the spec. This is in scenarios where multiple LS need to somehow collaborate / coordinate. E.g. the Java language server may advertise commands that are very specific to Java and they do not belong in a general LSP spec. However, other language servers also operating on Java code or related artefacts, might still want to rely on these commands which have a well-define and understood meaning / function in the context of Java tooling.

For example, the spring-boot language server relies on the Java language server for classpath information. This information is obtained by requesting it via a command. Boot LS basically just assumes the command is there (which should be the case if the Java language tooling is installed and so the Java language server provides it).

But it would probably be useful if rather than blindly assuming these kinds of 'inter server commands' exist, there would be a better way to check that (i.e. client capability to advertise the known commands). For this purpose a simple list of commands that 'exist' would be sufficient. We wouldn't need to standardize on the meaning of the commands in the spec. The meaning is an implicit agreement between two ls servers, one that defines the command and a second one that depends on it.

@dbaeumer
Copy link
Member

Good point in regards of using commands for inter server communication.

@matklad
Copy link
Contributor

matklad commented Jul 30, 2021

rust-analyzer now implements this as a custom extension:

Client Commands

Experimental Client Capability: { "commands?": ClientCommandOptions }

Certain LSP types originating on the server, notably code lenses, embed commands.
Commands can be serviced either by the server or by the client.
However, the server doesn't know which commands are available on the client.

This extensions allows the client to communicate this info.

export interface ClientCommandOptions {
	/**
	 * The commands to be executed on the client
	 */
	commands: string[];
}

https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/dev/lsp-extensions.md#client-commands.

See neovim/neovim#15183 for the context where this is required (TL;DR: code lenses)

@radeksimko
Copy link
Contributor

I implemented something similar in terraform-ls recently - opt-in code lens.

Instead of letting clients pass an arbitrary list of commands though I let them opt into a particular behaviour by passing their command ID. This gives clients a little bit more freedom at the cost of making the implementation "less universal". It reflects that clients may already implement the same command under a different name and potentially saves it from registering another command in the global namespace just for the server.

For example if/when VS Code decides to make editor.action.showReferences (or similar) actually callable by the server then all extensions/clients can just reuse it, instead of implementing a "proxy" command of their own.

See https://github.com/hashicorp/terraform-ls/blob/cd6d3c2/docs/language-clients.md#code-lens for more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
commands feature-request Request for new features or functionality help wanted Issues identified as good community contribution opportunities
Projects
None yet
Development

No branches or pull requests

7 participants