feat(lsp)!: promote LspRequest to a full autocmd and enrich with additional data (#23694)

BREAKING CHANGE: LspRequest is no longer a User autocmd but is now a
first class citizen.

LspRequest as a User autocmd had limited functionality. Namely, the only
thing you could do was use the notification to do a lookup on all the
clients' requests tables to figure out what changed.

Promoting the autocmd to a full autocmd lets us set the buffer the
request was initiated on (so people can set buffer-local autocmds for
listening to these events).

Additionally, when used from Lua, we can pass additional metadata about
the request along with the notification, including the client ID, the
request ID, and the actual request object stored on the client's
requests table. Users can now listen for these events and act on them
proactively instead of polling all of the requests tables and looking
for changes.
This commit is contained in:
jdrouhard 2023-05-30 13:56:29 -05:00 committed by GitHub
parent dd3fa64573
commit 58618d208a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 75 additions and 13 deletions

View File

@ -619,19 +619,54 @@ to the callback in the "data" table. The token fields are documented in
Note: doing anything other than calling
|vim.lsp.semantic_tokens.highlight_token()| is considered experimental.
Also the following |User| |autocommand|s are provided:
LspRequest *LspRequest*
For each request sent to an LSP server, this event is triggered for every
change to the request's status. The status can be one of `pending`,
`complete`, or `cancel` and is sent as the {type} on the "data" table passed
to the callback function.
It triggers when the initial request is sent ({type} == `pending`) and when
the LSP server responds ({type} == `complete`). If a cancelation is requested
using `client.cancel_request(request_id)`, then this event will trigger with
{type} == `cancel`.
When used from Lua, the client ID, request ID, and request are sent in the
"data" table. See {requests} in |vim.lsp.client| for details on the {request}
value. If the request type is `complete`, the request will be deleted from the
client's pending requests table immediately after calling the event's
callbacks. Example: >lua
vim.api.nvim_create_autocmd('LspRequest', {
callback = function(args)
local bufnr = args.buf
local client_id = args.data.client_id
local request_id = args.data.request_id
local request = args.data.request
if request.type == 'pending' then
-- do something with pending requests
track_pending(client_id, bufnr, request_id, request)
elseif request.type == 'cancel' then
-- do something with pending cancel requests
track_canceling(client_id, bufnr, request_id, request)
elseif request.type == 'complete' then
-- do something with finished requests. this pending
-- request entry is about to be removed since it is complete
track_finish(client_id, bufnr, request_id, request)
end
end,
})
<
Also the following |User| |autocommand| is provided:
LspProgressUpdate *LspProgressUpdate*
Upon receipt of a progress notification from the server. See
|vim.lsp.util.get_progress_messages()|.
LspRequest *LspRequest*
After a change to the active set of pending LSP requests. See {requests}
in |vim.lsp.client|.
Example: >vim
autocmd User LspProgressUpdate redrawstatus
autocmd User LspRequest redrawstatus
<
==============================================================================
@ -764,7 +799,9 @@ client() *vim.lsp.client*
server. Entries are key-value pairs with the key being the request ID
while the value is a table with `type`, `bufnr`, and `method`
key-value pairs. `type` is either "pending" for an active request, or
"cancel" for a cancel request.
"cancel" for a cancel request. It will be "complete" ephemerally while
executing |LspRequest| autocmds when replies are received from the
server.
• {config} (table): copy of the table that was passed by the user to
|vim.lsp.start_client()|.
• {server_capabilities} (table): Response from the server sent on

View File

@ -31,6 +31,9 @@ The following changes may require adaptations in user config or plugins.
set keymodel=startsel,stopsel
<
• |LspRequest| autocmd was promoted from a |User| autocmd to a first class
citizen.
==============================================================================
ADDED FEATURES *news-added*
@ -80,6 +83,9 @@ The following changes to existing APIs or features add new behavior.
• The `workspace/didChangeWatchedFiles` LSP client capability is now enabled
by default.
• |LspRequest| autocmd callbacks now contain additional information about the LSP
request status update that occurred.
==============================================================================
REMOVED FEATURES *news-removed*

View File

@ -799,7 +799,9 @@ end
--- to the server. Entries are key-value pairs with the key
--- being the request ID while the value is a table with `type`,
--- `bufnr`, and `method` key-value pairs. `type` is either "pending"
--- for an active request, or "cancel" for a cancel request.
--- for an active request, or "cancel" for a cancel request. It will
--- be "complete" ephemerally while executing |LspRequest| autocmds
--- when replies are received from the server.
---
--- - {config} (table): copy of the table that was passed by the user
--- to |vim.lsp.start_client()|.
@ -1408,13 +1410,24 @@ function lsp.start_client(config)
{ method = method, client_id = client_id, bufnr = bufnr, params = params }
)
end, function(request_id)
local request = client.requests[request_id]
request.type = 'complete'
nvim_exec_autocmds('LspRequest', {
buffer = bufnr,
modeline = false,
data = { client_id = client_id, request_id = request_id, request = request },
})
client.requests[request_id] = nil
nvim_exec_autocmds('User', { pattern = 'LspRequest', modeline = false })
end)
if success and request_id then
client.requests[request_id] = { type = 'pending', bufnr = bufnr, method = method }
nvim_exec_autocmds('User', { pattern = 'LspRequest', modeline = false })
local request = { type = 'pending', bufnr = bufnr, method = method }
client.requests[request_id] = request
nvim_exec_autocmds('LspRequest', {
buffer = bufnr,
modeline = false,
data = { client_id = client_id, request_id = request_id, request = request },
})
end
return success, request_id
@ -1486,7 +1499,11 @@ function lsp.start_client(config)
local request = client.requests[id]
if request and request.type == 'pending' then
request.type = 'cancel'
nvim_exec_autocmds('User', { pattern = 'LspRequest', modeline = false })
nvim_exec_autocmds('LspRequest', {
buffer = request.bufnr,
modeline = false,
data = { client_id = client_id, request_id = id, request = request },
})
end
return rpc.notify('$/cancelRequest', { id = id })
end

View File

@ -72,6 +72,7 @@ return {
'InsertLeavePre', -- just before leaving Insert mode
'LspAttach', -- after an LSP client attaches to a buffer
'LspDetach', -- after an LSP client detaches from a buffer
'LspRequest', -- after an LSP request is started, canceled, or completed
'LspTokenUpdate', -- after a visible LSP token is updated
'MenuPopup', -- just before popup menu is displayed
'ModeChanged', -- after changing the mode
@ -152,6 +153,7 @@ return {
DiagnosticChanged=true,
LspAttach=true,
LspDetach=true,
LspRequest=true,
LspTokenUpdate=true,
RecordingEnter=true,
RecordingLeave=true,

View File

@ -948,7 +948,7 @@ describe('LSP', function()
test_name = "check_tracked_requests_cleared";
on_init = function(_client)
command('let g:requests = 0')
command('autocmd User LspRequest let g:requests+=1')
command('autocmd LspRequest * let g:requests+=1')
client = _client
client.request("slow_request")
eq(1, eval('g:requests'))