commit 965468fca1e7c465292ea9dd767f5cf02fc61dad
parent fe23168e2bc1a985b8206c3c9049d6f94233bf2d
Author: Yi Ming <ofseed@foxmail.com>
Date: Mon, 2 Feb 2026 09:40:31 +0800
feat(lsp): support `workspace/codeLens/refresh`
Diffstat:
5 files changed, 92 insertions(+), 2 deletions(-)
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
@@ -300,6 +300,8 @@ LSP
|hl-LspReferenceTarget| highlight group.
• Support for `textDocument/codeLens` |lsp-codelens| has been reimplemented:
https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_codeLens
+• Support for `workspace/codeLens/refresh`:
+ https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#codeLens_refresh
LUA
diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua
@@ -123,9 +123,10 @@ function Provider:handler(err, result, ctx)
self.version = ctx.version
end
----@private
+---@package
---@param client_id? integer
-function Provider:request(client_id)
+---@param on_response? function
+function Provider:request(client_id, on_response)
---@type lsp.CodeLensParams
local params = { textDocument = util.make_text_document_params(self.bufnr) }
for id in pairs(self.client_state) do
@@ -133,6 +134,10 @@ function Provider:request(client_id)
local client = assert(vim.lsp.get_client_by_id(id))
client:request('textDocument/codeLens', params, function(...)
self:handler(...)
+
+ if on_response then
+ on_response()
+ end
end, self.bufnr)
end
end
@@ -404,6 +409,28 @@ function M.run(opts)
end
end
+--- |lsp-handler| for the method `workspace/codeLens/refresh`
+---
+---@private
+---@type lsp.Handler
+function M.on_refresh(err, _, ctx)
+ if err then
+ return vim.NIL
+ end
+
+ for bufnr, provider in pairs(Provider.active) do
+ for client_id in pairs(provider.client_state) do
+ if client_id == ctx.client_id then
+ provider:request(client_id, function()
+ provider.row_version = {}
+ vim.api.nvim__redraw({ buf = bufnr, valid = true, flush = false })
+ end)
+ end
+ end
+ end
+ return vim.NIL
+end
+
---@deprecated
---@param client_id? integer
---@param bufnr? integer
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
@@ -649,6 +649,11 @@ RSC['window/showDocument'] = function(_, params, ctx)
return { success = success or false }
end
+---@see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#codeLens_refresh
+RSC['workspace/codeLens/refresh'] = function(err, result, ctx)
+ return vim.lsp.codelens.on_refresh(err, result, ctx)
+end
+
---@see https://microsoft.github.io/language-server-protocol/specification/#diagnostic_refresh
RSC['workspace/diagnostic/refresh'] = function(err, result, ctx)
return vim.lsp.diagnostic.on_refresh(err, result, ctx)
diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua
@@ -604,6 +604,9 @@ function protocol.make_client_capabilities()
dynamicRegistration = sysname == 'Darwin' or sysname == 'Windows_NT',
relativePatternSupport = true,
},
+ codeLens = {
+ refreshSupport = true,
+ },
inlayHint = {
refreshSupport = true,
},
diff --git a/test/functional/plugin/lsp/codelens_spec.lua b/test/functional/plugin/lsp/codelens_spec.lua
@@ -9,6 +9,7 @@ local eq = t.eq
local api = n.api
local exec_lua = n.exec_lua
local insert = n.insert
+local feed = n.feed
local clear_notrace = t_lsp.clear_notrace
local create_server_definition = t_lsp.create_server_definition
@@ -242,6 +243,58 @@ describe('vim.lsp.codelens', function()
}, result)
end)
+ it('refreshes code lenses on request', function()
+ feed('ggdd')
+
+ screen:expect([[
+ ^a: i32, {1:1 implementation} |
+ b: String, |
+ } |
+ |
+ impl S { |
+ fn new(a: i32, b: String) -> Self { |
+ S { a, b } |
+ } |
+ } |
+ |
+ fn main() { {1:▶︎ Run } |
+ let s = S::new(42, String::from("Hello, world!"))|
+ ; |
+ println!("S.a: {}, S.b: {}", s.a, s.b); |
+ } |
+ |
+ {1:~ }|*3
+ |
+ ]])
+ exec_lua(function()
+ vim.lsp.codelens.on_refresh(
+ nil,
+ nil,
+ { method = 'workspace/codeLens/refresh', client_id = client_id }
+ )
+ end)
+ screen:expect([[
+ ^a: i32, {1:1 implementation} |
+ b: String, |
+ } |
+ |
+ impl S { |
+ fn new(a: i32, b: String) -> Self { |
+ S { a, b } |
+ } |
+ } |
+ |
+ fn main() { |
+ let s = S::new(42, String::from("Hello, world!"))|
+ ; {1:▶︎ Run } |
+ println!("S.a: {}, S.b: {}", s.a, s.b); |
+ } |
+ |
+ {1:~ }|*3
+ |
+ ]])
+ end)
+
after_each(function()
api.nvim_exec_autocmds('VimLeavePre', { modeline = false })
end)