mirror of
https://github.com/neovim/neovim.git
synced 2025-12-10 16:42:42 +00:00
Merge pull request #18507 from gpanders/au-lsp-attached
This commit is contained in:
@@ -106,11 +106,13 @@ internally and are no longer exposed as part of the API. Instead, use
|
|||||||
*vim.lsp.diagnostic.set_underline()*
|
*vim.lsp.diagnostic.set_underline()*
|
||||||
*vim.lsp.diagnostic.set_virtual_text()*
|
*vim.lsp.diagnostic.set_virtual_text()*
|
||||||
|
|
||||||
LSP Utility Functions ~
|
LSP Functions ~
|
||||||
|
|
||||||
*vim.lsp.util.diagnostics_to_items()* Use |vim.diagnostic.toqflist()| instead.
|
*vim.lsp.util.diagnostics_to_items()* Use |vim.diagnostic.toqflist()| instead.
|
||||||
*vim.lsp.util.set_qflist()* Use |setqflist()| instead.
|
*vim.lsp.util.set_qflist()* Use |setqflist()| instead.
|
||||||
*vim.lsp.util.set_loclist()* Use |setloclist()| instead.
|
*vim.lsp.util.set_loclist()* Use |setloclist()| instead.
|
||||||
|
*vim.lsp.buf_get_clients()* Use |vim.lsp.get_active_clients()| with
|
||||||
|
{buffer = bufnr} instead.
|
||||||
|
|
||||||
Lua ~
|
Lua ~
|
||||||
*vim.register_keystroke_callback()* Use |vim.on_key()| instead.
|
*vim.register_keystroke_callback()* Use |vim.on_key()| instead.
|
||||||
|
|||||||
@@ -461,6 +461,39 @@ LspSignatureActiveParameter
|
|||||||
==============================================================================
|
==============================================================================
|
||||||
EVENTS *lsp-events*
|
EVENTS *lsp-events*
|
||||||
|
|
||||||
|
*LspAttach*
|
||||||
|
After an LSP client attaches to a buffer. The |autocmd-pattern| is the
|
||||||
|
name of the buffer. When used from Lua, the client ID is passed to the
|
||||||
|
callback in the "data" table. Example: >
|
||||||
|
|
||||||
|
vim.api.nvim_create_autocmd("LspAttach", {
|
||||||
|
callback = function(args)
|
||||||
|
local bufnr = args.buf
|
||||||
|
local client = vim.lsp.get_client_by_id(args.data.client_id)
|
||||||
|
if client.server_capabilities.completionProvider then
|
||||||
|
vim.bo[bufnr].omnifunc = "v:lua.vim.lsp.omnifunc"
|
||||||
|
end
|
||||||
|
if client.server_capabilities.definitionProvider then
|
||||||
|
vim.bo[bufnr].tagfunc = "v:lua.vim.lsp.tagfunc"
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
<
|
||||||
|
*LspDetach*
|
||||||
|
Just before an LSP client detaches from a buffer. The |autocmd-pattern| is the
|
||||||
|
name of the buffer. When used from Lua, the client ID is passed to the
|
||||||
|
callback in the "data" table. Example: >
|
||||||
|
|
||||||
|
vim.api.nvim_create_autocmd("LspDetach", {
|
||||||
|
callback = function(args)
|
||||||
|
local client = vim.lsp.get_client_by_id(args.data.client_id)
|
||||||
|
-- Do something with the client
|
||||||
|
vim.cmd("setlocal tagfunc< omnifunc<")
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
<
|
||||||
|
In addition, the following |User| |autocommands| are provided:
|
||||||
|
|
||||||
LspProgressUpdate *LspProgressUpdate*
|
LspProgressUpdate *LspProgressUpdate*
|
||||||
Upon receipt of a progress notification from the server. See
|
Upon receipt of a progress notification from the server. See
|
||||||
|vim.lsp.util.get_progress_messages()|.
|
|vim.lsp.util.get_progress_messages()|.
|
||||||
@@ -498,14 +531,6 @@ buf_detach_client({bufnr}, {client_id}) *vim.lsp.buf_detach_client()*
|
|||||||
{bufnr} (number) Buffer handle, or 0 for current
|
{bufnr} (number) Buffer handle, or 0 for current
|
||||||
{client_id} (number) Client id
|
{client_id} (number) Client id
|
||||||
|
|
||||||
buf_get_clients({bufnr}) *vim.lsp.buf_get_clients()*
|
|
||||||
Gets a map of client_id:client pairs for the given buffer,
|
|
||||||
where each value is a |vim.lsp.client| object.
|
|
||||||
|
|
||||||
Parameters: ~
|
|
||||||
{bufnr} (optional, number): Buffer handle, or 0 for
|
|
||||||
current
|
|
||||||
|
|
||||||
buf_is_attached({bufnr}, {client_id}) *vim.lsp.buf_is_attached()*
|
buf_is_attached({bufnr}, {client_id}) *vim.lsp.buf_is_attached()*
|
||||||
Checks if a buffer is attached for a particular client.
|
Checks if a buffer is attached for a particular client.
|
||||||
|
|
||||||
@@ -696,11 +721,22 @@ formatexpr({opts}) *vim.lsp.formatexpr()*
|
|||||||
• timeout_ms (default 500ms). The timeout period
|
• timeout_ms (default 500ms). The timeout period
|
||||||
for the formatting request.
|
for the formatting request.
|
||||||
|
|
||||||
get_active_clients() *vim.lsp.get_active_clients()*
|
get_active_clients({filter}) *vim.lsp.get_active_clients()*
|
||||||
Gets all active clients.
|
Get active clients.
|
||||||
|
|
||||||
|
Parameters: ~
|
||||||
|
{filter} (table|nil) A table with key-value pairs used to
|
||||||
|
filter the returned clients. The available keys
|
||||||
|
are:
|
||||||
|
• id (number): Only return clients with the
|
||||||
|
given id
|
||||||
|
• bufnr (number): Only return clients attached
|
||||||
|
to this buffer
|
||||||
|
• name (string): Only return clients with the
|
||||||
|
given name
|
||||||
|
|
||||||
Return: ~
|
Return: ~
|
||||||
Table of |vim.lsp.client| objects
|
(table) List of |vim.lsp.client| objects
|
||||||
|
|
||||||
*vim.lsp.get_buffers_by_client_id()*
|
*vim.lsp.get_buffers_by_client_id()*
|
||||||
get_buffers_by_client_id({client_id})
|
get_buffers_by_client_id({client_id})
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
local if_nil = vim.F.if_nil
|
|
||||||
|
|
||||||
local default_handlers = require('vim.lsp.handlers')
|
local default_handlers = require('vim.lsp.handlers')
|
||||||
local log = require('vim.lsp.log')
|
local log = require('vim.lsp.log')
|
||||||
local lsp_rpc = require('vim.lsp.rpc')
|
local lsp_rpc = require('vim.lsp.rpc')
|
||||||
@@ -8,11 +6,16 @@ local util = require('vim.lsp.util')
|
|||||||
local sync = require('vim.lsp.sync')
|
local sync = require('vim.lsp.sync')
|
||||||
|
|
||||||
local vim = vim
|
local vim = vim
|
||||||
local nvim_err_writeln, nvim_buf_get_lines, nvim_command, nvim_buf_get_option =
|
local nvim_err_writeln, nvim_buf_get_lines, nvim_command, nvim_buf_get_option, nvim_exec_autocmds =
|
||||||
vim.api.nvim_err_writeln, vim.api.nvim_buf_get_lines, vim.api.nvim_command, vim.api.nvim_buf_get_option
|
vim.api.nvim_err_writeln,
|
||||||
|
vim.api.nvim_buf_get_lines,
|
||||||
|
vim.api.nvim_command,
|
||||||
|
vim.api.nvim_buf_get_option,
|
||||||
|
vim.api.nvim_exec_autocmds
|
||||||
local uv = vim.loop
|
local uv = vim.loop
|
||||||
local tbl_isempty, tbl_extend = vim.tbl_isempty, vim.tbl_extend
|
local tbl_isempty, tbl_extend = vim.tbl_isempty, vim.tbl_extend
|
||||||
local validate = vim.validate
|
local validate = vim.validate
|
||||||
|
local if_nil = vim.F.if_nil
|
||||||
|
|
||||||
local lsp = {
|
local lsp = {
|
||||||
protocol = protocol,
|
protocol = protocol,
|
||||||
@@ -867,15 +870,27 @@ function lsp.start_client(config)
|
|||||||
pcall(config.on_exit, code, signal, client_id)
|
pcall(config.on_exit, code, signal, client_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
for bufnr, client_ids in pairs(all_buffer_active_clients) do
|
||||||
|
if client_ids[client_id] then
|
||||||
|
vim.schedule(function()
|
||||||
|
nvim_exec_autocmds('LspDetach', {
|
||||||
|
buffer = bufnr,
|
||||||
|
modeline = false,
|
||||||
|
data = { client_id = client_id },
|
||||||
|
})
|
||||||
|
|
||||||
|
local namespace = vim.lsp.diagnostic.get_namespace(client_id)
|
||||||
|
vim.diagnostic.reset(namespace, bufnr)
|
||||||
|
end)
|
||||||
|
|
||||||
|
client_ids[client_id] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
active_clients[client_id] = nil
|
active_clients[client_id] = nil
|
||||||
uninitialized_clients[client_id] = nil
|
uninitialized_clients[client_id] = nil
|
||||||
|
|
||||||
lsp.diagnostic.reset(client_id, all_buffer_active_clients)
|
|
||||||
changetracking.reset(client_id)
|
changetracking.reset(client_id)
|
||||||
for _, client_ids in pairs(all_buffer_active_clients) do
|
|
||||||
client_ids[client_id] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
if code ~= 0 or (signal ~= 0 and signal ~= 15) then
|
if code ~= 0 or (signal ~= 0 and signal ~= 15) then
|
||||||
local msg = string.format('Client %s quit with exit code %s and signal %s', client_id, code, signal)
|
local msg = string.format('Client %s quit with exit code %s and signal %s', client_id, code, signal)
|
||||||
vim.schedule(function()
|
vim.schedule(function()
|
||||||
@@ -1213,6 +1228,13 @@ function lsp.start_client(config)
|
|||||||
---@param bufnr (number) Buffer number
|
---@param bufnr (number) Buffer number
|
||||||
function client._on_attach(bufnr)
|
function client._on_attach(bufnr)
|
||||||
text_document_did_open_handler(bufnr, client)
|
text_document_did_open_handler(bufnr, client)
|
||||||
|
|
||||||
|
nvim_exec_autocmds('LspAttach', {
|
||||||
|
buffer = bufnr,
|
||||||
|
modeline = false,
|
||||||
|
data = { client_id = client.id },
|
||||||
|
})
|
||||||
|
|
||||||
if config.on_attach then
|
if config.on_attach then
|
||||||
-- TODO(ashkan) handle errors.
|
-- TODO(ashkan) handle errors.
|
||||||
pcall(config.on_attach, client, bufnr)
|
pcall(config.on_attach, client, bufnr)
|
||||||
@@ -1359,6 +1381,12 @@ function lsp.buf_detach_client(bufnr, client_id)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
nvim_exec_autocmds('LspDetach', {
|
||||||
|
buffer = bufnr,
|
||||||
|
modeline = false,
|
||||||
|
data = { client_id = client_id },
|
||||||
|
})
|
||||||
|
|
||||||
changetracking.reset_buf(client, bufnr)
|
changetracking.reset_buf(client, bufnr)
|
||||||
|
|
||||||
if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then
|
if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then
|
||||||
@@ -1435,11 +1463,29 @@ function lsp.stop_client(client_id, force)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Gets all active clients.
|
--- Get active clients.
|
||||||
---
|
---
|
||||||
---@returns Table of |vim.lsp.client| objects
|
---@param filter (table|nil) A table with key-value pairs used to filter the
|
||||||
function lsp.get_active_clients()
|
--- returned clients. The available keys are:
|
||||||
return vim.tbl_values(active_clients)
|
--- - id (number): Only return clients with the given id
|
||||||
|
--- - bufnr (number): Only return clients attached to this buffer
|
||||||
|
--- - name (string): Only return clients with the given name
|
||||||
|
---@returns (table) List of |vim.lsp.client| objects
|
||||||
|
function lsp.get_active_clients(filter)
|
||||||
|
validate({ filter = { filter, 't', true } })
|
||||||
|
|
||||||
|
filter = filter or {}
|
||||||
|
|
||||||
|
local clients = {}
|
||||||
|
|
||||||
|
local t = filter.bufnr and (all_buffer_active_clients[resolve_bufnr(filter.bufnr)] or {}) or active_clients
|
||||||
|
for client_id in pairs(t) do
|
||||||
|
local client = active_clients[client_id]
|
||||||
|
if (filter.id == nil or client.id == filter.id) and (filter.name == nil or client.name == filter.name) then
|
||||||
|
clients[#clients + 1] = client
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return clients
|
||||||
end
|
end
|
||||||
|
|
||||||
function lsp._vim_exit_handler()
|
function lsp._vim_exit_handler()
|
||||||
@@ -1814,12 +1860,13 @@ end
|
|||||||
--- is a |vim.lsp.client| object.
|
--- is a |vim.lsp.client| object.
|
||||||
---
|
---
|
||||||
---@param bufnr (optional, number): Buffer handle, or 0 for current
|
---@param bufnr (optional, number): Buffer handle, or 0 for current
|
||||||
|
---@returns (table) Table of (client_id, client) pairs
|
||||||
|
---@deprecated Use |vim.lsp.get_active_clients()| instead.
|
||||||
function lsp.buf_get_clients(bufnr)
|
function lsp.buf_get_clients(bufnr)
|
||||||
bufnr = resolve_bufnr(bufnr)
|
|
||||||
local result = {}
|
local result = {}
|
||||||
for_each_buffer_client(bufnr, function(client, client_id)
|
for _, client in ipairs(lsp.get_active_clients({ bufnr = resolve_bufnr(bufnr) })) do
|
||||||
result[client_id] = client
|
result[client.id] = client
|
||||||
end)
|
end
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,8 @@ return {
|
|||||||
'InsertEnter', -- when entering Insert mode
|
'InsertEnter', -- when entering Insert mode
|
||||||
'InsertLeave', -- just after leaving Insert mode
|
'InsertLeave', -- just after leaving Insert mode
|
||||||
'InsertLeavePre', -- just before leaving Insert mode
|
'InsertLeavePre', -- just before leaving Insert mode
|
||||||
|
'LspAttach', -- after an LSP client attaches to a buffer
|
||||||
|
'LspDetach', -- after an LSP client detaches from a buffer
|
||||||
'MenuPopup', -- just before popup menu is displayed
|
'MenuPopup', -- just before popup menu is displayed
|
||||||
'ModeChanged', -- after changing the mode
|
'ModeChanged', -- after changing the mode
|
||||||
'OptionSet', -- after setting any option
|
'OptionSet', -- after setting any option
|
||||||
@@ -133,6 +135,8 @@ return {
|
|||||||
nvim_specific = {
|
nvim_specific = {
|
||||||
BufModifiedSet=true,
|
BufModifiedSet=true,
|
||||||
DiagnosticChanged=true,
|
DiagnosticChanged=true,
|
||||||
|
LspAttach=true,
|
||||||
|
LspDetach=true,
|
||||||
RecordingEnter=true,
|
RecordingEnter=true,
|
||||||
RecordingLeave=true,
|
RecordingLeave=true,
|
||||||
Signal=true,
|
Signal=true,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ local NIL = helpers.NIL
|
|||||||
local read_file = require('test.helpers').read_file
|
local read_file = require('test.helpers').read_file
|
||||||
local write_file = require('test.helpers').write_file
|
local write_file = require('test.helpers').write_file
|
||||||
local isCI = helpers.isCI
|
local isCI = helpers.isCI
|
||||||
|
local meths = helpers.meths
|
||||||
|
|
||||||
-- Use these to get access to a coroutine so that I can run async tests and use
|
-- Use these to get access to a coroutine so that I can run async tests and use
|
||||||
-- yield.
|
-- yield.
|
||||||
@@ -341,6 +342,43 @@ describe('LSP', function()
|
|||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('should fire autocommands on attach and detach', function()
|
||||||
|
local client
|
||||||
|
test_rpc_server {
|
||||||
|
test_name = "basic_init";
|
||||||
|
on_setup = function()
|
||||||
|
exec_lua [[
|
||||||
|
BUFFER = vim.api.nvim_create_buf(false, true)
|
||||||
|
vim.api.nvim_create_autocmd('LspAttach', {
|
||||||
|
callback = function(args)
|
||||||
|
local client = vim.lsp.get_client_by_id(args.data.client_id)
|
||||||
|
vim.g.lsp_attached = client.name
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
vim.api.nvim_create_autocmd('LspDetach', {
|
||||||
|
callback = function(args)
|
||||||
|
local client = vim.lsp.get_client_by_id(args.data.client_id)
|
||||||
|
vim.g.lsp_detached = client.name
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
]]
|
||||||
|
end;
|
||||||
|
on_init = function(_client)
|
||||||
|
client = _client
|
||||||
|
eq(true, exec_lua("return lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)"))
|
||||||
|
client.notify('finish')
|
||||||
|
end;
|
||||||
|
on_handler = function(_, _, ctx)
|
||||||
|
if ctx.method == 'finish' then
|
||||||
|
eq('basic_init', meths.get_var('lsp_attached'))
|
||||||
|
exec_lua("return lsp.buf_detach_client(BUFFER, TEST_RPC_CLIENT_ID)")
|
||||||
|
eq('basic_init', meths.get_var('lsp_detached'))
|
||||||
|
client.stop()
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|
||||||
it('client should return settings via workspace/configuration handler', function()
|
it('client should return settings via workspace/configuration handler', function()
|
||||||
local expected_handlers = {
|
local expected_handlers = {
|
||||||
{NIL, {}, {method="shutdown", client_id=1}};
|
{NIL, {}, {method="shutdown", client_id=1}};
|
||||||
|
|||||||
Reference in New Issue
Block a user