mirror of
https://github.com/neovim/neovim.git
synced 2026-05-23 21:30:11 +00:00
fix(lsp): support nested workspace registrations #39574
Problem: Nested workspace capabilities like workspace.fileOperations.didCreate and workspace.textDocumentContent are not handled consistently for dynamic and static registration provider lookup. Solution: Generate explicit registration-provider mappings from the LSP metadata and use them when registering and querying capabilities. Add coverage for dynamic and static nested workspace registrations.
This commit is contained in:
@@ -9,6 +9,12 @@ local validate = vim.validate
|
||||
---@type table<integer,vim.lsp.Client>
|
||||
local all_clients = {}
|
||||
|
||||
---@param provider string
|
||||
---@param capability string[]?
|
||||
local function is_nested_server_capability_provider(provider, capability)
|
||||
return capability ~= nil and #capability > 1 and provider == table.concat(capability, '.')
|
||||
end
|
||||
|
||||
--- @alias vim.lsp.client.on_init_cb fun(client: vim.lsp.Client, init_result: lsp.InitializeResult)
|
||||
--- @alias vim.lsp.client.on_attach_cb fun(client: vim.lsp.Client, bufnr: integer)
|
||||
--- @alias vim.lsp.client.on_exit_cb fun(code: integer, signal: integer, client_id: integer)
|
||||
@@ -644,11 +650,11 @@ function Client:_process_static_registrations()
|
||||
|
||||
for method in pairs(lsp.protocol._method_supports_static_registration) do
|
||||
local capability = lsp.protocol._request_name_to_server_capability[method]
|
||||
if
|
||||
vim.tbl_get(self.server_capabilities, capability[1], 'id')
|
||||
and self:_supports_registration(method)
|
||||
then
|
||||
local cap = vim.tbl_get(self.server_capabilities, capability[1])
|
||||
local provider = self:_registration_provider(method)
|
||||
local cap = is_nested_server_capability_provider(provider, capability)
|
||||
and vim.tbl_get(self.server_capabilities, unpack(capability))
|
||||
or vim.tbl_get(self.server_capabilities, capability[1])
|
||||
if type(cap) == 'table' and cap.id then
|
||||
static_registrations[#static_registrations + 1] = {
|
||||
id = cap.id,
|
||||
method = method,
|
||||
@@ -974,8 +980,7 @@ end
|
||||
--- Get provider for a method to be registered dynamically.
|
||||
--- @param method vim.lsp.protocol.Method | vim.lsp.protocol.Method.Registration
|
||||
function Client:_registration_provider(method)
|
||||
local capability_path = lsp.protocol._request_name_to_server_capability[method]
|
||||
return capability_path and capability_path[1] or method
|
||||
return lsp.protocol._request_name_to_registration_provider[method] or method
|
||||
end
|
||||
|
||||
--- @private
|
||||
@@ -1236,6 +1241,7 @@ function Client:supports_method(method, bufnr)
|
||||
bufnr = bufnr.bufnr
|
||||
end
|
||||
local required_capability = lsp.protocol._request_name_to_server_capability[method]
|
||||
local has_subcap = required_capability and #required_capability > 1
|
||||
local is_self_mapping = required_capability
|
||||
and #required_capability == 1
|
||||
and required_capability[1] == method
|
||||
@@ -1249,13 +1255,21 @@ function Client:supports_method(method, bufnr)
|
||||
end
|
||||
|
||||
local provider = self:_registration_provider(method)
|
||||
local has_subprovider = is_nested_server_capability_provider(provider, required_capability)
|
||||
local regs = self:_get_registrations(provider, bufnr)
|
||||
if lsp.protocol._method_supports_dynamic_registration[method] and not regs then
|
||||
return false
|
||||
end
|
||||
if regs then
|
||||
for _, reg in ipairs(regs or {}) do
|
||||
if required_capability and #required_capability > 1 then
|
||||
if has_subprovider then
|
||||
if
|
||||
vim.tbl_get(reg, 'registerOptions')
|
||||
or lsp.protocol._methods_with_no_registration_options[method]
|
||||
then
|
||||
return self:_supports_registration(reg.method)
|
||||
end
|
||||
elseif has_subcap then
|
||||
if vim.tbl_get(reg, 'registerOptions', unpack(required_capability, 2)) then
|
||||
return self:_supports_registration(reg.method)
|
||||
end
|
||||
@@ -1303,18 +1317,33 @@ function Client:_provider_foreach(method, fn)
|
||||
local required_capability = lsp.protocol._request_name_to_server_capability[method]
|
||||
local dynamic_regs = self:_get_registrations(provider)
|
||||
local has_subcap = required_capability and #required_capability > 1
|
||||
local has_subprovider = is_nested_server_capability_provider(provider, required_capability)
|
||||
if not dynamic_regs then
|
||||
-- First check static capabilities
|
||||
local static_reg = vim.tbl_get(self.server_capabilities, provider)
|
||||
local static_reg = has_subprovider
|
||||
and vim.tbl_get(self.server_capabilities, unpack(required_capability))
|
||||
or vim.tbl_get(self.server_capabilities, provider)
|
||||
if static_reg then
|
||||
if not has_subcap or vim.tbl_get(static_reg, unpack(required_capability, 2)) then
|
||||
if
|
||||
has_subprovider
|
||||
or not has_subcap
|
||||
or vim.tbl_get(static_reg, unpack(required_capability, 2))
|
||||
then
|
||||
fn(static_reg)
|
||||
end
|
||||
end
|
||||
else
|
||||
for _, reg in ipairs(dynamic_regs) do
|
||||
if not has_subcap or vim.tbl_get(reg, 'registerOptions', unpack(required_capability, 2)) then
|
||||
fn(vim.tbl_get(reg, 'registerOptions') or {})
|
||||
local regoptions = vim.tbl_get(reg, 'registerOptions')
|
||||
if
|
||||
(
|
||||
has_subprovider
|
||||
and (regoptions or lsp.protocol._methods_with_no_registration_options[method])
|
||||
)
|
||||
or not has_subcap
|
||||
or vim.tbl_get(regoptions, unpack(required_capability, 2))
|
||||
then
|
||||
fn(regoptions or {})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1175,7 +1175,6 @@ protocol.Methods = {
|
||||
-- stylua: ignore start
|
||||
-- Generated by gen_lsp.lua, keep at end of file.
|
||||
--- Maps method names to the required client capability
|
||||
---TODO: also has workspace/* items because spec lacks a top-level "workspaceProvider"
|
||||
protocol._provider_to_client_registration = {
|
||||
['callHierarchyProvider'] = { 'textDocument', 'callHierarchy' },
|
||||
['codeActionProvider'] = { 'textDocument', 'codeAction' },
|
||||
@@ -1208,19 +1207,121 @@ protocol._provider_to_client_registration = {
|
||||
['textDocumentSync'] = { 'textDocument', 'synchronization' },
|
||||
['typeDefinitionProvider'] = { 'textDocument', 'typeDefinition' },
|
||||
['typeHierarchyProvider'] = { 'textDocument', 'typeHierarchy' },
|
||||
['workspace.fileOperations.didCreate'] = { 'workspace', 'fileOperations', 'didCreate' },
|
||||
['workspace.fileOperations.didDelete'] = { 'workspace', 'fileOperations', 'didDelete' },
|
||||
['workspace.fileOperations.didRename'] = { 'workspace', 'fileOperations', 'didRename' },
|
||||
['workspace.fileOperations.willCreate'] = { 'workspace', 'fileOperations', 'willCreate' },
|
||||
['workspace.fileOperations.willDelete'] = { 'workspace', 'fileOperations', 'willDelete' },
|
||||
['workspace.fileOperations.willRename'] = { 'workspace', 'fileOperations', 'willRename' },
|
||||
['workspace.textDocumentContent'] = { 'workspace', 'textDocumentContent' },
|
||||
['workspace/didChangeConfiguration'] = { 'workspace', 'didChangeConfiguration' },
|
||||
['workspace/didChangeWatchedFiles'] = { 'workspace', 'didChangeWatchedFiles' },
|
||||
['workspace/didCreateFiles'] = { 'workspace', 'fileOperations', 'didCreate' },
|
||||
['workspace/didDeleteFiles'] = { 'workspace', 'fileOperations', 'didDelete' },
|
||||
['workspace/didRenameFiles'] = { 'workspace', 'fileOperations', 'didRename' },
|
||||
['workspace/textDocumentContent'] = { 'workspace', 'textDocumentContent' },
|
||||
['workspace/willCreateFiles'] = { 'workspace', 'fileOperations', 'willCreate' },
|
||||
['workspace/willDeleteFiles'] = { 'workspace', 'fileOperations', 'willDelete' },
|
||||
['workspace/willRenameFiles'] = { 'workspace', 'fileOperations', 'willRename' },
|
||||
['workspaceSymbolProvider'] = { 'workspace', 'symbol' },
|
||||
}
|
||||
-- stylua: ignore end
|
||||
|
||||
-- stylua: ignore start
|
||||
-- Generated by gen_lsp.lua, keep at end of file.
|
||||
--- Maps method names to dynamic/static registration providers
|
||||
protocol._request_name_to_registration_provider = {
|
||||
['callHierarchy/incomingCalls'] = 'callHierarchyProvider',
|
||||
['callHierarchy/outgoingCalls'] = 'callHierarchyProvider',
|
||||
['client/registerCapability'] = 'client/registerCapability',
|
||||
['client/unregisterCapability'] = 'client/unregisterCapability',
|
||||
['codeAction/resolve'] = 'codeActionProvider',
|
||||
['codeLens/resolve'] = 'codeLensProvider',
|
||||
['completionItem/resolve'] = 'completionProvider',
|
||||
['documentLink/resolve'] = 'documentLinkProvider',
|
||||
['$/cancelRequest'] = '$/cancelRequest',
|
||||
['$/logTrace'] = '$/logTrace',
|
||||
['$/progress'] = '$/progress',
|
||||
['$/setTrace'] = '$/setTrace',
|
||||
['exit'] = 'exit',
|
||||
['initialize'] = 'initialize',
|
||||
['initialized'] = 'initialized',
|
||||
['inlayHint/resolve'] = 'inlayHintProvider',
|
||||
['notebookDocument/didChange'] = 'notebookDocument/didChange',
|
||||
['notebookDocument/didClose'] = 'notebookDocument/didClose',
|
||||
['notebookDocument/didOpen'] = 'notebookDocument/didOpen',
|
||||
['notebookDocument/didSave'] = 'notebookDocument/didSave',
|
||||
['shutdown'] = 'shutdown',
|
||||
['telemetry/event'] = 'telemetry/event',
|
||||
['textDocument/codeAction'] = 'codeActionProvider',
|
||||
['textDocument/codeLens'] = 'codeLensProvider',
|
||||
['textDocument/colorPresentation'] = 'colorProvider',
|
||||
['textDocument/completion'] = 'completionProvider',
|
||||
['textDocument/declaration'] = 'declarationProvider',
|
||||
['textDocument/definition'] = 'definitionProvider',
|
||||
['textDocument/diagnostic'] = 'diagnosticProvider',
|
||||
['textDocument/didChange'] = 'textDocumentSync',
|
||||
['textDocument/didClose'] = 'textDocumentSync',
|
||||
['textDocument/didOpen'] = 'textDocumentSync',
|
||||
['textDocument/didSave'] = 'textDocumentSync',
|
||||
['textDocument/documentColor'] = 'colorProvider',
|
||||
['textDocument/documentHighlight'] = 'documentHighlightProvider',
|
||||
['textDocument/documentLink'] = 'documentLinkProvider',
|
||||
['textDocument/documentSymbol'] = 'documentSymbolProvider',
|
||||
['textDocument/foldingRange'] = 'foldingRangeProvider',
|
||||
['textDocument/formatting'] = 'documentFormattingProvider',
|
||||
['textDocument/hover'] = 'hoverProvider',
|
||||
['textDocument/implementation'] = 'implementationProvider',
|
||||
['textDocument/inlayHint'] = 'inlayHintProvider',
|
||||
['textDocument/inlineCompletion'] = 'inlineCompletionProvider',
|
||||
['textDocument/inlineValue'] = 'inlineValueProvider',
|
||||
['textDocument/linkedEditingRange'] = 'linkedEditingRangeProvider',
|
||||
['textDocument/moniker'] = 'monikerProvider',
|
||||
['textDocument/onTypeFormatting'] = 'documentOnTypeFormattingProvider',
|
||||
['textDocument/prepareCallHierarchy'] = 'callHierarchyProvider',
|
||||
['textDocument/prepareRename'] = 'renameProvider',
|
||||
['textDocument/prepareTypeHierarchy'] = 'typeHierarchyProvider',
|
||||
['textDocument/publishDiagnostics'] = 'textDocument/publishDiagnostics',
|
||||
['textDocument/rangeFormatting'] = 'documentRangeFormattingProvider',
|
||||
['textDocument/rangesFormatting'] = 'documentRangeFormattingProvider',
|
||||
['textDocument/references'] = 'referencesProvider',
|
||||
['textDocument/rename'] = 'renameProvider',
|
||||
['textDocument/selectionRange'] = 'selectionRangeProvider',
|
||||
['textDocument/semanticTokens/full'] = 'semanticTokensProvider',
|
||||
['textDocument/semanticTokens/full/delta'] = 'semanticTokensProvider',
|
||||
['textDocument/semanticTokens/range'] = 'semanticTokensProvider',
|
||||
['textDocument/signatureHelp'] = 'signatureHelpProvider',
|
||||
['textDocument/typeDefinition'] = 'typeDefinitionProvider',
|
||||
['textDocument/willSave'] = 'textDocumentSync',
|
||||
['textDocument/willSaveWaitUntil'] = 'textDocumentSync',
|
||||
['typeHierarchy/subtypes'] = 'typeHierarchy/subtypes',
|
||||
['typeHierarchy/supertypes'] = 'typeHierarchy/supertypes',
|
||||
['window/logMessage'] = 'window/logMessage',
|
||||
['window/showDocument'] = 'window/showDocument',
|
||||
['window/showMessage'] = 'window/showMessage',
|
||||
['window/showMessageRequest'] = 'window/showMessageRequest',
|
||||
['window/workDoneProgress/cancel'] = 'window/workDoneProgress/cancel',
|
||||
['window/workDoneProgress/create'] = 'window/workDoneProgress/create',
|
||||
['workspaceSymbol/resolve'] = 'workspaceSymbolProvider',
|
||||
['workspace/applyEdit'] = 'workspace/applyEdit',
|
||||
['workspace/codeLens/refresh'] = 'workspace/codeLens/refresh',
|
||||
['workspace/configuration'] = 'workspace/configuration',
|
||||
['workspace/diagnostic'] = 'diagnosticProvider',
|
||||
['workspace/diagnostic/refresh'] = 'workspace/diagnostic/refresh',
|
||||
['workspace/didChangeConfiguration'] = 'workspace/didChangeConfiguration',
|
||||
['workspace/didChangeWatchedFiles'] = 'workspace/didChangeWatchedFiles',
|
||||
['workspace/didChangeWorkspaceFolders'] = 'workspace.workspaceFolders.changeNotifications',
|
||||
['workspace/didCreateFiles'] = 'workspace.fileOperations.didCreate',
|
||||
['workspace/didDeleteFiles'] = 'workspace.fileOperations.didDelete',
|
||||
['workspace/didRenameFiles'] = 'workspace.fileOperations.didRename',
|
||||
['workspace/executeCommand'] = 'executeCommandProvider',
|
||||
['workspace/foldingRange/refresh'] = 'workspace/foldingRange/refresh',
|
||||
['workspace/inlayHint/refresh'] = 'workspace/inlayHint/refresh',
|
||||
['workspace/inlineValue/refresh'] = 'workspace/inlineValue/refresh',
|
||||
['workspace/semanticTokens/refresh'] = 'workspace/semanticTokens/refresh',
|
||||
['workspace/symbol'] = 'workspaceSymbolProvider',
|
||||
['workspace/textDocumentContent'] = 'workspace.textDocumentContent',
|
||||
['workspace/textDocumentContent/refresh'] = 'workspace/textDocumentContent/refresh',
|
||||
['workspace/willCreateFiles'] = 'workspace.fileOperations.willCreate',
|
||||
['workspace/willDeleteFiles'] = 'workspace.fileOperations.willDelete',
|
||||
['workspace/willRenameFiles'] = 'workspace.fileOperations.willRename',
|
||||
['workspace/workspaceFolders'] = 'workspace.workspaceFolders',
|
||||
}
|
||||
-- stylua: ignore end
|
||||
|
||||
-- stylua: ignore start
|
||||
-- Generated by gen_lsp.lua, keep at end of file.
|
||||
--- Maps method names to the required server capability
|
||||
@@ -1389,39 +1490,25 @@ protocol._method_supports_dynamic_registration = {
|
||||
-- stylua: ignore start
|
||||
-- Generated by gen_lsp.lua, keep at end of file.
|
||||
protocol._method_supports_static_registration = {
|
||||
['textDocument/codeAction'] = true,
|
||||
['textDocument/codeLens'] = true,
|
||||
['callHierarchy/incomingCalls'] = true,
|
||||
['callHierarchy/outgoingCalls'] = true,
|
||||
['textDocument/colorPresentation'] = true,
|
||||
['textDocument/completion'] = true,
|
||||
['textDocument/declaration'] = true,
|
||||
['textDocument/definition'] = true,
|
||||
['textDocument/diagnostic'] = true,
|
||||
['textDocument/didChange'] = true,
|
||||
['textDocument/documentColor'] = true,
|
||||
['textDocument/documentHighlight'] = true,
|
||||
['textDocument/documentLink'] = true,
|
||||
['textDocument/documentSymbol'] = true,
|
||||
['textDocument/foldingRange'] = true,
|
||||
['textDocument/formatting'] = true,
|
||||
['textDocument/hover'] = true,
|
||||
['textDocument/implementation'] = true,
|
||||
['textDocument/inlayHint'] = true,
|
||||
['textDocument/inlineCompletion'] = true,
|
||||
['textDocument/inlineValue'] = true,
|
||||
['textDocument/linkedEditingRange'] = true,
|
||||
['textDocument/moniker'] = true,
|
||||
['textDocument/onTypeFormatting'] = true,
|
||||
['textDocument/prepareCallHierarchy'] = true,
|
||||
['textDocument/prepareTypeHierarchy'] = true,
|
||||
['textDocument/rangeFormatting'] = true,
|
||||
['textDocument/references'] = true,
|
||||
['textDocument/rename'] = true,
|
||||
['textDocument/selectionRange'] = true,
|
||||
['textDocument/semanticTokens/full'] = true,
|
||||
['textDocument/signatureHelp'] = true,
|
||||
['textDocument/semanticTokens/full/delta'] = true,
|
||||
['textDocument/typeDefinition'] = true,
|
||||
['workspace/executeCommand'] = true,
|
||||
['workspace/symbol'] = true,
|
||||
['workspace/textDocumentContent'] = true,
|
||||
}
|
||||
-- stylua: ignore end
|
||||
|
||||
|
||||
@@ -134,12 +134,69 @@ local function compare_method(a, b)
|
||||
return to_luaname(a.method) < to_luaname(b.method)
|
||||
end
|
||||
|
||||
--- @param item vim._gen_lsp.Request|vim._gen_lsp.Notification
|
||||
--- @return string?
|
||||
local function registration_provider(item)
|
||||
local server_capability = item.serverCapability
|
||||
if not server_capability then
|
||||
return nil
|
||||
end
|
||||
if vim.startswith(server_capability, 'workspace.') then
|
||||
return server_capability
|
||||
end
|
||||
return server_capability:match('^[^%.]+')
|
||||
end
|
||||
|
||||
---@param structures table<string,vim._gen_lsp.Structure>
|
||||
---@param type vim._gen_lsp.Type
|
||||
---@param seen? table<string,true>
|
||||
---@return boolean
|
||||
local function supports_static_registration(structures, type, seen)
|
||||
if type.kind == 'reference' then
|
||||
if type.name == 'StaticRegistrationOptions' then
|
||||
return true
|
||||
end
|
||||
|
||||
seen = seen or {}
|
||||
if seen[type.name] then
|
||||
return false
|
||||
end
|
||||
seen[type.name] = true
|
||||
|
||||
local structure = structures[type.name]
|
||||
if not structure then
|
||||
return false
|
||||
end
|
||||
local bases = {}
|
||||
vim.list_extend(bases, structure.extends or {})
|
||||
vim.list_extend(bases, structure.mixins or {})
|
||||
for _, base in ipairs(bases) do
|
||||
if supports_static_registration(structures, base, seen) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
elseif type.kind == 'and' or type.kind == 'or' then
|
||||
for _, item in ipairs(type.items) do
|
||||
if supports_static_registration(structures, item, seen) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
---@param protocol vim._gen_lsp.Protocol
|
||||
local function write_to_vim_protocol(protocol)
|
||||
local all = {} --- @type (vim._gen_lsp.Request|vim._gen_lsp.Notification)[]
|
||||
vim.list_extend(all, protocol.notifications)
|
||||
vim.list_extend(all, protocol.requests)
|
||||
|
||||
local structures = {} --- @type table<string,vim._gen_lsp.Structure>
|
||||
for _, structure in ipairs(protocol.structures) do
|
||||
structures[structure.name] = structure
|
||||
end
|
||||
|
||||
table.sort(all, compare_method)
|
||||
table.sort(protocol.requests, compare_method)
|
||||
table.sort(protocol.notifications, compare_method)
|
||||
@@ -224,18 +281,20 @@ local function write_to_vim_protocol(protocol)
|
||||
'-- stylua: ignore start',
|
||||
'-- Generated by gen_lsp.lua, keep at end of file.',
|
||||
'--- Maps method names to the required client capability',
|
||||
'---TODO: also has workspace/* items because spec lacks a top-level "workspaceProvider"',
|
||||
'protocol._provider_to_client_registration = {',
|
||||
})
|
||||
|
||||
local providers = {} --- @type table<string, string>
|
||||
for _, item in ipairs(all) do
|
||||
local base_provider = item.serverCapability and item.serverCapability:match('^[^%.]+')
|
||||
if item.registrationOptions and not providers[base_provider] and item.clientCapability then
|
||||
if item.clientCapability == item.serverCapability then
|
||||
base_provider = nil
|
||||
local provider = registration_provider(item)
|
||||
if item.registrationOptions and not providers[provider] and item.clientCapability then
|
||||
if
|
||||
item.clientCapability == item.serverCapability
|
||||
and not (item.serverCapability and vim.startswith(item.serverCapability, 'workspace.'))
|
||||
then
|
||||
provider = nil
|
||||
end
|
||||
local key = base_provider or item.method
|
||||
local key = provider or item.method
|
||||
providers[key] = item.clientCapability
|
||||
end
|
||||
end
|
||||
@@ -258,6 +317,22 @@ local function write_to_vim_protocol(protocol)
|
||||
output[#output + 1] = '}'
|
||||
output[#output + 1] = '-- stylua: ignore end'
|
||||
|
||||
vim.list_extend(output, {
|
||||
'',
|
||||
'-- stylua: ignore start',
|
||||
'-- Generated by gen_lsp.lua, keep at end of file.',
|
||||
'--- Maps method names to dynamic/static registration providers',
|
||||
'protocol._request_name_to_registration_provider = {',
|
||||
})
|
||||
|
||||
for _, item in ipairs(all) do
|
||||
local provider = registration_provider(item) or item.method
|
||||
output[#output + 1] = (" ['%s'] = '%s',"):format(item.method, provider)
|
||||
end
|
||||
|
||||
output[#output + 1] = '}'
|
||||
output[#output + 1] = '-- stylua: ignore end'
|
||||
|
||||
vim.list_extend(output, {
|
||||
'',
|
||||
'-- stylua: ignore start',
|
||||
@@ -334,10 +409,25 @@ local function write_to_vim_protocol(protocol)
|
||||
'protocol._method_supports_static_registration = {',
|
||||
})
|
||||
|
||||
local static_registration_providers = {} --- @type table<string,true>
|
||||
for _, item in ipairs(all) do
|
||||
if
|
||||
item.registrationOptions
|
||||
and (item.serverCapability and not item.serverCapability:find('%.'))
|
||||
and item.serverCapability
|
||||
and supports_static_registration(structures, item.registrationOptions)
|
||||
then
|
||||
static_registration_providers[registration_provider(item)] = true
|
||||
end
|
||||
end
|
||||
|
||||
for _, item in ipairs(all) do
|
||||
local provider = registration_provider(item)
|
||||
local has_static_registration = item.registrationOptions
|
||||
and supports_static_registration(structures, item.registrationOptions)
|
||||
if
|
||||
item.serverCapability
|
||||
and static_registration_providers[provider]
|
||||
and (has_static_registration or item.serverCapability == provider)
|
||||
then
|
||||
output[#output + 1] = (" ['%s'] = %s,"):format(item.method, true)
|
||||
end
|
||||
|
||||
@@ -998,6 +998,8 @@ describe('LSP', function()
|
||||
eq(true, client:supports_method('textDocument/hover'))
|
||||
eq(false, client:supports_method('textDocument/definition'))
|
||||
|
||||
eq(false, client:supports_method('workspace/didCreateFiles'))
|
||||
|
||||
-- Self-mapped methods do not have a related server capability and should be assumed
|
||||
-- to be supported.
|
||||
eq(true, client:supports_method('shutdown'))
|
||||
@@ -3198,6 +3200,14 @@ describe('LSP', function()
|
||||
didChangeConfiguration = {
|
||||
dynamicRegistration = true,
|
||||
},
|
||||
fileOperations = {
|
||||
didCreate = {
|
||||
dynamicRegistration = true,
|
||||
},
|
||||
},
|
||||
textDocumentContent = {
|
||||
dynamicRegistration = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
||||
@@ -3381,6 +3391,42 @@ describe('LSP', function()
|
||||
}, { client_id = client_id })
|
||||
check('workspace/didChangeConfiguration', nil, 'section')
|
||||
|
||||
check('workspace/didCreateFiles')
|
||||
vim.lsp.handlers['client/registerCapability'](nil, {
|
||||
registrations = {
|
||||
{
|
||||
id = 'didCreateFiles-id',
|
||||
method = 'workspace/didCreateFiles',
|
||||
registerOptions = {
|
||||
label = 'did-create-files',
|
||||
filters = {
|
||||
{
|
||||
scheme = 'file',
|
||||
pattern = {
|
||||
glob = '**/*.foo',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, { client_id = client_id })
|
||||
check('workspace/didCreateFiles', nil, 'label')
|
||||
|
||||
check('workspace/textDocumentContent')
|
||||
vim.lsp.handlers['client/registerCapability'](nil, {
|
||||
registrations = {
|
||||
{
|
||||
id = 'textDocumentContent-id',
|
||||
method = 'workspace/textDocumentContent',
|
||||
registerOptions = {
|
||||
schemes = { 'git', 'untitled' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}, { client_id = client_id })
|
||||
check('workspace/textDocumentContent', nil, 'schemes')
|
||||
|
||||
vim.lsp.handlers['client/registerCapability'](nil, {
|
||||
registrations = {
|
||||
{
|
||||
@@ -3416,7 +3462,7 @@ describe('LSP', function()
|
||||
return result
|
||||
end)
|
||||
|
||||
eq(25, #result)
|
||||
eq(29, #result)
|
||||
eq({ method = 'textDocument/formatting', supported = false }, result[1])
|
||||
eq({ method = 'textDocument/formatting', supported = true, fname = tmpfile }, result[2])
|
||||
eq({ method = 'textDocument/rangeFormatting', supported = true }, result[3])
|
||||
@@ -3451,14 +3497,25 @@ describe('LSP', function()
|
||||
{ method = 'workspace/didChangeConfiguration', supported = true, cap = { 'dummy-section' } },
|
||||
result[21]
|
||||
)
|
||||
eq({ method = 'workspace/didCreateFiles', supported = false }, result[22])
|
||||
eq(
|
||||
{ method = 'workspace/didCreateFiles', supported = true, cap = { 'did-create-files' } },
|
||||
result[23]
|
||||
)
|
||||
eq({ method = 'workspace/textDocumentContent', supported = false }, result[24])
|
||||
eq({
|
||||
method = 'workspace/textDocumentContent',
|
||||
supported = true,
|
||||
cap = { { 'git', 'untitled' } },
|
||||
}, result[25])
|
||||
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])
|
||||
}, result[26])
|
||||
eq({ method = 'unknown-method-2', supported = true }, result[27])
|
||||
eq({ method = 'unknown-method-2', supported = false }, result[28])
|
||||
eq({ method = 'unknown-method-2', supported = true, fname = tmpfile }, result[29])
|
||||
end)
|
||||
|
||||
it('identifies client dynamic registration capability', function()
|
||||
@@ -3523,6 +3580,12 @@ describe('LSP', function()
|
||||
identifier = 'diag-ident-static',
|
||||
workspaceDiagnostics = true,
|
||||
},
|
||||
workspace = {
|
||||
textDocumentContent = {
|
||||
id = 'text-document-content-registration',
|
||||
schemes = { 'git', 'untitled' },
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -3585,6 +3648,35 @@ describe('LSP', function()
|
||||
return result
|
||||
end)
|
||||
)
|
||||
|
||||
eq(
|
||||
{
|
||||
{
|
||||
id = 'text-document-content-registration',
|
||||
method = 'workspace/textDocumentContent',
|
||||
registerOptions = {
|
||||
id = 'text-document-content-registration',
|
||||
schemes = { 'git', 'untitled' },
|
||||
},
|
||||
},
|
||||
},
|
||||
sort_method(exec_lua(function()
|
||||
local client = assert(vim.lsp.get_client_by_id(client_id))
|
||||
return client.dynamic_capabilities:get('workspace.textDocumentContent')
|
||||
end))
|
||||
)
|
||||
|
||||
eq(
|
||||
{ { 'git', 'untitled' } },
|
||||
exec_lua(function()
|
||||
local client = assert(vim.lsp.get_client_by_id(client_id))
|
||||
local result = {}
|
||||
client:_provider_foreach('workspace/textDocumentContent', function(cap)
|
||||
table.insert(result, cap.schemes)
|
||||
end)
|
||||
return result
|
||||
end)
|
||||
)
|
||||
end)
|
||||
end)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user