mirror of
https://github.com/neovim/neovim.git
synced 2024-12-19 10:45:16 -07:00
refactor(lsp): fold in dynamic_registration code into the client
Problem: Capability register logic is spread across 3 files. Solution: - Consolidate (and simplify) logic into the client. - Teach client.supports_method about resolve methods
This commit is contained in:
parent
e2ad251c8d
commit
989a37a594
@ -968,7 +968,7 @@ Lua module: vim.lsp.client *lsp-client*
|
|||||||
request before sending kill -15. If set to
|
request before sending kill -15. If set to
|
||||||
false, nvim exits immediately after sending
|
false, nvim exits immediately after sending
|
||||||
the "shutdown" request to the server.
|
the "shutdown" request to the server.
|
||||||
• {get_language_id} (`fun(bufnr: integer, filetype: string): string`)
|
• {get_language_id} (`fun(bufnr: integer, filetype?: string): string`)
|
||||||
• {capabilities} (`lsp.ClientCapabilities`) The capabilities
|
• {capabilities} (`lsp.ClientCapabilities`) The capabilities
|
||||||
provided by the client (editor or tool)
|
provided by the client (editor or tool)
|
||||||
• {dynamic_capabilities} (`lsp.DynamicCapabilities`)
|
• {dynamic_capabilities} (`lsp.DynamicCapabilities`)
|
||||||
@ -1089,8 +1089,9 @@ Lua module: vim.lsp.client *lsp-client*
|
|||||||
`initialize` in the LSP spec.
|
`initialize` in the LSP spec.
|
||||||
• {name}? (`string`, default: client-id) Name in log
|
• {name}? (`string`, default: client-id) Name in log
|
||||||
messages.
|
messages.
|
||||||
• {get_language_id}? (`fun(bufnr: integer, filetype: string): string`)
|
• {get_language_id}? (`fun(bufnr: integer, filetype?: string): string`)
|
||||||
Language ID as string. Defaults to the filetype.
|
Language ID as string. Defaults to the buffer
|
||||||
|
filetype.
|
||||||
• {offset_encoding}? (`'utf-8'|'utf-16'|'utf-32'`) The encoding that
|
• {offset_encoding}? (`'utf-8'|'utf-16'|'utf-32'`) The encoding that
|
||||||
the LSP server expects. Client does not verify
|
the LSP server expects. Client does not verify
|
||||||
this is correct.
|
this is correct.
|
||||||
|
@ -3,7 +3,6 @@ local validate = vim.validate
|
|||||||
|
|
||||||
local lsp = vim._defer_require('vim.lsp', {
|
local lsp = vim._defer_require('vim.lsp', {
|
||||||
_changetracking = ..., --- @module 'vim.lsp._changetracking'
|
_changetracking = ..., --- @module 'vim.lsp._changetracking'
|
||||||
_dynamic = ..., --- @module 'vim.lsp._dynamic'
|
|
||||||
_snippet_grammar = ..., --- @module 'vim.lsp._snippet_grammar'
|
_snippet_grammar = ..., --- @module 'vim.lsp._snippet_grammar'
|
||||||
_tagfunc = ..., --- @module 'vim.lsp._tagfunc'
|
_tagfunc = ..., --- @module 'vim.lsp._tagfunc'
|
||||||
_watchfiles = ..., --- @module 'vim.lsp._watchfiles'
|
_watchfiles = ..., --- @module 'vim.lsp._watchfiles'
|
||||||
@ -31,6 +30,13 @@ local changetracking = lsp._changetracking
|
|||||||
---@nodoc
|
---@nodoc
|
||||||
lsp.rpc_response_error = lsp.rpc.rpc_response_error
|
lsp.rpc_response_error = lsp.rpc.rpc_response_error
|
||||||
|
|
||||||
|
lsp._resolve_to_request = {
|
||||||
|
[ms.codeAction_resolve] = ms.textDocument_codeAction,
|
||||||
|
[ms.codeLens_resolve] = ms.textDocument_codeLens,
|
||||||
|
[ms.documentLink_resolve] = ms.textDocument_documentLink,
|
||||||
|
[ms.inlayHint_resolve] = ms.textDocument_inlayHint,
|
||||||
|
}
|
||||||
|
|
||||||
-- maps request name to the required server_capability in the client.
|
-- maps request name to the required server_capability in the client.
|
||||||
lsp._request_name_to_capability = {
|
lsp._request_name_to_capability = {
|
||||||
[ms.callHierarchy_incomingCalls] = { 'callHierarchyProvider' },
|
[ms.callHierarchy_incomingCalls] = { 'callHierarchyProvider' },
|
||||||
|
@ -1,110 +0,0 @@
|
|||||||
local glob = vim.glob
|
|
||||||
|
|
||||||
--- @class lsp.DynamicCapabilities
|
|
||||||
--- @field capabilities table<string, lsp.Registration[]>
|
|
||||||
--- @field client_id number
|
|
||||||
local M = {}
|
|
||||||
|
|
||||||
--- @param client_id number
|
|
||||||
--- @return lsp.DynamicCapabilities
|
|
||||||
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.capabilities, unpack(vim.split(method, '/')))
|
|
||||||
return type(capability) == 'table' and capability.dynamicRegistration
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param registrations lsp.Registration[]
|
|
||||||
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[]
|
|
||||||
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: integer?}
|
|
||||||
--- @return lsp.Registration? (table|nil) the registration if found
|
|
||||||
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 self:match(opts.bufnr, documentSelector) then
|
|
||||||
return reg
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param method string
|
|
||||||
--- @param opts? {bufnr: integer?}
|
|
||||||
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 client = vim.lsp.get_client_by_id(self.client_id)
|
|
||||||
if not client then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
local language = client.get_language_id(bufnr, 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 language ~= 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 glob.to_lpeg(filter.pattern):match(fname) then
|
|
||||||
matches = false
|
|
||||||
end
|
|
||||||
if matches then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
@ -44,9 +44,8 @@ M._poll_exclude_pattern = glob.to_lpeg('**/.git/{objects,subtree-cache}/**')
|
|||||||
--- Registers the workspace/didChangeWatchedFiles capability dynamically.
|
--- Registers the workspace/didChangeWatchedFiles capability dynamically.
|
||||||
---
|
---
|
||||||
---@param reg lsp.Registration LSP Registration object.
|
---@param reg lsp.Registration LSP Registration object.
|
||||||
---@param ctx lsp.HandlerContext Context from the |lsp-handler|.
|
---@param client_id integer Client ID.
|
||||||
function M.register(reg, ctx)
|
function M.register(reg, client_id)
|
||||||
local client_id = ctx.client_id
|
|
||||||
local client = assert(vim.lsp.get_client_by_id(client_id), 'Client must be running')
|
local client = assert(vim.lsp.get_client_by_id(client_id), 'Client must be running')
|
||||||
-- Ill-behaved servers may not honor the client capability and try to register
|
-- Ill-behaved servers may not honor the client capability and try to register
|
||||||
-- anyway, so ignore requests when the user has opted out of the feature.
|
-- anyway, so ignore requests when the user has opted out of the feature.
|
||||||
@ -155,9 +154,8 @@ end
|
|||||||
--- Unregisters the workspace/didChangeWatchedFiles capability dynamically.
|
--- Unregisters the workspace/didChangeWatchedFiles capability dynamically.
|
||||||
---
|
---
|
||||||
---@param unreg lsp.Unregistration LSP Unregistration object.
|
---@param unreg lsp.Unregistration LSP Unregistration object.
|
||||||
---@param ctx lsp.HandlerContext Context from the |lsp-handler|.
|
---@param client_id integer Client ID.
|
||||||
function M.unregister(unreg, ctx)
|
function M.unregister(unreg, client_id)
|
||||||
local client_id = ctx.client_id
|
|
||||||
local client_cancels = cancels[client_id]
|
local client_cancels = cancels[client_id]
|
||||||
local reg_cancels = client_cancels[unreg.id]
|
local reg_cancels = client_cancels[unreg.id]
|
||||||
while #reg_cancels > 0 do
|
while #reg_cancels > 0 do
|
||||||
|
@ -1131,12 +1131,7 @@ local function on_code_action_results(results, opts)
|
|||||||
local action = choice.action
|
local action = choice.action
|
||||||
local bufnr = assert(choice.ctx.bufnr, 'Must have buffer number')
|
local bufnr = assert(choice.ctx.bufnr, 'Must have buffer number')
|
||||||
|
|
||||||
local reg = client.dynamic_capabilities:get(ms.textDocument_codeAction, { bufnr = bufnr })
|
if not action.edit and client.supports_method(ms.codeAction_resolve) then
|
||||||
|
|
||||||
local supports_resolve = vim.tbl_get(reg or {}, 'registerOptions', 'resolveProvider')
|
|
||||||
or client.supports_method(ms.codeAction_resolve)
|
|
||||||
|
|
||||||
if not action.edit and client and supports_resolve then
|
|
||||||
client.request(ms.codeAction_resolve, action, function(err, resolved_action)
|
client.request(ms.codeAction_resolve, action, function(err, resolved_action)
|
||||||
if err then
|
if err then
|
||||||
if action.command then
|
if action.command then
|
||||||
|
@ -91,8 +91,8 @@ local validate = vim.validate
|
|||||||
--- (default: client-id)
|
--- (default: client-id)
|
||||||
--- @field name? string
|
--- @field name? string
|
||||||
---
|
---
|
||||||
--- Language ID as string. Defaults to the filetype.
|
--- Language ID as string. Defaults to the buffer filetype.
|
||||||
--- @field get_language_id? fun(bufnr: integer, filetype: string): string
|
--- @field get_language_id? fun(bufnr: integer, filetype?: string): string
|
||||||
---
|
---
|
||||||
--- The encoding that the LSP server expects. Client does not verify this is correct.
|
--- The encoding that the LSP server expects. Client does not verify this is correct.
|
||||||
--- @field offset_encoding? 'utf-8'|'utf-16'|'utf-32'
|
--- @field offset_encoding? 'utf-8'|'utf-16'|'utf-32'
|
||||||
@ -212,10 +212,11 @@ local validate = vim.validate
|
|||||||
--- A table with flags for the client. The current (experimental) flags are:
|
--- A table with flags for the client. The current (experimental) flags are:
|
||||||
--- @field flags vim.lsp.Client.Flags
|
--- @field flags vim.lsp.Client.Flags
|
||||||
---
|
---
|
||||||
--- @field get_language_id fun(bufnr: integer, filetype: string): string
|
--- @field get_language_id fun(bufnr: integer, filetype?: string): string
|
||||||
---
|
---
|
||||||
--- The capabilities provided by the client (editor or tool)
|
--- The capabilities provided by the client (editor or tool)
|
||||||
--- @field capabilities lsp.ClientCapabilities
|
--- @field capabilities lsp.ClientCapabilities
|
||||||
|
--- @field private registrations table<string,lsp.Registration[]>
|
||||||
--- @field dynamic_capabilities lsp.DynamicCapabilities
|
--- @field dynamic_capabilities lsp.DynamicCapabilities
|
||||||
---
|
---
|
||||||
--- Sends a request to the server.
|
--- Sends a request to the server.
|
||||||
@ -339,10 +340,10 @@ end
|
|||||||
--- By default, get_language_id just returns the exact filetype it is passed.
|
--- By default, get_language_id just returns the exact filetype it is passed.
|
||||||
--- It is possible to pass in something that will calculate a different filetype,
|
--- It is possible to pass in something that will calculate a different filetype,
|
||||||
--- to be sent by the client.
|
--- to be sent by the client.
|
||||||
--- @param _bufnr integer
|
--- @param bufnr integer
|
||||||
--- @param filetype string
|
--- @param filetype? string
|
||||||
local function default_get_language_id(_bufnr, filetype)
|
local function default_get_language_id(bufnr, filetype)
|
||||||
return filetype
|
return filetype or vim.bo[bufnr].filetype
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Validates a client configuration as given to |vim.lsp.start_client()|.
|
--- Validates a client configuration as given to |vim.lsp.start_client()|.
|
||||||
@ -403,18 +404,16 @@ local function get_name(id, config)
|
|||||||
return tostring(id)
|
return tostring(id)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param workspace_folders lsp.WorkspaceFolder[]?
|
--- @param workspace_folders string|lsp.WorkspaceFolder[]?
|
||||||
--- @param root_dir string?
|
|
||||||
--- @return lsp.WorkspaceFolder[]?
|
--- @return lsp.WorkspaceFolder[]?
|
||||||
local function get_workspace_folders(workspace_folders, root_dir)
|
local function get_workspace_folders(workspace_folders)
|
||||||
if workspace_folders then
|
if type(workspace_folders) == 'table' then
|
||||||
return workspace_folders
|
return workspace_folders
|
||||||
end
|
elseif type(workspace_folders) == 'string' then
|
||||||
if root_dir then
|
|
||||||
return {
|
return {
|
||||||
{
|
{
|
||||||
uri = vim.uri_from_fname(root_dir),
|
uri = vim.uri_from_fname(workspace_folders),
|
||||||
name = root_dir,
|
name = workspace_folders,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
@ -451,13 +450,13 @@ function Client.create(config)
|
|||||||
requests = {},
|
requests = {},
|
||||||
attached_buffers = {},
|
attached_buffers = {},
|
||||||
server_capabilities = {},
|
server_capabilities = {},
|
||||||
dynamic_capabilities = lsp._dynamic.new(id),
|
registrations = {},
|
||||||
commands = config.commands or {},
|
commands = config.commands or {},
|
||||||
settings = config.settings or {},
|
settings = config.settings or {},
|
||||||
flags = config.flags or {},
|
flags = config.flags or {},
|
||||||
get_language_id = config.get_language_id or default_get_language_id,
|
get_language_id = config.get_language_id or default_get_language_id,
|
||||||
capabilities = config.capabilities or lsp.protocol.make_client_capabilities(),
|
capabilities = config.capabilities or lsp.protocol.make_client_capabilities(),
|
||||||
workspace_folders = get_workspace_folders(config.workspace_folders, config.root_dir),
|
workspace_folders = get_workspace_folders(config.workspace_folders or config.root_dir),
|
||||||
root_dir = config.root_dir,
|
root_dir = config.root_dir,
|
||||||
_before_init_cb = config.before_init,
|
_before_init_cb = config.before_init,
|
||||||
_on_init_cbs = ensure_list(config.on_init),
|
_on_init_cbs = ensure_list(config.on_init),
|
||||||
@ -478,6 +477,28 @@ function Client.create(config)
|
|||||||
messages = { name = name, messages = {}, progress = {}, status = {} },
|
messages = { name = name, messages = {}, progress = {}, status = {} },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
--- @class lsp.DynamicCapabilities
|
||||||
|
--- @nodoc
|
||||||
|
self.dynamic_capabilities = {
|
||||||
|
capabilities = self.registrations,
|
||||||
|
client_id = id,
|
||||||
|
register = function(_, registrations)
|
||||||
|
return self:_register_dynamic(registrations)
|
||||||
|
end,
|
||||||
|
unregister = function(_, unregistrations)
|
||||||
|
return self:_unregister_dynamic(unregistrations)
|
||||||
|
end,
|
||||||
|
get = function(_, method, opts)
|
||||||
|
return self:_get_registration(method, opts and opts.bufnr)
|
||||||
|
end,
|
||||||
|
supports_registration = function(_, method)
|
||||||
|
return self:_supports_registration(method)
|
||||||
|
end,
|
||||||
|
supports = function(_, method, opts)
|
||||||
|
return self:_get_registration(method, opts and opts.bufnr) ~= nil
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
self.request = method_wrapper(self, Client._request)
|
self.request = method_wrapper(self, Client._request)
|
||||||
self.request_sync = method_wrapper(self, Client._request_sync)
|
self.request_sync = method_wrapper(self, Client._request_sync)
|
||||||
self.notify = method_wrapper(self, Client._notify)
|
self.notify = method_wrapper(self, Client._notify)
|
||||||
@ -846,6 +867,100 @@ function Client:_stop(force)
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get options for a method that is registered dynamically.
|
||||||
|
--- @param method string
|
||||||
|
function Client:_supports_registration(method)
|
||||||
|
local capability = vim.tbl_get(self.capabilities, unpack(vim.split(method, '/')))
|
||||||
|
return type(capability) == 'table' and capability.dynamicRegistration
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @private
|
||||||
|
--- @param registrations lsp.Registration[]
|
||||||
|
function Client:_register_dynamic(registrations)
|
||||||
|
-- remove duplicates
|
||||||
|
self:_unregister_dynamic(registrations)
|
||||||
|
for _, reg in ipairs(registrations) do
|
||||||
|
local method = reg.method
|
||||||
|
if not self.registrations[method] then
|
||||||
|
self.registrations[method] = {}
|
||||||
|
end
|
||||||
|
table.insert(self.registrations[method], reg)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @param registrations lsp.Registration[]
|
||||||
|
function Client:_register(registrations)
|
||||||
|
self:_register_dynamic(registrations)
|
||||||
|
|
||||||
|
local unsupported = {} --- @type string[]
|
||||||
|
|
||||||
|
for _, reg in ipairs(registrations) do
|
||||||
|
local method = reg.method
|
||||||
|
if method == ms.workspace_didChangeWatchedFiles then
|
||||||
|
vim.lsp._watchfiles.register(reg, self.id)
|
||||||
|
elseif not self:_supports_registration(method) then
|
||||||
|
unsupported[#unsupported + 1] = method
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #unsupported > 0 then
|
||||||
|
local warning_tpl = 'The language server %s triggers a registerCapability '
|
||||||
|
.. 'handler for %s despite dynamicRegistration set to false. '
|
||||||
|
.. 'Report upstream, this warning is harmless'
|
||||||
|
log.warn(string.format(warning_tpl, self.name, table.concat(unsupported, ', ')))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @private
|
||||||
|
--- @param unregistrations lsp.Unregistration[]
|
||||||
|
function Client:_unregister_dynamic(unregistrations)
|
||||||
|
for _, unreg in ipairs(unregistrations) do
|
||||||
|
local sreg = self.registrations[unreg.method]
|
||||||
|
-- Unegister dynamic capability
|
||||||
|
for i, reg in ipairs(sreg or {}) do
|
||||||
|
if reg.id == unreg.id then
|
||||||
|
table.remove(sreg, i)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @param unregistrations lsp.Unregistration[]
|
||||||
|
function Client:_unregister(unregistrations)
|
||||||
|
self:_unregister_dynamic(unregistrations)
|
||||||
|
for _, unreg in ipairs(unregistrations) do
|
||||||
|
if unreg.method == ms.workspace_didChangeWatchedFiles then
|
||||||
|
vim.lsp._watchfiles.unregister(unreg, self.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @param method string
|
||||||
|
--- @param bufnr? integer
|
||||||
|
--- @return lsp.Registration?
|
||||||
|
function Client:_get_registration(method, bufnr)
|
||||||
|
bufnr = bufnr or vim.api.nvim_get_current_buf()
|
||||||
|
for _, reg in ipairs(self.registrations[method] or {}) do
|
||||||
|
if not reg.registerOptions or not reg.registerOptions.documentSelector then
|
||||||
|
return reg
|
||||||
|
end
|
||||||
|
local documentSelector = reg.registerOptions.documentSelector
|
||||||
|
local language = self.get_language_id(bufnr)
|
||||||
|
local uri = vim.uri_from_bufnr(bufnr)
|
||||||
|
local fname = vim.uri_to_fname(uri)
|
||||||
|
for _, filter in ipairs(documentSelector) do
|
||||||
|
if
|
||||||
|
not (filter.language and language ~= filter.language)
|
||||||
|
and not (filter.scheme and not vim.startswith(uri, filter.scheme .. ':'))
|
||||||
|
and not (filter.pattern and not vim.glob.to_lpeg(filter.pattern):match(fname))
|
||||||
|
then
|
||||||
|
return reg
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--- @private
|
--- @private
|
||||||
--- Checks whether a client is stopped.
|
--- Checks whether a client is stopped.
|
||||||
---
|
---
|
||||||
@ -908,12 +1023,11 @@ function Client:_text_document_did_open_handler(bufnr)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local filetype = vim.bo[bufnr].filetype
|
|
||||||
self.notify(ms.textDocument_didOpen, {
|
self.notify(ms.textDocument_didOpen, {
|
||||||
textDocument = {
|
textDocument = {
|
||||||
version = lsp.util.buf_versions[bufnr],
|
version = lsp.util.buf_versions[bufnr],
|
||||||
uri = vim.uri_from_bufnr(bufnr),
|
uri = vim.uri_from_bufnr(bufnr),
|
||||||
languageId = self.get_language_id(bufnr, filetype),
|
languageId = self.get_language_id(bufnr),
|
||||||
text = lsp._buf_get_full_text(bufnr),
|
text = lsp._buf_get_full_text(bufnr),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -978,12 +1092,37 @@ function Client:_supports_method(method, opts)
|
|||||||
if vim.tbl_get(self.server_capabilities, unpack(required_capability)) then
|
if vim.tbl_get(self.server_capabilities, unpack(required_capability)) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
if self.dynamic_capabilities:supports_registration(method) then
|
|
||||||
return self.dynamic_capabilities:supports(method, opts)
|
local rmethod = lsp._resolve_to_request[method]
|
||||||
|
if rmethod then
|
||||||
|
if self:_supports_registration(rmethod) then
|
||||||
|
local reg = self:_get_registration(rmethod, opts and opts.bufnr)
|
||||||
|
return vim.tbl_get(reg or {}, 'registerOptions', 'resolveProvider') or false
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if self:_supports_registration(method) then
|
||||||
|
return self:_get_registration(method, opts and opts.bufnr) ~= nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get options for a method that is registered dynamically.
|
||||||
|
--- @param method string
|
||||||
|
--- @param bufnr? integer
|
||||||
|
--- @return lsp.LSPAny?
|
||||||
|
function Client:_get_registration_options(method, bufnr)
|
||||||
|
if not self:_supports_registration(method) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local reg = self:_get_registration(method, bufnr)
|
||||||
|
|
||||||
|
if reg then
|
||||||
|
return reg.registerOptions
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--- @private
|
--- @private
|
||||||
--- Handles a notification sent by an LSP server by invoking the
|
--- Handles a notification sent by an LSP server by invoking the
|
||||||
--- corresponding handler.
|
--- corresponding handler.
|
||||||
@ -1061,7 +1200,7 @@ function Client:_add_workspace_folder(dir)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local wf = assert(get_workspace_folders(nil, dir))
|
local wf = assert(get_workspace_folders(dir))
|
||||||
|
|
||||||
self:_notify(ms.workspace_didChangeWorkspaceFolders, {
|
self:_notify(ms.workspace_didChangeWorkspaceFolders, {
|
||||||
event = { added = wf, removed = {} },
|
event = { added = wf, removed = {} },
|
||||||
@ -1076,7 +1215,7 @@ end
|
|||||||
--- Remove a directory to the workspace folders.
|
--- Remove a directory to the workspace folders.
|
||||||
--- @param dir string?
|
--- @param dir string?
|
||||||
function Client:_remove_workspace_folder(dir)
|
function Client:_remove_workspace_folder(dir)
|
||||||
local wf = assert(get_workspace_folders(nil, dir))
|
local wf = assert(get_workspace_folders(dir))
|
||||||
|
|
||||||
self:_notify(ms.workspace_didChangeWorkspaceFolders, {
|
self:_notify(ms.workspace_didChangeWorkspaceFolders, {
|
||||||
event = { added = {}, removed = wf },
|
event = { added = {}, removed = wf },
|
||||||
|
@ -122,46 +122,19 @@ end
|
|||||||
--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_registerCapability
|
--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_registerCapability
|
||||||
--- @param params lsp.RegistrationParams
|
--- @param params lsp.RegistrationParams
|
||||||
RSC[ms.client_registerCapability] = function(_, params, ctx)
|
RSC[ms.client_registerCapability] = function(_, params, ctx)
|
||||||
local client_id = ctx.client_id
|
local client = assert(vim.lsp.get_client_by_id(ctx.client_id))
|
||||||
local client = assert(vim.lsp.get_client_by_id(client_id))
|
client:_register(params.registrations)
|
||||||
|
for bufnr in pairs(client.attached_buffers) do
|
||||||
client.dynamic_capabilities:register(params.registrations)
|
|
||||||
for bufnr, _ in pairs(client.attached_buffers) do
|
|
||||||
vim.lsp._set_defaults(client, bufnr)
|
vim.lsp._set_defaults(client, bufnr)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@type string[]
|
|
||||||
local unsupported = {}
|
|
||||||
for _, reg in ipairs(params.registrations) do
|
|
||||||
if reg.method == ms.workspace_didChangeWatchedFiles then
|
|
||||||
vim.lsp._watchfiles.register(reg, ctx)
|
|
||||||
elseif not client.dynamic_capabilities:supports_registration(reg.method) then
|
|
||||||
unsupported[#unsupported + 1] = reg.method
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if #unsupported > 0 then
|
|
||||||
local warning_tpl = 'The language server %s triggers a registerCapability '
|
|
||||||
.. 'handler for %s despite dynamicRegistration set to false. '
|
|
||||||
.. 'Report upstream, this warning is harmless'
|
|
||||||
local client_name = client and client.name or string.format('id=%d', client_id)
|
|
||||||
local warning = string.format(warning_tpl, client_name, table.concat(unsupported, ', '))
|
|
||||||
log.warn(warning)
|
|
||||||
end
|
|
||||||
return vim.NIL
|
return vim.NIL
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_unregisterCapability
|
--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_unregisterCapability
|
||||||
--- @param params lsp.UnregistrationParams
|
--- @param params lsp.UnregistrationParams
|
||||||
RSC[ms.client_unregisterCapability] = function(_, params, ctx)
|
RSC[ms.client_unregisterCapability] = function(_, params, ctx)
|
||||||
local client_id = ctx.client_id
|
local client = assert(vim.lsp.get_client_by_id(ctx.client_id))
|
||||||
local client = assert(vim.lsp.get_client_by_id(client_id))
|
client:_unregister(params.unregisterations)
|
||||||
client.dynamic_capabilities:unregister(params.unregisterations)
|
|
||||||
|
|
||||||
for _, unreg in ipairs(params.unregisterations) do
|
|
||||||
if unreg.method == ms.workspace_didChangeWatchedFiles then
|
|
||||||
vim.lsp._watchfiles.unregister(unreg, ctx)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return vim.NIL
|
return vim.NIL
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -173,8 +146,7 @@ RSC[ms.workspace_applyEdit] = function(_, params, ctx)
|
|||||||
'workspace/applyEdit must be called with `ApplyWorkspaceEditParams`. Server is violating the specification'
|
'workspace/applyEdit must be called with `ApplyWorkspaceEditParams`. Server is violating the specification'
|
||||||
)
|
)
|
||||||
-- TODO(ashkan) Do something more with label?
|
-- TODO(ashkan) Do something more with label?
|
||||||
local client_id = ctx.client_id
|
local client = assert(vim.lsp.get_client_by_id(ctx.client_id))
|
||||||
local client = assert(vim.lsp.get_client_by_id(client_id))
|
|
||||||
if params.label then
|
if params.label then
|
||||||
print('Workspace edit', params.label)
|
print('Workspace edit', params.label)
|
||||||
end
|
end
|
||||||
@ -196,12 +168,11 @@ end
|
|||||||
--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_configuration
|
--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_configuration
|
||||||
--- @param params lsp.ConfigurationParams
|
--- @param params lsp.ConfigurationParams
|
||||||
RSC[ms.workspace_configuration] = function(_, params, ctx)
|
RSC[ms.workspace_configuration] = function(_, params, ctx)
|
||||||
local client_id = ctx.client_id
|
local client = vim.lsp.get_client_by_id(ctx.client_id)
|
||||||
local client = vim.lsp.get_client_by_id(client_id)
|
|
||||||
if not client then
|
if not client then
|
||||||
err_message(
|
err_message(
|
||||||
'LSP[',
|
'LSP[',
|
||||||
client_id,
|
ctx.client_id,
|
||||||
'] client has shut down after sending a workspace/configuration request'
|
'] client has shut down after sending a workspace/configuration request'
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
@ -229,10 +200,9 @@ end
|
|||||||
|
|
||||||
--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_workspaceFolders
|
--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_workspaceFolders
|
||||||
RSC[ms.workspace_workspaceFolders] = function(_, _, ctx)
|
RSC[ms.workspace_workspaceFolders] = function(_, _, ctx)
|
||||||
local client_id = ctx.client_id
|
local client = vim.lsp.get_client_by_id(ctx.client_id)
|
||||||
local client = vim.lsp.get_client_by_id(client_id)
|
|
||||||
if not client then
|
if not client then
|
||||||
err_message('LSP[id=', client_id, '] client has shut down after sending the message')
|
err_message('LSP[id=', ctx.client_id, '] client has shut down after sending the message')
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
return client.workspace_folders or vim.NIL
|
return client.workspace_folders or vim.NIL
|
||||||
|
Loading…
Reference in New Issue
Block a user