diff options
Diffstat (limited to 'runtime/lua')
-rw-r--r-- | runtime/lua/vim/filetype.lua | 1 | ||||
-rw-r--r-- | runtime/lua/vim/lsp.lua | 99 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/buf.lua | 173 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/handlers.lua | 16 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/protocol.lua | 47 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/rpc.lua | 2 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/util.lua | 4 | ||||
-rw-r--r-- | runtime/lua/vim/shared.lua | 5 |
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 |