mirror of
https://github.com/neovim/neovim.git
synced 2024-12-19 10:45:16 -07:00
refactor(lsp): better tracking of requests
Not essential, but adds robustness and hardening for future changes.
This commit is contained in:
parent
ca4f688ad4
commit
4bfdd1ee9d
@ -946,7 +946,7 @@ Lua module: vim.lsp.client *lsp-client*
|
|||||||
• {handlers} (`table<string,lsp.Handler>`) The handlers
|
• {handlers} (`table<string,lsp.Handler>`) The handlers
|
||||||
used by the client as described in
|
used by the client as described in
|
||||||
|lsp-handler|.
|
|lsp-handler|.
|
||||||
• {requests} (`table<integer,{ type: string, bufnr: integer, method: string}>`)
|
• {requests} (`table<integer,{ type: string, bufnr: integer, method: string}?>`)
|
||||||
The current pending requests in flight to the
|
The current pending requests in flight to the
|
||||||
server. Entries are key-value pairs with the
|
server. Entries are key-value pairs with the
|
||||||
key being the request id while the value is a
|
key being the request id while the value is a
|
||||||
@ -982,9 +982,9 @@ Lua module: vim.lsp.client *lsp-client*
|
|||||||
lenses, ...) triggers the command. Client
|
lenses, ...) triggers the command. Client
|
||||||
commands take precedence over the global
|
commands take precedence over the global
|
||||||
command registry.
|
command registry.
|
||||||
• {settings} (`table`) Map with language server specific
|
• {settings} (`lsp.LSPObject`) Map with language server
|
||||||
settings. These are returned to the language
|
specific settings. These are returned to the
|
||||||
server if requested via
|
language server if requested via
|
||||||
`workspace/configuration`. Keys are
|
`workspace/configuration`. Keys are
|
||||||
case-sensitive.
|
case-sensitive.
|
||||||
• {flags} (`table`) A table with flags for the client.
|
• {flags} (`table`) A table with flags for the client.
|
||||||
@ -1077,8 +1077,8 @@ Lua module: vim.lsp.client *lsp-client*
|
|||||||
an array.
|
an array.
|
||||||
• {handlers}? (`table<string,function>`) Map of language
|
• {handlers}? (`table<string,function>`) Map of language
|
||||||
server method names to |lsp-handler|
|
server method names to |lsp-handler|
|
||||||
• {settings}? (`table`) Map with language server specific
|
• {settings}? (`lsp.LSPObject`) Map with language server
|
||||||
settings. See the {settings} in
|
specific settings. See the {settings} in
|
||||||
|vim.lsp.Client|.
|
|vim.lsp.Client|.
|
||||||
• {commands}? (`table<string,fun(command: lsp.Command, ctx: table)>`)
|
• {commands}? (`table<string,fun(command: lsp.Command, ctx: table)>`)
|
||||||
Table that maps string of clientside commands to
|
Table that maps string of clientside commands to
|
||||||
@ -1088,9 +1088,10 @@ Lua module: vim.lsp.client *lsp-client*
|
|||||||
command name, and the value is a function which
|
command name, and the value is a function which
|
||||||
is called if any LSP action (code action, code
|
is called if any LSP action (code action, code
|
||||||
lenses, ...) triggers the command.
|
lenses, ...) triggers the command.
|
||||||
• {init_options}? (`table`) Values to pass in the initialization
|
• {init_options}? (`lsp.LSPObject`) Values to pass in the
|
||||||
request as `initializationOptions`. See
|
initialization request as
|
||||||
`initialize` in the LSP spec.
|
`initializationOptions`. See `initialize` in the
|
||||||
|
LSP spec.
|
||||||
• {name}? (`string`, default: client-id) Name in log
|
• {name}? (`string`, default: client-id) Name in log
|
||||||
messages.
|
messages.
|
||||||
• {get_language_id}? (`fun(bufnr: integer, filetype: string): string`)
|
• {get_language_id}? (`fun(bufnr: integer, filetype: string): string`)
|
||||||
|
@ -75,7 +75,7 @@ local validate = vim.validate
|
|||||||
---
|
---
|
||||||
--- Map with language server specific settings.
|
--- Map with language server specific settings.
|
||||||
--- See the {settings} in |vim.lsp.Client|.
|
--- See the {settings} in |vim.lsp.Client|.
|
||||||
--- @field settings? table
|
--- @field settings? lsp.LSPObject
|
||||||
---
|
---
|
||||||
--- Table that maps string of clientside commands to user-defined functions.
|
--- Table that maps string of clientside commands to user-defined functions.
|
||||||
--- Commands passed to `start()` take precedence over the global command registry. Each key
|
--- Commands passed to `start()` take precedence over the global command registry. Each key
|
||||||
@ -85,7 +85,7 @@ local validate = vim.validate
|
|||||||
---
|
---
|
||||||
--- Values to pass in the initialization request as `initializationOptions`. See `initialize` in
|
--- Values to pass in the initialization request as `initializationOptions`. See `initialize` in
|
||||||
--- the LSP spec.
|
--- the LSP spec.
|
||||||
--- @field init_options? table
|
--- @field init_options? lsp.LSPObject
|
||||||
---
|
---
|
||||||
--- Name in log messages.
|
--- Name in log messages.
|
||||||
--- (default: client-id)
|
--- (default: client-id)
|
||||||
@ -164,7 +164,7 @@ local validate = vim.validate
|
|||||||
--- for an active request, or "cancel" for a cancel request. It will be
|
--- for an active request, or "cancel" for a cancel request. It will be
|
||||||
--- "complete" ephemerally while executing |LspRequest| autocmds when replies
|
--- "complete" ephemerally while executing |LspRequest| autocmds when replies
|
||||||
--- are received from the server.
|
--- are received from the server.
|
||||||
--- @field requests table<integer,{ type: string, bufnr: integer, method: string}>
|
--- @field requests table<integer,{ type: string, bufnr: integer, method: string}?>
|
||||||
---
|
---
|
||||||
--- copy of the table that was passed by the user
|
--- copy of the table that was passed by the user
|
||||||
--- to |vim.lsp.start()|.
|
--- to |vim.lsp.start()|.
|
||||||
@ -189,9 +189,6 @@ local validate = vim.validate
|
|||||||
---
|
---
|
||||||
--- @field attached_buffers table<integer,true>
|
--- @field attached_buffers table<integer,true>
|
||||||
---
|
---
|
||||||
--- Buffers that should be attached to upon initialize()
|
|
||||||
--- @field package _buffers_to_attach table<integer,true>
|
|
||||||
---
|
|
||||||
--- @field private _log_prefix string
|
--- @field private _log_prefix string
|
||||||
---
|
---
|
||||||
--- Track this so that we can escalate automatically if we've already tried a
|
--- Track this so that we can escalate automatically if we've already tried a
|
||||||
@ -210,7 +207,7 @@ local validate = vim.validate
|
|||||||
--- Map with language server specific settings. These are returned to the
|
--- Map with language server specific settings. These are returned to the
|
||||||
--- language server if requested via `workspace/configuration`. Keys are
|
--- language server if requested via `workspace/configuration`. Keys are
|
||||||
--- case-sensitive.
|
--- case-sensitive.
|
||||||
--- @field settings table
|
--- @field settings lsp.LSPObject
|
||||||
---
|
---
|
||||||
--- A table with flags for the client. The current (experimental) flags are:
|
--- A table with flags for the client. The current (experimental) flags are:
|
||||||
--- @field flags vim.lsp.Client.Flags
|
--- @field flags vim.lsp.Client.Flags
|
||||||
@ -265,9 +262,6 @@ local valid_encodings = {
|
|||||||
['utf8'] = 'utf-8',
|
['utf8'] = 'utf-8',
|
||||||
['utf16'] = 'utf-16',
|
['utf16'] = 'utf-16',
|
||||||
['utf32'] = 'utf-32',
|
['utf32'] = 'utf-32',
|
||||||
UTF8 = 'utf-8',
|
|
||||||
UTF16 = 'utf-16',
|
|
||||||
UTF32 = 'utf-32',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Normalizes {encoding} to valid LSP encoding names.
|
--- Normalizes {encoding} to valid LSP encoding names.
|
||||||
@ -276,7 +270,7 @@ local valid_encodings = {
|
|||||||
local function validate_encoding(encoding)
|
local function validate_encoding(encoding)
|
||||||
validate('encoding', encoding, 'string', true)
|
validate('encoding', encoding, 'string', true)
|
||||||
if not encoding then
|
if not encoding then
|
||||||
return valid_encodings.UTF16
|
return valid_encodings.utf16
|
||||||
end
|
end
|
||||||
return valid_encodings[encoding:lower()]
|
return valid_encodings[encoding:lower()]
|
||||||
or error(
|
or error(
|
||||||
@ -604,6 +598,57 @@ function Client:_resolve_handler(method)
|
|||||||
return self.handlers[method] or lsp.handlers[method]
|
return self.handlers[method] or lsp.handlers[method]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- @private
|
||||||
|
--- @param id integer
|
||||||
|
--- @param req_type 'pending'|'complete'|'cancel'|
|
||||||
|
--- @param bufnr? integer (only required for req_type='pending')
|
||||||
|
--- @param method? string (only required for req_type='pending')
|
||||||
|
function Client:_process_request(id, req_type, bufnr, method)
|
||||||
|
local pending = req_type == 'pending'
|
||||||
|
|
||||||
|
validate('id', id, 'number')
|
||||||
|
if pending then
|
||||||
|
validate('bufnr', bufnr, 'number')
|
||||||
|
validate('method', method, 'string')
|
||||||
|
end
|
||||||
|
|
||||||
|
local cur_request = self.requests[id]
|
||||||
|
|
||||||
|
if pending and cur_request then
|
||||||
|
log.error(
|
||||||
|
self._log_prefix,
|
||||||
|
('Cannot create request with id %d as one already exists'):format(id)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
elseif not pending and not cur_request then
|
||||||
|
log.error(
|
||||||
|
self._log_prefix,
|
||||||
|
('Cannot find request with id %d whilst attempting to %s'):format(id, req_type)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if cur_request then
|
||||||
|
bufnr = cur_request.bufnr
|
||||||
|
method = cur_request.method
|
||||||
|
end
|
||||||
|
|
||||||
|
assert(bufnr and method)
|
||||||
|
|
||||||
|
local request = { type = req_type, bufnr = bufnr, method = method }
|
||||||
|
|
||||||
|
-- Clear 'complete' requests
|
||||||
|
-- Note 'pending' and 'cancelled' requests are cleared when the server sends a response
|
||||||
|
-- which is processed via the notify_reply_callback argument to rpc.request.
|
||||||
|
self.requests[id] = req_type ~= 'complete' and request or nil
|
||||||
|
|
||||||
|
api.nvim_exec_autocmds('LspRequest', {
|
||||||
|
buffer = api.nvim_buf_is_valid(bufnr) and bufnr or nil,
|
||||||
|
modeline = false,
|
||||||
|
data = { client_id = self.id, request_id = id, request = request },
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
--- Sends a request to the server.
|
--- Sends a request to the server.
|
||||||
---
|
---
|
||||||
--- This is a thin wrapper around {client.rpc.request} with some additional
|
--- This is a thin wrapper around {client.rpc.request} with some additional
|
||||||
@ -632,33 +677,20 @@ function Client:request(method, params, handler, bufnr)
|
|||||||
local version = lsp.util.buf_versions[bufnr]
|
local version = lsp.util.buf_versions[bufnr]
|
||||||
log.debug(self._log_prefix, 'client.request', self.id, method, params, handler, bufnr)
|
log.debug(self._log_prefix, 'client.request', self.id, method, params, handler, bufnr)
|
||||||
local success, request_id = self.rpc.request(method, params, function(err, result)
|
local success, request_id = self.rpc.request(method, params, function(err, result)
|
||||||
local context = {
|
handler(err, result, {
|
||||||
method = method,
|
method = method,
|
||||||
client_id = self.id,
|
client_id = self.id,
|
||||||
bufnr = bufnr,
|
bufnr = bufnr,
|
||||||
params = params,
|
params = params,
|
||||||
version = version,
|
version = version,
|
||||||
}
|
|
||||||
handler(err, result, context)
|
|
||||||
end, function(request_id)
|
|
||||||
local request = self.requests[request_id]
|
|
||||||
request.type = 'complete'
|
|
||||||
api.nvim_exec_autocmds('LspRequest', {
|
|
||||||
buffer = api.nvim_buf_is_valid(bufnr) and bufnr or nil,
|
|
||||||
modeline = false,
|
|
||||||
data = { client_id = self.id, request_id = request_id, request = request },
|
|
||||||
})
|
})
|
||||||
self.requests[request_id] = nil
|
end, function(request_id)
|
||||||
|
-- Called when the server sends a response to the request (including cancelled acknowledgment).
|
||||||
|
self:_process_request(request_id, 'complete')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
if success and request_id then
|
if success and request_id then
|
||||||
local request = { type = 'pending', bufnr = bufnr, method = method }
|
self:_process_request(request_id, 'pending', bufnr, method)
|
||||||
self.requests[request_id] = request
|
|
||||||
api.nvim_exec_autocmds('LspRequest', {
|
|
||||||
buffer = api.nvim_buf_is_valid(bufnr) and bufnr or nil,
|
|
||||||
modeline = false,
|
|
||||||
data = { client_id = self.id, request_id = request_id, request = request },
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return success, request_id
|
return success, request_id
|
||||||
@ -756,16 +788,7 @@ end
|
|||||||
--- @return boolean status indicating if the notification was successful.
|
--- @return boolean status indicating if the notification was successful.
|
||||||
--- @see |Client:notify()|
|
--- @see |Client:notify()|
|
||||||
function Client:cancel_request(id)
|
function Client:cancel_request(id)
|
||||||
validate('id', id, 'number')
|
self:_process_request(id, 'cancel')
|
||||||
local request = self.requests[id]
|
|
||||||
if request and request.type == 'pending' then
|
|
||||||
request.type = 'cancel'
|
|
||||||
api.nvim_exec_autocmds('LspRequest', {
|
|
||||||
buffer = api.nvim_buf_is_valid(request.bufnr) and request.bufnr or nil,
|
|
||||||
modeline = false,
|
|
||||||
data = { client_id = self.id, request_id = id, request = request },
|
|
||||||
})
|
|
||||||
end
|
|
||||||
return self.rpc.notify(ms.dollar_cancelRequest, { id = id })
|
return self.rpc.notify(ms.dollar_cancelRequest, { id = id })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user