commit 903335a6d50b020b36d1c4d5e9da362c31439d6e
parent a8a0bba2f8e0ee65bc3b658cb98fffb50753e2ae
Author: Olivia Kinnear <git@superatomic.dev>
Date: Thu, 4 Dec 2025 21:50:00 -0600
feat(lsp): `Client:stop()` defaults to `exit_timeout` #36783
Problem:
If a `vim.lsp.config` explicitly sets `exit_timeout`, that indicates the
config wants that behavior for most usages of `:stop()`.
Solution:
Update `:stop()` to use `force=exit_timeout` if `force` was not
explicitly passed.
Diffstat:
3 files changed, 29 insertions(+), 17 deletions(-)
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
@@ -1653,7 +1653,7 @@ Lua module: vim.lsp.client *lsp-client*
See |Client:notify()|.
• {cancel_request} (`fun(self: vim.lsp.Client, id: integer): boolean`)
See |Client:cancel_request()|.
- • {stop} (`fun(self: vim.lsp.Client, force: boolean|integer?)`)
+ • {stop} (`fun(self: vim.lsp.Client, force: integer|boolean?)`)
See |Client:stop()|.
• {is_stopped} (`fun(self: vim.lsp.Client): boolean`) See
|Client:is_stopped()|.
@@ -1895,20 +1895,22 @@ Client:request_sync({method}, {params}, {timeout_ms}, {bufnr})
• |vim.lsp.buf_request_sync()|
Client:stop({force}) *Client:stop()*
- Stops a client, optionally with force.
+ Stops a client, optionally with force after a timeout.
- By default, it will just request the server to shutdown without force. If
+ By default, it will request the server to shutdown, then force a shutdown
+ if the server has not exited after `self.exit_timeout` milliseconds. If
you request to stop a client which has previously been requested to
- shutdown, it will automatically escalate and force shutdown.
-
- If `force` is a number, it will be treated as the time in milliseconds to
- wait before forcing the shutdown.
+ shutdown, it will automatically escalate and force shutdown immediately,
+ regardless of the value of `force` (or `self.exit_timeout` if `nil`).
Note: Forcing shutdown while a server is busy writing out project or index
files can lead to file corruption.
Parameters: ~
- • {force} (`boolean|integer?`)
+ • {force} (`integer|boolean?`, default: `self.exit_timeout`) Time in
+ milliseconds to wait before forcing a shutdown. If false,
+ only request the server to shutdown, but don't force it. If
+ true, force a shutdown immediately.
Client:supports_method({method}, {bufnr}) *Client:supports_method()*
Checks if a client supports a given method. Always returns true for
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
@@ -274,13 +274,14 @@ LSP
• Support for `textDocument/onTypeFormatting`: |lsp-on_type_formatting|
https://microsoft.github.io/language-server-protocol/specification/#textDocument_onTypeFormatting
• The filter option of |vim.lsp.buf.code_action()| now receives the client ID as an argument.
-• |Client:stop()| now accepts a numerical `force` argument to be interpreted as the time to wait
- before forcing the shutdown.
+• |Client:stop()| now accepts a numerical `force` argument to be interpreted
+ as the time to wait before forcing the shutdown.
• Add cmp field to opts of |vim.lsp.completion.enable()| for custom completion ordering.
• Push diagnostics (|vim.lsp.diagnostic.on_publish_diagnostics()|) now respect
the `version` property in the notification params.
• |vim.lsp.ClientConfig| has an `exit_timeout` field to control the timeout of
client force stopping. Defaults to `false`.
+• |Client:stop()| now uses the `Client.exit_timeout` field to control the default of `force`.
LUA
diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua
@@ -871,20 +871,29 @@ function Client:cancel_request(id)
return self.rpc.notify('$/cancelRequest', { id = id })
end
---- Stops a client, optionally with force.
+--- Stops a client, optionally with force after a timeout.
---
---- By default, it will just request the server to shutdown without force. If
+--- By default, it will request the server to shutdown, then force a shutdown
+--- if the server has not exited after `self.exit_timeout` milliseconds. If
--- you request to stop a client which has previously been requested to
---- shutdown, it will automatically escalate and force shutdown.
----
---- If `force` is a number, it will be treated as the time in milliseconds to
---- wait before forcing the shutdown.
+--- shutdown, it will automatically escalate and force shutdown immediately,
+--- regardless of the value of `force` (or `self.exit_timeout` if `nil`).
---
--- Note: Forcing shutdown while a server is busy writing out project or index
--- files can lead to file corruption.
---
---- @param force? boolean|integer
+--- @param force? integer|boolean Time in milliseconds to wait before forcing
+--- a shutdown. If false, only request the
+--- server to shutdown, but don't force it. If
+--- true, force a shutdown immediately.
+--- (default: `self.exit_timeout`)
function Client:stop(force)
+ validate('force', force, { 'number', 'boolean' }, true)
+
+ if force == nil then
+ force = self.exit_timeout
+ end
+
local rpc = self.rpc
if rpc.is_closing() then
return