fix(lsp): ignore stale codelens resolve responses (#38153)

This commit is contained in:
Lewis Russell
2026-03-05 01:43:40 +00:00
committed by GitHub
parent 843bc1aa8d
commit 8bfb91accc
2 changed files with 95 additions and 12 deletions

View File

@@ -186,20 +186,26 @@ function Provider:resolve(client, unresolved_lens)
end
local row = unresolved_lens.range.start.line
local lenses = assert(state.row_lenses[row])
for i, lens in ipairs(lenses) do
if lens == unresolved_lens then
lenses[i] = resolved_lens
end
local lenses = state.row_lenses[row]
-- A newer textDocument/codeLens response can replace row_lenses while resolve is in flight.
if not lenses then
return
end
self.row_version[row] = nil
api.nvim__redraw({
buf = self.bufnr,
range = { row, row + 1 },
valid = true,
flush = false,
})
for i, lens in ipairs(lenses) do
-- Only apply if this exact unresolved lens still exists; otherwise response is stale.
if lens == unresolved_lens then
lenses[i] = resolved_lens
self.row_version[row] = nil
api.nvim__redraw({
buf = self.bufnr,
range = { row, row + 1 },
valid = true,
flush = false,
})
return
end
end
end, self.bufnr)
end

View File

@@ -305,6 +305,83 @@ describe('vim.lsp.codelens', function()
]])
end)
it('ignores stale codeLens/resolve responses', function()
clear_notrace()
exec_lua(create_server_definition)
insert('line1\nline2\n')
exec_lua(function()
local codelens_request_count = 0
_G.stale_resolve_sent = false
_G.server = _G._create_server({
capabilities = {
codeLensProvider = {
resolveProvider = true,
},
},
handlers = {
['textDocument/codeLens'] = function(_, _, callback)
codelens_request_count = codelens_request_count + 1
if codelens_request_count == 1 then
callback(nil, {
{
range = {
['end'] = {
character = 1,
line = 0,
},
start = {
character = 0,
line = 0,
},
},
},
})
else
callback(nil, {})
end
end,
['codeLens/resolve'] = function(_, lens, callback)
vim.defer_fn(function()
_G.stale_resolve_sent = true
callback(nil, {
command = {
arguments = {},
command = 'dummy.command',
title = 'resolved',
},
range = lens.range,
})
end, 100)
end,
},
})
local stale_client_id = vim.lsp.start({ name = 'dummy', cmd = _G.server.cmd })
vim.lsp.codelens.enable()
vim.wait(1000, function()
return #vim.lsp.codelens.get() > 0
end)
vim.api.nvim__redraw({ flush = true })
vim.lsp.codelens.on_refresh(nil, nil, {
method = 'workspace/codeLens/refresh',
client_id = stale_client_id,
})
assert(
vim.wait(1000, function()
return _G.stale_resolve_sent
end),
'timed out waiting for stale resolve response'
)
end)
eq('', api.nvim_get_vvar('errmsg'))
end)
it('clears extmarks beyond the bottom of the buffer', function()
feed('13G4dd')
screen:expect([[