Lsp shutdown behavior - on_exit not triggered on `:q` - is this intentional?

heyya :wave:,

i am currently doing yet another neovim config rewrite. i am trying to learn more about neovim and thus am doing it without dependcies.
i wrote an lsp config for just rust-analyzer right now. as per help the vim.lsp.start_client takes a lsp.ClientConfig.
that ClientConfig has a key called on_exit(see help lsp 1034). my solution for avoiding to start a client per buffer was to write a lockfile in the stdpath state, the on_exit thus is meant to be a snippet that just calls os.remove(lockfile).
the general idea works and the same client is attached to multiple buffers and everything works. my issue is that when i exit vim, and thus close the client, the lock file persists. for debugging purposes i tried writing to another file in the same manner i am creating the lock file to check that its not an issue of my file removal code.

i would have expected for the on_exit callback to be triggered. now my question is whether this is intentional? (allthough i am unsure who i should be addressing with this :grimacing:)

i can see a world in which this is not a proper client exit on nvim shutdown. and its not like i have a orphaned rust-analyzer process hanging around.

my config is here:

  • git repo link:
  • web link

best regards :>

You’ll probably want to create an autocommand for VimLeavePre.

ah thanks that’s a good call. i mean the help does kinda sound like
there is an expectation that a user handles their own clean up.
it’s still a bit unclear to me whether the whole not having this be a
default is a good default or intentional, but i guess that’s just guessing.
maybe imma just file an issue and ask though :3

I believe on_exit is invoked when the LSP client exits (e.g. if the language server process terminated). So you can do cleanup like delete keymaps or autocommands you created in on_attach which interact with the. LSP client.

It’s not intended to be used like VimLeavePre.
There are a bunch of functions with an on_exit callback (e.g. vim.system in neovim nightly).

that’s also where my confusion stems from.
because simply exiting just the client cleanly via :lua vim.lsp.stop_client(1) then on_exit is called, my lockfile gets
removed and everything just works ™.
so are you kinda saying that its probably just a matter of time when
this will get integrated cleanly?

best regards!

You shouldn’t need a lock file to avoid spawning multiple clients per buffer.

vim.lsp.start will reattach the buffer to an existing client , as long as the root_dir doesn’t change.

I see in your config that you have root_dir = vim.fn.getcwd(), which might change. It should either be nil (for a standalone file) or the project root (of a cargo project in the case of rust-analyzer).

Typically, you would do something like root_dir = vim.fs.dirname(vim.fs.find(root_files, { upward = true })[1]) where root_files is a list of files you expect to only find in the project root (it will pick the first one it finds).

Since cargo projects can have multiple Cargo.toml files in subdirectories, it’s more reliable to get the project root using the cargo metadata command, which outputs json you can decode to a lua table with vim.json.decode().
Here’s an example.

i am aware of that now (someone pointe it out on github) :>
however my intention was never to ask how i can improve my config concretely (allthough certainly thanks for the tips!)
i was concerned with this behavior of on_exit being called when i manually exit a client, but not when i exit neovim.
however as you mentioned this:

i think i had the general idea that this on_exit hook in the client config is just not fully integrated yet and that its probably going to get integrated in a future release.

I don’t think on_exit ever will be called when neovim exits. There isn’t a use case for it, and it just adds complexity.

1 Like