From c9ccbe56179f1d4adb06fea47f4eea0c57736c2d Mon Sep 17 00:00:00 2001 From: Folke Lemaitre Date: Fri, 24 Oct 2025 07:20:23 +0200 Subject: [PATCH] fix(picker): fix race condition causing "Finder yielded after done" error. Closes #2327 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old code had a race condition where: 1. R:request() scheduled LSP request setup but immediately suspended 2. R:wait() checked #self.requests (still 0) and returned immediately 3. Finder completed and set running = false 4. Then scheduled function ran and LSP responses came back 5. Callbacks tried to yield items but finder was already done → error The fix uses a pending counter to track when the scheduled setup function is running, ensuring we wait for both the setup to complete AND all LSP responses before the finder completes. Also improved error handling by checking the err parameter in callbacks. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- lua/snacks/picker/source/lsp/init.lua | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lua/snacks/picker/source/lsp/init.lua b/lua/snacks/picker/source/lsp/init.lua index c7bd05dc..d9f02648 100644 --- a/lua/snacks/picker/source/lsp/init.lua +++ b/lua/snacks/picker/source/lsp/init.lua @@ -6,6 +6,7 @@ local Async = require("snacks.picker.util.async") local M = {} ---@alias lsp.Symbol lsp.SymbolInformation|lsp.DocumentSymbol +---@alias lsp.Loc lsp.LocationLink|lsp.Location ---@class snacks.picker.lsp.Loc: lsp.Location ---@field encoding string @@ -99,6 +100,7 @@ end ---@field async snacks.picker.Async ---@field requests {client_id:number, request_id:number}[] ---@field completed number +---@field pending integer local R = {} R.__index = R @@ -106,6 +108,7 @@ function R.new() local self = setmetatable({}, R) self.async = Async.running() self.requests = {} + self.pending = 0 self.completed = 0 self.async:on( "abort", @@ -132,6 +135,7 @@ end ---@param cb fun(client:vim.lsp.Client, result:table, params:table) ---@async function R:request(buf, method, params, cb) + self.pending = self.pending + 1 vim.schedule(function() local clients = type(buf) == "number" and M.get_clients(buf, method) or { @@ -139,11 +143,8 @@ function R:request(buf, method, params, cb) } for _, client in ipairs(clients) do local p = params(client) - local status, request_id = client:request(method, p, function(_, result) - if self.async._aborted then - return - end - if result then + local status, request_id = client:request(method, p, function(err, result) + if not err and result and not self.async._aborted then cb(client, result, p) end self.completed = self.completed + 1 @@ -153,14 +154,14 @@ function R:request(buf, method, params, cb) table.insert(self.requests, { client_id = client.id, request_id = request_id }) end end + self.pending = self.pending - 1 self.async:resume() end) - self.async:suspend() return self end function R:wait() - while self.completed < #self.requests do + while self.pending > 0 or self.completed < #self.requests do self.async:suspend() end end