backport fix(lsp): refresh codelens despite pending debounce #40177

Problem:
When a server sends workspace/codeLens/refresh while an automatic codelens
request is already scheduled, Nvim ignores the server refresh. This can leave
rendered codelens text stale until another buffer edit triggers a new request.

Solution:
Cancel the pending automatic request and send the server-requested refresh
immediately. This preserves request coalescing while giving explicit server
refreshes priority.

Co-authored-by: Tristan Knight <admin@snappeh.com>
This commit is contained in:
Justin M. Keyes
2026-06-14 11:23:32 -04:00
committed by GitHub
parent 83d9b9afca
commit ecda67662f
2 changed files with 66 additions and 9 deletions

View File

@@ -174,6 +174,14 @@ function Provider:automatic_request()
end, 200)
end
---@package
---@param client_id integer
---@param on_response function
function Provider:refresh(client_id, on_response)
self:reset_timer()
self:request(client_id, on_response)
end
---@private
---@param client vim.lsp.Client
---@param unresolved_lens lsp.CodeLens
@@ -484,15 +492,12 @@ function M.on_refresh(err, _, ctx)
for bufnr, provider in pairs(Provider.active) do
for client_id in pairs(provider.client_state) do
if client_id == ctx.client_id then
-- Do nothing if a request is already scheduled.
if not provider.timer then
provider:request(client_id, function()
if api.nvim_buf_is_valid(bufnr) then
provider.row_version = {}
vim.api.nvim__redraw({ buf = bufnr, valid = true, flush = false })
end
end)
end
provider:refresh(client_id, function()
if api.nvim_buf_is_valid(bufnr) then
provider.row_version = {}
vim.api.nvim__redraw({ buf = bufnr, valid = true, flush = false })
end
end)
end
end
end

View File

@@ -306,6 +306,58 @@ describe('vim.lsp.codelens', function()
]])
end)
it('refreshes immediately and cancels a pending automatic refresh', function()
exec_lua(function()
local deferred
local request_count = 0
local defer_fn = vim.defer_fn
local client = assert(vim.lsp.get_client_by_id(client_id))
local request = client.request
--- @diagnostic disable-next-line: duplicate-set-field
vim.defer_fn = function(callback)
deferred = {
callback = callback,
closed = false,
stopped = false,
is_closing = function(self)
return self.closed
end,
stop = function(self)
self.stopped = true
end,
close = function(self)
self.closed = true
end,
}
return deferred
end
client.request = function(self, method, ...)
if method == 'textDocument/codeLens' then
request_count = request_count + 1
end
return request(self, method, ...)
end
vim.api.nvim_buf_set_lines(0, 0, 0, false, { '// changed' })
assert(deferred, 'expected pending automatic codelens refresh')
vim.lsp.codelens.on_refresh(
nil,
nil,
{ method = 'workspace/codeLens/refresh', client_id = client_id }
)
vim.defer_fn = defer_fn
client.request = request
assert(deferred.stopped, 'expected pending codelens refresh to stop')
assert(deferred.closed, 'expected pending codelens refresh to close')
assert(request_count == 1, 'expected exactly one immediate codelens refresh request')
end)
end)
it('ignores stale codeLens/resolve responses', function()
clear_notrace()
exec_lua(create_server_definition)