aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/lsp.txt33
-rw-r--r--runtime/lua/vim/lsp.lua46
-rw-r--r--src/nvim/auevents.lua4
-rw-r--r--test/functional/plugin/lsp_spec.lua38
4 files changed, 112 insertions, 9 deletions
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index 569c570624..ba523d837f 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -461,6 +461,39 @@ LspSignatureActiveParameter
==============================================================================
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*
Upon receipt of a progress notification from the server. See
|vim.lsp.util.get_progress_messages()|.
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index e99a7c282c..cfa6a91cbb 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -1,5 +1,3 @@
-local if_nil = vim.F.if_nil
-
local default_handlers = require('vim.lsp.handlers')
local log = require('vim.lsp.log')
local lsp_rpc = require('vim.lsp.rpc')
@@ -8,11 +6,16 @@ local util = require('vim.lsp.util')
local sync = require('vim.lsp.sync')
local vim = vim
-local nvim_err_writeln, nvim_buf_get_lines, nvim_command, 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
+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_exec_autocmds
local uv = vim.loop
local tbl_isempty, tbl_extend = vim.tbl_isempty, vim.tbl_extend
local validate = vim.validate
+local if_nil = vim.F.if_nil
local lsp = {
protocol = protocol,
@@ -867,15 +870,27 @@ function lsp.start_client(config)
pcall(config.on_exit, code, signal, client_id)
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
uninitialized_clients[client_id] = nil
- lsp.diagnostic.reset(client_id, all_buffer_active_clients)
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
local msg = string.format('Client %s quit with exit code %s and signal %s', client_id, code, signal)
vim.schedule(function()
@@ -1213,6 +1228,13 @@ function lsp.start_client(config)
---@param bufnr (number) Buffer number
function client._on_attach(bufnr)
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
-- TODO(ashkan) handle errors.
pcall(config.on_attach, client, bufnr)
@@ -1359,6 +1381,12 @@ function lsp.buf_detach_client(bufnr, client_id)
return
end
+ nvim_exec_autocmds('LspDetach', {
+ buffer = bufnr,
+ modeline = false,
+ data = { client_id = client_id },
+ })
+
changetracking.reset_buf(client, bufnr)
if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index 9e3eb06752..93a870fe04 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -70,6 +70,8 @@ return {
'InsertEnter', -- when entering Insert mode
'InsertLeave', -- just after 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
'ModeChanged', -- after changing the mode
'OptionSet', -- after setting any option
@@ -133,6 +135,8 @@ return {
nvim_specific = {
BufModifiedSet=true,
DiagnosticChanged=true,
+ LspAttach=true,
+ LspDetach=true,
RecordingEnter=true,
RecordingLeave=true,
Signal=true,
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index 4cb7636825..6c961eff7d 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -18,6 +18,7 @@ local NIL = helpers.NIL
local read_file = require('test.helpers').read_file
local write_file = require('test.helpers').write_file
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
-- yield.
@@ -341,6 +342,43 @@ describe('LSP', function()
}
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()
local expected_handlers = {
{NIL, {}, {method="shutdown", client_id=1}};