feat(lsp): graduate ClientConfig exit_timeout #36750

Problem:
The `flags` field calls its sub-fields "experimental".
But `exit_timeout` is now used for multiple purposes.

Solution:
Graduate `exit_timeout` to a top-level ClientConfig field.
This commit is contained in:
Olivia Kinnear
2025-11-30 21:41:43 -05:00
committed by GitHub
parent 3f8e51cee7
commit acfb9bc614
4 changed files with 35 additions and 28 deletions

View File

@@ -1049,8 +1049,8 @@ enable({name}, {enable}) *vim.lsp.enable()*
• {name} (`string|string[]`) Name(s) of client(s) to enable. • {name} (`string|string[]`) Name(s) of client(s) to enable.
• {enable} (`boolean?`) `true|nil` to enable, `false` to disable • {enable} (`boolean?`) `true|nil` to enable, `false` to disable
(actively stops and detaches clients as needed, and force (actively stops and detaches clients as needed, and force
stops them if necessary after `client.flags.exit_timeout` stops them if necessary after `client.exit_timeout`
milliseconds, with a default time of 3000 milliseconds) milliseconds)
foldclose({kind}, {winid}) *vim.lsp.foldclose()* foldclose({kind}, {winid}) *vim.lsp.foldclose()*
Close all {kind} of folds in the the window with {winid}. Close all {kind} of folds in the the window with {winid}.
@@ -1593,6 +1593,12 @@ Lua module: vim.lsp.client *lsp-client*
|vim.lsp.ClientConfig|. |vim.lsp.ClientConfig|.
• {dynamic_capabilities} (`lsp.DynamicCapabilities`) Capabilities • {dynamic_capabilities} (`lsp.DynamicCapabilities`) Capabilities
provided at runtime (after startup). provided at runtime (after startup).
• {exit_timeout} (`integer|boolean`, default: `3000`)
Milliseconds to wait for server to exit
cleanly after sending the "shutdown" request
before sending kill -15. If set to false,
waits indefinitely. If set to true, nvim will
kill the server immediately.
• {flags} (`table`) A table with flags for the client. • {flags} (`table`) A table with flags for the client.
The current (experimental) flags are: The current (experimental) flags are:
• {allow_incremental_sync}? (`boolean`, • {allow_incremental_sync}? (`boolean`,
@@ -1602,12 +1608,6 @@ Lua module: vim.lsp.client *lsp-client*
`150`) Debounce `didChange` notifications to `150`) Debounce `didChange` notifications to
the server by the given number in the server by the given number in
milliseconds. No debounce occurs if `nil`. milliseconds. No debounce occurs if `nil`.
• {exit_timeout} (`integer|false`, default:
`false`) Milliseconds to wait for server to
exit cleanly after sending the "shutdown"
request before sending kill -15. If set to
false, nvim exits immediately after sending
the "shutdown" request to the server.
• {get_language_id} (`fun(bufnr: integer, filetype: string): string`) • {get_language_id} (`fun(bufnr: integer, filetype: string): string`)
See |vim.lsp.ClientConfig|. See |vim.lsp.ClientConfig|.
• {handlers} (`table<string,lsp.Handler>`) See • {handlers} (`table<string,lsp.Handler>`) See
@@ -1720,6 +1720,12 @@ Lua module: vim.lsp.client *lsp-client*
process on exit, but if Nvim fails to exit process on exit, but if Nvim fails to exit
cleanly this could leave behind orphaned server cleanly this could leave behind orphaned server
processes. processes.
• {exit_timeout}? (`integer|boolean`, default: `3000`)
Milliseconds to wait for server to exit cleanly
after sending the "shutdown" request before
sending kill -15. If set to false, waits
indefinitely. If set to true, nvim will kill
the server immediately.
• {flags}? (`table`) A table with flags for the client. • {flags}? (`table`) A table with flags for the client.
The current (experimental) flags are: The current (experimental) flags are:
• {allow_incremental_sync}? (`boolean`, • {allow_incremental_sync}? (`boolean`,
@@ -1729,12 +1735,6 @@ Lua module: vim.lsp.client *lsp-client*
`150`) Debounce `didChange` notifications to `150`) Debounce `didChange` notifications to
the server by the given number in the server by the given number in
milliseconds. No debounce occurs if `nil`. milliseconds. No debounce occurs if `nil`.
• {exit_timeout} (`integer|false`, default:
`false`) Milliseconds to wait for server to
exit cleanly after sending the "shutdown"
request before sending kill -15. If set to
false, nvim exits immediately after sending
the "shutdown" request to the server.
• {get_language_id}? (`fun(bufnr: integer, filetype: string): string`) • {get_language_id}? (`fun(bufnr: integer, filetype: string): string`)
Language ID as string. Defaults to the buffer Language ID as string. Defaults to the buffer
filetype. filetype.

View File

@@ -277,10 +277,12 @@ LSP
• |Client:stop()| now accepts a numerical `force` argument to be interpreted as the time to wait • |Client:stop()| now accepts a numerical `force` argument to be interpreted as the time to wait
before forcing the shutdown. before forcing the shutdown.
• Add cmp field to opts of |vim.lsp.completion.enable()| for custom completion ordering. • Add cmp field to opts of |vim.lsp.completion.enable()| for custom completion ordering.
• |vim.lsp.enable()| when `enable == false` now force stops the client after • |vim.lsp.enable()| when `enable == false` now force stops the client when it
3000 milliseconds when it takes too long to shutdown after being disabled. takes too long to shutdown after being disabled.
• Push diagnostics (|vim.lsp.diagnostic.on_publish_diagnostics()|) now respect • Push diagnostics (|vim.lsp.diagnostic.on_publish_diagnostics()|) now respect
the `version` property in the notification params. 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 3000 milliseconds.
LUA LUA

