mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 03:18:16 +00:00
Merge #35002 refactor(lsp): extract Client._on_detach
to reduce duplicated code
This commit is contained in:
@@ -84,9 +84,6 @@ function lsp._buf_get_line_ending(bufnr)
|
|||||||
return format_line_ending[vim.bo[bufnr].fileformat] or '\n'
|
return format_line_ending[vim.bo[bufnr].fileformat] or '\n'
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Tracks all clients created via lsp.start_client
|
|
||||||
local all_clients = {} --- @type table<integer,vim.lsp.Client>
|
|
||||||
|
|
||||||
local client_errors_base = table.maxn(lsp.rpc.client_errors)
|
local client_errors_base = table.maxn(lsp.rpc.client_errors)
|
||||||
local client_errors_offset = 0
|
local client_errors_offset = 0
|
||||||
|
|
||||||
@@ -175,78 +172,6 @@ local function reuse_client_default(client, config)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Reset defaults set by `set_defaults`.
|
|
||||||
--- Must only be called if the last client attached to a buffer exits.
|
|
||||||
local function reset_defaults(bufnr)
|
|
||||||
if vim.bo[bufnr].tagfunc == 'v:lua.vim.lsp.tagfunc' then
|
|
||||||
vim.bo[bufnr].tagfunc = nil
|
|
||||||
end
|
|
||||||
if vim.bo[bufnr].omnifunc == 'v:lua.vim.lsp.omnifunc' then
|
|
||||||
vim.bo[bufnr].omnifunc = nil
|
|
||||||
end
|
|
||||||
if vim.bo[bufnr].formatexpr == 'v:lua.vim.lsp.formatexpr()' then
|
|
||||||
vim.bo[bufnr].formatexpr = nil
|
|
||||||
end
|
|
||||||
vim._with({ buf = bufnr }, function()
|
|
||||||
local keymap = vim.fn.maparg('K', 'n', false, true)
|
|
||||||
if keymap and keymap.callback == vim.lsp.buf.hover and keymap.buffer == 1 then
|
|
||||||
vim.keymap.del('n', 'K', { buffer = bufnr })
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param code integer
|
|
||||||
--- @param signal integer
|
|
||||||
--- @param client_id integer
|
|
||||||
local function on_client_exit(code, signal, client_id)
|
|
||||||
local client = all_clients[client_id]
|
|
||||||
|
|
||||||
vim.schedule(function()
|
|
||||||
for bufnr in pairs(client.attached_buffers) do
|
|
||||||
if client and client.attached_buffers[bufnr] and api.nvim_buf_is_valid(bufnr) then
|
|
||||||
api.nvim_exec_autocmds('LspDetach', {
|
|
||||||
buffer = bufnr,
|
|
||||||
modeline = false,
|
|
||||||
data = { client_id = client_id },
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
client.attached_buffers[bufnr] = nil
|
|
||||||
|
|
||||||
if #lsp.get_clients({ bufnr = bufnr, _uninitialized = true }) == 0 then
|
|
||||||
reset_defaults(bufnr)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local namespace = vim.lsp.diagnostic.get_namespace(client_id)
|
|
||||||
vim.diagnostic.reset(namespace)
|
|
||||||
end)
|
|
||||||
|
|
||||||
local name = client.name or 'unknown'
|
|
||||||
|
|
||||||
-- Schedule the deletion of the client object so that it exists in the execution of LspDetach
|
|
||||||
-- autocommands
|
|
||||||
vim.schedule(function()
|
|
||||||
all_clients[client_id] = nil
|
|
||||||
|
|
||||||
-- Client can be absent if executable starts, but initialize fails
|
|
||||||
-- init/attach won't have happened
|
|
||||||
if client then
|
|
||||||
changetracking.reset(client)
|
|
||||||
end
|
|
||||||
if code ~= 0 or (signal ~= 0 and signal ~= 15) then
|
|
||||||
local msg = string.format(
|
|
||||||
'Client %s quit with exit code %s and signal %s. Check log for errors: %s',
|
|
||||||
name,
|
|
||||||
code,
|
|
||||||
signal,
|
|
||||||
lsp.get_log_path()
|
|
||||||
)
|
|
||||||
vim.notify(msg, vim.log.levels.WARN)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Creates and initializes a client with the given configuration.
|
--- Creates and initializes a client with the given configuration.
|
||||||
--- @param config vim.lsp.ClientConfig Configuration for the server.
|
--- @param config vim.lsp.ClientConfig Configuration for the server.
|
||||||
--- @return integer? client_id |vim.lsp.get_client_by_id()| Note: client may not be
|
--- @return integer? client_id |vim.lsp.get_client_by_id()| Note: client may not be
|
||||||
@@ -261,11 +186,6 @@ local function create_and_init_client(config)
|
|||||||
|
|
||||||
local client = assert(res)
|
local client = assert(res)
|
||||||
|
|
||||||
--- @diagnostic disable-next-line: invisible
|
|
||||||
table.insert(client._on_exit_cbs, on_client_exit)
|
|
||||||
|
|
||||||
all_clients[client.id] = client
|
|
||||||
|
|
||||||
client:initialize()
|
client:initialize()
|
||||||
|
|
||||||
return client.id, nil
|
return client.id, nil
|
||||||
@@ -743,7 +663,7 @@ function lsp.start(config, opts)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, client in pairs(all_clients) do
|
for _, client in pairs(lsp.client._all) do
|
||||||
if reuse_client(client, config) then
|
if reuse_client(client, config) then
|
||||||
if opts.attach == false then
|
if opts.attach == false then
|
||||||
return client.id
|
return client.id
|
||||||
@@ -914,29 +834,6 @@ local function text_document_did_save_handler(bufnr)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param bufnr integer resolved buffer
|
|
||||||
---@param client vim.lsp.Client
|
|
||||||
local function buf_detach_client(bufnr, client)
|
|
||||||
api.nvim_exec_autocmds('LspDetach', {
|
|
||||||
buffer = bufnr,
|
|
||||||
modeline = false,
|
|
||||||
data = { client_id = client.id },
|
|
||||||
})
|
|
||||||
|
|
||||||
changetracking.reset_buf(client, bufnr)
|
|
||||||
|
|
||||||
if client:supports_method(ms.textDocument_didClose) then
|
|
||||||
local uri = vim.uri_from_bufnr(bufnr)
|
|
||||||
local params = { textDocument = { uri = uri } }
|
|
||||||
client:notify(ms.textDocument_didClose, params)
|
|
||||||
end
|
|
||||||
|
|
||||||
client.attached_buffers[bufnr] = nil
|
|
||||||
|
|
||||||
local namespace = lsp.diagnostic.get_namespace(client.id)
|
|
||||||
vim.diagnostic.reset(namespace, bufnr)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @type table<integer,true>
|
--- @type table<integer,true>
|
||||||
local attached_buffers = {}
|
local attached_buffers = {}
|
||||||
|
|
||||||
@@ -1013,7 +910,7 @@ local function buf_attach(bufnr)
|
|||||||
on_detach = function()
|
on_detach = function()
|
||||||
local clients = lsp.get_clients({ bufnr = bufnr, _uninitialized = true })
|
local clients = lsp.get_clients({ bufnr = bufnr, _uninitialized = true })
|
||||||
for _, client in ipairs(clients) do
|
for _, client in ipairs(clients) do
|
||||||
buf_detach_client(bufnr, client)
|
client:_on_detach(bufnr)
|
||||||
end
|
end
|
||||||
attached_buffers[bufnr] = nil
|
attached_buffers[bufnr] = nil
|
||||||
util.buf_versions[bufnr] = nil
|
util.buf_versions[bufnr] = nil
|
||||||
@@ -1076,7 +973,7 @@ function lsp.buf_detach_client(bufnr, client_id)
|
|||||||
validate('client_id', client_id, 'number')
|
validate('client_id', client_id, 'number')
|
||||||
bufnr = vim._resolve_bufnr(bufnr)
|
bufnr = vim._resolve_bufnr(bufnr)
|
||||||
|
|
||||||
local client = all_clients[client_id]
|
local client = lsp.get_client_by_id(client_id)
|
||||||
if not client or not client.attached_buffers[bufnr] then
|
if not client or not client.attached_buffers[bufnr] then
|
||||||
vim.notify(
|
vim.notify(
|
||||||
string.format(
|
string.format(
|
||||||
@@ -1087,7 +984,7 @@ function lsp.buf_detach_client(bufnr, client_id)
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
buf_detach_client(bufnr, client)
|
client:_on_detach(bufnr)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1106,7 +1003,7 @@ end
|
|||||||
---
|
---
|
||||||
---@return vim.lsp.Client? client rpc object
|
---@return vim.lsp.Client? client rpc object
|
||||||
function lsp.get_client_by_id(client_id)
|
function lsp.get_client_by_id(client_id)
|
||||||
return all_clients[client_id]
|
return lsp.client._all[client_id]
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns list of buffers attached to client_id.
|
--- Returns list of buffers attached to client_id.
|
||||||
@@ -1114,7 +1011,7 @@ end
|
|||||||
---@param client_id integer client id
|
---@param client_id integer client id
|
||||||
---@return integer[] buffers list of buffer ids
|
---@return integer[] buffers list of buffer ids
|
||||||
function lsp.get_buffers_by_client_id(client_id)
|
function lsp.get_buffers_by_client_id(client_id)
|
||||||
local client = all_clients[client_id]
|
local client = lsp.get_client_by_id(client_id)
|
||||||
return client and vim.tbl_keys(client.attached_buffers) or {}
|
return client and vim.tbl_keys(client.attached_buffers) or {}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1142,7 +1039,7 @@ function lsp.stop_client(client_id, force)
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
--- @cast id -vim.lsp.Client
|
--- @cast id -vim.lsp.Client
|
||||||
local client = all_clients[id]
|
local client = lsp.get_client_by_id(id)
|
||||||
if client then
|
if client then
|
||||||
client:stop(force)
|
client:stop(force)
|
||||||
end
|
end
|
||||||
@@ -1184,7 +1081,7 @@ function lsp.get_clients(filter)
|
|||||||
|
|
||||||
local bufnr = filter.bufnr and vim._resolve_bufnr(filter.bufnr)
|
local bufnr = filter.bufnr and vim._resolve_bufnr(filter.bufnr)
|
||||||
|
|
||||||
for _, client in pairs(all_clients) do
|
for _, client in pairs(lsp.client._all) do
|
||||||
if
|
if
|
||||||
client
|
client
|
||||||
and (filter.id == nil or client.id == filter.id)
|
and (filter.id == nil or client.id == filter.id)
|
||||||
@@ -1210,7 +1107,7 @@ api.nvim_create_autocmd('VimLeavePre', {
|
|||||||
callback = function()
|
callback = function()
|
||||||
local active_clients = lsp.get_clients()
|
local active_clients = lsp.get_clients()
|
||||||
log.info('exit_handler', active_clients)
|
log.info('exit_handler', active_clients)
|
||||||
for _, client in pairs(all_clients) do
|
for _, client in pairs(lsp.client._all) do
|
||||||
client:stop()
|
client:stop()
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1308,8 +1205,8 @@ function lsp.buf_request(bufnr, method, params, handler, on_unsupported)
|
|||||||
|
|
||||||
local function _cancel_all_requests()
|
local function _cancel_all_requests()
|
||||||
for client_id, request_id in pairs(client_request_ids) do
|
for client_id, request_id in pairs(client_request_ids) do
|
||||||
local client = all_clients[client_id]
|
local client = lsp.get_client_by_id(client_id)
|
||||||
if client.requests[request_id] then
|
if client and client.requests[request_id] then
|
||||||
client:cancel_request(request_id)
|
client:cancel_request(request_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1571,7 +1468,7 @@ end
|
|||||||
function lsp.client_is_stopped(client_id)
|
function lsp.client_is_stopped(client_id)
|
||||||
vim.deprecate('vim.lsp.client_is_stopped()', 'vim.lsp.get_client_by_id()', '0.14')
|
vim.deprecate('vim.lsp.client_is_stopped()', 'vim.lsp.get_client_by_id()', '0.14')
|
||||||
assert(client_id, 'missing client_id param')
|
assert(client_id, 'missing client_id param')
|
||||||
return not all_clients[client_id]
|
return not lsp.get_client_by_id(client_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Gets a map of client_id:client pairs for the given buffer, where each value
|
--- Gets a map of client_id:client pairs for the given buffer, where each value
|
||||||
|
@@ -6,6 +6,10 @@ local ms = lsp.protocol.Methods
|
|||||||
local changetracking = lsp._changetracking
|
local changetracking = lsp._changetracking
|
||||||
local validate = vim.validate
|
local validate = vim.validate
|
||||||
|
|
||||||
|
--- Tracks all clients initialized.
|
||||||
|
---@type table<integer,vim.lsp.Client>
|
||||||
|
local all_clients = {}
|
||||||
|
|
||||||
--- @alias vim.lsp.client.on_init_cb fun(client: vim.lsp.Client, init_result: lsp.InitializeResult)
|
--- @alias vim.lsp.client.on_init_cb fun(client: vim.lsp.Client, init_result: lsp.InitializeResult)
|
||||||
--- @alias vim.lsp.client.on_attach_cb fun(client: vim.lsp.Client, bufnr: integer)
|
--- @alias vim.lsp.client.on_attach_cb fun(client: vim.lsp.Client, bufnr: integer)
|
||||||
--- @alias vim.lsp.client.on_exit_cb fun(code: integer, signal: integer, client_id: integer)
|
--- @alias vim.lsp.client.on_exit_cb fun(code: integer, signal: integer, client_id: integer)
|
||||||
@@ -493,6 +497,9 @@ end
|
|||||||
|
|
||||||
--- @nodoc
|
--- @nodoc
|
||||||
function Client:initialize()
|
function Client:initialize()
|
||||||
|
-- Register all initialized clients.
|
||||||
|
all_clients[self.id] = self
|
||||||
|
|
||||||
local config = self.config
|
local config = self.config
|
||||||
|
|
||||||
local root_uri --- @type string?
|
local root_uri --- @type string?
|
||||||
@@ -1186,12 +1193,87 @@ function Client:_on_error(code, err)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param bufnr integer resolved buffer
|
||||||
|
function Client:_on_detach(bufnr)
|
||||||
|
if self.attached_buffers[bufnr] and api.nvim_buf_is_valid(bufnr) then
|
||||||
|
api.nvim_exec_autocmds('LspDetach', {
|
||||||
|
buffer = bufnr,
|
||||||
|
modeline = false,
|
||||||
|
data = { client_id = self.id },
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
changetracking.reset_buf(self, bufnr)
|
||||||
|
|
||||||
|
if self:supports_method(ms.textDocument_didClose) then
|
||||||
|
local uri = vim.uri_from_bufnr(bufnr)
|
||||||
|
local params = { textDocument = { uri = uri } }
|
||||||
|
self:notify(ms.textDocument_didClose, params)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.attached_buffers[bufnr] = nil
|
||||||
|
|
||||||
|
local namespace = lsp.diagnostic.get_namespace(self.id)
|
||||||
|
vim.diagnostic.reset(namespace, bufnr)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Reset defaults set by `set_defaults`.
|
||||||
|
--- Must only be called if the last client attached to a buffer exits.
|
||||||
|
local function reset_defaults(bufnr)
|
||||||
|
if vim.bo[bufnr].tagfunc == 'v:lua.vim.lsp.tagfunc' then
|
||||||
|
vim.bo[bufnr].tagfunc = nil
|
||||||
|
end
|
||||||
|
if vim.bo[bufnr].omnifunc == 'v:lua.vim.lsp.omnifunc' then
|
||||||
|
vim.bo[bufnr].omnifunc = nil
|
||||||
|
end
|
||||||
|
if vim.bo[bufnr].formatexpr == 'v:lua.vim.lsp.formatexpr()' then
|
||||||
|
vim.bo[bufnr].formatexpr = nil
|
||||||
|
end
|
||||||
|
vim._with({ buf = bufnr }, function()
|
||||||
|
local keymap = vim.fn.maparg('K', 'n', false, true)
|
||||||
|
if keymap and keymap.callback == vim.lsp.buf.hover and keymap.buffer == 1 then
|
||||||
|
vim.keymap.del('n', 'K', { buffer = bufnr })
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
--- @private
|
--- @private
|
||||||
--- Invoked on client exit.
|
--- Invoked on client exit.
|
||||||
---
|
---
|
||||||
--- @param code integer) exit code of the process
|
--- @param code integer) exit code of the process
|
||||||
--- @param signal integer the signal used to terminate (if any)
|
--- @param signal integer the signal used to terminate (if any)
|
||||||
function Client:_on_exit(code, signal)
|
function Client:_on_exit(code, signal)
|
||||||
|
vim.schedule(function()
|
||||||
|
for bufnr in pairs(self.attached_buffers) do
|
||||||
|
self:_on_detach(bufnr)
|
||||||
|
if #lsp.get_clients({ bufnr = bufnr, _uninitialized = true }) == 0 then
|
||||||
|
reset_defaults(bufnr)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Schedule the deletion of the client object so that it exists in the execution of LspDetach
|
||||||
|
-- autocommands
|
||||||
|
vim.schedule(function()
|
||||||
|
all_clients[self.id] = nil
|
||||||
|
|
||||||
|
-- Client can be absent if executable starts, but initialize fails
|
||||||
|
-- init/attach won't have happened
|
||||||
|
if self then
|
||||||
|
changetracking.reset(self)
|
||||||
|
end
|
||||||
|
if code ~= 0 or (signal ~= 0 and signal ~= 15) then
|
||||||
|
local msg = string.format(
|
||||||
|
'Client %s quit with exit code %s and signal %s. Check log for errors: %s',
|
||||||
|
self and self.name or 'unknown',
|
||||||
|
code,
|
||||||
|
signal,
|
||||||
|
lsp.get_log_path()
|
||||||
|
)
|
||||||
|
vim.notify(msg, vim.log.levels.WARN)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
self:_run_callbacks(
|
self:_run_callbacks(
|
||||||
self._on_exit_cbs,
|
self._on_exit_cbs,
|
||||||
lsp.client_errors.ON_EXIT_CALLBACK_ERROR,
|
lsp.client_errors.ON_EXIT_CALLBACK_ERROR,
|
||||||
@@ -1240,4 +1322,7 @@ function Client:_remove_workspace_folder(dir)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Export for internal use only.
|
||||||
|
Client._all = all_clients
|
||||||
|
|
||||||
return Client
|
return Client
|
||||||
|
Reference in New Issue
Block a user