From 378435968f64b681cb9a16d8ebf966e5d472e3fc Mon Sep 17 00:00:00 2001 From: Yi Ming Date: Sun, 8 Mar 2026 10:42:38 +0800 Subject: [PATCH] fix(lsp): adjust codelens position based on the server-provided range --- runtime/lua/vim/lsp/codelens.lua | 33 ++++++------ test/functional/plugin/lsp/codelens_spec.lua | 57 +++++++++----------- 2 files changed, 40 insertions(+), 50 deletions(-) diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index f29677fde3..63688b96d0 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -227,35 +227,34 @@ function Provider:on_win(toprow, botrow) return a.range.start.character < b.range.start.character end) - ---@type integer - local indent = api.nvim_buf_call(bufnr, function() - return vim.fn.indent(row + 1) - end) + local client = assert(vim.lsp.get_client_by_id(client_id)) + local range = vim.range.lsp(bufnr, lenses[1].range, client.offset_encoding) + ---@type [string, string][] + local virt_text = { + { string.rep(' ', range.start.col), 'LspCodeLensSeparator' }, + } - ---@type [string, string|integer][][] - local virt_lines = { { { string.rep(' ', indent), 'LspCodeLensSeparator' } } } - local virt_text = virt_lines[1] for _, lens in ipairs(lenses) do -- A code lens is unresolved when no command is associated to it. if not lens.command then - local client = assert(vim.lsp.get_client_by_id(client_id)) ---@type vim.lsp.Client self:resolve(client, lens) else - virt_text[#virt_text + 1] = { lens.command.title, 'LspCodeLens' } - virt_text[#virt_text + 1] = { ' | ', 'LspCodeLensSeparator' } + vim.list_extend(virt_text, { + { lens.command.title, 'LspCodeLens' }, + { ' | ', 'LspCodeLensSeparator' }, + }) end end + -- Remove trailing separator. + table.remove(virt_text) - if #virt_text > 1 then - -- Remove trailing separator. - virt_text[#virt_text] = nil - else - -- Use a placeholder to prevent flickering caused by layout shifts. - virt_text[#virt_text + 1] = { '...', 'LspCodeLens' } + -- Use a placeholder to prevent flickering caused by layout shifts. + if #virt_text == 1 then + table.insert(virt_text, { '', 'LspCodeLens' }) end api.nvim_buf_set_extmark(bufnr, namespace, row, 0, { - virt_lines = virt_lines, + virt_lines = { virt_text }, virt_lines_above = true, virt_lines_overflow = 'scroll', hl_mode = 'combine', diff --git a/test/functional/plugin/lsp/codelens_spec.lua b/test/functional/plugin/lsp/codelens_spec.lua index 5c46452b30..e099aaf5d2 100644 --- a/test/functional/plugin/lsp/codelens_spec.lua +++ b/test/functional/plugin/lsp/codelens_spec.lua @@ -16,7 +16,6 @@ local create_server_definition = t_lsp.create_server_definition describe('vim.lsp.codelens', function() local text = dedent([[ - https://github.com/neovim/neovim/issues/16166 struct S { a: i32, b: String, @@ -35,8 +34,6 @@ describe('vim.lsp.codelens', function() ]]) local grid_with_lenses = dedent([[ - ^https://github.com/neovim/neovim/issues/16166 | - {1:1 implementation} | struct S { | a: i32, | b: String, | @@ -48,17 +45,18 @@ describe('vim.lsp.codelens', function() } | } | | - {1:▶︎ Run } | + {1: ▶︎ Run } | fn main() { | let s = S::new(42, String::from("Hello, world!"))| ; | println!("S.a: {}, S.b: {}", s.a, s.b); | } | - |*2 + ^ | + {1:~ }| + | ]]) local grid_without_lenses = dedent([[ - ^https://github.com/neovim/neovim/issues/16166 | struct S { | a: i32, | b: String, | @@ -75,7 +73,7 @@ describe('vim.lsp.codelens', function() ; | println!("S.a: {}, S.b: {}", s.a, s.b); | } | - | + ^ | {1:~ }|*2 | ]]) @@ -90,7 +88,7 @@ describe('vim.lsp.codelens', function() clear_notrace() exec_lua(create_server_definition) - screen = Screen.new(nil, 21) + screen = Screen.new(nil, 20) client_id = exec_lua(function() _G.server = _G._create_server({ @@ -108,7 +106,7 @@ describe('vim.lsp.codelens', function() impls = { position = { character = 7, - line = 1, + line = 0, }, }, }, @@ -117,11 +115,11 @@ describe('vim.lsp.codelens', function() range = { ['end'] = { character = 8, - line = 1, + line = 0, }, start = { character = 7, - line = 1, + line = 0, }, }, }, @@ -134,11 +132,11 @@ describe('vim.lsp.codelens', function() range = { ['end'] = { character = 7, - line = 12, + line = 11, }, start = { character = 3, - line = 12, + line = 11, }, }, }, @@ -155,11 +153,11 @@ describe('vim.lsp.codelens', function() range = { ['end'] = { character = 8, - line = 1, + line = 0, }, start = { character = 7, - line = 1, + line = 0, }, }, }) @@ -177,7 +175,6 @@ describe('vim.lsp.codelens', function() vim.lsp.codelens.enable() end) - feed('gg') screen:expect({ grid = grid_with_lenses }) end) @@ -215,11 +212,11 @@ describe('vim.lsp.codelens', function() range = { ['end'] = { character = 8, - line = 1, + line = 0, }, start = { character = 7, - line = 1, + line = 0, }, }, }, @@ -235,11 +232,11 @@ describe('vim.lsp.codelens', function() range = { ['end'] = { character = 7, - line = 12, + line = 11, }, start = { character = 3, - line = 12, + line = 11, }, }, }, @@ -248,11 +245,9 @@ describe('vim.lsp.codelens', function() end) it('refreshes code lenses on request', function() - feed('2Gdd') + feed('ggdd') screen:expect([[ - https://github.com/neovim/neovim/issues/16166 | - {1:1 implementation} | ^a: i32, | b: String, | } | @@ -263,14 +258,14 @@ describe('vim.lsp.codelens', function() } | } | | - {1:▶︎ Run } | + {1: ▶︎ Run } | fn main() { | let s = S::new(42, String::from("Hello, world!"))| ; | println!("S.a: {}, S.b: {}", s.a, s.b); | } | | - {1:~ }|*1 + {1:~ }|*2 | ]]) exec_lua(function() @@ -281,8 +276,6 @@ describe('vim.lsp.codelens', function() ) end) screen:expect([[ - https://github.com/neovim/neovim/issues/16166 | - {1: 1 implementation} | ^a: i32, | b: String, | } | @@ -294,13 +287,13 @@ describe('vim.lsp.codelens', function() } | | fn main() { | - {1: ▶︎ Run } | + {1: ▶︎ Run } | let s = S::new(42, String::from("Hello, world!"))| ; | println!("S.a: {}, S.b: {}", s.a, s.b); | } | | - {1:~ }|*1 + {1:~ }|*2 | ]]) end) @@ -383,10 +376,8 @@ describe('vim.lsp.codelens', function() end) it('clears extmarks beyond the bottom of the buffer', function() - feed('13G4dd') + feed('12G4dd') screen:expect([[ - https://github.com/neovim/neovim/issues/16166 | - {1:1 implementation} | struct S { | a: i32, | b: String, | @@ -399,7 +390,7 @@ describe('vim.lsp.codelens', function() } | | ^ | - {1:~ }|*6 + {1:~ }|*7 4 fewer lines | ]]) end)