fix(lsp): on detach, cancel pending foldingRange requests #31509

Problem:
1. Open a relatively large file (so the server needs some time to
   process the request).
2. Then immediately execute `:bdelete`.
3. Once the request is completed, the handler will obtain the bufstate
   of a buffer already unloaded.

    Error executing vim.schedule lua callback: ...7841_1/share/nvim/runtime/lua/vim/lsp/_folding_range.lua:119: assertion failed!
    stack traceback:
            [C]: in function 'assert'
            ...7841_1/share/nvim/runtime/lua/vim/lsp/_folding_range.lua:119: in function 'multi_handler'
            ...7841_1/share/nvim/runtime/lua/vim/lsp/_folding_range.lua:140: in function 'handler'
            ...HEAD-c137841_1/share/nvim/runtime/lua/vim/lsp/client.lua:669: in function ''
            vim/_editor.lua: in function <vim/_editor.lua:0>

Solution:
On detach, cancel all pending textDocument_foldingRange requests.
This commit is contained in:
Yi Ming
2025-02-12 22:22:59 +08:00
committed by GitHub
parent 2c629ad13f
commit be8d87014c
2 changed files with 45 additions and 5 deletions

View File

@ -2153,6 +2153,41 @@ local function make_line_range_params(bufnr, start_line, end_line, position_enco
}
end
---@class (private) vim.lsp.util._cancel_requests.Filter
---@field bufnr? integer
---@field clients? vim.lsp.Client[]
---@field method? string
---@field type? string
---@private
--- Cancel all {filter}ed requests.
---
---@param filter? vim.lsp.util._cancel_requests.Filter
function M._cancel_requests(filter)
filter = filter or {}
local bufnr = filter.bufnr and vim._resolve_bufnr(filter.bufnr) or nil
local clients = filter.clients
local method = filter.method
local type = filter.type
for _, client in
ipairs(clients or vim.lsp.get_clients({
bufnr = bufnr,
method = method,
}))
do
for id, request in pairs(client.requests) do
if
(bufnr == nil or bufnr == request.bufnr)
and (method == nil or method == request.method)
and (type == nil or type == request.type)
then
client:cancel_request(id)
end
end
end
end
---@class (private) vim.lsp.util._refresh.Opts
---@field bufnr integer? Buffer to refresh (default: 0)
---@field only_visible? boolean Whether to only refresh for the visible regions of the buffer (default: false)
@ -2180,12 +2215,12 @@ function M._refresh(method, opts)
if api.nvim_win_get_buf(window) == bufnr then
local first = vim.fn.line('w0', window)
local last = vim.fn.line('w$', window)
M._cancel_requests({
bufnr = bufnr,
clients = clients,
type = 'pending',
})
for _, client in ipairs(clients) do
for rid, req in pairs(client.requests) do
if req.method == method and req.type == 'pending' and req.bufnr == bufnr then
client:cancel_request(rid)
end
end
client:request(method, {
textDocument = textDocument,
range = make_line_range_params(bufnr, first - 1, last - 1, client.offset_encoding),