feat(lsp): support workspace/foldingRange/refresh #41780

Problem:
Servers cannot request refreshing stale LSP folding ranges after a
project-wide change.

Solution:
Advertise `workspace.foldingRange.refreshSupport` and re-request folding
ranges for active buffers when handling `workspace/foldingRange/refresh`.

AI-assisted
This commit is contained in:
Volodymyr Chernetskyi
2026-09-09 14:45:10 +02:00
committed by GitHub
parent 0a844adc13
commit 8e0045f43a
5 changed files with 57 additions and 1 deletions

View File

@@ -375,6 +375,8 @@ LSP
• |vim.lsp.formatexpr()| now falls back to `textDocument/formatting` if the whole
buffer is being formatted and the language server doesn't support
`textDocument/rangeFormatting`.
• Support for `workspace/foldingRange/refresh`:
https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#workspace_foldingRange_refresh
LUA

View File

@@ -289,6 +289,26 @@ function State:foldclose(kind, winid)
end)
end
--- |lsp-handler| for the method `workspace/foldingRange/refresh`
---
--- Refresh requests are sent by the server to indicate a project-wide change
--- that requires all folding ranges to be re-requested by the client.
---@param ctx lsp.HandlerContext
---@internal
function M.on_refresh(err, _, ctx)
if err then
return vim.NIL
end
for _, state in pairs(State.active) do
if state.client_state[ctx.client_id] then
state:refresh(ctx.client_id)
end
end
return vim.NIL
end
---@param kind lsp.FoldingRangeKind
---@param winid? integer
function M.foldclose(kind, winid)

View File

@@ -673,6 +673,11 @@ RSC['workspace/diagnostic/refresh'] = function(err, result, ctx)
return vim.lsp.diagnostic.on_refresh(err, result, ctx)
end
---@see https://microsoft.github.io/language-server-protocol/specification/#workspace_foldingRange_refresh
RSC['workspace/foldingRange/refresh'] = function(err, result, ctx)
return vim.lsp._folding_range.on_refresh(err, result, ctx)
end
---@see https://microsoft.github.io/language-server-protocol/specification/#workspace_inlayHint_refresh
RSC['workspace/inlayHint/refresh'] = function(err, result, ctx)
return vim.lsp.inlay_hint.on_refresh(err, result, ctx)

View File

@@ -639,6 +639,9 @@ function protocol.make_client_capabilities()
diagnostics = {
refreshSupport = true,
},
foldingRange = {
refreshSupport = true,
},
fileOperations = {
dynamicRegistration = false,
didCreate = false,

View File

@@ -101,13 +101,14 @@ static int foldLevel(linenr_T lnum)
exec_lua(create_server_definition)
bufnr = n.api.nvim_get_current_buf()
client_id = exec_lua(function()
_G.folding_ranges = result
_G.server = _G._create_server({
capabilities = {
foldingRangeProvider = true,
},
handlers = {
['textDocument/foldingRange'] = function(_, _, callback)
callback(nil, result)
callback(nil, _G.folding_ranges)
end,
},
})
@@ -186,6 +187,31 @@ static int foldLevel(linenr_T lnum)
}, foldlevels)
end)
it('refreshes folding ranges on request', function()
local function foldlevels()
return exec_lua(function()
return { vim.lsp.foldexpr(1), vim.lsp.foldexpr(2), vim.lsp.foldexpr(3) }
end)
end
retry(nil, nil, function()
eq({ '>1', '<1', '0' }, foldlevels())
end)
exec_lua(function()
_G.folding_ranges = { { startLine = 1, endLine = 2 } }
vim.lsp._folding_range.on_refresh(
nil,
nil,
{ method = 'workspace/foldingRange/refresh', client_id = client_id }
)
end)
retry(nil, nil, function()
eq({ '0', '>1', '<1' }, foldlevels())
end)
end)
it('updates folds in all windows', function()
screen:expect({
grid = [[