From aa8a318779ca6b5f0bba2cf383ddf596db199c09 Mon Sep 17 00:00:00 2001 From: Folke Lemaitre Date: Thu, 30 Oct 2025 11:01:07 +0100 Subject: [PATCH] fix(picker.lsp): some LSP servers notify completion before sending the actual result. See #2372 --- lua/snacks/picker/source/lsp/init.lua | 49 ++++++++++++++++----------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/lua/snacks/picker/source/lsp/init.lua b/lua/snacks/picker/source/lsp/init.lua index de5e9863..e8de3d60 100644 --- a/lua/snacks/picker/source/lsp/init.lua +++ b/lua/snacks/picker/source/lsp/init.lua @@ -128,6 +128,22 @@ function R.new() return self end +---@param clients vim.lsp.Client[] +---@param ctx lsp.HandlerContext +function R:debug(clients, ctx) + Snacks.debug.inspect({ + error = "LSP request callback yielded after done.", + method = ctx.method, + requests = vim.deepcopy(self.requests), + pending = self.pending, + client_id = ctx.client_id, + ---@param c vim.lsp.Client + clients = vim.tbl_map(function(c) + return { id = c.id, name = c.name } + end, clients), + }) +end + ---@param client_id number ---@param request_id number ---@param completed? boolean @@ -154,14 +170,14 @@ function R:cancel() end end -function R:autocmd() +function R:track_cancel() if self.autocmd_id then return end self.autocmd_id = vim.api.nvim_create_autocmd("LspRequest", { - group = vim.api.nvim_create_augroup("snacks.picker.lsp." .. R._id, { clear = true }), + group = vim.api.nvim_create_augroup("snacks.picker.lsp.cancel." .. R._id, { clear = true }), callback = function(ev) - if ev.data.request.type == "complete" or ev.data.request.type == "cancel" then + if ev.data.request.type == "cancel" then self:track(ev.data.client_id, ev.data.request_id, true) end end, @@ -176,31 +192,24 @@ end function R:request(buf, method, params, cb) self.pending = self.pending + 1 vim.schedule(function() - self:autocmd() -- setup autocmd here, since this must be called in the main loop + self:track_cancel() -- setup autocmd here, since this must be called in the main loop ---@diagnostic disable-next-line: param-type-mismatch local clients = type(buf) == "number" and M.get_clients(buf, method) or { wrap(buf) } for _, client in ipairs(clients) do - local p, done = params(client), false - local status, request_id = client:request(method, p, function(err, result) + local done = false + local status, request_id ---@type boolean, number? + status, request_id = client:request(method, params(client), function(err, result, ctx) done = true if not err and result and not self.async:aborted() then - if not self.async:running() then - Snacks.debug.inspect({ - error = "LSP request callback yielded after done.", - method = method, - requests = vim.deepcopy(self.requests), - pending = self.pending, - client_id = client.id, - client_name = client.name, - ---@param c vim.lsp.Client - clients = vim.tbl_map(function(c) - return { id = c.id, name = c.name } - end, clients), - }) + if not self.async:running() or self.pending <= 0 then + self:debug(clients, ctx) end - cb(client, result, p) + cb(client, result, ctx.params) + end + if request_id then + self:track(client.id, request_id, true) end end) -- skip tracking if the request failed