diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index d459f73708..7cda4a5c9f 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -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|: diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index 3b72b1990b..87cbbe425b 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -347,6 +347,65 @@ function M.get(filter) return result end +---@param lnum integer +---@param opts vim.lsp.codelens.run.Opts +---@param results table +---@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`