aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/lua')
-rw-r--r--runtime/lua/vim/filetype.lua1
-rw-r--r--runtime/lua/vim/lsp.lua99
-rw-r--r--runtime/lua/vim/lsp/buf.lua173
-rw-r--r--runtime/lua/vim/lsp/handlers.lua16
-rw-r--r--runtime/lua/vim/lsp/protocol.lua47
-rw-r--r--runtime/lua/vim/lsp/rpc.lua2
-rw-r--r--runtime/lua/vim/lsp/util.lua4
-rw-r--r--runtime/lua/vim/shared.lua5
8 files changed, 268 insertions, 79 deletions
diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 1297ef6241..2a34fec7f2 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -241,6 +241,7 @@ local extension = {
gmi = "gemtext",
gemini = "gemtext",
gift = "gift",
+ gleam = "gleam",
glsl = "glsl",
gpi = "gnuplot",
gnuplot = "gnuplot",
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 7bbe3637db..19ee75a1b6 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -31,28 +31,28 @@ local lsp = {
rpc_response_error = lsp_rpc.rpc_response_error;
}
--- maps request name to the required resolved_capability in the client.
+-- maps request name to the required server_capability in the client.
lsp._request_name_to_capability = {
- ['textDocument/hover'] = 'hover';
- ['textDocument/signatureHelp'] = 'signature_help';
- ['textDocument/definition'] = 'goto_definition';
- ['textDocument/implementation'] = 'implementation';
- ['textDocument/declaration'] = 'declaration';
- ['textDocument/typeDefinition'] = 'type_definition';
- ['textDocument/documentSymbol'] = 'document_symbol';
- ['textDocument/prepareCallHierarchy'] = 'call_hierarchy';
- ['textDocument/rename'] = 'rename';
- ['textDocument/prepareRename'] = 'rename';
- ['textDocument/codeAction'] = 'code_action';
- ['textDocument/codeLens'] = 'code_lens';
- ['codeLens/resolve'] = 'code_lens_resolve';
- ['workspace/executeCommand'] = 'execute_command';
- ['workspace/symbol'] = 'workspace_symbol';
- ['textDocument/references'] = 'find_references';
- ['textDocument/rangeFormatting'] = 'document_range_formatting';
- ['textDocument/formatting'] = 'document_formatting';
- ['textDocument/completion'] = 'completion';
- ['textDocument/documentHighlight'] = 'document_highlight';
+ ['textDocument/hover'] = { 'hoverProvider' };
+ ['textDocument/signatureHelp'] = { 'signatureHelpProvider' };
+ ['textDocument/definition'] = { 'definitionProvider' };
+ ['textDocument/implementation'] = { 'implementationProvider' };
+ ['textDocument/declaration'] = { 'declarationProvider' };
+ ['textDocument/typeDefinition'] = { 'typeDefinitionProvider' };
+ ['textDocument/documentSymbol'] = { 'documentSymbolProvider' };
+ ['textDocument/prepareCallHierarchy'] = { 'callHierarchyProvider' };
+ ['textDocument/rename'] = { 'renameProvider' };
+ ['textDocument/prepareRename'] = { 'renameProvider', 'prepareProvider'} ;
+ ['textDocument/codeAction'] = { 'codeActionProvider' };
+ ['textDocument/codeLens'] = { 'codeLensProvider' };
+ ['codeLens/resolve'] = { 'codeLensProvider', 'resolveProvider' };
+ ['workspace/executeCommand'] = { 'executeCommandProvider' };
+ ['workspace/symbol'] = { 'workspaceSymbolProvider' };
+ ['textDocument/references'] = { 'referencesProvider' };
+ ['textDocument/rangeFormatting'] = { 'documentRangeFormattingProvider' };
+ ['textDocument/formatting'] = { 'documentFormattingProvider' };
+ ['textDocument/completion'] = { 'completionProvider' };
+ ['textDocument/documentHighlight'] = { 'documentHighlightProvider' };
}
-- TODO improve handling of scratch buffers with LSP attached.
@@ -328,7 +328,7 @@ do
function changetracking.init(client, bufnr)
local use_incremental_sync = (
if_nil(client.config.flags.allow_incremental_sync, true)
- and client.resolved_capabilities.text_document_did_change == protocol.TextDocumentSyncKind.Incremental
+ and vim.tbl_get(client.server_capabilities, "textDocumentSync", "change") == protocol.TextDocumentSyncKind.Incremental
)
local state = state_by_client[client.id]
if not state then
@@ -447,7 +447,7 @@ do
end)
local uri = vim.uri_from_bufnr(bufnr)
return function(client)
- if client.resolved_capabilities.text_document_did_change == protocol.TextDocumentSyncKind.None then
+ if vim.tbl_get(client.server_capabilities, "textDocumentSync", "change") == protocol.TextDocumentSyncKind.None then
return
end
local state = state_by_client[client.id]
@@ -526,7 +526,7 @@ end
---@param client Client object
local function text_document_did_open_handler(bufnr, client)
changetracking.init(client, bufnr)
- if not client.resolved_capabilities.text_document_open_close then
+ if not vim.tbl_get(client.server_capabilities, "textDocumentSync", "openClose") then
return
end
if not vim.api.nvim_buf_is_loaded(bufnr) then
@@ -632,10 +632,6 @@ end
---
--- - {server_capabilities} (table): Response from the server sent on
--- `initialize` describing the server's capabilities.
----
---- - {resolved_capabilities} (table): Normalized table of
---- capabilities that we have detected based on the initialize
---- response from the server in `server_capabilities`.
function lsp.client()
error()
end
@@ -884,6 +880,7 @@ function lsp.start_client(config)
messages = { name = name, messages = {}, progress = {}, status = {} };
}
+
-- Store the uninitialized_clients for cleanup in case we exit before initialize finishes.
uninitialized_clients[client_id] = client;
@@ -960,27 +957,48 @@ function lsp.start_client(config)
client.workspace_folders = workspace_folders
-- TODO(mjlbach): Backwards compatibility, to be removed in 0.7
client.workspaceFolders = client.workspace_folders
- client.server_capabilities = assert(result.capabilities, "initialize result doesn't contain capabilities")
+
-- These are the cleaned up capabilities we use for dynamically deciding
-- when to send certain events to clients.
- client.resolved_capabilities = protocol.resolve_capabilities(client.server_capabilities)
+ client.server_capabilities = assert(result.capabilities, "initialize result doesn't contain capabilities")
+ client.server_capabilities = protocol.resolve_capabilities(client.server_capabilities)
+
+ -- Deprecation wrapper: this will be removed in 0.8
+ local mt = {}
+ mt.__index = function(table, key)
+ if key == 'resolved_capabilities' then
+ vim.notify_once("[LSP] Accessing client.resolved_capabilities is deprecated, " ..
+ "update your plugins or configuration to access client.server_capabilities instead." ..
+ "The new key/value pairs in server_capabilities directly match those " ..
+ "defined in the language server protocol", vim.log.levels.WARN)
+ rawset(table, key, protocol._resolve_capabilities_compat(client.server_capabilities))
+ return rawget(table, key)
+ else
+ return rawget(table, key)
+ end
+ end
+ setmetatable(client, mt)
+
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
-
- return client.resolved_capabilities[required_capability]
+ if vim.tbl_get(client.server_capabilities, unpack(required_capability)) then
+ return true
+ else
+ return false
+ end
end
+
if config.on_init then
local status, err = pcall(config.on_init, client, result)
if not status then
pcall(handlers.on_error, lsp.client_errors.ON_INIT_CALLBACK_ERROR, err)
end
end
- local _ = log.debug() and log.debug(log_prefix, "server_capabilities", client.server_capabilities)
- local _ = log.info() and log.info(log_prefix, "initialized", { resolved_capabilities = client.resolved_capabilities })
+ local _ = log.info() and log.info(log_prefix, "server_capabilities", { server_capabilities = client.server_capabilities })
-- Only assign after initialized.
active_clients[client_id] = client
@@ -1190,10 +1208,11 @@ function lsp._text_document_did_save_handler(bufnr)
bufnr = resolve_bufnr(bufnr)
local uri = vim.uri_from_bufnr(bufnr)
local text = once(buf_get_full_text)
- for_each_buffer_client(bufnr, function(client, _client_id)
- if client.resolved_capabilities.text_document_save then
+ for_each_buffer_client(bufnr, function(client)
+ local save_capability = vim.tbl_get(client.server_capabilities, "textDocumentSync", "save")
+ if save_capability then
local included_text
- if client.resolved_capabilities.text_document_save_include_text then
+ if type(save_capability) == "table" and save_capability.includeText then
included_text = text(bufnr)
end
client.notify('textDocument/didSave', {
@@ -1246,7 +1265,7 @@ function lsp.buf_attach_client(bufnr, client_id)
local params = { textDocument = { uri = uri; } }
for_each_buffer_client(bufnr, function(client, _)
changetracking.reset_buf(client, bufnr)
- if client.resolved_capabilities.text_document_open_close then
+ if vim.tbl_get(client.server_capabilities, "textDocumentSync", "openClose") then
client.notify('textDocument/didClose', params)
end
text_document_did_open_handler(bufnr, client)
@@ -1256,7 +1275,7 @@ function lsp.buf_attach_client(bufnr, client_id)
local params = { textDocument = { uri = uri; } }
for_each_buffer_client(bufnr, function(client, _)
changetracking.reset_buf(client, bufnr)
- if client.resolved_capabilities.text_document_open_close then
+ if vim.tbl_get(client.server_capabilities, "textDocumentSync", "openClose") then
client.notify('textDocument/didClose', params)
end
end)
@@ -1306,7 +1325,7 @@ function lsp.buf_detach_client(bufnr, client_id)
changetracking.reset_buf(client, bufnr)
- if client.resolved_capabilities.text_document_open_close then
+ if vim.tbl_get(client.server_capabilities, "textDocumentSync", "openClose") then
local uri = vim.uri_from_bufnr(bufnr)
local params = { textDocument = { uri = uri; } }
client.notify('textDocument/didClose', params)
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index eb7ec579f1..aabafc422f 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -143,14 +143,114 @@ local function select_client(method, on_choice)
end
end
+--- Formats a buffer using the attached (and optionally filtered) language
+--- server clients.
+---
+--- @param options table|nil Optional table which holds the following optional fields:
+--- - formatting_options (table|nil):
+--- Can be used to specify FormattingOptions. Some unspecified options will be
+--- automatically derived from the current Neovim options.
+--- @see https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting
+--- - timeout_ms (integer|nil, default 1000):
+--- Time in milliseconds to block for formatting requests. No effect if async=true
+--- - bufnr (number|nil):
+--- Restrict formatting to the clients attached to the given buffer, defaults to the current
+--- buffer (0).
+--- - filter (function|nil):
+--- Predicate to filter clients used for formatting. Receives the list of clients attached
+--- to bufnr as the argument and must return the list of clients on which to request
+--- formatting. Example:
+---
+--- <pre>
+--- -- Never request typescript-language-server for formatting
+--- vim.lsp.buf.format {
+--- filter = function(clients)
+--- return vim.tbl_filter(
+--- function(client) return client.name ~= "tsserver" end,
+--- clients
+--- )
+--- end
+--- }
+--- </pre>
+---
+--- - async boolean|nil
+--- If true the method won't block. Defaults to false.
+--- Editing the buffer while formatting asynchronous can lead to unexpected
+--- changes.
+---
+--- - id (number|nil):
+--- Restrict formatting to the client with ID (client.id) matching this field.
+--- - name (string|nil):
+--- Restrict formatting to the client with name (client.name) matching this field.
+
+function M.format(options)
+ options = options or {}
+ local bufnr = options.bufnr or vim.api.nvim_get_current_buf()
+ local clients = vim.lsp.buf_get_clients(bufnr)
+
+ if options.filter then
+ clients = options.filter(clients)
+ elseif options.id then
+ clients = vim.tbl_filter(
+ function(client) return client.id == options.id end,
+ clients
+ )
+ elseif options.name then
+ clients = vim.tbl_filter(
+ function(client) return client.name == options.name end,
+ clients
+ )
+ end
+
+ clients = vim.tbl_filter(
+ function(client) return client.supports_method("textDocument/formatting") end,
+ clients
+ )
+
+ if #clients == 0 then
+ vim.notify("[LSP] Format request failed, no matching language servers.")
+ end
+
+ if options.async then
+ local do_format
+ do_format = function(idx, client)
+ if not client then
+ return
+ end
+ local params = util.make_formatting_params(options.formatting_options)
+ client.request("textDocument/formatting", params, function(...)
+ local handler = client.handlers['textDocument/formatting'] or vim.lsp.handlers['textDocument/formatting']
+ handler(...)
+ do_format(next(clients, idx))
+ end, bufnr)
+ end
+ do_format(next(clients))
+ else
+ local timeout_ms = options.timeout_ms or 1000
+ for _, client in pairs(clients) do
+ local params = util.make_formatting_params(options.formatting_options)
+ local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, bufnr)
+ if result and result.result then
+ util.apply_text_edits(result.result, bufnr, client.offset_encoding)
+ elseif err then
+ vim.notify(string.format("[LSP][%s] %s", client.name, err), vim.log.levels.WARN)
+ end
+ end
+ end
+end
+
--- Formats the current buffer.
---
----@param options (optional, table) Can be used to specify FormattingOptions.
+---@param options (table|nil) Can be used to specify FormattingOptions.
--- Some unspecified options will be automatically derived from the current
--- Neovim options.
--
---@see https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting
function M.formatting(options)
+ vim.notify_once(
+ 'vim.lsp.buf.formatting is deprecated. Use vim.lsp.buf.format { async = true } instead',
+ vim.log.levels.WARN
+ )
local params = util.make_formatting_params(options)
local bufnr = vim.api.nvim_get_current_buf()
select_client('textDocument/formatting', function(client)
@@ -171,10 +271,11 @@ end
--- autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_sync()
--- </pre>
---
----@param options Table with valid `FormattingOptions` entries
+---@param options table|nil with valid `FormattingOptions` entries
---@param timeout_ms (number) Request timeout
---@see |vim.lsp.buf.formatting_seq_sync|
function M.formatting_sync(options, timeout_ms)
+ vim.notify_once('vim.lsp.buf.formatting_sync is deprecated. Use vim.lsp.buf.format instead', vim.log.levels.WARN)
local params = util.make_formatting_params(options)
local bufnr = vim.api.nvim_get_current_buf()
select_client('textDocument/formatting', function(client)
@@ -202,12 +303,13 @@ end
--- vim.api.nvim_command[[autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_seq_sync()]]
--- </pre>
---
----@param options (optional, table) `FormattingOptions` entries
----@param timeout_ms (optional, number) Request timeout
----@param order (optional, table) List of client names. Formatting is requested from clients
+---@param options (table|nil) `FormattingOptions` entries
+---@param timeout_ms (number|nil) Request timeout
+---@param order (table|nil) List of client names. Formatting is requested from clients
---in the following order: first all clients that are not in the `order` list, then
---the remaining clients in the order as they occur in the `order` list.
function M.formatting_seq_sync(options, timeout_ms, order)
+ vim.notify_once('vim.lsp.buf.formatting_seq_sync is deprecated. Use vim.lsp.buf.format instead', vim.log.levels.WARN)
local clients = vim.tbl_values(vim.lsp.buf_get_clients());
local bufnr = vim.api.nvim_get_current_buf()
@@ -224,7 +326,7 @@ function M.formatting_seq_sync(options, timeout_ms, order)
-- loop through the clients and make synchronous formatting requests
for _, client in pairs(clients) do
- if client.resolved_capabilities.document_formatting then
+ if vim.tbl_get(client.server_capabilities, "documentFormattingProvider") then
local params = util.make_formatting_params(options)
local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, vim.api.nvim_get_current_buf())
if result and result.result then
@@ -491,11 +593,14 @@ end
--- from multiple clients to have 1 single UI prompt for the user, yet we still
--- need to be able to link a `CodeAction|Command` to the right client for
--- `codeAction/resolve`
-local function on_code_action_results(results, ctx)
+local function on_code_action_results(results, ctx, options)
local action_tuples = {}
+ local filter = options and options.filter
for client_id, result in pairs(results) do
for _, action in pairs(result.result or {}) do
- table.insert(action_tuples, { client_id, action })
+ if not filter or filter(action) then
+ table.insert(action_tuples, { client_id, action })
+ end
end
end
if #action_tuples == 0 then
@@ -542,8 +647,7 @@ local function on_code_action_results(results, ctx)
local action = action_tuple[2]
if not action.edit
and client
- and type(client.resolved_capabilities.code_action) == 'table'
- and client.resolved_capabilities.code_action.resolveProvider then
+ and vim.tbl_get(client.server_capabilities, "codeActionProvider", "resolveProvider") then
client.request('codeAction/resolve', action, function(err, resolved_action)
if err then
@@ -557,6 +661,13 @@ local function on_code_action_results(results, ctx)
end
end
+ -- If options.apply is given, and there are just one remaining code action,
+ -- apply it directly without querying the user.
+ if options and options.apply and #action_tuples == 1 then
+ on_user_choice(action_tuples[1])
+ return
+ end
+
vim.ui.select(action_tuples, {
prompt = 'Code actions:',
kind = 'codeaction',
@@ -571,35 +682,49 @@ end
--- Requests code actions from all clients and calls the handler exactly once
--- with all aggregated results
---@private
-local function code_action_request(params)
+local function code_action_request(params, options)
local bufnr = vim.api.nvim_get_current_buf()
local method = 'textDocument/codeAction'
vim.lsp.buf_request_all(bufnr, method, params, function(results)
- on_code_action_results(results, { bufnr = bufnr, method = method, params = params })
+ local ctx = { bufnr = bufnr, method = method, params = params}
+ on_code_action_results(results, ctx, options)
end)
end
--- Selects a code action available at the current
--- cursor position.
---
----@param context table|nil `CodeActionContext` of the LSP specification:
---- - diagnostics: (table|nil)
---- LSP `Diagnostic[]`. Inferred from the current
---- position if not provided.
---- - only: (string|nil)
---- LSP `CodeActionKind` used to filter the code actions.
---- Most language servers support values like `refactor`
---- or `quickfix`.
+---@param options table|nil Optional table which holds the following optional fields:
+--- - context (table|nil):
+--- Corresponds to `CodeActionContext` of the LSP specification:
+--- - diagnostics (table|nil):
+--- LSP `Diagnostic[]`. Inferred from the current
+--- position if not provided.
+--- - only (string|nil):
+--- LSP `CodeActionKind` used to filter the code actions.
+--- Most language servers support values like `refactor`
+--- or `quickfix`.
+--- - filter (function|nil):
+--- Predicate function taking an `CodeAction` and returning a boolean.
+--- - apply (boolean|nil):
+--- When set to `true`, and there is just one remaining action
+--- (after filtering), the action is applied without user query.
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction
-function M.code_action(context)
- validate { context = { context, 't', true } }
- context = context or {}
+function M.code_action(options)
+ validate { options = { options, 't', true } }
+ options = options or {}
+ -- Detect old API call code_action(context) which should now be
+ -- code_action({ context = context} )
+ if options.diagnostics or options.only then
+ options = { options = options }
+ end
+ local context = options.context or {}
if not context.diagnostics then
context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics()
end
local params = util.make_range_params()
params.context = context
- code_action_request(params)
+ code_action_request(params, options)
end
--- Performs |vim.lsp.buf.code_action()| for a given range.
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index 71b4d33ec0..5c80ed0d10 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -27,7 +27,7 @@ local function progress_handler(_, result, ctx, _)
local client = vim.lsp.get_client_by_id(client_id)
local client_name = client and client.name or string.format("id=%d", client_id)
if not client then
- err_message("LSP[", client_name, "] client has shut down after sending the message")
+ err_message("LSP[", client_name, "] client has shut down during progress update")
return vim.NIL
end
local val = result.value -- unspecified yet
@@ -70,7 +70,7 @@ M['window/workDoneProgress/create'] = function(_, result, ctx)
local token = result.token -- string or number
local client_name = client and client.name or string.format("id=%d", client_id)
if not client then
- err_message("LSP[", client_name, "] client has shut down after sending the message")
+ err_message("LSP[", client_name, "] client has shut down while creating progress report")
return vim.NIL
end
client.messages.progress[token] = {}
@@ -132,7 +132,7 @@ M['workspace/configuration'] = function(_, result, ctx)
local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(client_id)
if not client then
- err_message("LSP[id=", client_id, "] client has shut down after sending the message")
+ err_message("LSP[", client_id, "] client has shut down after sending a workspace/configuration request")
return
end
if not result.items then
@@ -298,13 +298,13 @@ function M.hover(_, result, ctx, config)
config = config or {}
config.focus_id = ctx.method
if not (result and result.contents) then
- -- return { 'No information available' }
+ vim.notify('No information available')
return
end
local markdown_lines = util.convert_input_to_markdown_lines(result.contents)
markdown_lines = util.trim_empty_lines(markdown_lines)
if vim.tbl_isempty(markdown_lines) then
- -- return { 'No information available' }
+ vim.notify('No information available')
return
end
return util.open_floating_preview(markdown_lines, "markdown", config)
@@ -379,7 +379,7 @@ function M.signature_help(_, result, ctx, config)
return
end
local client = vim.lsp.get_client_by_id(ctx.client_id)
- local triggers = client.resolved_capabilities.signature_help_trigger_characters
+ local triggers = vim.tbl_get(client.server_capabilities, "signatureHelpProvider", "triggerCharacters")
local ft = api.nvim_buf_get_option(ctx.bufnr, 'filetype')
local lines, hl = util.convert_signature_help_to_markdown_lines(result, ft, triggers)
lines = util.trim_empty_lines(lines)
@@ -449,7 +449,7 @@ M['window/logMessage'] = function(_, result, ctx, _)
local client = vim.lsp.get_client_by_id(client_id)
local client_name = client and client.name or string.format("id=%d", client_id)
if not client then
- err_message("LSP[", client_name, "] client has shut down after sending the message")
+ err_message("LSP[", client_name, "] client has shut down after sending ", message)
end
if message_type == protocol.MessageType.Error then
log.error(message)
@@ -471,7 +471,7 @@ M['window/showMessage'] = function(_, result, ctx, _)
local client = vim.lsp.get_client_by_id(client_id)
local client_name = client and client.name or string.format("id=%d", client_id)
if not client then
- err_message("LSP[", client_name, "] client has shut down after sending the message")
+ err_message("LSP[", client_name, "] client has shut down after sending ", message)
end
if message_type == protocol.MessageType.Error then
err_message("LSP[", client_name, "] ", message)
diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua
index 86c9e2fd58..8f50863360 100644
--- a/runtime/lua/vim/lsp/protocol.lua
+++ b/runtime/lua/vim/lsp/protocol.lua
@@ -1,7 +1,5 @@
-- Protocol for the Microsoft Language Server Protocol (mslsp)
-local if_nil = vim.F.if_nil
-
local protocol = {}
--[=[
@@ -645,6 +643,7 @@ function protocol.make_client_capabilities()
end)();
};
};
+ isPreferredSupport = true;
dataSupport = true;
resolveSupport = {
properties = { 'edit', }
@@ -776,10 +775,50 @@ function protocol.make_client_capabilities()
}
end
+local if_nil = vim.F.if_nil
--- Creates a normalized object describing LSP server capabilities.
---@param server_capabilities table Table of capabilities supported by the server
---@return table Normalized table of capabilities
function protocol.resolve_capabilities(server_capabilities)
+ local TextDocumentSyncKind = protocol.TextDocumentSyncKind
+ local textDocumentSync = server_capabilities.textDocumentSync
+ if textDocumentSync == nil then
+ -- Defaults if omitted.
+ server_capabilities.textDocumentSync = {
+ openClose = false,
+ change = TextDocumentSyncKind.None,
+ willSave = false,
+ willSaveWaitUntil = false,
+ save = {
+ includeText = false,
+ }
+ }
+ elseif type(textDocumentSync) == 'number' then
+ -- Backwards compatibility
+ if not TextDocumentSyncKind[textDocumentSync] then
+ return nil, "Invalid server TextDocumentSyncKind for textDocumentSync"
+ end
+ server_capabilities.textDocumentSync = {
+ openClose = true,
+ change = textDocumentSync,
+ willSave = false,
+ willSaveWaitUntil = false,
+ save = {
+ includeText = false,
+ }
+ }
+ elseif type(textDocumentSync) ~= 'table' then
+ return nil, string.format("Invalid type for textDocumentSync: %q", type(textDocumentSync))
+ end
+ return server_capabilities
+end
+
+---@private
+--- Creates a normalized object describing LSP server capabilities.
+-- @deprecated access resolved_capabilities instead
+---@param server_capabilities table Table of capabilities supported by the server
+---@return table Normalized table of capabilities
+function protocol._resolve_capabilities_compat(server_capabilities)
local general_properties = {}
local text_document_sync_properties
do
@@ -930,12 +969,14 @@ function protocol.resolve_capabilities(server_capabilities)
error("The server sent invalid signatureHelpProvider")
end
- return vim.tbl_extend("error"
+ local capabilities = vim.tbl_extend("error"
, text_document_sync_properties
, signature_help_properties
, workspace_properties
, general_properties
)
+
+ return capabilities
end
return protocol
diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua
index 1ecac50df4..6d0a78fba8 100644
--- a/runtime/lua/vim/lsp/rpc.lua
+++ b/runtime/lua/vim/lsp/rpc.lua
@@ -385,7 +385,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
---@param method (string) The invoked LSP method
---@param params (table) Parameters for the invoked LSP method
---@param callback (function) Callback to invoke
- ---@param notify_reply_callback (function) Callback to invoke as soon as a request is no longer pending
+ ---@param notify_reply_callback (function|nil) Callback to invoke as soon as a request is no longer pending
---@returns (bool, number) `(true, message_id)` if request could be sent, `false` if not
local function request(method, params, callback, notify_reply_callback)
validate {
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 1f1a34b04a..77ab1d4224 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -1873,7 +1873,7 @@ end
--- Returns indentation size.
---
---@see |shiftwidth|
----@param bufnr (optional, number): Buffer handle, defaults to current
+---@param bufnr (number|nil): Buffer handle, defaults to current
---@returns (number) indentation size
function M.get_effective_tabstop(bufnr)
validate { bufnr = {bufnr, 'n', true} }
@@ -1884,7 +1884,7 @@ end
--- Creates a `DocumentFormattingParams` object for the current buffer and cursor position.
---
----@param options Table with valid `FormattingOptions` entries
+---@param options table|nil with valid `FormattingOptions` entries
---@returns `DocumentFormattingParams` object
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting
function M.make_formatting_params(options)
diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index f0dc34608c..172fac3a88 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -365,7 +365,10 @@ function vim.tbl_get(o, ...)
if #keys == 0 then
return
end
- for _, k in ipairs(keys) do
+ for i, k in ipairs(keys) do
+ if type(o[k]) ~= 'table' and next(keys, i) then
+ return nil
+ end
o = o[k]
if o == nil then
return