From 903335a6d50b020b36d1c4d5e9da362c31439d6e Mon Sep 17 00:00:00 2001 From: Olivia Kinnear Date: Thu, 4 Dec 2025 21:50:00 -0600 Subject: [PATCH] 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. --- runtime/doc/lsp.txt | 18 ++++++++++-------- runtime/doc/news.txt | 5 +++-- runtime/lua/vim/lsp/client.lua | 23 ++++++++++++++++------- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 722ff86822..dd30bff204 100644 --- 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 index 03b59647bc..3de53593f1 100644 --- 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 index 796f93b278..4259ae3203 100644 --- 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