diff options
Diffstat (limited to 'runtime/lua/vim/lsp.lua')
-rw-r--r-- | runtime/lua/vim/lsp.lua | 360 |
1 files changed, 245 insertions, 115 deletions
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 5f7a95ae14..ca4851f8d7 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -60,6 +60,8 @@ lsp._request_name_to_capability = { ['textDocument/documentHighlight'] = { 'documentHighlightProvider' }, ['textDocument/semanticTokens/full'] = { 'semanticTokensProvider' }, ['textDocument/semanticTokens/full/delta'] = { 'semanticTokensProvider' }, + ['textDocument/inlayHint'] = { 'inlayHintProvider' }, + ['inlayHint/resolve'] = { 'inlayHintProvider', 'resolveProvider' }, } -- TODO improve handling of scratch buffers with LSP attached. @@ -147,9 +149,9 @@ local function next_client_id() return client_index end -- Tracks all clients created via lsp.start_client -local active_clients = {} -local all_buffer_active_clients = {} -local uninitialized_clients = {} +local active_clients = {} --- @type table<integer,lsp.Client> +local all_buffer_active_clients = {} --- @type table<integer,table<integer,true>> +local uninitialized_clients = {} --- @type table<integer,lsp.Client> ---@private ---@param bufnr? integer @@ -166,7 +168,7 @@ local function for_each_buffer_client(bufnr, fn, restrict_client_ids) end if restrict_client_ids and #restrict_client_ids > 0 then - local filtered_client_ids = {} + local filtered_client_ids = {} --- @type table<integer,true> for client_id in pairs(client_ids) do if vim.list_contains(restrict_client_ids, client_id) then filtered_client_ids[client_id] = true @@ -257,9 +259,10 @@ end ---@private --- Validates a client configuration as given to |vim.lsp.start_client()|. --- ----@param config (table) ----@return table config Cleaned config, containing the command, its ----arguments, and a valid encoding. +---@param config (lsp.ClientConfig) +---@return (string|fun(dispatchers:table):table) Command +---@return string[] Arguments +---@return string Encoding. local function validate_client_config(config) validate({ config = { config, 't' }, @@ -290,22 +293,19 @@ local function validate_client_config(config) 'flags.debounce_text_changes must be a number with the debounce time in milliseconds' ) - local cmd, cmd_args - if type(config.cmd) == 'function' then - cmd = config.cmd + local cmd, cmd_args --- @type (string|fun(dispatchers:table):table), string[] + local config_cmd = config.cmd + if type(config_cmd) == 'function' then + cmd = config_cmd else - cmd, cmd_args = lsp._cmd_parts(config.cmd) + cmd, cmd_args = lsp._cmd_parts(config_cmd) end local offset_encoding = valid_encodings.UTF16 if config.offset_encoding then offset_encoding = validate_encoding(config.offset_encoding) end - return { - cmd = cmd, - cmd_args = cmd_args, - offset_encoding = offset_encoding, - } + return cmd, cmd_args, offset_encoding end ---@private @@ -328,10 +328,11 @@ end --- only the first returned value will be memoized and returned. The function will only be run once, --- even if it has side effects. --- ----@param fn (function) Function to run ----@return function fn Memoized function +---@generic T: function +---@param fn (T) Function to run +---@return T local function once(fn) - local value + local value --- @type any local ran = false return function(...) if not ran then @@ -371,7 +372,7 @@ do --- @field lines string[] snapshot of buffer lines from last didChange --- @field lines_tmp string[] --- @field pending_changes table[] List of debounced changes in incremental sync mode - --- @field timer nil|uv.uv_timer_t uv_timer + --- @field timer nil|uv_timer_t uv_timer --- @field last_flush nil|number uv.hrtime of the last flush/didChange-notification --- @field needs_flush boolean true if buffer updates haven't been sent to clients/servers yet --- @field refs integer how many clients are using this group @@ -610,7 +611,7 @@ do ---@private function changetracking.send_changes(bufnr, firstline, lastline, new_lastline) - local groups = {} + local groups = {} ---@type table<string,CTGroup> for _, client in pairs(lsp.get_active_clients({ bufnr = bufnr })) do local group = get_group(client) groups[group_key(group)] = group @@ -808,10 +809,17 @@ end --- --- - {server_capabilities} (table): Response from the server sent on --- `initialize` describing the server's capabilities. +--- +--- - {progress} A ring buffer (|vim.ringbuf()|) containing progress messages +--- sent by the server. function lsp.client() error() end +--- @class lsp.StartOpts +--- @field reuse_client fun(client: lsp.Client, config: table): boolean +--- @field bufnr integer + --- Create a new LSP client and start a language server or reuses an already --- running client if one is found matching `name` and `root_dir`. --- Attaches the current buffer to the client. @@ -849,7 +857,7 @@ end --- `ftplugin/<filetype_name>.lua` (See |ftplugin-name|) --- ---@param config table Same configuration as documented in |vim.lsp.start_client()| ----@param opts nil|table Optional keyword arguments: +---@param opts (nil|lsp.StartOpts) Optional keyword arguments: --- - reuse_client (fun(client: client, config: table): boolean) --- Predicate used to decide if a client should be re-used. --- Used on all running clients. @@ -858,14 +866,13 @@ end --- - bufnr (number) --- Buffer handle to attach to if starting or re-using a --- client (0 for current). ----@return number|nil client_id +---@return integer|nil client_id function lsp.start(config, opts) opts = opts or {} local reuse_client = opts.reuse_client or function(client, conf) return client.config.root_dir == conf.root_dir and client.name == conf.name end - config.name = config.name if not config.name and type(config.cmd) == 'table' then config.name = config.cmd[1] and vim.fs.basename(config.cmd[1]) or nil end @@ -889,6 +896,34 @@ function lsp.start(config, opts) return client_id end +--- Consumes the latest progress messages from all clients and formats them as a string. +--- Empty if there are no clients or if no new messages +--- +---@return string +function lsp.status() + local percentage = nil + local messages = {} + for _, client in ipairs(vim.lsp.get_active_clients()) do + for progress in client.progress do + local value = progress.value + if type(value) == 'table' and value.kind then + local message = value.message and (value.title .. ': ' .. value.message) or value.title + messages[#messages + 1] = message + if value.percentage then + percentage = math.max(percentage or 0, value.percentage) + end + end + -- else: Doesn't look like work done progress and can be in any format + -- Just ignore it as there is no sensible way to display it + end + end + local message = table.concat(messages, ', ') + if percentage then + return string.format('%3d%%: %s', percentage, message) + end + return message +end + ---@private -- Determines whether the given option can be set by `set_defaults`. local function is_empty_or_default(bufnr, option) @@ -930,6 +965,29 @@ function lsp._set_defaults(client, bufnr) end end +--- @class lsp.ClientConfig +--- @field cmd (string[]|fun(dispatchers: table):table) +--- @field cmd_cwd string +--- @field cmd_env (table) +--- @field detached boolean +--- @field workspace_folders (table) +--- @field capabilities lsp.ClientCapabilities +--- @field handlers table<string,function> +--- @field settings table +--- @field commands table +--- @field init_options table +--- @field name string +--- @field get_language_id fun(bufnr: integer, filetype: string): string +--- @field offset_encoding string +--- @field on_error fun(code: integer) +--- @field before_init function +--- @field on_init function +--- @field on_exit fun(code: integer, signal: integer, client_id: integer) +--- @field on_attach fun(client: lsp.Client, bufnr: integer) +--- @field trace 'off'|'messages'|'verbose'|nil +--- @field flags table +--- @field root_dir string + -- 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 @@ -940,7 +998,7 @@ end --- --- Field `cmd` in {config} is required. --- ----@param config (table) Configuration for the server: +---@param config (lsp.ClientConfig) Configuration for the server: --- - cmd: (string[]|fun(dispatchers: table):table) command a list of --- strings treated like |jobstart()|. The command must launch the language server --- process. `cmd` can also be a function that creates an RPC client. @@ -970,7 +1028,7 @@ end --- the LSP spec. --- --- - capabilities: Map overriding the default capabilities defined by ---- |vim.lsp.protocol.make_client_capabilities()|, passed to the language +--- \|vim.lsp.protocol.make_client_capabilities()|, passed to the language --- server on initialization. Hint: use make_client_capabilities() and modify --- its result. --- - Note: To send an empty dictionary use @@ -1018,7 +1076,7 @@ end --- `initialize_result.offsetEncoding` if `capabilities.offsetEncoding` was --- sent to it. You can only modify the `client.offset_encoding` here before --- any notifications are sent. Most language servers expect to be sent client specified settings after ---- initialization. Neovim does not make this assumption. A +--- initialization. Nvim does not make this assumption. A --- `workspace/didChangeConfiguration` notification should be sent --- to the server during on_init. --- @@ -1051,9 +1109,7 @@ end --- fully initialized. Use `on_init` to do any actions once --- the client has been initialized. function lsp.start_client(config) - local cleaned_config = validate_client_config(config) - local cmd, cmd_args, offset_encoding = - cleaned_config.cmd, cleaned_config.cmd_args, cleaned_config.offset_encoding + local cmd, cmd_args, offset_encoding = validate_client_config(config) config.flags = config.flags or {} config.settings = config.settings or {} @@ -1090,7 +1146,9 @@ function lsp.start_client(config) ---@param method (string) LSP method name ---@param params (table) The parameters for that method. function dispatch.notification(method, params) - local _ = log.trace() and log.trace('notification', method, params) + if log.trace() then + log.trace('notification', method, params) + end local handler = resolve_handler(method) if handler then -- Method name is provided here for convenience. @@ -1104,13 +1162,19 @@ function lsp.start_client(config) ---@param method (string) LSP method name ---@param params (table) The parameters for that method function dispatch.server_request(method, params) - local _ = log.trace() and log.trace('server_request', method, params) + if log.trace() then + log.trace('server_request', method, params) + end local handler = resolve_handler(method) if handler then - local _ = log.trace() and log.trace('server_request: found handler for', method) + if log.trace() then + log.trace('server_request: found handler for', method) + end return handler(nil, params, { method = method, client_id = client_id }) end - local _ = log.warn() and log.warn('server_request: no handler found for', method) + if log.warn() then + log.warn('server_request: no handler found for', method) + end return nil, lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound) end @@ -1119,11 +1183,12 @@ function lsp.start_client(config) --- ---@param code (integer) Error code ---@param err (...) Other arguments may be passed depending on the error kind - ---@see `vim.lsp.rpc.client_errors` for possible errors. Use + ---@see vim.lsp.rpc.client_errors for possible errors. Use ---`vim.lsp.rpc.client_errors[code]` to get a human-friendly name. function dispatch.on_error(code, err) - local _ = log.error() - and log.error(log_prefix, 'on_error', { code = lsp.client_errors[code], err = err }) + if log.error() then + log.error(log_prefix, 'on_error', { code = lsp.client_errors[code], err = err }) + end err_message(log_prefix, ': Error ', lsp.client_errors[code], ': ', vim.inspect(err)) if config.on_error then local status, usererr = pcall(config.on_error, code, err) @@ -1232,11 +1297,26 @@ function lsp.start_client(config) handlers = handlers, commands = config.commands or {}, + --- @type table<integer,{ type: string, bufnr: integer, method: string}> requests = {}, - -- for $/progress report + + --- Contains $/progress report messages. + --- They have the format {token: integer|string, value: any} + --- For "work done progress", value will be one of: + --- - lsp.WorkDoneProgressBegin, + --- - lsp.WorkDoneProgressReport (extended with title from Begin) + --- - lsp.WorkDoneProgressEnd (extended with title from Begin) + progress = vim.ringbuf(50), + + ---@deprecated use client.progress instead messages = { name = name, messages = {}, progress = {}, status = {} }, dynamic_capabilities = require('vim.lsp._dynamic').new(client_id), } + + ---@type table<string|integer, string> title of unfinished progress sequences by token + client.progress.pending = {} + + --- @type lsp.ClientCapabilities client.config.capabilities = config.capabilities or protocol.make_client_capabilities() -- Store the uninitialized_clients for cleanup in case we exit before initialize finishes. @@ -1249,11 +1329,10 @@ function lsp.start_client(config) messages = 'messages', verbose = 'verbose', } - local version = vim.version() - local workspace_folders - local root_uri - local root_path + local workspace_folders --- @type table[]? + local root_uri --- @type string? + local root_path --- @type string? if config.workspace_folders or config.root_dir then if config.root_dir and not config.workspace_folders then workspace_folders = { @@ -1278,12 +1357,12 @@ function lsp.start_client(config) -- the process has not been started by another process. If the parent -- process is not alive then the server should exit (see exit notification) -- its process. - processId = uv.getpid(), + processId = uv.os_getpid(), -- Information about the client -- since 3.15.0 clientInfo = { name = 'Neovim', - version = string.format('%s.%s.%s', version.major, version.minor, version.patch), + version = tostring(vim.version()), }, -- The rootPath of the workspace. Is null if no folder is open. -- @@ -1394,7 +1473,7 @@ function lsp.start_client(config) ---successful, then it will return {request_id} as the ---second result. You can use this with `client.cancel_request(request_id)` ---to cancel the-request. - ---@see |vim.lsp.buf_request()| + ---@see |vim.lsp.buf_request_all()| function client.request(method, params, handler, bufnr) if not handler then handler = assert( @@ -1404,20 +1483,25 @@ function lsp.start_client(config) end -- Ensure pending didChange notifications are sent so that the server doesn't operate on a stale state changetracking.flush(client, bufnr) + local version = util.buf_versions[bufnr] bufnr = resolve_bufnr(bufnr) - local _ = log.debug() - and log.debug(log_prefix, 'client.request', client_id, method, params, handler, bufnr) + if log.debug() then + log.debug(log_prefix, 'client.request', client_id, method, params, handler, bufnr) + end local success, request_id = rpc.request(method, params, function(err, result) - handler( - err, - result, - { method = method, client_id = client_id, bufnr = bufnr, params = params } - ) + local context = { + method = method, + client_id = client_id, + bufnr = bufnr, + params = params, + version = version, + } + handler(err, result, context) end, function(request_id) local request = client.requests[request_id] request.type = 'complete' nvim_exec_autocmds('LspRequest', { - buffer = bufnr, + buffer = api.nvim_buf_is_valid(bufnr) and bufnr or nil, modeline = false, data = { client_id = client_id, request_id = request_id, request = request }, }) @@ -1553,6 +1637,46 @@ function lsp.start_client(config) end ---@private + --- Execute a lsp command, either via client command function (if available) + --- or via workspace/executeCommand (if supported by the server) + --- + ---@param command lsp.Command + ---@param context? {bufnr: integer} + ---@param handler? lsp-handler only called if a server command + function client._exec_cmd(command, context, handler) + context = vim.deepcopy(context or {}) + context.bufnr = context.bufnr or api.nvim_get_current_buf() + context.client_id = client.id + local cmdname = command.command + local fn = client.commands[cmdname] or lsp.commands[cmdname] + if fn then + fn(command, context) + return + end + + local command_provider = client.server_capabilities.executeCommandProvider + local commands = type(command_provider) == 'table' and command_provider.commands or {} + if not vim.list_contains(commands, cmdname) then + vim.notify_once( + string.format( + 'Language server `%s` does not support command `%s`. This command may require a client extension.', + client.name, + cmdname + ), + vim.log.levels.WARN + ) + return + end + -- Not using command directly to exclude extra properties, + -- see https://github.com/python-lsp/python-lsp-server/issues/146 + local params = { + command = command.command, + arguments = command.arguments, + } + client.request('workspace/executeCommand', params, handler, context.bufnr) + end + + ---@private --- Runs the on_attach function from the client's config if it was defined. ---@param bufnr integer Buffer number function client._on_attach(bufnr) @@ -1616,7 +1740,7 @@ local function 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) + for _, client in ipairs(lsp.get_active_clients({ bufnr = bufnr })) do local name = api.nvim_buf_get_name(bufnr) local old_name = changetracking._get_and_set_name(client, bufnr, name) if old_name and name ~= old_name then @@ -1648,7 +1772,7 @@ local function text_document_did_save_handler(bufnr) text = included_text, }) end - end) + end end --- Implements the `textDocument/did…` notifications required to track a buffer @@ -1658,6 +1782,7 @@ end --- ---@param bufnr (integer) Buffer handle, or 0 for current ---@param client_id (integer) Client id +---@return boolean success `true` if client was attached successfully; `false` otherwise function lsp.buf_attach_client(bufnr, client_id) validate({ bufnr = { bufnr, 'n', true }, @@ -1683,7 +1808,7 @@ function lsp.buf_attach_client(bufnr, client_id) buffer = bufnr, desc = 'vim.lsp: textDocument/willSave', callback = function(ctx) - for_each_buffer_client(ctx.buf, function(client) + for _, client in ipairs(lsp.get_active_clients({ bufnr = ctx.buf })) do local params = { textDocument = { uri = uri, @@ -1702,7 +1827,7 @@ function lsp.buf_attach_client(bufnr, client_id) log.error(vim.inspect(err)) end end - end) + end end, }) api.nvim_create_autocmd('BufWritePost', { @@ -1718,23 +1843,23 @@ function lsp.buf_attach_client(bufnr, client_id) on_lines = text_document_did_change_handler, on_reload = function() local params = { textDocument = { uri = uri } } - for_each_buffer_client(bufnr, function(client, _) + for _, client in ipairs(lsp.get_active_clients({ bufnr = bufnr })) do changetracking.reset_buf(client, bufnr) if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then client.notify('textDocument/didClose', params) end text_document_did_open_handler(bufnr, client) - end) + end end, on_detach = function() local params = { textDocument = { uri = uri } } - for_each_buffer_client(bufnr, function(client, _) + for _, client in ipairs(lsp.get_active_clients({ bufnr = bufnr })) do changetracking.reset_buf(client, bufnr) if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then client.notify('textDocument/didClose', params) end client.attached_buffers[bufnr] = nil - end) + end util.buf_versions[bufnr] = nil all_buffer_active_clients[bufnr] = nil end, @@ -1746,7 +1871,7 @@ function lsp.buf_attach_client(bufnr, client_id) end if buffer_client_ids[client_id] then - return + return true end -- This is our first time attaching this client to this buffer. buffer_client_ids[client_id] = true @@ -1807,7 +1932,7 @@ function lsp.buf_detach_client(bufnr, client_id) all_buffer_active_clients[bufnr] = nil end - local namespace = vim.lsp.diagnostic.get_namespace(client_id) + local namespace = lsp.diagnostic.get_namespace(client_id) vim.diagnostic.reset(namespace, bufnr) vim.notify(string.format('Detached buffer (id: %d) from client (id: %d)', bufnr, client_id)) @@ -1879,13 +2004,13 @@ end --- - id (number): Only return clients with the given id --- - bufnr (number): Only return clients attached to this buffer --- - name (string): Only return clients with the given name ----@returns (table) List of |vim.lsp.client| objects +---@return lsp.Client[]: List of |vim.lsp.client| objects function lsp.get_active_clients(filter) validate({ filter = { filter, 't', true } }) filter = filter or {} - local clients = {} + local clients = {} --- @type lsp.Client[] local t = filter.bufnr and (all_buffer_active_clients[resolve_bufnr(filter.bufnr)] or {}) or active_clients @@ -1979,34 +2104,30 @@ function lsp.buf_request(bufnr, method, params, handler) handler = { handler, 'f', true }, }) - local supported_clients = {} + bufnr = resolve_bufnr(bufnr) local method_supported = false - for_each_buffer_client(bufnr, function(client, client_id) + local clients = lsp.get_active_clients({ bufnr = bufnr }) + local client_request_ids = {} + for _, client in ipairs(clients) do if client.supports_method(method, { bufnr = bufnr }) then method_supported = true - table.insert(supported_clients, client_id) + + local request_success, request_id = client.request(method, params, handler, bufnr) + -- This could only fail if the client shut down in the time since we looked + -- it up and we did the request, which should be rare. + if request_success then + client_request_ids[client.id] = request_id + end end - end) + end -- if has client but no clients support the given method, notify the user - if - not tbl_isempty(all_buffer_active_clients[resolve_bufnr(bufnr)] or {}) and not method_supported - then + if next(clients) and not method_supported then vim.notify(lsp._unsupported_method(method), vim.log.levels.ERROR) nvim_command('redraw') return {}, function() end end - local client_request_ids = {} - for_each_buffer_client(bufnr, function(client, client_id, resolved_bufnr) - local request_success, request_id = client.request(method, params, handler, resolved_bufnr) - -- This could only fail if the client shut down in the time since we looked - -- it up and we did the request, which should be rare. - if request_success then - client_request_ids[client_id] = request_id - end - end, supported_clients) - local function _cancel_all_requests() for client_id, request_id in pairs(client_request_ids) do local client = active_clients[client_id] @@ -2017,40 +2138,37 @@ function lsp.buf_request(bufnr, method, params, handler) return client_request_ids, _cancel_all_requests end ----Sends an async request for all active clients attached to the buffer. ----Executes the callback on the combined result. ----Parameters are the same as |vim.lsp.buf_request()| but the return result and callback are ----different. +--- Sends an async request for all active clients attached to the buffer and executes the `handler` +--- callback with the combined result. --- ---@param bufnr (integer) Buffer handle, or 0 for current. ---@param method (string) LSP method name ---@param params (table|nil) Parameters to send to the server ----@param callback fun(request_results: table<integer, {error: lsp.ResponseError, result: any}>) (function) ---- The callback to call when all requests are finished. ---- Unlike `buf_request`, this will collect all the responses from each server instead of handling them. ---- A map of client_id:request_result will be provided to the callback. ---- ----@return fun() cancel A function that will cancel all requests -function lsp.buf_request_all(bufnr, method, params, callback) - local request_results = {} +---@param handler fun(results: table<integer, {error: lsp.ResponseError, result: any}>) (function) +--- Handler called after all requests are completed. Server results are passed as +--- a `client_id:result` map. +--- +---@return fun() cancel Function that cancels all requests. +function lsp.buf_request_all(bufnr, method, params, handler) + local results = {} local result_count = 0 local expected_result_count = 0 local set_expected_result_count = once(function() - for_each_buffer_client(bufnr, function(client) + for _, client in ipairs(lsp.get_active_clients({ bufnr = bufnr })) do if client.supports_method(method, { bufnr = bufnr }) then expected_result_count = expected_result_count + 1 end - end) + end end) local function _sync_handler(err, result, ctx) - request_results[ctx.client_id] = { error = err, result = result } + results[ctx.client_id] = { error = err, result = result } result_count = result_count + 1 set_expected_result_count() if result_count >= expected_result_count then - callback(request_results) + handler(results) end end @@ -2062,8 +2180,8 @@ end --- Sends a request to all server and waits for the response of all of them. --- --- Calls |vim.lsp.buf_request_all()| but blocks Nvim while awaiting the result. ---- Parameters are the same as |vim.lsp.buf_request()| but the return result is ---- different. Wait maximum of {timeout_ms} (default 1000) ms. +--- Parameters are the same as |vim.lsp.buf_request_all()| but the result is +--- different. Waits a maximum of {timeout_ms} (default 1000) ms. --- ---@param bufnr (integer) Buffer handle, or 0 for current. ---@param method (string) LSP method name @@ -2104,11 +2222,11 @@ function lsp.buf_notify(bufnr, method, params) method = { method, 's' }, }) local resp = false - for_each_buffer_client(bufnr, function(client, _client_id, _resolved_bufnr) + for _, client in ipairs(lsp.get_active_clients({ bufnr = bufnr })) do if client.rpc.notify(method, params) then resp = true end - end) + end return resp end @@ -2143,20 +2261,20 @@ end --- - findstart=0: column where the completion starts, or -2 or -3 --- - findstart=1: list of matches (actually just calls |complete()|) function lsp.omnifunc(findstart, base) - local _ = log.debug() and log.debug('omnifunc.findstart', { findstart = findstart, base = base }) + if log.debug() then + log.debug('omnifunc.findstart', { findstart = findstart, base = base }) + end local bufnr = resolve_bufnr() local has_buffer_clients = not tbl_isempty(all_buffer_active_clients[bufnr] or {}) if not has_buffer_clients then - if findstart == 1 then - return -1 - else - return {} - end + return findstart == 1 and -1 or {} end -- Then, perform standard completion request - local _ = log.info() and log.info('base ', base) + if log.info() then + log.info('base ', base) + end local pos = api.nvim_win_get_cursor(0) local line = api.nvim_get_current_line() @@ -2248,8 +2366,8 @@ function lsp.formatexpr(opts) } local response = client.request_sync('textDocument/rangeFormatting', params, timeout_ms, bufnr) - if response.result then - vim.lsp.util.apply_text_edits(response.result, 0, client.offset_encoding) + if response and response.result then + lsp.util.apply_text_edits(response.result, 0, client.offset_encoding) return 0 end end @@ -2270,8 +2388,8 @@ end ---@param flags string See |tag-function| --- ---@return table[] tags A list of matching tags -function lsp.tagfunc(...) - return require('vim.lsp.tagfunc')(...) +function lsp.tagfunc(pattern, flags) + return require('vim.lsp.tagfunc')(pattern, flags) end ---Checks whether a client is stopped. @@ -2330,6 +2448,7 @@ function lsp.get_log_path() return log.get_filename() end +---@private --- Invokes a function for each LSP client attached to a buffer. --- ---@param bufnr integer Buffer number @@ -2338,9 +2457,10 @@ end --- buffer number as arguments. Example: --- <pre>lua --- vim.lsp.for_each_buffer_client(0, function(client, client_id, bufnr) ---- print(vim.inspect(client)) +--- vim.print(client) --- end) --- </pre> +---@deprecated use lsp.get_active_clients({ bufnr = bufnr }) with regular loop function lsp.for_each_buffer_client(bufnr, fn) return for_each_buffer_client(bufnr, fn) end @@ -2354,15 +2474,25 @@ function lsp.with(handler, override_config) end end +--- Enable/disable/toggle inlay hints for a buffer +---@param bufnr (integer) Buffer handle, or 0 for current +---@param enable (boolean|nil) true/false to enable/disable, nil to toggle +function lsp.inlay_hint(bufnr, enable) + return require('vim.lsp.inlay_hint')(bufnr, enable) +end + --- Helper function to use when implementing a handler. --- This will check that all of the keys in the user configuration --- are valid keys and make sense to include for this handler. --- --- Will error on invalid keys (i.e. keys that do not exist in the options) +--- @param name string +--- @param options table<string,any> +--- @param user_config table<string,any> function lsp._with_extend(name, options, user_config) user_config = user_config or {} - local resulting_config = {} + local resulting_config = {} --- @type table<string,any> for k, v in pairs(user_config) do if options[k] == nil then error( |