feat(lsp): support auto-force escalation in client stop #36430

(cherry picked from commit 02cd564896)
This commit is contained in:
Maria Solano
2025-11-02 09:01:41 -08:00
committed by GitHub
parent e0fdfd3d4b
commit b92e92b20d
3 changed files with 24 additions and 40 deletions

View File

@@ -1278,7 +1278,7 @@ Lua module: vim.lsp.client *lsp-client*
See |Client:notify()|. See |Client:notify()|.
• {cancel_request} (`fun(self: vim.lsp.Client, id: integer): boolean`) • {cancel_request} (`fun(self: vim.lsp.Client, id: integer): boolean`)
See |Client:cancel_request()|. See |Client:cancel_request()|.
• {stop} (`fun(self: vim.lsp.Client, force: boolean?)`) • {stop} (`fun(self: vim.lsp.Client, force: boolean|integer?)`)
See |Client:stop()|. See |Client:stop()|.
• {is_stopped} (`fun(self: vim.lsp.Client): boolean`) See • {is_stopped} (`fun(self: vim.lsp.Client): boolean`) See
|Client:is_stopped()|. |Client:is_stopped()|.
@@ -1531,8 +1531,14 @@ Client:stop({force}) *Client:stop()*
you request to stop a client which has previously been requested to you request to stop a client which has previously been requested to
shutdown, it will automatically escalate and force shutdown. 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.
Note: Forcing shutdown while a server is busy writing out project or index
files can lead to file corruption.
Parameters: ~ Parameters: ~
• {force} (`boolean?`) • {force} (`boolean|integer?`)
Client:supports_method({method}, {bufnr}) *Client:supports_method()* Client:supports_method({method}, {bufnr}) *Client:supports_method()*
Checks if a client supports a given method. Always returns true for Checks if a client supports a given method. Always returns true for

View File

@@ -1208,42 +1208,8 @@ api.nvim_create_autocmd('VimLeavePre', {
client:stop() client:stop()
end end
local timeouts = {} --- @type table<integer,integer> for _, client in pairs(active_clients) do
local max_timeout = 0 client:stop(client.flags.exit_timeout)
local send_kill = false
for client_id, client in pairs(active_clients) do
local timeout = client.flags.exit_timeout
if timeout then
send_kill = true
timeouts[client_id] = timeout
max_timeout = math.max(timeout, max_timeout)
end
end
local poll_time = 50
local function check_clients_closed()
for client_id, timeout in pairs(timeouts) do
timeouts[client_id] = timeout - poll_time
end
for client_id, _ in pairs(active_clients) do
if timeouts[client_id] ~= nil and timeouts[client_id] > 0 then
return false
end
end
return true
end
if send_kill then
if not vim.wait(max_timeout, check_clients_closed, poll_time) then
for client_id, client in pairs(active_clients) do
if timeouts[client_id] ~= nil then
client:stop(true)
end
end
end
end end
end, end,
}) })

View File

@@ -804,8 +804,20 @@ end
--- you request to stop a client which has previously been requested to --- you request to stop a client which has previously been requested to
--- shutdown, it will automatically escalate and force shutdown. --- shutdown, it will automatically escalate and force shutdown.
--- ---
--- @param force? boolean --- If `force` is a number, it will be treated as the time in milliseconds to
--- wait before forcing the shutdown.
---
--- Note: Forcing shutdown while a server is busy writing out project or index
--- files can lead to file corruption.
---
--- @param force? boolean|integer
function Client:stop(force) function Client:stop(force)
if type(force) == 'number' then
vim.defer_fn(function()
self:stop(true)
end, force)
end
local rpc = self.rpc local rpc = self.rpc
if rpc.is_closing() then if rpc.is_closing() then
return return
@@ -825,7 +837,7 @@ function Client:stop(force)
if err == nil then if err == nil then
rpc.notify(ms.exit) rpc.notify(ms.exit)
else else
-- If there was an error in the shutdown request, then term to be safe. -- If there was an error in the shutdown request, then terminate to be safe.
rpc.terminate() rpc.terminate()
self._graceful_shutdown_failed = true self._graceful_shutdown_failed = true
end end