aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/lsp.lua
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/lua/vim/lsp.lua')
-rw-r--r--runtime/lua/vim/lsp.lua151
1 files changed, 92 insertions, 59 deletions
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 2e6ca7a0ac..d64ed0b5a3 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -50,6 +50,7 @@ lsp._request_name_to_capability = {
['textDocument/codeAction'] = { 'codeActionProvider' },
['textDocument/codeLens'] = { 'codeLensProvider' },
['codeLens/resolve'] = { 'codeLensProvider', 'resolveProvider' },
+ ['codeAction/resolve'] = { 'codeActionProvider', 'resolveProvider' },
['workspace/executeCommand'] = { 'executeCommandProvider' },
['workspace/symbol'] = { 'workspaceSymbolProvider' },
['textDocument/references'] = { 'referencesProvider' },
@@ -798,7 +799,9 @@ end
--- to the server. Entries are key-value pairs with the key
--- being the request ID while the value is a table with `type`,
--- `bufnr`, and `method` key-value pairs. `type` is either "pending"
---- for an active request, or "cancel" for a cancel request.
+--- for an active request, or "cancel" for a cancel request. It will
+--- be "complete" ephemerally while executing |LspRequest| autocmds
+--- when replies are received from the server.
---
--- - {config} (table): copy of the table that was passed by the user
--- to |vim.lsp.start_client()|.
@@ -886,6 +889,47 @@ function lsp.start(config, opts)
return client_id
end
+---@private
+-- Determines whether the given option can be set by `set_defaults`.
+local function is_empty_or_default(bufnr, option)
+ if vim.bo[bufnr][option] == '' then
+ return true
+ end
+
+ local info = vim.api.nvim_get_option_info2(option, { buf = bufnr })
+ local scriptinfo = vim.tbl_filter(function(e)
+ return e.sid == info.last_set_sid
+ end, vim.fn.getscriptinfo())
+
+ if #scriptinfo ~= 1 then
+ return false
+ end
+
+ return vim.startswith(scriptinfo[1].name, vim.fn.expand('$VIMRUNTIME'))
+end
+
+---@private
+---@param client lsp.Client
+function lsp._set_defaults(client, bufnr)
+ if
+ client.supports_method('textDocument/definition') and is_empty_or_default(bufnr, 'tagfunc')
+ then
+ vim.bo[bufnr].tagfunc = 'v:lua.vim.lsp.tagfunc'
+ end
+ if
+ client.supports_method('textDocument/completion') and is_empty_or_default(bufnr, 'omnifunc')
+ then
+ vim.bo[bufnr].omnifunc = 'v:lua.vim.lsp.omnifunc'
+ end
+ if
+ client.supports_method('textDocument/rangeFormatting')
+ and is_empty_or_default(bufnr, 'formatprg')
+ and is_empty_or_default(bufnr, 'formatexpr')
+ then
+ vim.bo[bufnr].formatexpr = 'v:lua.vim.lsp.formatexpr()'
+ end
+end
+
-- FIXME: DOC: Currently all methods on the `vim.lsp.client` object are
-- documented twice: Here, and on the methods themselves (e.g.
-- `client.request()`). This is a workaround for the vimdoc generator script
@@ -1091,43 +1135,6 @@ function lsp.start_client(config)
end
---@private
- -- Determines whether the given option can be set by `set_defaults`.
- local function is_empty_or_default(bufnr, option)
- if vim.bo[bufnr][option] == '' then
- return true
- end
-
- local info = vim.api.nvim_get_option_info2(option, { buf = bufnr })
- local scriptinfo = vim.tbl_filter(function(e)
- return e.sid == info.last_set_sid
- end, vim.fn.getscriptinfo())
-
- if #scriptinfo ~= 1 then
- return false
- end
-
- return vim.startswith(scriptinfo[1].name, vim.fn.expand('$VIMRUNTIME'))
- end
-
- ---@private
- local function set_defaults(client, bufnr)
- local capabilities = client.server_capabilities
- if capabilities.definitionProvider and is_empty_or_default(bufnr, 'tagfunc') then
- vim.bo[bufnr].tagfunc = 'v:lua.vim.lsp.tagfunc'
- end
- if capabilities.completionProvider and is_empty_or_default(bufnr, 'omnifunc') then
- vim.bo[bufnr].omnifunc = 'v:lua.vim.lsp.omnifunc'
- end
- if
- capabilities.documentRangeFormattingProvider
- and is_empty_or_default(bufnr, 'formatprg')
- and is_empty_or_default(bufnr, 'formatexpr')
- then
- vim.bo[bufnr].formatexpr = 'v:lua.vim.lsp.formatexpr()'
- end
- end
-
- ---@private
--- Reset defaults set by `set_defaults`.
--- Must only be called if the last client attached to a buffer exits.
local function unset_defaults(bufnr)
@@ -1228,7 +1235,9 @@ function lsp.start_client(config)
requests = {},
-- for $/progress report
messages = { name = name, messages = {}, progress = {}, status = {} },
+ dynamic_capabilities = require('vim.lsp._dynamic').new(client_id),
}
+ client.config.capabilities = config.capabilities or protocol.make_client_capabilities()
-- Store the uninitialized_clients for cleanup in case we exit before initialize finishes.
uninitialized_clients[client_id] = client
@@ -1291,7 +1300,7 @@ function lsp.start_client(config)
-- User provided initialization options.
initializationOptions = config.init_options,
-- The capabilities provided by the client (editor or tool)
- capabilities = config.capabilities or protocol.make_client_capabilities(),
+ capabilities = config.capabilities,
-- The initial trace setting. If omitted trace is disabled ("off").
-- trace = "off" | "messages" | "verbose";
trace = valid_traces[config.trace] or 'off',
@@ -1300,6 +1309,26 @@ function lsp.start_client(config)
-- TODO(ashkan) handle errors here.
pcall(config.before_init, initialize_params, config)
end
+
+ --- @param method string
+ --- @param opts? {bufnr?: number}
+ client.supports_method = function(method, opts)
+ opts = opts or {}
+ local required_capability = lsp._request_name_to_capability[method]
+ -- if we don't know about the method, assume that the client supports it.
+ if not required_capability then
+ return true
+ end
+ if vim.tbl_get(client.server_capabilities or {}, unpack(required_capability)) then
+ return true
+ else
+ if client.dynamic_capabilities:supports_registration(method) then
+ return client.dynamic_capabilities:supports(method, opts)
+ end
+ return false
+ end
+ end
+
local _ = log.trace() and log.trace(log_prefix, 'initialize_params', initialize_params)
rpc.request('initialize', initialize_params, function(init_err, result)
assert(not init_err, tostring(init_err))
@@ -1314,18 +1343,6 @@ function lsp.start_client(config)
client.server_capabilities =
assert(result.capabilities, "initialize result doesn't contain capabilities")
client.server_capabilities = protocol.resolve_capabilities(client.server_capabilities)
- client.supports_method = function(method)
- local required_capability = lsp._request_name_to_capability[method]
- -- if we don't know about the method, assume that the client supports it.
- if not required_capability then
- return true
- end
- if vim.tbl_get(client.server_capabilities, unpack(required_capability)) then
- return true
- else
- return false
- end
- end
if next(config.settings) then
client.notify('workspace/didChangeConfiguration', { settings = config.settings })
@@ -1393,13 +1410,24 @@ function lsp.start_client(config)
{ method = method, client_id = client_id, bufnr = bufnr, params = params }
)
end, function(request_id)
+ local request = client.requests[request_id]
+ request.type = 'complete'
+ nvim_exec_autocmds('LspRequest', {
+ buffer = bufnr,
+ modeline = false,
+ data = { client_id = client_id, request_id = request_id, request = request },
+ })
client.requests[request_id] = nil
- nvim_exec_autocmds('User', { pattern = 'LspRequest', modeline = false })
end)
if success and request_id then
- client.requests[request_id] = { type = 'pending', bufnr = bufnr, method = method }
- nvim_exec_autocmds('User', { pattern = 'LspRequest', modeline = false })
+ local request = { type = 'pending', bufnr = bufnr, method = method }
+ client.requests[request_id] = request
+ nvim_exec_autocmds('LspRequest', {
+ buffer = bufnr,
+ modeline = false,
+ data = { client_id = client_id, request_id = request_id, request = request },
+ })
end
return success, request_id
@@ -1471,7 +1499,11 @@ function lsp.start_client(config)
local request = client.requests[id]
if request and request.type == 'pending' then
request.type = 'cancel'
- nvim_exec_autocmds('User', { pattern = 'LspRequest', modeline = false })
+ nvim_exec_autocmds('LspRequest', {
+ buffer = request.bufnr,
+ modeline = false,
+ data = { client_id = client_id, request_id = id, request = request },
+ })
end
return rpc.notify('$/cancelRequest', { id = id })
end
@@ -1522,7 +1554,7 @@ function lsp.start_client(config)
function client._on_attach(bufnr)
text_document_did_open_handler(bufnr, client)
- set_defaults(client, bufnr)
+ lsp._set_defaults(client, bufnr)
nvim_exec_autocmds('LspAttach', {
buffer = bufnr,
@@ -1946,7 +1978,7 @@ function lsp.buf_request(bufnr, method, params, handler)
local supported_clients = {}
local method_supported = false
for_each_buffer_client(bufnr, function(client, client_id)
- if client.supports_method(method) then
+ if client.supports_method(method, { bufnr = bufnr }) then
method_supported = true
table.insert(supported_clients, client_id)
end
@@ -2002,7 +2034,7 @@ function lsp.buf_request_all(bufnr, method, params, callback)
local set_expected_result_count = once(function()
for_each_buffer_client(bufnr, function(client)
- if client.supports_method(method) then
+ if client.supports_method(method, { bufnr = bufnr }) then
expected_result_count = expected_result_count + 1
end
end)
@@ -2243,7 +2275,8 @@ end
---@param client_id (integer)
---@return boolean stopped true if client is stopped, false otherwise.
function lsp.client_is_stopped(client_id)
- return active_clients[client_id] == nil
+ assert(client_id, 'missing client_id param')
+ return active_clients[client_id] == nil and not uninitialized_clients[client_id]
end
--- Gets a map of client_id:client pairs for the given buffer, where each value