mirror of
https://github.com/neovim/neovim.git
synced 2026-10-25 23:20:23 -05:00
fix(lsp): dynamic registration for off-spec method #39544
Problem:
LSP clients previously did not handle dynamic registration for off-spec methods
Solution:
Update the client logic to assume support for dynamic registration when
the method is unknown. Adjust the registration provider fallback and
enhance tests to verify correct behaviour for unknown methods and their
registration options. This improves compatibility with servers using
custom dynamic registrations.
AI-assisted: OpenCode
(cherry picked from commit 344d984ed2)
This commit is contained in:
1 parent
e230ff0439
commit
8919b02eba
2 files changed
+51
-5
No files matched your search
@@ -948,6 +948,10 @@ function Client:_supports_registration(method)
|
|||||||
end
|
end
|
||||||
local provider = self:_registration_provider(method)
|
local provider = self:_registration_provider(method)
|
||||||
local capability_path = lsp.protocol._provider_to_client_registration[provider]
|
local capability_path = lsp.protocol._provider_to_client_registration[provider]
|
||||||
|
if not capability_path then
|
||||||
|
-- If we don't know about the method, assume the client supports dynamic registration for it.
|
||||||
|
return true
|
||||||
|
end
|
||||||
local capability = vim.tbl_get(self.capabilities, unpack(capability_path))
|
local capability = vim.tbl_get(self.capabilities, unpack(capability_path))
|
||||||
return type(capability) == 'table' and capability.dynamicRegistration
|
return type(capability) == 'table' and capability.dynamicRegistration
|
||||||
end
|
end
|
||||||
@@ -956,7 +960,7 @@ end
|
|||||||
--- @param method vim.lsp.protocol.Method | vim.lsp.protocol.Method.Registration
|
--- @param method vim.lsp.protocol.Method | vim.lsp.protocol.Method.Registration
|
||||||
function Client:_registration_provider(method)
|
function Client:_registration_provider(method)
|
||||||
local capability_path = lsp.protocol._request_name_to_server_capability[method]
|
local capability_path = lsp.protocol._request_name_to_server_capability[method]
|
||||||
return capability_path and capability_path[1]
|
return capability_path and capability_path[1] or method
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @private
|
--- @private
|
||||||
@@ -1250,6 +1254,10 @@ function Client:supports_method(method, bufnr)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if required_capability == nil and next(self.registrations[method] or {}) ~= nil then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
-- If we don't know about the method, or if it is a self-mapping(method=required_capability)
|
-- If we don't know about the method, or if it is a self-mapping(method=required_capability)
|
||||||
-- assume that the client supports it.
|
-- assume that the client supports it.
|
||||||
-- This needs to be at the end, so that dynamic_capabilities are checked first.
|
-- This needs to be at the end, so that dynamic_capabilities are checked first.
|
||||||
@@ -1280,9 +1288,7 @@ function Client:_provider_foreach(method, fn)
|
|||||||
local required_capability = lsp.protocol._request_name_to_server_capability[method]
|
local required_capability = lsp.protocol._request_name_to_server_capability[method]
|
||||||
local dynamic_regs = self:_get_registrations(provider)
|
local dynamic_regs = self:_get_registrations(provider)
|
||||||
local has_subcap = required_capability and #required_capability > 1
|
local has_subcap = required_capability and #required_capability > 1
|
||||||
if not provider then
|
if not dynamic_regs then
|
||||||
return
|
|
||||||
elseif not dynamic_regs then
|
|
||||||
-- First check static capabilities
|
-- First check static capabilities
|
||||||
local static_reg = vim.tbl_get(self.server_capabilities, provider)
|
local static_reg = vim.tbl_get(self.server_capabilities, provider)
|
||||||
if static_reg then
|
if static_reg then
|
||||||
|
|||||||
@@ -3419,10 +3419,42 @@ describe('LSP', function()
|
|||||||
}, { client_id = client_id })
|
}, { client_id = client_id })
|
||||||
check('workspace/didChangeConfiguration', nil, 'section')
|
check('workspace/didChangeConfiguration', nil, 'section')
|
||||||
|
|
||||||
|
vim.lsp.handlers['client/registerCapability'](nil, {
|
||||||
|
registrations = {
|
||||||
|
{
|
||||||
|
id = 'unknown-method-id',
|
||||||
|
method = 'unknown-method',
|
||||||
|
registerOptions = {
|
||||||
|
some_opt = 'unknown-dummy-opt',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, { client_id = client_id })
|
||||||
|
check('unknown-method', nil, 'some_opt')
|
||||||
|
|
||||||
|
check('unknown-method-2')
|
||||||
|
vim.lsp.handlers['client/registerCapability'](nil, {
|
||||||
|
registrations = {
|
||||||
|
{
|
||||||
|
id = 'unknown-method-2-id',
|
||||||
|
method = 'unknown-method-2',
|
||||||
|
registerOptions = {
|
||||||
|
documentSelector = {
|
||||||
|
{
|
||||||
|
pattern = root_dir .. '/*.foo',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, { client_id = client_id })
|
||||||
|
check('unknown-method-2')
|
||||||
|
check('unknown-method-2', tmpfile)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end)
|
end)
|
||||||
|
|
||||||
eq(21, #result)
|
eq(25, #result)
|
||||||
eq({ method = 'textDocument/formatting', supported = false }, result[1])
|
eq({ method = 'textDocument/formatting', supported = false }, result[1])
|
||||||
eq({ method = 'textDocument/formatting', supported = true, fname = tmpfile }, result[2])
|
eq({ method = 'textDocument/formatting', supported = true, fname = tmpfile }, result[2])
|
||||||
eq({ method = 'textDocument/rangeFormatting', supported = true }, result[3])
|
eq({ method = 'textDocument/rangeFormatting', supported = true }, result[3])
|
||||||
@@ -3457,6 +3489,14 @@ describe('LSP', function()
|
|||||||
{ method = 'workspace/didChangeConfiguration', supported = true, cap = { 'dummy-section' } },
|
{ method = 'workspace/didChangeConfiguration', supported = true, cap = { 'dummy-section' } },
|
||||||
result[21]
|
result[21]
|
||||||
)
|
)
|
||||||
|
eq({
|
||||||
|
method = 'unknown-method',
|
||||||
|
supported = true,
|
||||||
|
cap = { 'unknown-dummy-opt' },
|
||||||
|
}, result[22])
|
||||||
|
eq({ method = 'unknown-method-2', supported = true }, result[23])
|
||||||
|
eq({ method = 'unknown-method-2', supported = false }, result[24])
|
||||||
|
eq({ method = 'unknown-method-2', supported = true, fname = tmpfile }, result[25])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('identifies client dynamic registration capability', function()
|
it('identifies client dynamic registration capability', function()
|
||||||
|
|||||||
Reference in new issue
Block a user