fix(lsp): codelens text flickers #38782

Problem:
When a new textDocument/codeLens response arrives with unresolved lenses,
on_win clears the existing codelens row before codeLens/resolve
completes. This causes the displayed codelens text to flicker while
typing.

Solution:
Keep the current virtual lines if any of the refreshed lenses are still
unresolved. Clear the old virtual lines only when the line no longer has
lenses or all its lenses are resolved.

A trade-off is that the user may temporarily see outdated codelenses.
However, that's preferable to spamming updates on every refresh.

AI-assisted: Codex
This commit is contained in:
Jaehwang Jung
2026-04-11 23:27:11 +09:00
committed by GitHub
parent 01567ad4f6
commit 417d16df50

View File

@@ -227,11 +227,10 @@ function Provider:on_win(toprow, botrow)
for client_id, state in pairs(self.client_state) do
local bufnr = self.bufnr
local namespace = state.namespace
api.nvim_buf_clear_namespace(bufnr, namespace, row, row + 1)
local lenses = state.row_lenses[row]
if lenses then
if not lenses then
api.nvim_buf_clear_namespace(bufnr, namespace, row, row + 1)
else
table.sort(lenses, function(a, b)
return a.range.start.character < b.range.start.character
end)
@@ -242,10 +241,12 @@ function Provider:on_win(toprow, botrow)
local virt_text = {
{ string.rep(' ', range.start_col), 'LspCodeLensSeparator' },
}
local has_unresolved = false
for _, lens in ipairs(lenses) do
-- A code lens is unresolved when no command is associated to it.
if not lens.command then
has_unresolved = true
self:resolve(client, lens)
else
vim.list_extend(virt_text, {
@@ -254,25 +255,37 @@ function Provider:on_win(toprow, botrow)
})
end
end
-- Remove trailing separator.
table.remove(virt_text)
-- Use a placeholder to prevent flickering caused by layout shifts.
if #virt_text == 1 then
table.insert(virt_text, { '', 'LspCodeLens' })
end
local had_extmark = #api.nvim_buf_get_extmarks(
bufnr,
namespace,
{ row, 0 },
{ row, -1 },
{}
) > 0
api.nvim_buf_set_extmark(bufnr, namespace, row, 0, {
virt_lines = { virt_text },
virt_lines_above = true,
virt_lines_overflow = 'scroll',
hl_mode = 'combine',
})
if not has_unresolved or not had_extmark then
-- Remove trailing separator.
table.remove(virt_text)
-- Fix https://github.com/neovim/neovim/issues/16166
-- Make sure the code lens on the first line is visible when updating.
if row == 0 then
vim.fn.winrestview({ topfill = 1 })
-- Use a placeholder to prevent flickering caused by layout shifts.
if #virt_text == 1 then
table.insert(virt_text, { '', 'LspCodeLens' })
end
api.nvim_buf_clear_namespace(bufnr, namespace, row, row + 1)
api.nvim_buf_set_extmark(bufnr, namespace, row, 0, {
virt_lines = { virt_text },
virt_lines_above = true,
virt_lines_overflow = 'scroll',
hl_mode = 'combine',
})
-- Fix https://github.com/neovim/neovim/issues/16166
-- Make sure the code lens on the first line is visible when updating.
if row == 0 then
vim.fn.winrestview({ topfill = 1 })
end
end
end
self.row_version[row] = self.version