From 13cf80deefe78a362c2c274201c592197922ce9e Mon Sep 17 00:00:00 2001 From: Tristan Knight Date: Mon, 16 Feb 2026 21:20:34 +0000 Subject: [PATCH] fix(lsp): map all LSP methods to server capabilities (#37910) --- runtime/lua/vim/lsp/protocol.lua | 39 ++++++++++++++++++++++++++++- src/gen/gen_lsp.lua | 18 ++++--------- test/functional/plugin/lsp_spec.lua | 24 +++++++++++++++++- 3 files changed, 66 insertions(+), 15 deletions(-) diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index b9a77c6689..268a117109 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -1222,12 +1222,30 @@ protocol._provider_to_client_registration = { -- stylua: ignore start -- Generated by gen_lsp.lua, keep at end of file. --- Maps method names to the required server capability +-- A server capability equal to the method means there is no related server capability protocol._request_name_to_server_capability = { + ['callHierarchy/incomingCalls'] = { 'callHierarchy/incomingCalls' }, + ['callHierarchy/outgoingCalls'] = { 'callHierarchy/outgoingCalls' }, + ['client/registerCapability'] = { 'client/registerCapability' }, + ['client/unregisterCapability'] = { 'client/unregisterCapability' }, ['codeAction/resolve'] = { 'codeActionProvider', 'resolveProvider' }, ['codeLens/resolve'] = { 'codeLensProvider', 'resolveProvider' }, ['completionItem/resolve'] = { 'completionProvider', 'resolveProvider' }, ['documentLink/resolve'] = { 'documentLinkProvider', 'resolveProvider' }, + ['$/cancelRequest'] = { '$/cancelRequest' }, + ['$/logTrace'] = { '$/logTrace' }, + ['$/progress'] = { '$/progress' }, + ['$/setTrace'] = { '$/setTrace' }, + ['exit'] = { 'exit' }, + ['initialize'] = { 'initialize' }, + ['initialized'] = { 'initialized' }, ['inlayHint/resolve'] = { 'inlayHintProvider', 'resolveProvider' }, + ['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' }, @@ -1256,6 +1274,7 @@ protocol._request_name_to_server_capability = { ['textDocument/prepareCallHierarchy'] = { 'callHierarchyProvider' }, ['textDocument/prepareRename'] = { 'renameProvider', 'prepareProvider' }, ['textDocument/prepareTypeHierarchy'] = { 'typeHierarchyProvider' }, + ['textDocument/publishDiagnostics'] = { 'textDocument/publishDiagnostics' }, ['textDocument/rangeFormatting'] = { 'documentRangeFormattingProvider' }, ['textDocument/rangesFormatting'] = { 'documentRangeFormattingProvider', 'rangesSupport' }, ['textDocument/references'] = { 'referencesProvider' }, @@ -1268,21 +1287,39 @@ protocol._request_name_to_server_capability = { ['textDocument/typeDefinition'] = { 'typeDefinitionProvider' }, ['textDocument/willSave'] = { 'textDocumentSync', 'willSave' }, ['textDocument/willSaveWaitUntil'] = { 'textDocumentSync', 'willSaveWaitUntil' }, + ['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', 'resolveProvider' }, + ['workspace/applyEdit'] = { 'workspace/applyEdit' }, + ['workspace/codeLens/refresh'] = { 'workspace/codeLens/refresh' }, + ['workspace/configuration'] = { 'workspace/configuration' }, ['workspace/diagnostic'] = { 'diagnosticProvider', 'workspaceDiagnostics' }, + ['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' }, ['textDocument/semanticTokens'] = { 'semanticTokensProvider' }, - ['workspace/didChangeWatchedFiles'] = { 'workspace/didChangeWatchedFiles' }, } -- stylua: ignore end diff --git a/src/gen/gen_lsp.lua b/src/gen/gen_lsp.lua index c4af75f535..02551a6203 100755 --- a/src/gen/gen_lsp.lua +++ b/src/gen/gen_lsp.lua @@ -263,16 +263,15 @@ 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 server capability', + '-- A server capability equal to the method means there is no related server capability', 'protocol._request_name_to_server_capability = {', }) for _, item in ipairs(all) do - if item.serverCapability then - output[#output + 1] = (" ['%s'] = { %s },"):format( - item.method, - "'" .. item.serverCapability:gsub('%.', "', '") .. "'" - ) - end + output[#output + 1] = (" ['%s'] = { %s },"):format( + item.method, + "'" .. (item.serverCapability or item.method):gsub('%.', "', '") .. "'" + ) end ---@type table @@ -299,13 +298,6 @@ local function write_to_vim_protocol(protocol) ) end - --- workspace/didChangeWatchedFiles has no server capability but we need to map it for - --- registration - output[#output + 1] = (" ['%s'] = { '%s' },"):format( - 'workspace/didChangeWatchedFiles', - 'workspace/didChangeWatchedFiles' - ) - output[#output + 1] = '}' output[#output + 1] = '-- stylua: ignore end' diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 7abad1757f..45a2ad33fc 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -5644,6 +5644,9 @@ describe('LSP', function() didChangeWatchedFiles = { dynamicRegistration = true, }, + didChangeConfiguration = { + dynamicRegistration = true, + }, }, }, })) @@ -5806,10 +5809,24 @@ describe('LSP', function() }, { client_id = client_id }) check('workspace/didChangeWorkspaceFolders') + check('workspace/didChangeConfiguration') + vim.lsp.handlers['client/registerCapability'](nil, { + registrations = { + { + id = 'didChangeConfiguration-id', + method = 'workspace/didChangeConfiguration', + registerOptions = { + section = 'dummy-section', + }, + }, + }, + }, { client_id = client_id }) + check('workspace/didChangeConfiguration', nil, 'section') + return result end) - eq(19, #result) + eq(21, #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]) @@ -5839,6 +5856,11 @@ describe('LSP', function() eq({ method = 'codeAction/resolve', supported = true }, result[17]) eq({ method = 'workspace/didChangeWorkspaceFolders', supported = false }, result[18]) eq({ method = 'workspace/didChangeWorkspaceFolders', supported = true }, result[19]) + eq({ method = 'workspace/didChangeConfiguration', supported = false }, result[20]) + eq( + { method = 'workspace/didChangeConfiguration', supported = true, cap = { 'dummy-section' } }, + result[21] + ) end) it('identifies client dynamic registration capability', function()