mirror of
https://github.com/neovim/neovim.git
synced 2026-02-08 21:08:47 +00:00
refactor(lsp): always fetch lenses again in codelens.run (#37720)
The auto-refresh has a bit of a delay so it can happen that when a user runs `codelens.run` it operates on an outdated state and either does nothing, or fails. This changes the logic for `.run` to always fetch the current lenses before (optional) prompt and execution. See discussion in https://github.com/neovim/neovim/pull/37689#discussion_r2764235931 This could potentially be optimized to first check if there's local state with a version that matches the current buf-version, but in my testing re-fetching them always was quickly enough that `run` still feels instant and doing it this way simplifies the logic. Side effect of the change is that `.run` also works if codelens aren't enabled - for power users who know what the codelens would show that can be useful.
This commit is contained in:
committed by
GitHub
parent
6dd0a7d60a
commit
c4f322b769
@@ -1950,7 +1950,7 @@ is_enabled({filter}) *vim.lsp.codelens.is_enabled()*
|
||||
(`boolean`) whether code lens is enabled.
|
||||
|
||||
run({opts}) *vim.lsp.codelens.run()*
|
||||
Run code lens above the current cursor position.
|
||||
Run code lens at the current cursor position.
|
||||
|
||||
Parameters: ~
|
||||
• {opts} (`table?`) Optional parameters |kwargs|:
|
||||
|
||||
@@ -347,6 +347,65 @@ function M.get(filter)
|
||||
return result
|
||||
end
|
||||
|
||||
---@param lnum integer
|
||||
---@param opts vim.lsp.codelens.run.Opts
|
||||
---@param results table<integer, {err: lsp.ResponseError?, result: lsp.CodeLens[]?}>
|
||||
---@param context lsp.HandlerContext
|
||||
local function on_lenses_run(lnum, opts, results, context)
|
||||
local bufnr = context.bufnr or 0
|
||||
|
||||
---@type {client: vim.lsp.Client, lens: lsp.CodeLens}[]
|
||||
local candidates = {}
|
||||
local pending_resolve = 1
|
||||
local function on_resolved()
|
||||
pending_resolve = pending_resolve - 1
|
||||
if pending_resolve > 0 then
|
||||
return
|
||||
end
|
||||
if #candidates == 0 then
|
||||
vim.notify('No codelens at current line')
|
||||
elseif #candidates == 1 then
|
||||
local candidate = candidates[1]
|
||||
candidate.client:exec_cmd(candidate.lens.command, { bufnr = bufnr })
|
||||
else
|
||||
local selectopts = {
|
||||
prompt = 'Code lenses: ',
|
||||
kind = 'codelens',
|
||||
---@param candidate {client: vim.lsp.Client, lens: lsp.CodeLens}
|
||||
format_item = function(candidate)
|
||||
return string.format('%s [%s]', candidate.lens.command.title, candidate.client.name)
|
||||
end,
|
||||
}
|
||||
vim.ui.select(candidates, selectopts, function(candidate)
|
||||
if candidate then
|
||||
candidate.client:exec_cmd(candidate.lens.command, { bufnr = bufnr })
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
for client_id, result in pairs(results) do
|
||||
if opts.client_id == nil or opts.client_id == client_id then
|
||||
local client = assert(vim.lsp.get_client_by_id(client_id))
|
||||
for _, lens in ipairs(result.result or {}) do
|
||||
if lens.range.start.line == lnum then
|
||||
if lens.command then
|
||||
table.insert(candidates, { client = client, lens = lens })
|
||||
else
|
||||
pending_resolve = pending_resolve + 1
|
||||
client:request('codeLens/resolve', lens, function(_, resolved_lens)
|
||||
if resolved_lens then
|
||||
table.insert(candidates, { client = client, lens = resolved_lens })
|
||||
end
|
||||
on_resolved()
|
||||
end, bufnr)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
on_resolved()
|
||||
end
|
||||
|
||||
--- Optional parameters |kwargs|:
|
||||
---@class vim.lsp.codelens.run.Opts
|
||||
---@inlinedoc
|
||||
@@ -355,7 +414,7 @@ end
|
||||
--- (default: all)
|
||||
---@field client_id? integer
|
||||
|
||||
--- Run code lens above the current cursor position.
|
||||
--- Run code lens at the current cursor position.
|
||||
---
|
||||
---@param opts? vim.lsp.codelens.run.Opts
|
||||
function M.run(opts)
|
||||
@@ -365,48 +424,12 @@ function M.run(opts)
|
||||
local winid = api.nvim_get_current_win()
|
||||
local bufnr = api.nvim_win_get_buf(winid)
|
||||
local pos = vim.pos.cursor(api.nvim_win_get_cursor(winid))
|
||||
local provider = Provider.active[bufnr]
|
||||
if not provider then
|
||||
return
|
||||
end
|
||||
|
||||
---@type [integer, lsp.CodeLens][]
|
||||
local items = {}
|
||||
for client_id, state in pairs(provider.client_state) do
|
||||
if not opts.client_id or opts.client_id == client_id then
|
||||
for _, lens in ipairs(state.row_lenses[pos.row] or {}) do
|
||||
-- Ignore unresolved and empty command lenses.
|
||||
if lens.command and lens.command.command ~= '' then
|
||||
table.insert(items, { client_id, lens })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #items == 0 then
|
||||
vim.notify('No code lens avaliable')
|
||||
return
|
||||
elseif #items == 1 then
|
||||
local client_id, lens = unpack(items[1])
|
||||
local client = assert(vim.lsp.get_client_by_id(client_id))
|
||||
client:exec_cmd(lens.command)
|
||||
else
|
||||
vim.ui.select(items, {
|
||||
prompt = 'Code Lens',
|
||||
---@param item [integer, lsp.CodeLens]
|
||||
format_item = function(item)
|
||||
local client_id, lens = unpack(item)
|
||||
local client = assert(vim.lsp.get_client_by_id(client_id))
|
||||
return ('%s [%s]'):format(lens.command.title, client.name)
|
||||
end,
|
||||
}, function(item)
|
||||
if item then
|
||||
local client_id, lens = unpack(item)
|
||||
local client = assert(vim.lsp.get_client_by_id(client_id))
|
||||
client:exec_cmd(lens.command)
|
||||
end
|
||||
end)
|
||||
end
|
||||
local params = {
|
||||
textDocument = vim.lsp.util.make_text_document_params(bufnr),
|
||||
}
|
||||
vim.lsp.buf_request_all(bufnr, 'textDocument/codeLens', params, function(results, context)
|
||||
on_lenses_run(pos.row, opts, results, context)
|
||||
end)
|
||||
end
|
||||
|
||||
--- |lsp-handler| for the method `workspace/codeLens/refresh`
|
||||
|
||||
Reference in New Issue
Block a user