-
Notifications
You must be signed in to change notification settings - Fork 324
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
Disposing server before initialisation is complete results in commands not being unregistered #725
Comments
I fixed it by ensuring that the client is ready before allowing a stop(). Everything else gets very complicated in terms of ensuring that all state can be cleaned up correctly. |
I'm hitting this issue again, and I'm struggling to understand what the correct way is if I need to restart and/or shutdown the language server. If I try to I tried using things like Could you describe what the correct process is for stopping an LSP client (in any state), and then ensuring the underlying server process can be terminated without showing errors, and ensuring that all commands have been unregistered? I think what I'd really like is a function we can call at any time to ask for shutdown, which immediately unregisters all VS Code commands/providers, puts the client into a silent mode (do not show any errors caused by shutting down), and then attempt to gracefully shut the server down with a timeout. This would allow me to call |
How do you start the server and which communication mechanism do you use to talk to it? |
I spawn the process and use The code to start the client is like this const client = new LanguageClient(
"dartAnalysisLSP",
"Dart Analysis Server",
async () => {
const streamInfo = await this.spawnServer(logger, sdks, dartCapabilities); // This spawns the server and logs the streams
const jsonEncoder = ls.RAL().applicationJson.encoder;
return {
detached: streamInfo.detached,
reader: new StreamMessageReader(streamInfo.reader),
writer: new StreamMessageWriter(streamInfo.writer, {
contentTypeEncoder: {
encode: (msg, options) => {
(msg as any).clientRequestTime = Date.now(); // This adds the timestamp
return jsonEncoder.encode(msg, options);
},
name: "withTiming",
},
}),
};
},
clientOptions,
);
// And shortly after:
client.start() |
Lets move the discussion into #1559 to have everything in one place since I guess these issues are related. One problem might be that the server process is not finished since the client doesn't know how to kill it. One solution might be to pass in a terminate callback into the restart method. |
If a server lists commands in
executeCommandProvider.commands
in its capabilities, the client registers these with VS Code and adds them to the disposables list.However, - if you dispose before this has happened, the commands are not unregistered (since they're not registered at the time of dispose) but they are still later registered as the initialisation completes. This results in "Error: command 'foo.command' already exists" if you try to restart a server before it was fully initialised.
Repro:
Add this to testServer.ts in the server capabilities:
Add a test that disposes the server immediately then tries to recreate it:
@dbaeumer any suggestions on how this should be handled? I would guess that after calling dispose/stop, it should probably prevent further initialisation code from running (or at least, anything that registers disposables)?
The text was updated successfully, but these errors were encountered: