diff options
Diffstat (limited to 'runtime/lua/vim/lsp')
-rw-r--r-- | runtime/lua/vim/lsp/_dynamic.lua | 109 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/buf.lua | 6 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/handlers.lua | 28 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/protocol.lua | 13 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/types.lua | 28 |
5 files changed, 168 insertions, 16 deletions
diff --git a/runtime/lua/vim/lsp/_dynamic.lua b/runtime/lua/vim/lsp/_dynamic.lua new file mode 100644 index 0000000000..04040e8e28 --- /dev/null +++ b/runtime/lua/vim/lsp/_dynamic.lua @@ -0,0 +1,109 @@ +local wf = require('vim.lsp._watchfiles') + +--- @class lsp.DynamicCapabilities +--- @field capabilities table<string, lsp.Registration[]> +--- @field client_id number +local M = {} + +--- @param client_id number +function M.new(client_id) + return setmetatable({ + capabilities = {}, + client_id = client_id, + }, { __index = M }) +end + +function M:supports_registration(method) + local client = vim.lsp.get_client_by_id(self.client_id) + if not client then + return false + end + local capability = vim.tbl_get(client.config.capabilities, unpack(vim.split(method, '/'))) + return type(capability) == 'table' and capability.dynamicRegistration +end + +--- @param registrations lsp.Registration[] +--- @private +function M:register(registrations) + -- remove duplicates + self:unregister(registrations) + for _, reg in ipairs(registrations) do + local method = reg.method + if not self.capabilities[method] then + self.capabilities[method] = {} + end + table.insert(self.capabilities[method], reg) + end +end + +--- @param unregisterations lsp.Unregistration[] +--- @private +function M:unregister(unregisterations) + for _, unreg in ipairs(unregisterations) do + local method = unreg.method + if not self.capabilities[method] then + return + end + local id = unreg.id + for i, reg in ipairs(self.capabilities[method]) do + if reg.id == id then + table.remove(self.capabilities[method], i) + break + end + end + end +end + +--- @param method string +--- @param opts? {bufnr?: number} +--- @return lsp.Registration? (table|nil) the registration if found +--- @private +function M:get(method, opts) + opts = opts or {} + opts.bufnr = opts.bufnr or vim.api.nvim_get_current_buf() + for _, reg in ipairs(self.capabilities[method] or {}) do + if not reg.registerOptions then + return reg + end + local documentSelector = reg.registerOptions.documentSelector + if not documentSelector then + return reg + end + if M.match(opts.bufnr, documentSelector) then + return reg + end + end +end + +--- @param method string +--- @param opts? {bufnr?: number} +--- @private +function M:supports(method, opts) + return self:get(method, opts) ~= nil +end + +--- @param bufnr number +--- @param documentSelector lsp.DocumentSelector +--- @private +function M.match(bufnr, documentSelector) + local ft = vim.bo[bufnr].filetype + local uri = vim.uri_from_bufnr(bufnr) + local fname = vim.uri_to_fname(uri) + for _, filter in ipairs(documentSelector) do + local matches = true + if filter.language and ft ~= filter.language then + matches = false + end + if matches and filter.scheme and not vim.startswith(uri, filter.scheme .. ':') then + matches = false + end + if matches and filter.pattern and not wf._match(filter.pattern, fname) then + matches = false + end + if matches then + return true + end + end +end + +return M diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index a307dea673..b2f202c4ba 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -683,11 +683,7 @@ local function on_code_action_results(results, ctx, options) -- local client = vim.lsp.get_client_by_id(action_tuple[1]) local action = action_tuple[2] - if - not action.edit - and client - and vim.tbl_get(client.server_capabilities, 'codeActionProvider', 'resolveProvider') - then + if not action.edit and client and client.supports_method('codeAction/resolve') then client.request('codeAction/resolve', action, function(err, resolved_action) if err then vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR) diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 8e926c4644..5346160871 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -118,22 +118,30 @@ end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_registerCapability M['client/registerCapability'] = function(_, result, ctx) - local log_unsupported = false + local client_id = ctx.client_id + ---@type lsp.Client + local client = vim.lsp.get_client_by_id(client_id) + + client.dynamic_capabilities:register(result.registrations) + for bufnr, _ in ipairs(client.attached_buffers) do + vim.lsp._set_defaults(client, bufnr) + end + + ---@type string[] + local unsupported = {} for _, reg in ipairs(result.registrations) do if reg.method == 'workspace/didChangeWatchedFiles' then require('vim.lsp._watchfiles').register(reg, ctx) - else - log_unsupported = true + elseif not client.dynamic_capabilities:supports_registration(reg.method) then + unsupported[#unsupported + 1] = reg.method end end - if log_unsupported then - local client_id = ctx.client_id + if #unsupported > 0 then local warning_tpl = 'The language server %s triggers a registerCapability ' - .. 'handler despite dynamicRegistration set to false. ' + .. 'handler for %s despite dynamicRegistration set to false. ' .. 'Report upstream, this warning is harmless' - local client = vim.lsp.get_client_by_id(client_id) local client_name = client and client.name or string.format('id=%d', client_id) - local warning = string.format(warning_tpl, client_name) + local warning = string.format(warning_tpl, client_name, table.concat(unsupported, ', ')) log.warn(warning) end return vim.NIL @@ -141,6 +149,10 @@ end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_unregisterCapability M['client/unregisterCapability'] = function(_, result, ctx) + local client_id = ctx.client_id + local client = vim.lsp.get_client_by_id(client_id) + client.dynamic_capabilities:unregister(result.unregisterations) + for _, unreg in ipairs(result.unregisterations) do if unreg.method == 'workspace/didChangeWatchedFiles' then require('vim.lsp._watchfiles').unregister(unreg, ctx) diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index a7919f12f5..a28ff407b7 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -697,7 +697,7 @@ function protocol.make_client_capabilities() didSave = true, }, codeAction = { - dynamicRegistration = false, + dynamicRegistration = true, codeActionLiteralSupport = { codeActionKind = { @@ -714,6 +714,12 @@ function protocol.make_client_capabilities() properties = { 'edit' }, }, }, + formatting = { + dynamicRegistration = true, + }, + rangeFormatting = { + dynamicRegistration = true, + }, completion = { dynamicRegistration = false, completionItem = { @@ -747,6 +753,7 @@ function protocol.make_client_capabilities() }, definition = { linkSupport = true, + dynamicRegistration = true, }, implementation = { linkSupport = true, @@ -755,7 +762,7 @@ function protocol.make_client_capabilities() linkSupport = true, }, hover = { - dynamicRegistration = false, + dynamicRegistration = true, contentFormat = { protocol.MarkupKind.Markdown, protocol.MarkupKind.PlainText }, }, signatureHelp = { @@ -790,7 +797,7 @@ function protocol.make_client_capabilities() hierarchicalDocumentSymbolSupport = true, }, rename = { - dynamicRegistration = false, + dynamicRegistration = true, prepareSupport = true, }, publishDiagnostics = { diff --git a/runtime/lua/vim/lsp/types.lua b/runtime/lua/vim/lsp/types.lua index 779f313aa7..e77e1fb63a 100644 --- a/runtime/lua/vim/lsp/types.lua +++ b/runtime/lua/vim/lsp/types.lua @@ -35,3 +35,31 @@ ---@field source string ---@field tags? lsp.DiagnosticTag[] ---@field relatedInformation DiagnosticRelatedInformation[] + +--- @class lsp.DocumentFilter +--- @field language? string +--- @field scheme? string +--- @field pattern? string + +--- @alias lsp.DocumentSelector lsp.DocumentFilter[] + +--- @alias lsp.RegisterOptions any | lsp.StaticRegistrationOptions | lsp.TextDocumentRegistrationOptions + +--- @class lsp.Registration +--- @field id string +--- @field method string +--- @field registerOptions? lsp.RegisterOptions + +--- @alias lsp.RegistrationParams {registrations: lsp.Registration[]} + +--- @class lsp.StaticRegistrationOptions +--- @field id? string + +--- @class lsp.TextDocumentRegistrationOptions +--- @field documentSelector? lsp.DocumentSelector + +--- @class lsp.Unregistration +--- @field id string +--- @field method string + +--- @alias lsp.UnregistrationParams {unregisterations: lsp.Unregistration[]} |