View File

@@ -550,8 +550,7 @@ end
--- ---
--- @param name string|string[] Name(s) of client(s) to enable. --- @param name string|string[] Name(s) of client(s) to enable.
--- @param enable? boolean `true|nil` to enable, `false` to disable (actively stops and detaches --- @param enable? boolean `true|nil` to enable, `false` to disable (actively stops and detaches
--- clients as needed, and force stops them if necessary after `client.flags.exit_timeout` --- clients as needed, and force stops them if necessary after `client.exit_timeout` milliseconds)
--- milliseconds, with a default time of 3000 milliseconds)
function lsp.enable(name, enable) function lsp.enable(name, enable)
validate('name', name, { 'string', 'table' }) validate('name', name, { 'string', 'table' })
@@ -588,9 +587,7 @@ function lsp.enable(name, enable)
else else
for _, nm in ipairs(names) do for _, nm in ipairs(names) do
for _, client in ipairs(lsp.get_clients({ name = nm })) do for _, client in ipairs(lsp.get_clients({ name = nm })) do
local t = client.flags.exit_timeout client:stop(client.exit_timeout)
local force_timeout = t and tonumber(t) or (t ~= false and 3000 or nil)
client:stop(force_timeout)
end end
end end
end end
@@ -1147,7 +1144,7 @@ api.nvim_create_autocmd('VimLeavePre', {
log.info('exit_handler', active_clients) log.info('exit_handler', active_clients)
for _, client in pairs(active_clients) do for _, client in pairs(active_clients) do
client:stop(client.flags.exit_timeout) client:stop(client.exit_timeout)
end end
end, end,
}) })

View File

@@ -25,12 +25,6 @@ local all_clients = {}
--- No debounce occurs if `nil`. --- No debounce occurs if `nil`.
--- (default: `150`) --- (default: `150`)
--- @field debounce_text_changes integer --- @field debounce_text_changes integer
---
--- Milliseconds to wait for server to exit cleanly after sending the
--- "shutdown" request before sending kill -15. If set to false, nvim exits
--- immediately after sending the "shutdown" request to the server.
--- (default: `false`)
--- @field exit_timeout integer|false
--- @class vim.lsp.ClientConfig --- @class vim.lsp.ClientConfig
--- ---
@@ -75,6 +69,12 @@ local all_clients = {}
--- (default: `true`) --- (default: `true`)
--- @field detached? boolean --- @field detached? boolean
--- ---
--- Milliseconds to wait for server to exit cleanly after sending the "shutdown" request before
--- sending kill -15. If set to false, waits indefinitely. If set to true, nvim will kill the
--- server immediately.
--- (default: `3000`)
--- @field exit_timeout? integer|boolean
---
--- A table with flags for the client. The current (experimental) flags are: --- A table with flags for the client. The current (experimental) flags are:
--- @field flags? vim.lsp.Client.Flags --- @field flags? vim.lsp.Client.Flags
--- ---
@@ -157,6 +157,12 @@ local all_clients = {}
--- Capabilities provided at runtime (after startup). --- Capabilities provided at runtime (after startup).
--- @field dynamic_capabilities lsp.DynamicCapabilities --- @field dynamic_capabilities lsp.DynamicCapabilities
--- ---
--- Milliseconds to wait for server to exit cleanly after sending the "shutdown" request before
--- sending kill -15. If set to false, waits indefinitely. If set to true, nvim will kill the
--- server immediately.
--- (default: `3000`)
--- @field exit_timeout integer|boolean
---
--- A table with flags for the client. The current (experimental) flags are: --- A table with flags for the client. The current (experimental) flags are:
--- @field flags vim.lsp.Client.Flags --- @field flags vim.lsp.Client.Flags
--- ---
@@ -314,6 +320,7 @@ local function validate_config(config)
validate('cmd_cwd', config.cmd_cwd, optional_validator(is_dir), 'directory') validate('cmd_cwd', config.cmd_cwd, optional_validator(is_dir), 'directory')
validate('cmd_env', config.cmd_env, 'table', true) validate('cmd_env', config.cmd_env, 'table', true)
validate('detached', config.detached, 'boolean', true) validate('detached', config.detached, 'boolean', true)
validate('exit_timeout', config.exit_timeout, { 'number', 'boolean' }, true)
validate('name', config.name, 'string', true) validate('name', config.name, 'string', true)
validate('on_error', config.on_error, 'function', true) validate('on_error', config.on_error, 'function', true)
validate('on_exit', config.on_exit, { 'function', 'table' }, true) validate('on_exit', config.on_exit, { 'function', 'table' }, true)
@@ -388,6 +395,7 @@ function Client.create(config)
commands = config.commands or {}, commands = config.commands or {},
settings = config.settings or {}, settings = config.settings or {},
flags = config.flags or {}, flags = config.flags or {},
exit_timeout = config.exit_timeout == nil and 3000 or config.exit_timeout --[[@as integer|boolean]],
get_language_id = config.get_language_id or default_get_language_id, get_language_id = config.get_language_id or default_get_language_id,
capabilities = config.capabilities, capabilities = config.capabilities,
workspace_folders = lsp._get_workspace_folders(config.workspace_folders or config.root_dir), workspace_folders = lsp._get_workspace_folders(config.workspace_folders or config.root_dir),