aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/lsp
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/lua/vim/lsp')
-rw-r--r--runtime/lua/vim/lsp/_dynamic.lua109
-rw-r--r--runtime/lua/vim/lsp/buf.lua6
-rw-r--r--runtime/lua/vim/lsp/handlers.lua28
-rw-r--r--runtime/lua/vim/lsp/protocol.lua13
-rw-r--r--runtime/lua/vim/lsp/types.lua28
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[]}