From bdf6d8733e2a954031de86e9a2d09c3b03f6641d Mon Sep 17 00:00:00 2001 From: Jason Hansen Date: Mon, 20 Feb 2023 23:24:47 -0700 Subject: fix(lsp): wrong format of bufnr and client order in error message (#22336) --- runtime/lua/vim/lsp.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index c5392ac154..0a620e1367 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1736,8 +1736,8 @@ function lsp.buf_detach_client(bufnr, client_id) vim.notify( string.format( 'Buffer (id: %d) is not attached to client (id: %d). Cannot detach.', - client_id, - bufnr + bufnr, + client_id ) ) return -- cgit From 1803dadb209b9193ad8673b243091a4602b3a855 Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 24 Feb 2023 19:55:50 +0800 Subject: refactor(lsp): remove deprecated code (#22389) --- runtime/lua/vim/lsp.lua | 20 -------------------- 1 file changed, 20 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 0a620e1367..206c7c9b7e 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1294,26 +1294,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) - - -- 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. -- cgit From c1514d7e6762ed62dee027ecc29bafd4aae2206e Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sat, 25 Feb 2023 18:47:05 +0100 Subject: fix(lsp): fix some type annotations (#22397) --- runtime/lua/vim/lsp.lua | 97 ++++++++++++++++++++++++------------------------- 1 file changed, 47 insertions(+), 50 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 206c7c9b7e..d215b4c47e 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -67,7 +67,7 @@ lsp._request_name_to_capability = { ---@private --- Concatenates and writes a list of strings to the Vim error buffer. --- ----@param {...} table[] List to write to the buffer +---@param ... string List to write to the buffer local function err_message(...) nvim_err_writeln(table.concat(vim.tbl_flatten({ ... }))) nvim_command('redraw') @@ -76,9 +76,8 @@ end ---@private --- Returns the buffer number for the given {bufnr}. --- ----@param bufnr (number|nil) Buffer number to resolve. Defaults to the current ----buffer if not given. ----@returns bufnr (number) Number of requested buffer +---@param bufnr (integer|nil) Buffer number to resolve. Defaults to current buffer +---@return integer bufnr local function resolve_bufnr(bufnr) validate({ bufnr = { bufnr, 'n', true } }) if bufnr == nil or bufnr == 0 then @@ -104,7 +103,7 @@ end --- Checks whether a given path is a directory. --- ---@param filename (string) path to check ----@returns true if {filename} exists and is a directory, false otherwise +---@return boolean # true if {filename} exists and is a directory, false otherwise local function is_dir(filename) validate({ filename = { filename, 's' } }) local stat = uv.fs_stat(filename) @@ -133,7 +132,7 @@ local format_line_ending = { ---@private ---@param bufnr (number) ----@returns (string) +---@return string local function buf_get_line_ending(bufnr) return format_line_ending[nvim_buf_get_option(bufnr, 'fileformat')] or '\n' end @@ -142,7 +141,7 @@ local client_index = 0 ---@private --- Returns a new, unused client id. --- ----@returns (number) client id +---@return integer client_id local function next_client_id() client_index = client_index + 1 return client_index @@ -197,7 +196,7 @@ lsp.client_errors = tbl_extend( --- Normalizes {encoding} to valid LSP encoding names. --- ---@param encoding (string) Encoding to normalize ----@returns (string) normalized encoding name +---@return string # normalized encoding name local function validate_encoding(encoding) validate({ encoding = { encoding, 's' }, @@ -215,9 +214,8 @@ end --- Parses a command invocation into the command itself and its args. If there --- are no arguments, an empty table is returned as the second argument. --- ----@param input (List) ----@returns (string) the command ----@returns (list of strings) its arguments +---@param input string[] +---@return string command, string[] args #the command and arguments function lsp._cmd_parts(input) validate({ cmd = { @@ -244,9 +242,9 @@ end ---@private --- Augments a validator function with support for optional (nil) values. --- ----@param fn (fun(v)) The original validator function; should return a +---@param fn (fun(v): boolean) The original validator function; should return a ---bool. ----@returns (fun(v)) The augmented function. Also returns true if {v} is +---@return fun(v): boolean # The augmented function. Also returns true if {v} is ---`nil`. local function optional_validator(fn) return function(v) @@ -258,10 +256,8 @@ end --- Validates a client configuration as given to |vim.lsp.start_client()|. --- ---@param config (table) ----@returns (table) "Cleaned" config, containing only the command, its +---@return table config Cleaned config, containing the command, its ---arguments, and a valid encoding. ---- ----@see |vim.lsp.start_client()| local function validate_client_config(config) validate({ config = { config, 't' }, @@ -314,7 +310,7 @@ end --- Returns full text of buffer {bufnr} as a string. --- ---@param bufnr (number) Buffer handle, or 0 for current. ----@returns Buffer text as string. +---@return string # Buffer text as string. local function buf_get_full_text(bufnr) local line_ending = buf_get_line_ending(bufnr) local text = table.concat(nvim_buf_get_lines(bufnr, 0, -1, true), line_ending) @@ -331,7 +327,7 @@ end --- even if it has side effects. --- ---@param fn (function) Function to run ----@returns (function) Memoized function +---@return function fn Memoized function local function once(fn) local value local ran = false @@ -373,7 +369,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|userdata uv_timer + --- @field timer nil|uv.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 number how many clients are using this group @@ -648,7 +644,7 @@ do if debounce == 0 then send_changes(bufnr, group.sync_kind, state, buf_state) else - local timer = uv.new_timer() + local timer = assert(uv.new_timer(), 'Must be able to create timer') buf_state.timer = timer timer:start( debounce, @@ -1006,7 +1002,7 @@ end --- server will base its workspaceFolders, rootUri, and rootPath --- on initialization. --- ----@returns Client id. |vim.lsp.get_client_by_id()| Note: client may not be +---@return integer|nil client_id. |vim.lsp.get_client_by_id()| Note: client may not be --- fully initialized. Use `on_init` to do any actions once --- the client has been initialized. function lsp.start_client(config) @@ -1037,7 +1033,7 @@ function lsp.start_client(config) --- Returns the default handler if the user hasn't set a custom one. --- ---@param method (string) LSP method name - ---@returns (fn) The handler for the given method, if defined, or the default from |vim.lsp.handlers| + ---@return function|nil The handler for the given method, if defined, or the default from |vim.lsp.handlers| local function resolve_handler(method) return handlers[method] or default_handlers[method] end @@ -1343,11 +1339,11 @@ function lsp.start_client(config) --- This is a thin wrapper around {client.rpc.request} with some additional --- checks for capabilities and handler availability. --- - ---@param method (string) LSP method name. - ---@param params (table) LSP request params. - ---@param handler (function|nil) Response |lsp-handler| for this method. - ---@param bufnr (number) Buffer handle (0 for current). - ---@returns ({status}, [request_id]): {status} is a bool indicating + ---@param method string LSP method name. + ---@param params table LSP request params. + ---@param handler lsp-handler|nil Response |lsp-handler| for this method. + ---@param bufnr integer Buffer handle (0 for current). + ---@return boolean status, integer|nil request_id {status} is a bool indicating ---whether the request was successful. If it is `false`, then it will ---always be `false` (the client has shutdown). If it was ---successful, then it will return {request_id} as the @@ -1395,10 +1391,11 @@ function lsp.start_client(config) ---@param timeout_ms (number|nil) Maximum time in milliseconds to wait for --- a result. Defaults to 1000 ---@param bufnr (number) Buffer handle (0 for current). - ---@returns { err=err, result=result }, a dictionary, where `err` and `result` come from the |lsp-handler|. - ---On timeout, cancel or error, returns `(nil, err)` where `err` is a - ---string describing the failure reason. If the request was unsuccessful - ---returns `nil`. + ---@return {err: lsp.ResponseError|nil, result:any}|nil, string|nil err # a dictionary, where + --- `err` and `result` come from the |lsp-handler|. + --- On timeout, cancel or error, returns `(nil, err)` where `err` is a + --- string describing the failure reason. If the request was unsuccessful + --- returns `nil`. ---@see |vim.lsp.buf_request_sync()| function client.request_sync(method, params, timeout_ms, bufnr) local request_result = nil @@ -1429,7 +1426,7 @@ function lsp.start_client(config) --- ---@param method string LSP method name. ---@param params table|nil LSP request params. - ---@returns {status} (bool) true if the notification was successful. + ---@return boolean status true if the notification was successful. ---If it is false, then it will always be false ---(the client has shutdown). function client.notify(method, params) @@ -1443,7 +1440,7 @@ function lsp.start_client(config) --- Cancels a request with a given request id. --- ---@param id (number) id of request to cancel - ---@returns true if any client returns true; false otherwise + ---@return boolean status true if notification was successful. false otherwise ---@see |vim.lsp.client.notify()| function client.cancel_request(id) validate({ id = { id, 'n' } }) @@ -1489,7 +1486,7 @@ function lsp.start_client(config) ---@private --- Checks whether a client is stopped. --- - ---@returns (bool) true if client is stopped or in the process of being + ---@return boolean # true if client is stopped or in the process of being ---stopped; false otherwise function client.is_stopped() return rpc.is_closing() @@ -1497,7 +1494,7 @@ function lsp.start_client(config) ---@private --- Runs the on_attach function from the client's config if it was defined. - ---@param bufnr (number) Buffer number + ---@param bufnr integer Buffer number function client._on_attach(bufnr) text_document_did_open_handler(bufnr, client) @@ -1771,8 +1768,8 @@ end --- Returns list of buffers attached to client_id. --- ----@param client_id number client id ----@returns list of buffer ids +---@param client_id integer client id +---@return integer[] buffers list of buffer ids function lsp.get_buffers_by_client_id(client_id) local client = lsp.get_client_by_id(client_id) return client and vim.tbl_keys(client.attached_buffers) or {} @@ -1906,7 +1903,7 @@ api.nvim_create_autocmd('VimLeavePre', { ---@param handler function|nil See |lsp-handler| --- If nil, follows resolution strategy defined in |lsp-handler-configuration| --- ----@returns 2-tuple: +---@return table, fun() 2-tuple: --- - Map of client-id:request-id pairs for all successful requests. --- - Function which can be used to cancel all the requests. You could instead --- iterate all clients and call their `cancel_request()` methods. @@ -1964,10 +1961,10 @@ end ---@param method (string) LSP method name ---@param params (table|nil) Parameters to send to the server ---@param callback (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 --- ----@returns (function) A function that will cancel all requests which is the same as the one returned from `buf_request`. +--- 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 = {} local result_count = 0 @@ -2008,9 +2005,9 @@ end ---@param timeout_ms (number|nil) Maximum time in milliseconds to wait for a --- result. Defaults to 1000 --- ----@returns Map of client_id:request_result. On timeout, cancel or error, ---- returns `(nil, err)` where `err` is a string describing the failure ---- reason. +---@return table|nil result, string|nil err Map of client_id:request_result. +--- On timeout, cancel or error, returns `(nil, err)` where `err` is a string describing +--- the failure reason. function lsp.buf_request_sync(bufnr, method, params, timeout_ms) local request_results @@ -2035,7 +2032,7 @@ end ---@param method (string) Name of the request method ---@param params (any) Arguments to send to the server --- ----@returns true if any client returns true; false otherwise +---@return boolean success true if any client returns true; false otherwise function lsp.buf_notify(bufnr, method, params) validate({ bufnr = { bufnr, 'n', true }, @@ -2172,7 +2169,7 @@ function lsp.formatexpr(opts) for _, client in pairs(lsp.get_active_clients({ bufnr = bufnr })) do if client.supports_method('textDocument/rangeFormatting') then local params = util.make_formatting_params() - local end_line = vim.fn.getline(end_lnum) + local end_line = vim.fn.getline(end_lnum) --[[@as string]] local end_col = util._str_utfindex_enc(end_line, nil, client.offset_encoding) params.range = { start = { @@ -2207,7 +2204,7 @@ end ---@param pattern string Pattern used to find a workspace symbol ---@param flags string See |tag-function| --- ----@returns A list of matching tags +---@return table[] tags A list of matching tags function lsp.tagfunc(...) return require('vim.lsp.tagfunc')(...) end @@ -2215,7 +2212,7 @@ end ---Checks whether a client is stopped. --- ---@param client_id (number) ----@returns true if client is stopped, false otherwise. +---@return boolean stopped true if client is stopped, false otherwise. function lsp.client_is_stopped(client_id) return active_clients[client_id] == nil end @@ -2262,7 +2259,7 @@ function lsp.set_log_level(level) end --- Gets the path of the logfile used by the LSP client. ----@returns (String) Path to logfile. +---@return string path to log file function lsp.get_log_path() return log.get_filename() end -- cgit From f89e3497c88f59916e3bce2e902be9b0f8bf15ba Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 27 Feb 2023 14:19:41 -0600 Subject: docs(lsp): update cmd_env description (#22438) --- runtime/lua/vim/lsp.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index d215b4c47e..61a06ff7a7 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -908,11 +908,11 @@ end --- the `cmd` process. Not related to `root_dir`. --- --- - cmd_env: (table) Environment flags to pass to the LSP on ---- spawn. Can be specified using keys like a map or as a list with `k=v` ---- pairs or both. Non-string values are coerced to string. +--- spawn. Must be specified using a map-like table. +--- Non-string values are coerced to string. --- Example: ---
----                   { "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; }
+---                   { PORT = 8080; HOST = "0.0.0.0"; }
 ---       
--- --- - detached: (boolean, default true) Daemonize the server process so that it runs in a -- cgit From 96d3616a531b2626e3ab9b4fa2c5dea03a927717 Mon Sep 17 00:00:00 2001 From: Jens Claes Date: Wed, 1 Mar 2023 11:35:16 +0100 Subject: fix(lsp): callHierarchy methods also require the callHierarchyProvider (#22427) --- runtime/lua/vim/lsp.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 61a06ff7a7..fed56ff846 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -46,6 +46,8 @@ lsp._request_name_to_capability = { ['textDocument/typeDefinition'] = { 'typeDefinitionProvider' }, ['textDocument/documentSymbol'] = { 'documentSymbolProvider' }, ['textDocument/prepareCallHierarchy'] = { 'callHierarchyProvider' }, + ['callHierarchy/incomingCalls'] = { 'callHierarchyProvider' }, + ['callHierarchy/outgoingCalls'] = { 'callHierarchyProvider' }, ['textDocument/rename'] = { 'renameProvider' }, ['textDocument/prepareRename'] = { 'renameProvider', 'prepareProvider' }, ['textDocument/codeAction'] = { 'codeActionProvider' }, -- cgit From 896d672736b32a8f4a4fa51844b44f266dcdcc6c Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Wed, 1 Mar 2023 15:33:13 +0100 Subject: fix(lsp): use buffer scheme for files not stored on disk (#22407) Sending `didOpen` with a `file` scheme causes problems with some language servers because they expect the file to exist on disk. See https://github.com/microsoft/language-server-protocol/pull/1679 --- runtime/lua/vim/lsp.lua | 54 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 12 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index fed56ff846..a6d550f48f 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -367,7 +367,7 @@ do --- @field offset_encoding "utf-8"|"utf-16"|"utf-32" --- --- @class CTBufferState - --- @field name string name of the buffer + --- @field uri string uri of the buffer --- @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 @@ -486,8 +486,12 @@ do if buf_state then buf_state.refs = buf_state.refs + 1 else + local uri = vim.uri_from_bufnr(bufnr) + if not uv.fs_stat(api.nvim_buf_get_name(bufnr)) then + uri = uri:gsub('^file://', 'buffer://') + end buf_state = { - name = api.nvim_buf_get_name(bufnr), + uri = uri, lines = {}, lines_tmp = {}, pending_changes = {}, @@ -502,12 +506,26 @@ do end ---@private - function changetracking._get_and_set_name(client, bufnr, name) + ---@param client table + ---@param bufnr integer + ---@return string uri + function changetracking._get_uri(client, bufnr) local state = state_by_group[get_group(client)] or {} local buf_state = (state.buffers or {})[bufnr] - local old_name = buf_state.name - buf_state.name = name - return old_name + return assert(buf_state.uri, 'Must have an URI set') + end + + ---@private + ---@param client table + ---@param bufnr integer + ---@param uri string + ---@return string uri + function changetracking._get_and_set_uri(client, bufnr, uri) + local state = state_by_group[get_group(client)] or {} + local buf_state = (state.buffers or {})[bufnr] + local old_uri = buf_state.uri + buf_state.uri = uri + return old_uri end ---@private @@ -594,7 +612,7 @@ do { text = buf_get_full_text(bufnr) }, } end - local uri = vim.uri_from_bufnr(bufnr) + local uri = buf_state.uri for _, client in pairs(state.clients) do if not client.is_stopped() and lsp.buf_is_attached(bufnr, client.id) then client.notify('textDocument/didChange', { @@ -707,11 +725,14 @@ local function text_document_did_open_handler(bufnr, client) return end local filetype = nvim_buf_get_option(bufnr, 'filetype') - + local uri = vim.uri_from_bufnr(bufnr) + if not uv.fs_stat(api.nvim_buf_get_name(bufnr)) then + uri = uri:gsub('^file://', 'buffer://') + end local params = { textDocument = { version = 0, - uri = vim.uri_from_bufnr(bufnr), + uri = uri, languageId = client.config.get_language_id(bufnr, filetype), text = buf_get_full_text(bufnr), }, @@ -1560,8 +1581,13 @@ local function text_document_did_save_handler(bufnr) local text = once(buf_get_full_text) for_each_buffer_client(bufnr, function(client) 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 + local old_uri = changetracking._get_and_set_uri(client, bufnr, uri) + if old_uri and name ~= old_uri then + client.notify('textDocument/didClose', { + textDocument = { + uri = old_uri, + }, + }) client.notify('textDocument/didOpen', { textDocument = { version = 0, @@ -1664,8 +1690,12 @@ function lsp.buf_attach_client(bufnr, client_id) end) end, on_detach = function() - local params = { textDocument = { uri = uri } } for_each_buffer_client(bufnr, function(client, _) + local params = { + textDocument = { + uri = changetracking._get_uri(client, bufnr), + }, + } changetracking.reset_buf(client, bufnr) if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then client.notify('textDocument/didClose', params) -- cgit From 014981c9006f9b96b8045e609dc27f4a84da5263 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Wed, 1 Mar 2023 09:47:56 -0700 Subject: fix(lsp): only fire LspDetach for attached buffers (#22468) If the LSP server fails to start then the client never initializes and thus never calls its on_attach function and an LspAttach event is never fired. However, the on_exit function still fires a LspDetach event, so user autocommands that attempt to "clean up" in LspDetach may run into problems if they assume that the buffer was already attached. The solution is to only fire an LspDetach event if the buffer was already attached in the first place. --- runtime/lua/vim/lsp.lua | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index a6d550f48f..a56e141c29 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1155,14 +1155,19 @@ function lsp.start_client(config) pcall(config.on_exit, code, signal, client_id) end + local client = active_clients[client_id] and active_clients[client_id] + or uninitialized_clients[client_id] + for bufnr, client_ids in pairs(all_buffer_active_clients) do if client_ids[client_id] then vim.schedule(function() - nvim_exec_autocmds('LspDetach', { - buffer = bufnr, - modeline = false, - data = { client_id = client_id }, - }) + if client and client.attached_buffers[bufnr] then + nvim_exec_autocmds('LspDetach', { + buffer = bufnr, + modeline = false, + data = { client_id = client_id }, + }) + end local namespace = vim.lsp.diagnostic.get_namespace(client_id) vim.diagnostic.reset(namespace, bufnr) @@ -1178,8 +1183,6 @@ function lsp.start_client(config) -- Schedule the deletion of the client object so that it exists in the execution of LspDetach -- autocommands vim.schedule(function() - local client = active_clients[client_id] and active_clients[client_id] - or uninitialized_clients[client_id] active_clients[client_id] = nil uninitialized_clients[client_id] = nil -- cgit From 706bcab75eaad2c370d61bf828531054439d3a3e Mon Sep 17 00:00:00 2001 From: Jaehwang Jung Date: Tue, 7 Mar 2023 15:17:52 +0900 Subject: docs(lsp): change type annotations from number → integer (#22510) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- runtime/lua/vim/lsp.lua | 72 ++++++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index a56e141c29..9a0b3f3100 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -363,7 +363,7 @@ do --- smallest debounce interval is used and we don't group clients by different intervals. --- --- @class CTGroup - --- @field sync_kind number TextDocumentSyncKind, considers config.flags.allow_incremental_sync + --- @field sync_kind integer TextDocumentSyncKind, considers config.flags.allow_incremental_sync --- @field offset_encoding "utf-8"|"utf-16"|"utf-32" --- --- @class CTBufferState @@ -374,12 +374,12 @@ do --- @field timer nil|uv.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 number how many clients are using this group + --- @field refs integer how many clients are using this group --- --- @class CTGroupState - --- @field buffers table - --- @field debounce number debounce duration in ms - --- @field clients table clients using this state. {client_id, client} + --- @field buffers table + --- @field debounce integer debounce duration in ms + --- @field clients table clients using this state. {client_id, client} ---@private ---@param group CTGroup @@ -568,7 +568,7 @@ do -- -- This turns the debounce into a kind of client rate limiting -- - ---@param debounce number + ---@param debounce integer ---@param buf_state CTBufferState ---@return number local function next_debounce(debounce, buf_state) @@ -585,8 +585,8 @@ do end ---@private - ---@param bufnr number - ---@param sync_kind number protocol.TextDocumentSyncKind + ---@param bufnr integer + ---@param sync_kind integer protocol.TextDocumentSyncKind ---@param state CTGroupState ---@param buf_state CTBufferState local function send_changes(bufnr, sync_kind, state, buf_state) @@ -714,7 +714,7 @@ end ---@private --- Default handler for the 'textDocument/didOpen' LSP notification. --- ----@param bufnr number Number of the buffer, or 0 for current +---@param bufnr integer Number of the buffer, or 0 for current ---@param client table Client object local function text_document_did_open_handler(bufnr, client) changetracking.init(client, bufnr) @@ -1095,7 +1095,7 @@ function lsp.start_client(config) ---@private --- Invoked when the client operation throws an error. --- - ---@param code (number) Error code + ---@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 ---`vim.lsp.rpc.client_errors[code]` to get a human-friendly name. @@ -1148,8 +1148,8 @@ function lsp.start_client(config) ---@private --- Invoked on client exit. --- - ---@param code (number) exit code of the process - ---@param signal (number) the signal used to terminate (if any) + ---@param code (integer) exit code of the process + ---@param signal (integer) the signal used to terminate (if any) function dispatch.on_exit(code, signal) if config.on_exit then pcall(config.on_exit, code, signal, client_id) @@ -1414,9 +1414,9 @@ function lsp.start_client(config) --- ---@param method (string) LSP method name. ---@param params (table) LSP request params. - ---@param timeout_ms (number|nil) Maximum time in milliseconds to wait for + ---@param timeout_ms (integer|nil) Maximum time in milliseconds to wait for --- a result. Defaults to 1000 - ---@param bufnr (number) Buffer handle (0 for current). + ---@param bufnr (integer) Buffer handle (0 for current). ---@return {err: lsp.ResponseError|nil, result:any}|nil, string|nil err # a dictionary, where --- `err` and `result` come from the |lsp-handler|. --- On timeout, cancel or error, returns `(nil, err)` where `err` is a @@ -1465,7 +1465,7 @@ function lsp.start_client(config) ---@private --- Cancels a request with a given request id. --- - ---@param id (number) id of request to cancel + ---@param id (integer) id of request to cancel ---@return boolean status true if notification was successful. false otherwise ---@see |vim.lsp.client.notify()| function client.cancel_request(id) @@ -1622,8 +1622,8 @@ end --- --- Without calling this, the server won't be notified of changes to a buffer. --- ----@param bufnr (number) Buffer handle, or 0 for current ----@param client_id (number) Client id +---@param bufnr (integer) Buffer handle, or 0 for current +---@param client_id (integer) Client id function lsp.buf_attach_client(bufnr, client_id) validate({ bufnr = { bufnr, 'n', true }, @@ -1734,8 +1734,8 @@ end --- Note: While the server is notified that the text document (buffer) --- was closed, it is still able to send notifications should it ignore this notification. --- ----@param bufnr number Buffer handle, or 0 for current ----@param client_id number Client id +---@param bufnr integer Buffer handle, or 0 for current +---@param client_id integer Client id function lsp.buf_detach_client(bufnr, client_id) validate({ bufnr = { bufnr, 'n', true }, @@ -1785,8 +1785,8 @@ end --- Checks if a buffer is attached for a particular client. --- ----@param bufnr (number) Buffer handle, or 0 for current ----@param client_id (number) the client id +---@param bufnr (integer) Buffer handle, or 0 for current +---@param client_id (integer) the client id function lsp.buf_is_attached(bufnr, client_id) return (all_buffer_active_clients[resolve_bufnr(bufnr)] or {})[client_id] == true end @@ -1794,7 +1794,7 @@ end --- Gets a client by id, or nil if the id is invalid. --- The returned client may not yet be fully initialized. --- ----@param client_id number client id +---@param client_id integer client id --- ---@returns |vim.lsp.client| object, or nil function lsp.get_client_by_id(client_id) @@ -1821,7 +1821,7 @@ end --- By default asks the server to shutdown, unless stop was requested --- already for this client, then force-shutdown is attempted. --- ----@param client_id number|table id or |vim.lsp.client| object, or list thereof +---@param client_id integer|table id or |vim.lsp.client| object, or list thereof ---@param force boolean|nil shutdown forcefully function lsp.stop_client(client_id, force) local ids = type(client_id) == 'table' and client_id or { client_id } @@ -1837,8 +1837,8 @@ function lsp.stop_client(client_id, force) end ---@class vim.lsp.get_active_clients.filter ----@field id number|nil Match clients by id ----@field bufnr number|nil match clients attached to the given buffer +---@field id integer|nil Match clients by id +---@field bufnr integer|nil match clients attached to the given buffer ---@field name string|nil match clients by name --- Get active clients. @@ -1932,7 +1932,7 @@ api.nvim_create_autocmd('VimLeavePre', { --- Sends an async request for all active clients attached to the --- buffer. --- ----@param bufnr (number) Buffer handle, or 0 for current. +---@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 handler function|nil See |lsp-handler| @@ -1992,7 +1992,7 @@ end ---Parameters are the same as |vim.lsp.buf_request()| but the return result and callback are ---different. --- ----@param bufnr (number) Buffer handle, or 0 for current. +---@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 (function) The callback to call when all requests are finished. @@ -2034,10 +2034,10 @@ end --- Parameters are the same as |vim.lsp.buf_request()| but the return result is --- different. Wait maximum of {timeout_ms} (default 1000) ms. --- ----@param bufnr (number) Buffer handle, or 0 for current. +---@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 timeout_ms (number|nil) Maximum time in milliseconds to wait for a +---@param timeout_ms (integer|nil) Maximum time in milliseconds to wait for a --- result. Defaults to 1000 --- ---@return table|nil result, string|nil err Map of client_id:request_result. @@ -2106,10 +2106,10 @@ end ---@see |complete-items| ---@see |CompleteDone| --- ----@param findstart number 0 or 1, decides behavior ----@param base number findstart=0, text to match against +---@param findstart integer 0 or 1, decides behavior +---@param base integer findstart=0, text to match against --- ----@returns (number) Decided by {findstart}: +---@returns (integer) Decided by {findstart}: --- - 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) @@ -2246,7 +2246,7 @@ end ---Checks whether a client is stopped. --- ----@param client_id (number) +---@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 @@ -2255,7 +2255,7 @@ end --- Gets a map of client_id:client pairs for the given buffer, where each value --- is a |vim.lsp.client| object. --- ----@param bufnr (number|nil): Buffer handle, or 0 for current +---@param bufnr (integer|nil): Buffer handle, or 0 for current ---@returns (table) Table of (client_id, client) pairs ---@deprecated Use |vim.lsp.get_active_clients()| instead. function lsp.buf_get_clients(bufnr) @@ -2284,7 +2284,7 @@ lsp.log_levels = log.levels --- ---@see |vim.lsp.log_levels| --- ----@param level (number|string) the case insensitive level name or number +---@param level (integer|string) the case insensitive level name or number function lsp.set_log_level(level) if type(level) == 'string' or type(level) == 'number' then log.set_level(level) @@ -2301,7 +2301,7 @@ end --- Invokes a function for each LSP client attached to a buffer. --- ----@param bufnr number Buffer number +---@param bufnr integer Buffer number ---@param fn function Function to run on each client attached to buffer --- {bufnr}. The function takes the client, client ID, and --- buffer number as arguments. Example: -- cgit From 9ef7297ef142354ace8b1f3f277d0eee3cfdc6d4 Mon Sep 17 00:00:00 2001 From: Michal Liszcz Date: Thu, 9 Mar 2023 15:12:56 +0100 Subject: feat(lsp): overwrite omnifunc/tagfunc set by ftplugin #22267 Problem: Some built-in ftplugins set omnifunc/tagfunc/formatexpr which causes lsp.lua:set_defaults() to skip setup of defaults for those filetypes. For example the C++ ftplugin has: omnifunc=ccomplete#Complete Last set from /usr/share/nvim/runtime/ftplugin/c.vim line 30 so the changes done in #95c65a6b221fe6e1cf91e8322e7d7571dc511a71 will always be skipped for C++ files. Solution: Overwrite omnifunc/tagfunc/formatexpr options that were set by stock ftplugin. Fixes #21001 --- runtime/lua/vim/lsp.lua | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 9a0b3f3100..cb77e9636f 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1112,19 +1112,43 @@ function lsp.start_client(config) end 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 old_bufnr = vim.fn.bufnr('') + local last_set_from = vim.fn.gettext('\n\tLast set from ') + local line = vim.fn.gettext(' line ') + + vim.cmd.buffer(bufnr) + local scriptname = vim.fn + .execute('verbose set ' .. option .. '?') + :match(last_set_from .. '(.*)' .. line .. '%d+') + vim.cmd.buffer(old_bufnr) + + if not scriptname then + return false + end + local vimruntime = vim.fn.getenv('VIMRUNTIME') + return vim.startswith(vim.fn.expand(scriptname), vim.fn.expand(vimruntime)) + end + ---@private local function set_defaults(client, bufnr) local capabilities = client.server_capabilities - if capabilities.definitionProvider and vim.bo[bufnr].tagfunc == '' then + 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 vim.bo[bufnr].omnifunc == '' then + 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 vim.bo[bufnr].formatprg == '' - and vim.bo[bufnr].formatexpr == '' + 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 -- cgit From 0ecb4d725e9e2086b045670e4c2fa8962b63d99c Mon Sep 17 00:00:00 2001 From: Jaehwang Jung Date: Fri, 10 Mar 2023 06:17:08 +0900 Subject: docs(lsp): type annotation for lsp.client (#22509) * Also fix newly found type mismatch. * Note that it generates new warnings about using @private client methods. A proper fix would be to revamp the lsp client documentation altogether. --- runtime/lua/vim/lsp.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index cb77e9636f..c85d38a50e 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -154,6 +154,8 @@ local all_buffer_active_clients = {} local uninitialized_clients = {} ---@private +---@param bufnr? integer +---@param fn fun(client: lsp.Client, client_id: integer, bufnr: integer) local function for_each_buffer_client(bufnr, fn, restrict_client_ids) validate({ fn = { fn, 'f' }, @@ -1240,6 +1242,7 @@ function lsp.start_client(config) return end + ---@class lsp.Client local client = { id = client_id, name = name, @@ -1390,7 +1393,7 @@ function lsp.start_client(config) --- checks for capabilities and handler availability. --- ---@param method string LSP method name. - ---@param params table LSP request params. + ---@param params table|nil LSP request params. ---@param handler lsp-handler|nil Response |lsp-handler| for this method. ---@param bufnr integer Buffer handle (0 for current). ---@return boolean status, integer|nil request_id {status} is a bool indicating @@ -2087,7 +2090,7 @@ function lsp.buf_request_sync(bufnr, method, params, timeout_ms) end --- Send a notification to a server ----@param bufnr (number|nil) The number of the buffer +---@param bufnr (integer|nil) The number of the buffer ---@param method (string) Name of the request method ---@param params (any) Arguments to send to the server --- -- cgit From 236c20795eb9f11e21e0719b735ea741711acc08 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sat, 11 Mar 2023 07:35:23 +0100 Subject: revert: "fix(lsp): use buffer scheme for files not stored on disk" (#22604) Although using `buffer://` for unsaved file buffers fixes issues with language servers like eclipse.jdt.ls or ansible-language-server, it breaks completion and signature help for clangd. A regression is worse than a fix for something else, so this reverts commit 896d672736b32a8f4a4fa51844b44f266dcdcc6c. The spec change is also still in dicussion, see https://github.com/microsoft/language-server-protocol/pull/1679#discussion_r1130704886 --- runtime/lua/vim/lsp.lua | 54 +++++++++++-------------------------------------- 1 file changed, 12 insertions(+), 42 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index c85d38a50e..117b32dc57 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -369,7 +369,7 @@ do --- @field offset_encoding "utf-8"|"utf-16"|"utf-32" --- --- @class CTBufferState - --- @field uri string uri of the buffer + --- @field name string name of the buffer --- @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 @@ -488,12 +488,8 @@ do if buf_state then buf_state.refs = buf_state.refs + 1 else - local uri = vim.uri_from_bufnr(bufnr) - if not uv.fs_stat(api.nvim_buf_get_name(bufnr)) then - uri = uri:gsub('^file://', 'buffer://') - end buf_state = { - uri = uri, + name = api.nvim_buf_get_name(bufnr), lines = {}, lines_tmp = {}, pending_changes = {}, @@ -508,26 +504,12 @@ do end ---@private - ---@param client table - ---@param bufnr integer - ---@return string uri - function changetracking._get_uri(client, bufnr) - local state = state_by_group[get_group(client)] or {} - local buf_state = (state.buffers or {})[bufnr] - return assert(buf_state.uri, 'Must have an URI set') - end - - ---@private - ---@param client table - ---@param bufnr integer - ---@param uri string - ---@return string uri - function changetracking._get_and_set_uri(client, bufnr, uri) + function changetracking._get_and_set_name(client, bufnr, name) local state = state_by_group[get_group(client)] or {} local buf_state = (state.buffers or {})[bufnr] - local old_uri = buf_state.uri - buf_state.uri = uri - return old_uri + local old_name = buf_state.name + buf_state.name = name + return old_name end ---@private @@ -614,7 +596,7 @@ do { text = buf_get_full_text(bufnr) }, } end - local uri = buf_state.uri + local uri = vim.uri_from_bufnr(bufnr) for _, client in pairs(state.clients) do if not client.is_stopped() and lsp.buf_is_attached(bufnr, client.id) then client.notify('textDocument/didChange', { @@ -727,14 +709,11 @@ local function text_document_did_open_handler(bufnr, client) return end local filetype = nvim_buf_get_option(bufnr, 'filetype') - local uri = vim.uri_from_bufnr(bufnr) - if not uv.fs_stat(api.nvim_buf_get_name(bufnr)) then - uri = uri:gsub('^file://', 'buffer://') - end + local params = { textDocument = { version = 0, - uri = uri, + uri = vim.uri_from_bufnr(bufnr), languageId = client.config.get_language_id(bufnr, filetype), text = buf_get_full_text(bufnr), }, @@ -1611,13 +1590,8 @@ local function text_document_did_save_handler(bufnr) local text = once(buf_get_full_text) for_each_buffer_client(bufnr, function(client) local name = api.nvim_buf_get_name(bufnr) - local old_uri = changetracking._get_and_set_uri(client, bufnr, uri) - if old_uri and name ~= old_uri then - client.notify('textDocument/didClose', { - textDocument = { - uri = old_uri, - }, - }) + local old_name = changetracking._get_and_set_name(client, bufnr, name) + if old_name and name ~= old_name then client.notify('textDocument/didOpen', { textDocument = { version = 0, @@ -1720,12 +1694,8 @@ function lsp.buf_attach_client(bufnr, client_id) end) end, on_detach = function() + local params = { textDocument = { uri = uri } } for_each_buffer_client(bufnr, function(client, _) - local params = { - textDocument = { - uri = changetracking._get_uri(client, bufnr), - }, - } changetracking.reset_buf(client, bufnr) if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then client.notify('textDocument/didClose', params) -- cgit From 23dc2a59b6e13b0dbab47c6c64ac5a55095b258b Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sat, 11 Mar 2023 14:50:14 +0100 Subject: fix(lsp): send didClose on buffer rename (#22623) Subset of https://github.com/neovim/neovim/pull/22407 that was reverted in https://github.com/neovim/neovim/pull/22604 If a buffer is renamed sending `didClose` for the old buffer helps ensure the language server doesn't keep a stale document in memory. --- runtime/lua/vim/lsp.lua | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 117b32dc57..1896543da3 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1,3 +1,4 @@ +---@diagnostic disable: invisible local default_handlers = require('vim.lsp.handlers') local log = require('vim.lsp.log') local lsp_rpc = require('vim.lsp.rpc') @@ -1592,6 +1593,11 @@ local function text_document_did_save_handler(bufnr) 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 + client.notify('textDocument/didClose', { + textDocument = { + uri = vim.uri_from_fname(old_name), + }, + }) client.notify('textDocument/didOpen', { textDocument = { version = 0, -- cgit From 0ce626b783dcca8eed08a2bb5a18e2c3ef931fbe Mon Sep 17 00:00:00 2001 From: Jaehwang Jung Date: Sat, 11 Mar 2023 22:50:53 +0900 Subject: docs(lsp): more precise type annotations (#22621) --- runtime/lua/vim/lsp.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 1896543da3..39665a3d4f 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1038,7 +1038,7 @@ function lsp.start_client(config) --- Returns the default handler if the user hasn't set a custom one. --- ---@param method (string) LSP method name - ---@return function|nil The handler for the given method, if defined, or the default from |vim.lsp.handlers| + ---@return lsp-handler|nil The handler for the given method, if defined, or the default from |vim.lsp.handlers| local function resolve_handler(method) return handlers[method] or default_handlers[method] end @@ -1938,7 +1938,7 @@ api.nvim_create_autocmd('VimLeavePre', { ---@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 handler function|nil See |lsp-handler| +---@param handler lsp-handler|nil See |lsp-handler| --- If nil, follows resolution strategy defined in |lsp-handler-configuration| --- ---@return table, fun() 2-tuple: @@ -1998,9 +1998,10 @@ end ---@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 (function) The callback to call when all requests are finished. +---@param callback fun(request_results: table) (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 +--- 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) @@ -2043,9 +2044,8 @@ end ---@param timeout_ms (integer|nil) Maximum time in milliseconds to wait for a --- result. Defaults to 1000 --- ----@return table|nil result, string|nil err Map of client_id:request_result. ---- On timeout, cancel or error, returns `(nil, err)` where `err` is a string describing ---- the failure reason. +---@return table|nil (table) result Map of client_id:request_result. +---@return string|nil err On timeout, cancel, or error, `err` is a string describing the failure reason, and `result` is nil. function lsp.buf_request_sync(bufnr, method, params, timeout_ms) local request_results -- cgit From 6162269fa3107b23fd18c6c6d0e3e8fb73cfb45e Mon Sep 17 00:00:00 2001 From: August Masquelier <31262046+levouh@users.noreply.github.com> Date: Fri, 17 Mar 2023 05:26:13 -0600 Subject: fix(lsp): avoid switching buffers on lsp attach (#22689) --- runtime/lua/vim/lsp.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 39665a3d4f..7e8c73ddb6 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1101,21 +1101,21 @@ function lsp.start_client(config) return true end - local old_bufnr = vim.fn.bufnr('') local last_set_from = vim.fn.gettext('\n\tLast set from ') local line = vim.fn.gettext(' line ') + local scriptname - vim.cmd.buffer(bufnr) - local scriptname = vim.fn - .execute('verbose set ' .. option .. '?') - :match(last_set_from .. '(.*)' .. line .. '%d+') - vim.cmd.buffer(old_bufnr) + vim.api.nvim_buf_call(bufnr, function() + scriptname = vim.fn + .execute('verbose set ' .. option .. '?') + :match(last_set_from .. '(.*)' .. line .. '%d+') + end) if not scriptname then return false end - local vimruntime = vim.fn.getenv('VIMRUNTIME') - return vim.startswith(vim.fn.expand(scriptname), vim.fn.expand(vimruntime)) + + return vim.startswith(vim.fn.expand(scriptname), vim.fn.expand('$VIMRUNTIME')) end ---@private -- cgit From 999cb36c2bb64d1f93bb6f8e607e0eb26eadcd63 Mon Sep 17 00:00:00 2001 From: Michal Liszcz Date: Wed, 5 Apr 2023 14:02:08 +0200 Subject: refactor(lsp): do not parse verbose output when overwriting options (#22810) --- runtime/lua/vim/lsp.lua | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 7e8c73ddb6..2d39f2d45d 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1101,21 +1101,16 @@ function lsp.start_client(config) return true end - local last_set_from = vim.fn.gettext('\n\tLast set from ') - local line = vim.fn.gettext(' line ') - local scriptname - - vim.api.nvim_buf_call(bufnr, function() - scriptname = vim.fn - .execute('verbose set ' .. option .. '?') - :match(last_set_from .. '(.*)' .. line .. '%d+') - 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 not scriptname then + if #scriptinfo ~= 1 then return false end - return vim.startswith(vim.fn.expand(scriptname), vim.fn.expand('$VIMRUNTIME')) + return vim.startswith(scriptinfo[1].name, vim.fn.expand('$VIMRUNTIME')) end ---@private -- cgit From bfb28b62dab756ec76a73506c2070ddf491a0cdd Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Thu, 13 Apr 2023 15:29:13 -0600 Subject: refactor: remove modelines from Lua files Now that we have builtin EditorConfig support and a formatting check in CI, these are not necessary. --- runtime/lua/vim/lsp.lua | 1 - 1 file changed, 1 deletion(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 2d39f2d45d..3db3545786 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -2384,4 +2384,3 @@ lsp.commands = setmetatable({}, { }) return lsp --- vim:sw=2 ts=2 et -- cgit From 4d04feb6629cb049cb2a13ba35f0c8d3c6b67ff4 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 14 Apr 2023 10:39:57 +0200 Subject: feat(lua): vim.tbl_contains supports general tables and predicates (#23040) * feat(lua): vim.tbl_contains supports general tables and predicates Problem: `vim.tbl_contains` only works for list-like tables (integer keys without gaps) and primitive values (in particular, not for nested tables). Solution: Rename `vim.tbl_contains` to `vim.list_contains` and add new `vim.tbl_contains` that works for general tables and optionally allows `value` to be a predicate function that is checked for every key. --- runtime/lua/vim/lsp.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 3db3545786..5c78bd7580 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -171,7 +171,7 @@ local function for_each_buffer_client(bufnr, fn, restrict_client_ids) if restrict_client_ids and #restrict_client_ids > 0 then local filtered_client_ids = {} for client_id in pairs(client_ids) do - if vim.tbl_contains(restrict_client_ids, client_id) then + if vim.list_contains(restrict_client_ids, client_id) then filtered_client_ids[client_id] = true end end @@ -2186,7 +2186,7 @@ function lsp.formatexpr(opts) opts = opts or {} local timeout_ms = opts.timeout_ms or 500 - if vim.tbl_contains({ 'i', 'R', 'ic', 'ix' }, vim.fn.mode()) then + if vim.list_contains({ 'i', 'R', 'ic', 'ix' }, vim.fn.mode()) then -- `formatexpr` is also called when exceeding `textwidth` in insert mode -- fall back to internal formatting return 1 -- cgit From 02f92978fe4f4d465260a9936dca1526bbe76931 Mon Sep 17 00:00:00 2001 From: hituzi no sippo <43565959+hituzi-no-sippo@users.noreply.github.com> Date: Wed, 10 May 2023 00:24:49 +0900 Subject: docs(lsp): fix type of `config.cmd` argument for `vim.lsp.start_client` (#23550) --- runtime/lua/vim/lsp.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 5c78bd7580..b86d41b234 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -901,7 +901,7 @@ end --- Field `cmd` in {config} is required. --- ---@param config (table) Configuration for the server: ---- - cmd: (table|string|fun(dispatchers: table):table) command string or +--- - cmd: (string[]|fun(dispatchers: table):table) command string or --- list treated like |jobstart()|. The command must launch the language server --- process. `cmd` can also be a function that creates an RPC client. --- The function receives a dispatchers table and must return a table with the -- cgit From 4e5061dba765df2a74ac4a8182f6e7fe21da125d Mon Sep 17 00:00:00 2001 From: hituzi no sippo <43565959+hituzi-no-sippo@users.noreply.github.com> Date: Wed, 10 May 2023 04:00:29 +0900 Subject: docs(lsp): fix `config.cmd` argument for `vim.lsp.start_client` (#23560) --- runtime/lua/vim/lsp.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index b86d41b234..a724593188 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -901,8 +901,8 @@ end --- Field `cmd` in {config} is required. --- ---@param config (table) Configuration for the server: ---- - cmd: (string[]|fun(dispatchers: table):table) command string or ---- list treated like |jobstart()|. The command must launch the language 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. --- The function receives a dispatchers table and must return a table with the --- functions `request`, `notify`, `is_closing` and `terminate` -- cgit From 08991b078267e5de0a19a136d00d4f71ad651a32 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sat, 13 May 2023 21:33:22 +0200 Subject: docs: small fixes Co-authored-by: Christian Clason Co-authored-by: Gregory Anders Co-authored-by: HiPhish Co-authored-by: Julio B Co-authored-by: T727 <74924917+T-727@users.noreply.github.com> Co-authored-by: camoz Co-authored-by: champignoom <66909116+champignoom@users.noreply.github.com> --- runtime/lua/vim/lsp.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index a724593188..92f5653158 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -913,7 +913,7 @@ end --- the `cmd` process. Not related to `root_dir`. --- --- - cmd_env: (table) Environment flags to pass to the LSP on ---- spawn. Must be specified using a map-like table. +--- spawn. Must be specified using a table. --- Non-string values are coerced to string. --- Example: ---
-- 
cgit 


From 1fe1bb084d0099fc4f9bfdc11189485d0f74b75a Mon Sep 17 00:00:00 2001
From: Lewis Russell 
Date: Mon, 19 Dec 2022 16:37:45 +0000
Subject: refactor(options): deprecate nvim[_buf|_win]_[gs]et_option

Co-authored-by: zeertzjq 
Co-authored-by: famiu 
---
 runtime/lua/vim/lsp.lua | 16 ++++++----------
 1 file changed, 6 insertions(+), 10 deletions(-)

(limited to 'runtime/lua/vim/lsp.lua')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 92f5653158..2e6ca7a0ac 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -8,12 +8,8 @@ local sync = require('vim.lsp.sync')
 local semantic_tokens = require('vim.lsp.semantic_tokens')
 
 local api = vim.api
-local nvim_err_writeln, nvim_buf_get_lines, nvim_command, nvim_buf_get_option, nvim_exec_autocmds =
-  api.nvim_err_writeln,
-  api.nvim_buf_get_lines,
-  api.nvim_command,
-  api.nvim_buf_get_option,
-  api.nvim_exec_autocmds
+local nvim_err_writeln, nvim_buf_get_lines, nvim_command, nvim_exec_autocmds =
+  api.nvim_err_writeln, api.nvim_buf_get_lines, api.nvim_command, api.nvim_exec_autocmds
 local uv = vim.loop
 local tbl_isempty, tbl_extend = vim.tbl_isempty, vim.tbl_extend
 local validate = vim.validate
@@ -137,7 +133,7 @@ local format_line_ending = {
 ---@param bufnr (number)
 ---@return string
 local function buf_get_line_ending(bufnr)
-  return format_line_ending[nvim_buf_get_option(bufnr, 'fileformat')] or '\n'
+  return format_line_ending[vim.bo[bufnr].fileformat] or '\n'
 end
 
 local client_index = 0
@@ -319,7 +315,7 @@ end
 local function buf_get_full_text(bufnr)
   local line_ending = buf_get_line_ending(bufnr)
   local text = table.concat(nvim_buf_get_lines(bufnr, 0, -1, true), line_ending)
-  if nvim_buf_get_option(bufnr, 'eol') then
+  if vim.bo[bufnr].eol then
     text = text .. line_ending
   end
   return text
@@ -709,7 +705,7 @@ local function text_document_did_open_handler(bufnr, client)
   if not api.nvim_buf_is_loaded(bufnr) then
     return
   end
-  local filetype = nvim_buf_get_option(bufnr, 'filetype')
+  local filetype = vim.bo[bufnr].filetype
 
   local params = {
     textDocument = {
@@ -2177,7 +2173,7 @@ end
 ---
 --- Currently only supports a single client. This can be set via
 --- `setlocal formatexpr=v:lua.vim.lsp.formatexpr()` but will typically or in `on_attach`
---- via ``vim.api.nvim_buf_set_option(bufnr, 'formatexpr', 'v:lua.vim.lsp.formatexpr(#{timeout_ms:250})')``.
+--- via ``vim.bo[bufnr].formatexpr = 'v:lua.vim.lsp.formatexpr(#{timeout_ms:250})'``.
 ---
 ---@param opts table options for customizing the formatting expression which takes the
 ---                   following optional keys:
-- 
cgit 


From ddd92a70d2aab5247895e89abaaa79c62ba7dbb4 Mon Sep 17 00:00:00 2001
From: Folke Lemaitre 
Date: Sun, 28 May 2023 07:51:28 +0200
Subject: feat(lsp): initial support for dynamic capabilities (#23681)

- `client.dynamic_capabilities` is an object that tracks client register/unregister
- `client.supports_method` will additionally check if a dynamic capability supports the method, taking document filters into account. But only if the client enabled `dynamicRegistration` for the capability
- updated the default client capabilities to include dynamicRegistration for:
    - formatting
    - rangeFormatting
    - hover
    - codeAction
    - hover
    - rename
---
 runtime/lua/vim/lsp.lua | 121 +++++++++++++++++++++++++++---------------------
 1 file changed, 68 insertions(+), 53 deletions(-)

(limited to 'runtime/lua/vim/lsp.lua')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 2e6ca7a0ac..5337abea25 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' },
@@ -886,6 +887,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
@@ -1090,43 +1132,6 @@ function lsp.start_client(config)
     end
   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.
@@ -1228,7 +1233,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 +1298,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 +1307,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 +1341,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 })
@@ -1522,7 +1537,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 +1961,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 +2017,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)
-- 
cgit 


From 58618d208acd3827c4e86668529edb619bb9b8dd Mon Sep 17 00:00:00 2001
From: jdrouhard 
Date: Tue, 30 May 2023 13:56:29 -0500
Subject: feat(lsp)!: promote LspRequest to a full autocmd and enrich with
 additional data (#23694)

BREAKING CHANGE: LspRequest is no longer a User autocmd but is now a
first class citizen.

LspRequest as a User autocmd had limited functionality. Namely, the only
thing you could do was use the notification to do a lookup on all the
clients' requests tables to figure out what changed.

Promoting the autocmd to a full autocmd lets us set the buffer the
request was initiated on (so people can set buffer-local autocmds for
listening to these events).

Additionally, when used from Lua, we can pass additional metadata about
the request along with the notification, including the client ID, the
request ID, and the actual request object stored on the client's
requests table. Users can now listen for these events and act on them
proactively instead of polling all of the requests tables and looking
for changes.
---
 runtime/lua/vim/lsp.lua | 27 ++++++++++++++++++++++-----
 1 file changed, 22 insertions(+), 5 deletions(-)

(limited to 'runtime/lua/vim/lsp.lua')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 5337abea25..c9ca8cd224 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -799,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()|.
@@ -1408,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
@@ -1486,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
-- 
cgit 


From be5e3611541051d9fa5b752a4fe1da6ab78b141e Mon Sep 17 00:00:00 2001
From: Raphael 
Date: Thu, 1 Jun 2023 14:38:38 +0800
Subject: fix(lsp): add param assert in client_is_stopped (#23857)

---
 runtime/lua/vim/lsp.lua | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

(limited to 'runtime/lua/vim/lsp.lua')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index c9ca8cd224..d64ed0b5a3 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -2275,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
-- 
cgit 


From fb54e6980ea6fec218a11f118e97ef65f250395a Mon Sep 17 00:00:00 2001
From: Gregory Anders 
Date: Thu, 1 Jun 2023 11:15:33 -0500
Subject: feat(lsp): set client offset_encoding if server supports
 positionEncoding

If the server sends the positionEncoding capability in its
initialization response, automatically set the client's offset_encoding
to use the value provided.
---
 runtime/lua/vim/lsp.lua | 4 ++++
 1 file changed, 4 insertions(+)

(limited to 'runtime/lua/vim/lsp.lua')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index d64ed0b5a3..baf8b5c1a8 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -1344,6 +1344,10 @@ function lsp.start_client(config)
         assert(result.capabilities, "initialize result doesn't contain capabilities")
       client.server_capabilities = protocol.resolve_capabilities(client.server_capabilities)
 
+      if client.server_capabilities.positionEncoding then
+        client.offset_encoding = client.server_capabilities.positionEncoding
+      end
+
       if next(config.settings) then
         client.notify('workspace/didChangeConfiguration', { settings = config.settings })
       end
-- 
cgit 


From 2db719f6c2b677fcbc197b02fe52764a851523b2 Mon Sep 17 00:00:00 2001
From: Lewis Russell 
Date: Sat, 3 Jun 2023 11:06:00 +0100
Subject: feat(lua): rename vim.loop -> vim.uv (#22846)

---
 runtime/lua/vim/lsp.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'runtime/lua/vim/lsp.lua')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index baf8b5c1a8..5f7a95ae14 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -10,7 +10,7 @@ local semantic_tokens = require('vim.lsp.semantic_tokens')
 local api = vim.api
 local nvim_err_writeln, nvim_buf_get_lines, nvim_command, nvim_exec_autocmds =
   api.nvim_err_writeln, api.nvim_buf_get_lines, api.nvim_command, api.nvim_exec_autocmds
-local uv = vim.loop
+local uv = vim.uv
 local tbl_isempty, tbl_extend = vim.tbl_isempty, vim.tbl_extend
 local validate = vim.validate
 local if_nil = vim.F.if_nil
-- 
cgit 


From 4ecc71f6fc7377403ed91ae5bc32992a5d08f678 Mon Sep 17 00:00:00 2001
From: Lewis Russell 
Date: Wed, 7 Jun 2023 13:39:41 +0100
Subject: fix(lsp): reduce diagnostics and add more types (#23948)

---
 runtime/lua/vim/lsp.lua | 144 ++++++++++++++++++++++++++++++------------------
 1 file changed, 91 insertions(+), 53 deletions(-)

(limited to 'runtime/lua/vim/lsp.lua')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 5f7a95ae14..532504a7db 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -147,9 +147,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
+local all_buffer_active_clients = {} --- @type table>
+local uninitialized_clients = {} --- @type table
 
 ---@private
 ---@param bufnr? integer
@@ -166,7 +166,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
     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 +257,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 +291,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 +326,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 +370,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 +609,7 @@ do
 
   ---@private
   function changetracking.send_changes(bufnr, firstline, lastline, new_lastline)
-    local groups = {}
+    local groups = {} ---@type table
     for _, client in pairs(lsp.get_active_clients({ bufnr = bufnr })) do
       local group = get_group(client)
       groups[group_key(group)] = group
@@ -812,6 +811,10 @@ 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 +852,7 @@ end
 --- `ftplugin/.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 +861,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
@@ -930,6 +932,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
+--- @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 +965,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 +995,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
@@ -1051,9 +1076,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 +1113,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 +1129,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
 
@@ -1122,8 +1153,9 @@ function lsp.start_client(config)
   ---@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 +1264,13 @@ function lsp.start_client(config)
     handlers = handlers,
     commands = config.commands or {},
 
+    --- @type table
     requests = {},
     -- for $/progress report
     messages = { name = name, messages = {}, progress = {}, status = {} },
     dynamic_capabilities = require('vim.lsp._dynamic').new(client_id),
   }
+  --- @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.
@@ -1251,9 +1285,9 @@ function lsp.start_client(config)
     }
     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,7 +1312,7 @@ 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 = {
@@ -1405,8 +1439,9 @@ function lsp.start_client(config)
     -- Ensure pending didChange notifications are sent so that the server doesn't operate on a stale state
     changetracking.flush(client, 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,
@@ -1879,13 +1914,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
@@ -2143,20 +2178,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()
@@ -2270,8 +2305,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.
@@ -2359,10 +2394,13 @@ end
 --- 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
+--- @param user_config table
 function lsp._with_extend(name, options, user_config)
   user_config = user_config or {}
 
-  local resulting_config = {}
+  local resulting_config = {} --- @type table
   for k, v in pairs(user_config) do
     if options[k] == nil then
       error(
-- 
cgit 


From e5e0bda41b640d324350c5147b956e37e9f8b32c Mon Sep 17 00:00:00 2001
From: Mathias Fußenegger 
Date: Fri, 9 Jun 2023 11:32:43 +0200
Subject: feat(lsp)!: add vim.lsp.status, client.progress and promote
 LspProgressUpdate (#23958)

`client.messages` could grow unbounded because the default handler only
added new messages, never removing them.

A user either had to consume the messages by calling
`vim.lsp.util.get_progress_messages` or by manually removing them from
`client.messages.progress`. If they didn't do that, using LSP
effectively leaked memory.

To fix this, this deprecates the `messages` property and instead adds a
`progress` ring buffer that only keeps at most 50 messages. In addition
it deprecates `vim.lsp.util.get_progress_messages` in favour of a new
`vim.lsp.status()` and also promotes the `LspProgressUpdate` user
autocmd to a regular autocmd to allow users to pattern match on the
progress kind.

Also closes https://github.com/neovim/neovim/pull/20327
---
 runtime/lua/vim/lsp.lua | 62 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 61 insertions(+), 1 deletion(-)

(limited to 'runtime/lua/vim/lsp.lua')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 532504a7db..6ddbfc6df7 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -807,6 +807,9 @@ 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
@@ -891,6 +894,50 @@ 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 groups = {}
+  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 group = groups[progress.token]
+        if not group then
+          group = {}
+          groups[progress.token] = group
+        end
+        group.title = value.title or group.title
+        group.message = value.message or group.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 messages = {}
+  for _, group in pairs(groups) do
+    if group.title then
+      table.insert(
+        messages,
+        group.message and (group.title .. ': ' .. group.message) or group.title
+      )
+    elseif group.message then
+      table.insert(messages, group.message)
+    end
+  end
+  local message = table.concat(messages, ', ')
+  if percentage then
+    return string.format('%03d: %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)
@@ -1266,10 +1313,23 @@ function lsp.start_client(config)
 
     --- @type table
     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 title of unfinished progress sequences by token
+  client.progress.pending = {}
+
   --- @type lsp.ClientCapabilities
   client.config.capabilities = config.capabilities or protocol.make_client_capabilities()
 
-- 
cgit 


From b302da9ad220a7699d4b0ebf642529d142a0b9cf Mon Sep 17 00:00:00 2001
From: Raphael 
Date: Sun, 11 Jun 2023 02:32:41 +0800
Subject: fix(lsp): use percentage format on lsp.status (#23971)

---
 runtime/lua/vim/lsp.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'runtime/lua/vim/lsp.lua')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 6ddbfc6df7..1d9a91801a 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -933,7 +933,7 @@ function lsp.status()
   end
   local message = table.concat(messages, ', ')
   if percentage then
-    return string.format('%03d: %s', percentage, message)
+    return string.format('%3d%%: %s', percentage, message)
   end
   return message
 end
-- 
cgit 


From 643546b82b4bc0c29ca869f81af868a019723d83 Mon Sep 17 00:00:00 2001
From: Chinmay Dalal 
Date: Sun, 11 Jun 2023 15:23:37 +0530
Subject: feat(lsp): add handlers for inlay hints (#23736)

initial support; public API left for a follow-up PR
---
 runtime/lua/vim/lsp.lua | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

(limited to 'runtime/lua/vim/lsp.lua')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 1d9a91801a..6f9a6c460b 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -17,6 +17,7 @@ local if_nil = vim.F.if_nil
 
 local lsp = {
   protocol = protocol,
+  _inlay_hint = require('vim.lsp._inlay_hint'),
 
   handlers = default_handlers,
 
@@ -60,6 +61,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.
@@ -1498,16 +1501,20 @@ 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)
     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'
-- 
cgit 


From 91f67fabe69f5e3be19f37709261ea7abaa1a3cd Mon Sep 17 00:00:00 2001
From: Mathias Fußenegger 
Date: Tue, 13 Jun 2023 16:53:13 +0200
Subject: fix(lsp): handle stale bufnr on LspRequest autocmd trigger (#24013)

Fixes a `Invalid buffer id: 123` race when the buffer gets deleted before
the callback triggered.

Alternative to https://github.com/neovim/neovim/pull/23981
---
 runtime/lua/vim/lsp.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'runtime/lua/vim/lsp.lua')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 6f9a6c460b..9118e7e2e1 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -1519,7 +1519,7 @@ function lsp.start_client(config)
       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 },
       })
-- 
cgit 


From 72a6643b1380cdf6f1153d70eeaffb90bdca30d6 Mon Sep 17 00:00:00 2001
From: "Justin M. Keyes" 
Date: Mon, 19 Jun 2023 08:40:33 -0700
Subject: docs #24061

- nvim requires rpc responses in reverse order. https://github.com/neovim/neovim/issues/19932
- NVIM_APPNAME: UIs normally should NOT set this.

ref #23520
fix #24050
fix #23660
fix #23353
fix #23337
fix #22213
fix #19161
fix #18088
fix #20693
---
 runtime/lua/vim/lsp.lua | 31 ++++++++++++++-----------------
 1 file changed, 14 insertions(+), 17 deletions(-)

(limited to 'runtime/lua/vim/lsp.lua')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 9118e7e2e1..761a8406f2 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -1491,7 +1491,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(
@@ -2119,22 +2119,19 @@ 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) (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) (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
 
@@ -2147,12 +2144,12 @@ function lsp.buf_request_all(bufnr, method, params, callback)
   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
 
@@ -2164,8 +2161,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
-- 
cgit 


From ca5de9306c00d07cce1daef1f0038c937098bc66 Mon Sep 17 00:00:00 2001
From: Chinmay Dalal 
Date: Tue, 20 Jun 2023 11:36:54 +0530
Subject: feat(lsp): inlay hints #23984

Add automatic refresh and a public interface on top of #23736

 * add on_reload, on_detach handlers in `enable()` buf_attach, and
  LspDetach autocommand in case of manual detach
* unify `__buffers` and `hint_cache_by_buf`
* use callback bufnr in `on_lines` callback, bufstate: remove __index override
* move user-facing functions into vim.lsp.buf, unify enable/disable/toggle

Closes #18086
---
 runtime/lua/vim/lsp.lua | 1 -
 1 file changed, 1 deletion(-)

(limited to 'runtime/lua/vim/lsp.lua')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 761a8406f2..917aeb6604 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -17,7 +17,6 @@ local if_nil = vim.F.if_nil
 
 local lsp = {
   protocol = protocol,
-  _inlay_hint = require('vim.lsp._inlay_hint'),
 
   handlers = default_handlers,
 
-- 
cgit 


From 64f2691a984a5b1e2958d5656a910054982a6f0e Mon Sep 17 00:00:00 2001
From: Mathias Fußenegger 
Date: Tue, 20 Jun 2023 18:36:18 +0200
Subject: refactor(lsp): extract common execute command functionality (#24065)

---
 runtime/lua/vim/lsp.lua | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

(limited to 'runtime/lua/vim/lsp.lua')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 917aeb6604..25e69a8006 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -1653,6 +1653,46 @@ function lsp.start_client(config)
     return rpc.is_closing()
   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
-- 
cgit 


From 3bf887f6e08fa272679187340ca483809275b20a Mon Sep 17 00:00:00 2001
From: Sooryakiran Ponnath 
Date: Tue, 20 Jun 2023 15:17:13 -0400
Subject: fix(lsp): always return boolean in lsp.buf_client_attach (#24077)

Co-authored-by: Mathias Fussenegger 
---
 runtime/lua/vim/lsp.lua | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

(limited to 'runtime/lua/vim/lsp.lua')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 25e69a8006..cb1c101c58 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -1799,6 +1799,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 },
@@ -1887,7 +1888,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
-- 
cgit 


From 21b074feb09b7f2e4807a816be6fd0687f23c564 Mon Sep 17 00:00:00 2001
From: "Justin M. Keyes" 
Date: Wed, 21 Jun 2023 11:34:49 +0200
Subject: refactor(lsp): report full Nvim version string in clientInfo

---
 runtime/lua/vim/lsp.lua | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

(limited to 'runtime/lua/vim/lsp.lua')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index cb1c101c58..ea81244c68 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -1345,7 +1345,6 @@ function lsp.start_client(config)
       messages = 'messages',
       verbose = 'verbose',
     }
-    local version = vim.version()
 
     local workspace_folders --- @type table[]?
     local root_uri --- @type string?
@@ -1379,7 +1378,7 @@ function lsp.start_client(config)
       -- 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.
       --
-- 
cgit 


From 4d3a04279d32bc97d18ab2883c678c94f80487bc Mon Sep 17 00:00:00 2001
From: Mathias Fußenegger 
Date: Thu, 22 Jun 2023 10:18:49 +0200
Subject: perf(lsp): remove grouping logic from lsp.status (#24096)

With the title carry-over logic in the `$/progress` handler it's not
necessary to group again in vim.lsp.status
---
 runtime/lua/vim/lsp.lua | 22 +++-------------------
 1 file changed, 3 insertions(+), 19 deletions(-)

(limited to 'runtime/lua/vim/lsp.lua')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index ea81244c68..38e0e34790 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -902,18 +902,13 @@ end
 ---@return string
 function lsp.status()
   local percentage = nil
-  local groups = {}
+  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 group = groups[progress.token]
-        if not group then
-          group = {}
-          groups[progress.token] = group
-        end
-        group.title = value.title or group.title
-        group.message = value.message or group.message
+        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
@@ -922,17 +917,6 @@ function lsp.status()
       -- Just ignore it as there is no sensible way to display it
     end
   end
-  local messages = {}
-  for _, group in pairs(groups) do
-    if group.title then
-      table.insert(
-        messages,
-        group.message and (group.title .. ': ' .. group.message) or group.title
-      )
-    elseif group.message then
-      table.insert(messages, group.message)
-    end
-  end
   local message = table.concat(messages, ', ')
   if percentage then
     return string.format('%3d%%: %s', percentage, message)
-- 
cgit 


From 4e6356559c8cd44dbcaa765d1f39e176064526ec Mon Sep 17 00:00:00 2001
From: "Justin M. Keyes" 
Date: Thu, 22 Jun 2023 03:44:51 -0700
Subject: test: spellcheck :help (vimdoc) files #24109

Enforce consistent terminology (defined in
`gen_help_html.lua:spell_dict`) for common misspellings.

This does not spellcheck English in general (perhaps a future TODO,
though it may be noisy).
---
 runtime/lua/vim/lsp.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'runtime/lua/vim/lsp.lua')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 38e0e34790..5b192ca514 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -1076,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.
 ---
-- 
cgit 


From 134b9ec483616e20d96c26fdb7ef3f3e912108a8 Mon Sep 17 00:00:00 2001
From: Mathias Fußenegger 
Date: Thu, 22 Jun 2023 13:54:35 +0200
Subject: feat(lsp): soft deprecate vim.lsp.for_each_buffer_client (#24104)

There is no need for two ways to access all clients of a buffer.

This doesn't add a `vim.deprecate` call yet, as the function is probably
used a lot, but removes it from the documentation and annotates it with
`@deprecated`
---
 runtime/lua/vim/lsp.lua | 60 ++++++++++++++++++++++++-------------------------
 1 file changed, 29 insertions(+), 31 deletions(-)

(limited to 'runtime/lua/vim/lsp.lua')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 5b192ca514..970bb56478 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -1740,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
@@ -1772,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
@@ -1808,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,
@@ -1827,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', {
@@ -1843,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,
@@ -1932,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))
@@ -2104,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]
@@ -2159,11 +2155,11 @@ function lsp.buf_request_all(bufnr, method, params, handler)
   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)
@@ -2226,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
 
@@ -2371,7 +2367,7 @@ 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)
+        lsp.util.apply_text_edits(response.result, 0, client.offset_encoding)
         return 0
       end
     end
@@ -2452,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
@@ -2463,6 +2460,7 @@ end
 ---                 print(vim.inspect(client))
 ---               end)
 ---             
+---@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 -- cgit From 4dc86477b674d056b137a3afafd8824f7b7717ec Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 23 Jun 2023 19:54:47 +0800 Subject: build(luarc.json): disable luadoc-miss-see-name #24108 --- runtime/lua/vim/lsp.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 970bb56478..2c115007de 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1183,7 +1183,7 @@ 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) if log.error() then @@ -2366,7 +2366,7 @@ function lsp.formatexpr(opts) } local response = client.request_sync('textDocument/rangeFormatting', params, timeout_ms, bufnr) - if response.result then + if response and response.result then lsp.util.apply_text_edits(response.result, 0, client.offset_encoding) return 0 end -- cgit From 49a7585981cdf7403e76a614558e602a98e64301 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 23 Jun 2023 12:16:55 +0200 Subject: docs: autocmds, misc --- runtime/lua/vim/lsp.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 970bb56478..a8e75c4dc9 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -2457,7 +2457,7 @@ end --- buffer number as arguments. Example: ---
lua
 ---               vim.lsp.for_each_buffer_client(0, function(client, client_id, bufnr)
----                 print(vim.inspect(client))
+---                 vim.print(client)
 ---               end)
 ---             
---@deprecated use lsp.get_active_clients({ bufnr = bufnr }) with regular loop -- cgit From 37079fca58f396fd866dc7b7d87a0100c17ee760 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Fri, 30 Jun 2023 11:33:28 +0200 Subject: feat(lsp): move inlay_hint() to vim.lsp (#24130) Allows to keep more functions hidden and gives a path forward for further inlay_hint related functions - like applying textEdits. See https://github.com/neovim/neovim/pull/23984#pullrequestreview-1486624668 --- runtime/lua/vim/lsp.lua | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 1e5ce8fa10..ca4851f8d7 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -2474,6 +2474,13 @@ 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. -- cgit From 766f4978d6cb146511cf0b676c01e5327db46647 Mon Sep 17 00:00:00 2001 From: Raphael Date: Mon, 10 Jul 2023 19:38:15 +0800 Subject: fix(lint): lint warnings #24226 --- runtime/lua/vim/lsp.lua | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index ca4851f8d7..ed431e080e 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1951,7 +1951,7 @@ end --- ---@param client_id integer client id --- ----@returns |vim.lsp.client| object, or nil +---@return (nil|lsp.Client) client rpc object function lsp.get_client_by_id(client_id) return active_clients[client_id] or uninitialized_clients[client_id] end @@ -2090,13 +2090,14 @@ api.nvim_create_autocmd('VimLeavePre', { ---@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 handler lsp-handler|nil See |lsp-handler| +---@param handler lsp-handler See |lsp-handler| --- If nil, follows resolution strategy defined in |lsp-handler-configuration| --- ----@return table, fun() 2-tuple: ---- - Map of client-id:request-id pairs for all successful requests. ---- - Function which can be used to cancel all the requests. You could instead ---- iterate all clients and call their `cancel_request()` methods. +---@return table client_request_ids Map of client-id:request-id pairs +---for all successful requests. +---@return function _cancel_all_requests Function which can be used to +---cancel all the requests. You could instead +---iterate all clients and call their `cancel_request()` methods. function lsp.buf_request(bufnr, method, params, handler) validate({ bufnr = { bufnr, 'n', true }, @@ -2147,8 +2148,7 @@ end ---@param handler fun(results: table) (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. +---@return function cancel Function that cancels all requests. function lsp.buf_request_all(bufnr, method, params, handler) local results = {} local result_count = 0 @@ -2257,7 +2257,7 @@ end ---@param findstart integer 0 or 1, decides behavior ---@param base integer findstart=0, text to match against --- ----@returns (integer) Decided by {findstart}: +---@return integer|table Decided by {findstart}: --- - 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) @@ -2405,7 +2405,7 @@ end --- is a |vim.lsp.client| object. --- ---@param bufnr (integer|nil): Buffer handle, or 0 for current ----@returns (table) Table of (client_id, client) pairs +---@return table result is table of (client_id, client) pairs ---@deprecated Use |vim.lsp.get_active_clients()| instead. function lsp.buf_get_clients(bufnr) local result = {} -- cgit From 317c80f460a7826421f40f57ee8bdbd736b0f225 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Wed, 12 Jul 2023 14:48:21 +0200 Subject: feat(lsp): add method filter to get_active_clients (#24319) --- runtime/lua/vim/lsp.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index ed431e080e..1f9b6c4360 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1995,6 +1995,7 @@ end ---@field id integer|nil Match clients by id ---@field bufnr integer|nil match clients attached to the given buffer ---@field name string|nil match clients by name +---@field method string|nil match client by supported method name --- Get active clients. --- @@ -2004,6 +2005,7 @@ 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 +--- - method (string): Only return clients supporting the given method ---@return lsp.Client[]: List of |vim.lsp.client| objects function lsp.get_active_clients(filter) validate({ filter = { filter, 't', true } }) @@ -2020,6 +2022,7 @@ function lsp.get_active_clients(filter) client and (filter.id == nil or client.id == filter.id) and (filter.name == nil or client.name == filter.name) + and (filter.method == nil or client.supports_method(filter.method, { bufnr = filter.bufnr })) then clients[#clients + 1] = client end -- cgit From 33e1a8cd7042816a064c0d2bf32b6570d7e88b79 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Fri, 14 Jul 2023 18:47:18 +0200 Subject: feat(lsp): map K to hover by default #24331 Related: https://github.com/neovim/neovim/issues/24252 --- runtime/lua/vim/lsp.lua | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 1f9b6c4360..e9a1423d2d 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -963,6 +963,15 @@ function lsp._set_defaults(client, bufnr) then vim.bo[bufnr].formatexpr = 'v:lua.vim.lsp.formatexpr()' end + api.nvim_buf_call(bufnr, function() + if + client.supports_method('textDocument/hover') + and is_empty_or_default(bufnr, 'keywordprg') + and vim.fn.maparg('K', 'n', false, false) == '' + then + vim.keymap.set('n', 'K', vim.lsp.buf.hover, { buffer = bufnr }) + end + end) end --- @class lsp.ClientConfig @@ -1202,7 +1211,7 @@ function lsp.start_client(config) ---@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) + local function reset_defaults(bufnr) if vim.bo[bufnr].tagfunc == 'v:lua.vim.lsp.tagfunc' then vim.bo[bufnr].tagfunc = nil end @@ -1212,6 +1221,12 @@ function lsp.start_client(config) if vim.bo[bufnr].formatexpr == 'v:lua.vim.lsp.formatexpr()' then vim.bo[bufnr].formatexpr = nil end + api.nvim_buf_call(bufnr, function() + local keymap = vim.fn.maparg('K', 'n', false, true) + if keymap and keymap.callback == vim.lsp.buf.hover then + vim.keymap.del('n', 'K', { buffer = bufnr }) + end + end) end ---@private @@ -1243,7 +1258,7 @@ function lsp.start_client(config) client_ids[client_id] = nil if vim.tbl_isempty(client_ids) then - unset_defaults(bufnr) + reset_defaults(bufnr) end end) end -- cgit From 1b9ccd38a12f8fdbdff51ef0b3ff363540f745ec Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Mon, 17 Jul 2023 18:27:16 +0200 Subject: feat(lsp)!: rename vim.lsp.get_active_clients to get_clients (#24113) --- runtime/lua/vim/lsp.lua | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index e9a1423d2d..97aab45f2c 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -612,7 +612,7 @@ do ---@private function changetracking.send_changes(bufnr, firstline, lastline, new_lastline) local groups = {} ---@type table - for _, client in pairs(lsp.get_active_clients({ bufnr = bufnr })) do + for _, client in pairs(lsp.get_clients({ bufnr = bufnr })) do local group = get_group(client) groups[group_key(group)] = group end @@ -734,7 +734,7 @@ end -- FIXME: DOC: Shouldn't need to use a dummy function -- --- LSP client object. You can get an active client object via ---- |vim.lsp.get_client_by_id()| or |vim.lsp.get_active_clients()|. +--- |vim.lsp.get_client_by_id()| or |vim.lsp.get_clients()|. --- --- - Methods: --- @@ -880,7 +880,7 @@ function lsp.start(config, opts) if bufnr == nil or bufnr == 0 then bufnr = api.nvim_get_current_buf() end - for _, clients in ipairs({ uninitialized_clients, lsp.get_active_clients() }) do + for _, clients in ipairs({ uninitialized_clients, lsp.get_clients() }) do for _, client in pairs(clients) do if reuse_client(client, config) then lsp.buf_attach_client(bufnr, client.id) @@ -903,7 +903,7 @@ end function lsp.status() local percentage = nil local messages = {} - for _, client in ipairs(vim.lsp.get_active_clients()) do + for _, client in ipairs(vim.lsp.get_clients()) do for progress in client.progress do local value = progress.value if type(value) == 'table' and value.kind then @@ -1755,7 +1755,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 _, client in ipairs(lsp.get_active_clients({ bufnr = bufnr })) do + for _, client in ipairs(lsp.get_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 @@ -1823,7 +1823,7 @@ function lsp.buf_attach_client(bufnr, client_id) buffer = bufnr, desc = 'vim.lsp: textDocument/willSave', callback = function(ctx) - for _, client in ipairs(lsp.get_active_clients({ bufnr = ctx.buf })) do + for _, client in ipairs(lsp.get_clients({ bufnr = ctx.buf })) do local params = { textDocument = { uri = uri, @@ -1858,7 +1858,7 @@ function lsp.buf_attach_client(bufnr, client_id) on_lines = text_document_did_change_handler, on_reload = function() local params = { textDocument = { uri = uri } } - for _, client in ipairs(lsp.get_active_clients({ bufnr = bufnr })) do + for _, client in ipairs(lsp.get_clients({ bufnr = bufnr })) do changetracking.reset_buf(client, bufnr) if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then client.notify('textDocument/didClose', params) @@ -1868,7 +1868,7 @@ function lsp.buf_attach_client(bufnr, client_id) end, on_detach = function() local params = { textDocument = { uri = uri } } - for _, client in ipairs(lsp.get_active_clients({ bufnr = bufnr })) do + for _, client in ipairs(lsp.get_clients({ bufnr = bufnr })) do changetracking.reset_buf(client, bufnr) if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then client.notify('textDocument/didClose', params) @@ -1985,7 +1985,7 @@ end --- You can also use the `stop()` function on a |vim.lsp.client| object. --- To stop all clients: ---
lua
---- vim.lsp.stop_client(vim.lsp.get_active_clients())
+--- vim.lsp.stop_client(vim.lsp.get_clients())
 --- 
--- --- By default asks the server to shutdown, unless stop was requested @@ -2006,7 +2006,7 @@ function lsp.stop_client(client_id, force) end end ----@class vim.lsp.get_active_clients.filter +---@class vim.lsp.get_clients.filter ---@field id integer|nil Match clients by id ---@field bufnr integer|nil match clients attached to the given buffer ---@field name string|nil match clients by name @@ -2014,7 +2014,7 @@ end --- Get active clients. --- ----@param filter vim.lsp.get_active_clients.filter|nil (table|nil) A table with +---@param filter vim.lsp.get_clients.filter|nil (table|nil) A table with --- key-value pairs used to filter the returned clients. --- The available keys are: --- - id (number): Only return clients with the given id @@ -2022,7 +2022,7 @@ end --- - name (string): Only return clients with the given name --- - method (string): Only return clients supporting the given method ---@return lsp.Client[]: List of |vim.lsp.client| objects -function lsp.get_active_clients(filter) +function lsp.get_clients(filter) validate({ filter = { filter, 't', true } }) filter = filter or {} @@ -2045,6 +2045,13 @@ function lsp.get_active_clients(filter) return clients end +---@private +---@deprecated +function lsp.get_active_clients(filter) + -- TODO: add vim.deprecate call after 0.10 is out for removal in 0.12 + return lsp.get_clients(filter) +end + api.nvim_create_autocmd('VimLeavePre', { desc = 'vim.lsp: exit handler', callback = function() @@ -2125,7 +2132,7 @@ function lsp.buf_request(bufnr, method, params, handler) bufnr = resolve_bufnr(bufnr) local method_supported = false - local clients = lsp.get_active_clients({ bufnr = bufnr }) + local clients = lsp.get_clients({ bufnr = bufnr }) local client_request_ids = {} for _, client in ipairs(clients) do if client.supports_method(method, { bufnr = bufnr }) then @@ -2173,7 +2180,7 @@ function lsp.buf_request_all(bufnr, method, params, handler) local expected_result_count = 0 local set_expected_result_count = once(function() - for _, client in ipairs(lsp.get_active_clients({ bufnr = bufnr })) do + for _, client in ipairs(lsp.get_clients({ bufnr = bufnr })) do if client.supports_method(method, { bufnr = bufnr }) then expected_result_count = expected_result_count + 1 end @@ -2240,7 +2247,7 @@ function lsp.buf_notify(bufnr, method, params) method = { method, 's' }, }) local resp = false - for _, client in ipairs(lsp.get_active_clients({ bufnr = bufnr })) do + for _, client in ipairs(lsp.get_clients({ bufnr = bufnr })) do if client.rpc.notify(method, params) then resp = true end @@ -2367,7 +2374,7 @@ function lsp.formatexpr(opts) return 0 end local bufnr = api.nvim_get_current_buf() - for _, client in pairs(lsp.get_active_clients({ bufnr = bufnr })) do + for _, client in pairs(lsp.get_clients({ bufnr = bufnr })) do if client.supports_method('textDocument/rangeFormatting') then local params = util.make_formatting_params() local end_line = vim.fn.getline(end_lnum) --[[@as string]] @@ -2424,10 +2431,10 @@ end --- ---@param bufnr (integer|nil): Buffer handle, or 0 for current ---@return table result is table of (client_id, client) pairs ----@deprecated Use |vim.lsp.get_active_clients()| instead. +---@deprecated Use |vim.lsp.get_clients()| instead. function lsp.buf_get_clients(bufnr) local result = {} - for _, client in ipairs(lsp.get_active_clients({ bufnr = resolve_bufnr(bufnr) })) do + for _, client in ipairs(lsp.get_clients({ bufnr = resolve_bufnr(bufnr) })) do result[client.id] = client end return result @@ -2478,7 +2485,7 @@ end --- vim.print(client) --- end) --- ----@deprecated use lsp.get_active_clients({ bufnr = bufnr }) with regular loop +---@deprecated use lsp.get_clients({ bufnr = bufnr }) with regular loop function lsp.for_each_buffer_client(bufnr, fn) return for_each_buffer_client(bufnr, fn) end -- cgit From be74807eef13ff8c90d55cf8b22b01d6d33b1641 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 18 Jul 2023 15:42:30 +0100 Subject: docs(lua): more improvements (#24387) * docs(lua): teach lua2dox how to table * docs(lua): teach gen_vimdoc.py about local functions No more need to mark local functions with @private * docs(lua): mention @nodoc and @meta in dev-lua-doc * fixup! Co-authored-by: Justin M. Keyes --------- Co-authored-by: Justin M. Keyes --- runtime/lua/vim/lsp.lua | 44 +++++++++++--------------------------------- 1 file changed, 11 insertions(+), 33 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 97aab45f2c..35e4bc9dd8 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -66,7 +66,6 @@ lsp._request_name_to_capability = { -- TODO improve handling of scratch buffers with LSP attached. ----@private --- Concatenates and writes a list of strings to the Vim error buffer. --- ---@param ... string List to write to the buffer @@ -75,7 +74,6 @@ local function err_message(...) nvim_command('redraw') end ----@private --- Returns the buffer number for the given {bufnr}. --- ---@param bufnr (integer|nil) Buffer number to resolve. Defaults to current buffer @@ -101,7 +99,6 @@ function lsp._unsupported_method(method) return msg end ----@private --- Checks whether a given path is a directory. --- ---@param filename (string) path to check @@ -132,7 +129,6 @@ local format_line_ending = { ['mac'] = '\r', } ----@private ---@param bufnr (number) ---@return string local function buf_get_line_ending(bufnr) @@ -140,7 +136,6 @@ local function buf_get_line_ending(bufnr) end local client_index = 0 ----@private --- Returns a new, unused client id. --- ---@return integer client_id @@ -153,7 +148,6 @@ local active_clients = {} --- @type table local all_buffer_active_clients = {} --- @type table> local uninitialized_clients = {} --- @type table ----@private ---@param bufnr? integer ---@param fn fun(client: lsp.Client, client_id: integer, bufnr: integer) local function for_each_buffer_client(bufnr, fn, restrict_client_ids) @@ -185,9 +179,10 @@ local function for_each_buffer_client(bufnr, fn, restrict_client_ids) end end --- Error codes to be used with `on_error` from |vim.lsp.start_client|. --- Can be used to look up the string from a the number or the number --- from the string. +--- Error codes to be used with `on_error` from |vim.lsp.start_client|. +--- Can be used to look up the string from a the number or the number +--- from the string. +--- @nodoc lsp.client_errors = tbl_extend( 'error', lsp_rpc.client_errors, @@ -196,7 +191,6 @@ lsp.client_errors = tbl_extend( }) ) ----@private --- Normalizes {encoding} to valid LSP encoding names. --- ---@param encoding (string) Encoding to normalize @@ -243,7 +237,6 @@ function lsp._cmd_parts(input) return cmd, cmd_args end ----@private --- Augments a validator function with support for optional (nil) values. --- ---@param fn (fun(v): boolean) The original validator function; should return a @@ -256,7 +249,6 @@ local function optional_validator(fn) end end ----@private --- Validates a client configuration as given to |vim.lsp.start_client()|. --- ---@param config (lsp.ClientConfig) @@ -308,7 +300,6 @@ local function validate_client_config(config) return cmd, cmd_args, offset_encoding end ----@private --- Returns full text of buffer {bufnr} as a string. --- ---@param bufnr (number) Buffer handle, or 0 for current. @@ -322,7 +313,6 @@ local function buf_get_full_text(bufnr) return text end ----@private --- Memoizes a function. On first run, the function return value is saved and --- immediately returned on subsequent runs. If the function returns a multival, --- only the first returned value will be memoized and returned. The function will only be run once, @@ -382,7 +372,6 @@ do --- @field debounce integer debounce duration in ms --- @field clients table clients using this state. {client_id, client} - ---@private ---@param group CTGroup ---@return string local function group_key(group) @@ -403,7 +392,6 @@ do end, }) - ---@private ---@return CTGroup local function get_group(client) local allow_inc_sync = if_nil(client.config.flags.allow_incremental_sync, true) @@ -419,7 +407,6 @@ do } end - ---@private ---@param state CTBufferState local function incremental_changes(state, encoding, bufnr, firstline, lastline, new_lastline) local prev_lines = state.lines @@ -543,8 +530,6 @@ do end end - ---@private - -- -- Adjust debounce time by taking time of last didChange notification into -- consideration. If the last didChange happened more than `debounce` time ago, -- debounce can be skipped and otherwise maybe reduced. @@ -567,7 +552,6 @@ do return math.max(debounce - ms_since_last_flush, 0) end - ---@private ---@param bufnr integer ---@param sync_kind integer protocol.TextDocumentSyncKind ---@param state CTGroupState @@ -694,7 +678,6 @@ do end end ----@private --- Default handler for the 'textDocument/didOpen' LSP notification. --- ---@param bufnr integer Number of the buffer, or 0 for current @@ -924,7 +907,6 @@ function lsp.status() return message 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 @@ -1138,7 +1120,6 @@ function lsp.start_client(config) local dispatch = {} - ---@private --- Returns the handler associated with an LSP method. --- Returns the default handler if the user hasn't set a custom one. --- @@ -1208,7 +1189,6 @@ function lsp.start_client(config) end end - ---@private --- Reset defaults set by `set_defaults`. --- Must only be called if the last client attached to a buffer exits. local function reset_defaults(bufnr) @@ -1337,7 +1317,6 @@ function lsp.start_client(config) -- Store the uninitialized_clients for cleanup in case we exit before initialize finishes. uninitialized_clients[client_id] = client - ---@private local function initialize() local valid_traces = { off = 'off', @@ -1749,7 +1728,6 @@ do end end ----@private ---Buffer lifecycle handler for textDocument/didSave local function text_document_did_save_handler(bufnr) bufnr = resolve_bufnr(bufnr) @@ -2082,7 +2060,6 @@ api.nvim_create_autocmd('VimLeavePre', { local poll_time = 50 - ---@private local function check_clients_closed() for client_id, timeout in pairs(timeouts) do timeouts[client_id] = timeout - poll_time @@ -2440,12 +2417,13 @@ function lsp.buf_get_clients(bufnr) return result end --- Log level dictionary with reverse lookup as well. --- --- Can be used to lookup the number from the name or the --- name from the number. --- Levels by name: "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF" --- Level numbers begin with "TRACE" at 0 +--- Log level dictionary with reverse lookup as well. +--- +--- Can be used to lookup the number from the name or the +--- name from the number. +--- Levels by name: "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF" +--- Level numbers begin with "TRACE" at 0 +--- @nodoc lsp.log_levels = log.levels --- Sets the global log level for LSP logging. -- cgit From 2f22ed6a00db10c4852a8fa232b8782f8b6a6646 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Wed, 19 Jul 2023 07:10:11 +0200 Subject: feat(lsp): handle multiple clients in omnifunc (#24381) Also fixes https://github.com/neovim/neovim/issues/24369 by adding an extra `vim.schedule` to ensure the `vim.fn.complete` call happens outside of a luv callback --- runtime/lua/vim/lsp.lua | 88 ++++++++++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 34 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 35e4bc9dd8..65cce6af47 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -2268,8 +2268,9 @@ function lsp.omnifunc(findstart, 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 + local clients = lsp.get_clients({ bufnr = bufnr, method = 'textDocument/completion' }) + local remaining = #clients + if remaining == 0 then return findstart == 1 and -1 or {} end @@ -2278,47 +2279,66 @@ function lsp.omnifunc(findstart, base) log.info('base ', base) end - local pos = api.nvim_win_get_cursor(0) + local win = api.nvim_get_current_win() + local pos = api.nvim_win_get_cursor(win) local line = api.nvim_get_current_line() local line_to_cursor = line:sub(1, pos[2]) local _ = log.trace() and log.trace('omnifunc.line', pos, line) -- Get the start position of the current keyword - local textMatch = vim.fn.match(line_to_cursor, '\\k*$') + local match_pos = vim.fn.match(line_to_cursor, '\\k*$') + 1 + local items = {} - local params = util.make_position_params() + local startbyte - local items = {} - lsp.buf_request(bufnr, 'textDocument/completion', params, function(err, result, ctx) - if err or not result or vim.fn.mode() ~= 'i' then - return + ---@private + local function on_done() + local mode = api.nvim_get_mode()['mode'] + if mode == 'i' or mode == 'ic' then + vim.fn.complete(startbyte or match_pos, items) end + end - -- Completion response items may be relative to a position different than `textMatch`. - -- Concrete example, with sumneko/lua-language-server: - -- - -- require('plenary.asy| - -- ▲ ▲ ▲ - -- │ │ └── cursor_pos: 20 - -- │ └────── textMatch: 17 - -- └────────────── textEdit.range.start.character: 9 - -- .newText = 'plenary.async' - -- ^^^ - -- prefix (We'd remove everything not starting with `asy`, - -- so we'd eliminate the `plenary.async` result - -- - -- `adjust_start_col` is used to prefer the language server boundary. - -- - local client = lsp.get_client_by_id(ctx.client_id) - local encoding = client and client.offset_encoding or 'utf-16' - local candidates = util.extract_completion_items(result) - local startbyte = adjust_start_col(pos[1], line, candidates, encoding) or textMatch - local prefix = line:sub(startbyte + 1, pos[2]) - local matches = util.text_document_completion_list_to_complete_items(result, prefix) - -- TODO(ashkan): is this the best way to do this? - vim.list_extend(items, matches) - vim.fn.complete(startbyte + 1, items) - end) + for _, client in ipairs(clients) do + local params = util.make_position_params(win, client.offset_encoding) + client.request('textDocument/completion', params, function(err, result) + if err then + log.warn(err.message) + end + if result and vim.fn.mode() == 'i' then + -- Completion response items may be relative to a position different than `textMatch`. + -- Concrete example, with sumneko/lua-language-server: + -- + -- require('plenary.asy| + -- ▲ ▲ ▲ + -- │ │ └── cursor_pos: 20 + -- │ └────── textMatch: 17 + -- └────────────── textEdit.range.start.character: 9 + -- .newText = 'plenary.async' + -- ^^^ + -- prefix (We'd remove everything not starting with `asy`, + -- so we'd eliminate the `plenary.async` result + -- + -- `adjust_start_col` is used to prefer the language server boundary. + -- + local encoding = client.offset_encoding + local candidates = util.extract_completion_items(result) + local curstartbyte = adjust_start_col(pos[1], line, candidates, encoding) + if startbyte == nil then + startbyte = curstartbyte + elseif curstartbyte ~= nil and curstartbyte ~= startbyte then + startbyte = match_pos + end + local prefix = startbyte and line:sub(startbyte + 1) or line_to_cursor:sub(match_pos) + local matches = util.text_document_completion_list_to_complete_items(result, prefix) + vim.list_extend(items, matches) + end + remaining = remaining - 1 + if remaining == 0 then + vim.schedule(on_done) + end + end, bufnr) + end -- Return -2 to signal that we should continue completion so that we can -- async complete. -- cgit From 63b3408551561127f7845470eb51404bcd6f547b Mon Sep 17 00:00:00 2001 From: Chris AtLee Date: Thu, 20 Jul 2023 03:03:48 -0400 Subject: feat(lsp): implement textDocument/diagnostic (#24128) --- runtime/lua/vim/lsp.lua | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 65cce6af47..fa2a888a54 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -61,6 +61,7 @@ lsp._request_name_to_capability = { ['textDocument/semanticTokens/full'] = { 'semanticTokensProvider' }, ['textDocument/semanticTokens/full/delta'] = { 'semanticTokensProvider' }, ['textDocument/inlayHint'] = { 'inlayHintProvider' }, + ['textDocument/diagnostic'] = { 'diagnosticProvider' }, ['inlayHint/resolve'] = { 'inlayHintProvider', 'resolveProvider' }, } @@ -954,6 +955,9 @@ function lsp._set_defaults(client, bufnr) vim.keymap.set('n', 'K', vim.lsp.buf.hover, { buffer = bufnr }) end end) + if client.supports_method('textDocument/diagnostic') then + lsp.diagnostic._enable(bufnr) + end end --- @class lsp.ClientConfig @@ -1567,7 +1571,23 @@ function lsp.start_client(config) if method ~= 'textDocument/didChange' then changetracking.flush(client) end - return rpc.notify(method, params) + + local result = rpc.notify(method, params) + + if result then + vim.schedule(function() + nvim_exec_autocmds('LspNotify', { + modeline = false, + data = { + client_id = client.id, + method = method, + params = params, + }, + }) + end) + end + + return result end ---@private -- cgit From 4b57ff77febbe6073bc4c5c3a45b0ad0d5d40e6c Mon Sep 17 00:00:00 2001 From: Chris AtLee Date: Sat, 22 Jul 2023 05:00:17 -0400 Subject: refactor(lsp): use LspNotify for inlay_hint (#24411) --- runtime/lua/vim/lsp.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index fa2a888a54..78b5f53723 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1572,9 +1572,9 @@ function lsp.start_client(config) changetracking.flush(client) end - local result = rpc.notify(method, params) + local client_active = rpc.notify(method, params) - if result then + if client_active then vim.schedule(function() nvim_exec_autocmds('LspNotify', { modeline = false, @@ -1587,7 +1587,7 @@ function lsp.start_client(config) end) end - return result + return client_active end ---@private -- cgit From 7668f89d5be6d463bf6ab0c2d3a0393e3ec26e7f Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Mon, 24 Jul 2023 20:21:35 +0200 Subject: fix(lsp): replace @private with @nodoc for public client functions (#24415) * fix(lsp): replace @private with @nodoc for public client functions To prevent lua-ls warnings in plugins which use the functions. * fix(lsp): remove duplicate type annotations/class definitions These annotations became duplicate with https://github.com/neovim/neovim/pull/23750 --- runtime/lua/vim/lsp.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 78b5f53723..1a1dc684ba 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1455,7 +1455,7 @@ function lsp.start_client(config) end) end - ---@private + ---@nodoc --- Sends a request to the server. --- --- This is a thin wrapper around {client.rpc.request} with some additional @@ -1559,7 +1559,7 @@ function lsp.start_client(config) return request_result end - ---@private + ---@nodoc --- Sends a notification to an LSP server. --- ---@param method string LSP method name. @@ -1590,7 +1590,7 @@ function lsp.start_client(config) return client_active end - ---@private + ---@nodoc --- Cancels a request with a given request id. --- ---@param id (integer) id of request to cancel @@ -1613,7 +1613,8 @@ function lsp.start_client(config) -- Track this so that we can escalate automatically if we've already tried a -- graceful shutdown local graceful_shutdown_failed = false - ---@private + + ---@nodoc --- Stops a client, optionally with force. --- ---By default, it will just ask the - server to shutdown without force. If @@ -2311,7 +2312,6 @@ function lsp.omnifunc(findstart, base) local startbyte - ---@private local function on_done() local mode = api.nvim_get_mode()['mode'] if mode == 'i' or mode == 'ic' then -- cgit From 996dd36c77321ea91758b5996d152a2f37af99dd Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sat, 29 Jul 2023 16:10:40 +0200 Subject: feat(lsp): add actionable advice to lsp client quit error msg (#24510) Co-authored-by: Justin M. Keyes --- runtime/lua/vim/lsp.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 1a1dc684ba..edf69af718 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1260,8 +1260,13 @@ function lsp.start_client(config) changetracking.reset(client) end if code ~= 0 or (signal ~= 0 and signal ~= 15) then - local msg = - string.format('Client %s quit with exit code %s and signal %s', client_id, code, signal) + local msg = string.format( + 'Client %s quit with exit code %s and signal %s. Check log for errors: %s', + name, + code, + signal, + lsp.get_log_path() + ) vim.notify(msg, vim.log.levels.WARN) end end) -- cgit From f1772272b4fda43c093fc495f54b5e7c11968d62 Mon Sep 17 00:00:00 2001 From: Raphael Date: Thu, 3 Aug 2023 19:03:48 +0800 Subject: refactor(lsp): use protocol.Methods instead of strings #24537 --- runtime/lua/vim/lsp.lua | 101 ++++++++++++++++++++++++------------------------ 1 file changed, 51 insertions(+), 50 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index edf69af718..b4c853d3d4 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -3,6 +3,7 @@ local default_handlers = require('vim.lsp.handlers') local log = require('vim.lsp.log') local lsp_rpc = require('vim.lsp.rpc') local protocol = require('vim.lsp.protocol') +local ms = protocol.Methods local util = require('vim.lsp.util') local sync = require('vim.lsp.sync') local semantic_tokens = require('vim.lsp.semantic_tokens') @@ -35,34 +36,34 @@ local lsp = { -- maps request name to the required server_capability in the client. lsp._request_name_to_capability = { - ['textDocument/hover'] = { 'hoverProvider' }, - ['textDocument/signatureHelp'] = { 'signatureHelpProvider' }, - ['textDocument/definition'] = { 'definitionProvider' }, - ['textDocument/implementation'] = { 'implementationProvider' }, - ['textDocument/declaration'] = { 'declarationProvider' }, - ['textDocument/typeDefinition'] = { 'typeDefinitionProvider' }, - ['textDocument/documentSymbol'] = { 'documentSymbolProvider' }, - ['textDocument/prepareCallHierarchy'] = { 'callHierarchyProvider' }, - ['callHierarchy/incomingCalls'] = { 'callHierarchyProvider' }, - ['callHierarchy/outgoingCalls'] = { 'callHierarchyProvider' }, - ['textDocument/rename'] = { 'renameProvider' }, - ['textDocument/prepareRename'] = { 'renameProvider', 'prepareProvider' }, - ['textDocument/codeAction'] = { 'codeActionProvider' }, - ['textDocument/codeLens'] = { 'codeLensProvider' }, - ['codeLens/resolve'] = { 'codeLensProvider', 'resolveProvider' }, - ['codeAction/resolve'] = { 'codeActionProvider', 'resolveProvider' }, - ['workspace/executeCommand'] = { 'executeCommandProvider' }, - ['workspace/symbol'] = { 'workspaceSymbolProvider' }, - ['textDocument/references'] = { 'referencesProvider' }, - ['textDocument/rangeFormatting'] = { 'documentRangeFormattingProvider' }, - ['textDocument/formatting'] = { 'documentFormattingProvider' }, - ['textDocument/completion'] = { 'completionProvider' }, - ['textDocument/documentHighlight'] = { 'documentHighlightProvider' }, - ['textDocument/semanticTokens/full'] = { 'semanticTokensProvider' }, - ['textDocument/semanticTokens/full/delta'] = { 'semanticTokensProvider' }, - ['textDocument/inlayHint'] = { 'inlayHintProvider' }, - ['textDocument/diagnostic'] = { 'diagnosticProvider' }, - ['inlayHint/resolve'] = { 'inlayHintProvider', 'resolveProvider' }, + [ms.textDocument_hover] = { 'hoverProvider' }, + [ms.textDocument_signatureHelp] = { 'signatureHelpProvider' }, + [ms.textDocument_definition] = { 'definitionProvider' }, + [ms.textDocument_implementation] = { 'implementationProvider' }, + [ms.textDocument_declaration] = { 'declarationProvider' }, + [ms.textDocument_typeDefinition] = { 'typeDefinitionProvider' }, + [ms.textDocument_documentSymbol] = { 'documentSymbolProvider' }, + [ms.textDocument_prepareCallHierarchy] = { 'callHierarchyProvider' }, + [ms.callHierarchy_incomingCalls] = { 'callHierarchyProvider' }, + [ms.callHierarchy_outgoingCalls] = { 'callHierarchyProvider' }, + [ms.textDocument_rename] = { 'renameProvider' }, + [ms.textDocument_prepareRename] = { 'renameProvider', 'prepareProvider' }, + [ms.textDocument_codeAction] = { 'codeActionProvider' }, + [ms.textDocument_codeLens] = { 'codeLensProvider' }, + [ms.codeLens_resolve] = { 'codeLensProvider', 'resolveProvider' }, + [ms.codeAction_resolve] = { 'codeActionProvider', 'resolveProvider' }, + [ms.workspace_executeCommand] = { 'executeCommandProvider' }, + [ms.workspace_symbol] = { 'workspaceSymbolProvider' }, + [ms.textDocument_references] = { 'referencesProvider' }, + [ms.textDocument_rangeFormatting] = { 'documentRangeFormattingProvider' }, + [ms.textDocument_formatting] = { 'documentFormattingProvider' }, + [ms.textDocument_completion] = { 'completionProvider' }, + [ms.textDocument_documentHighlight] = { 'documentHighlightProvider' }, + [ms.textDocument_semanticTokens_full] = { 'semanticTokensProvider' }, + [ms.textDocument_semanticTokens_full_delta] = { 'semanticTokensProvider' }, + [ms.textDocument_inlayHint] = { 'inlayHintProvider' }, + [ms.textDocument_diagnostic] = { 'diagnosticProvider' }, + [ms.inlayHint_resolve] = { 'inlayHintProvider', 'resolveProvider' }, } -- TODO improve handling of scratch buffers with LSP attached. @@ -583,7 +584,7 @@ do local uri = vim.uri_from_bufnr(bufnr) for _, client in pairs(state.clients) do if not client.is_stopped() and lsp.buf_is_attached(bufnr, client.id) then - client.notify('textDocument/didChange', { + client.notify(ms.textDocument_didChange, { textDocument = { uri = uri, version = util.buf_versions[bufnr], @@ -701,7 +702,7 @@ local function text_document_did_open_handler(bufnr, client) text = buf_get_full_text(bufnr), }, } - client.notify('textDocument/didOpen', params) + client.notify(ms.textDocument_didOpen, params) util.buf_versions[bufnr] = params.textDocument.version -- Next chance we get, we should re-do the diagnostics @@ -930,17 +931,17 @@ end ---@param client lsp.Client function lsp._set_defaults(client, bufnr) if - client.supports_method('textDocument/definition') and is_empty_or_default(bufnr, 'tagfunc') + client.supports_method(ms.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') + client.supports_method(ms.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') + client.supports_method(ms.textDocument_rangeFormatting) and is_empty_or_default(bufnr, 'formatprg') and is_empty_or_default(bufnr, 'formatexpr') then @@ -948,14 +949,14 @@ function lsp._set_defaults(client, bufnr) end api.nvim_buf_call(bufnr, function() if - client.supports_method('textDocument/hover') + client.supports_method(ms.textDocument_hover) and is_empty_or_default(bufnr, 'keywordprg') and vim.fn.maparg('K', 'n', false, false) == '' then vim.keymap.set('n', 'K', vim.lsp.buf.hover, { buffer = bufnr }) end end) - if client.supports_method('textDocument/diagnostic') then + if client.supports_method(ms.textDocument_diagnostic) then lsp.diagnostic._enable(bufnr) end end @@ -1431,7 +1432,7 @@ function lsp.start_client(config) end if next(config.settings) then - client.notify('workspace/didChangeConfiguration', { settings = config.settings }) + client.notify(ms.workspace_didChangeConfiguration, { settings = config.settings }) end if config.on_init then @@ -1573,7 +1574,7 @@ function lsp.start_client(config) ---If it is false, then it will always be false ---(the client has shutdown). function client.notify(method, params) - if method ~= 'textDocument/didChange' then + if method ~= ms.textDocument_didChange then changetracking.flush(client) end @@ -1693,7 +1694,7 @@ function lsp.start_client(config) command = command.command, arguments = command.arguments, } - client.request('workspace/executeCommand', params, handler, context.bufnr) + client.request(ms.workspace_executeCommand, params, handler, context.bufnr) end ---@private @@ -1763,12 +1764,12 @@ local function text_document_did_save_handler(bufnr) 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 - client.notify('textDocument/didClose', { + client.notify(ms.textDocument_didClose, { textDocument = { uri = vim.uri_from_fname(old_name), }, }) - client.notify('textDocument/didOpen', { + client.notify(ms.textDocument_didOpen, { textDocument = { version = 0, uri = uri, @@ -1784,7 +1785,7 @@ local function text_document_did_save_handler(bufnr) if type(save_capability) == 'table' and save_capability.includeText then included_text = text(bufnr) end - client.notify('textDocument/didSave', { + client.notify(ms.textDocument_didSave, { textDocument = { uri = uri, }, @@ -1835,11 +1836,11 @@ function lsp.buf_attach_client(bufnr, client_id) reason = protocol.TextDocumentSaveReason.Manual, } if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'willSave') then - client.notify('textDocument/willSave', params) + client.notify(ms.textDocument_willSave, params) end if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'willSaveWaitUntil') then local result, err = - client.request_sync('textDocument/willSaveWaitUntil', params, 1000, ctx.buf) + client.request_sync(ms.textDocument_willSaveWaitUntil, params, 1000, ctx.buf) if result and result.result then util.apply_text_edits(result.result, ctx.buf, client.offset_encoding) elseif err then @@ -1865,7 +1866,7 @@ function lsp.buf_attach_client(bufnr, client_id) for _, client in ipairs(lsp.get_clients({ bufnr = bufnr })) do changetracking.reset_buf(client, bufnr) if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then - client.notify('textDocument/didClose', params) + client.notify(ms.textDocument_didClose, params) end text_document_did_open_handler(bufnr, client) end @@ -1875,7 +1876,7 @@ function lsp.buf_attach_client(bufnr, client_id) for _, client in ipairs(lsp.get_clients({ bufnr = bufnr })) do changetracking.reset_buf(client, bufnr) if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then - client.notify('textDocument/didClose', params) + client.notify(ms.textDocument_didClose, params) end client.attached_buffers[bufnr] = nil end @@ -1940,7 +1941,7 @@ function lsp.buf_detach_client(bufnr, client_id) 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) + client.notify(ms.textDocument_didClose, params) end client.attached_buffers[bufnr] = nil @@ -2294,7 +2295,7 @@ function lsp.omnifunc(findstart, base) end local bufnr = resolve_bufnr() - local clients = lsp.get_clients({ bufnr = bufnr, method = 'textDocument/completion' }) + local clients = lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_completion }) local remaining = #clients if remaining == 0 then return findstart == 1 and -1 or {} @@ -2326,7 +2327,7 @@ function lsp.omnifunc(findstart, base) for _, client in ipairs(clients) do local params = util.make_position_params(win, client.offset_encoding) - client.request('textDocument/completion', params, function(err, result) + client.request(ms.textDocument_completion, params, function(err, result) if err then log.warn(err.message) end @@ -2397,7 +2398,7 @@ function lsp.formatexpr(opts) end local bufnr = api.nvim_get_current_buf() for _, client in pairs(lsp.get_clients({ bufnr = bufnr })) do - if client.supports_method('textDocument/rangeFormatting') then + if client.supports_method(ms.textDocument_rangeFormatting) then local params = util.make_formatting_params() local end_line = vim.fn.getline(end_lnum) --[[@as string]] local end_col = util._str_utfindex_enc(end_line, nil, client.offset_encoding) @@ -2412,7 +2413,7 @@ function lsp.formatexpr(opts) }, } local response = - client.request_sync('textDocument/rangeFormatting', params, timeout_ms, bufnr) + client.request_sync(ms.textDocument_rangeFormatting, params, timeout_ms, bufnr) if response and response.result then lsp.util.apply_text_edits(response.result, 0, client.offset_encoding) return 0 -- cgit From cc87dda31a5b5637ade7ddcfe5199f2df5fd47df Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 4 Aug 2023 07:10:54 +0100 Subject: fix(lsp): do not assume client capability exists in watchfiles check (#24550) PR #23689 assumes `client.config.capabilities.workspace.didChangeWatchedFiles` exists when checking `dynamicRegistration`, but thats's true only if it was passed to `vim.lsp.start{_client}`. This caused #23806 (still an issue in v0.9.1; needs manual backport), but #23681 fixed it by defaulting `config.capabilities` to `make_client_capabilities` if not passed to `vim.lsp.start{_client}`. However, the bug resurfaces on HEAD if you provide a non-nil `capabilities` to `vim.lsp.start{_client}` with missing fields (e.g: not made via `make_client_capabilities`). From what I see, the spec says such missing fields should be interpreted as an absence of the capability (including those indicated by missing sub-fields): https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#clientCapabilities Also, suggest `vim.empty_dict()` for an empty dict in `:h vim.lsp.start_client()` (`{[vim.type_idx]=vim.types.dictionary}` no longer works anyway, probably since the cjson switch). --- runtime/lua/vim/lsp.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index b4c853d3d4..ea6b386b28 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1027,8 +1027,7 @@ end --- \|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 ---- `{[vim.type_idx]=vim.types.dictionary}`, else it will be encoded as an +--- - Note: To send an empty dictionary use |vim.empty_dict()|, else it will be encoded as an --- array. --- --- - handlers: Map of language server method names to |lsp-handler| -- cgit From 42630923fc00633d806af97c1792b2ed4a71e1cc Mon Sep 17 00:00:00 2001 From: Raphael Date: Sat, 5 Aug 2023 17:03:57 +0800 Subject: refactor(lsp): use protocol.Methods instead of strings #24570 --- runtime/lua/vim/lsp.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index ea6b386b28..bfba275c33 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1612,7 +1612,7 @@ function lsp.start_client(config) data = { client_id = client_id, request_id = id, request = request }, }) end - return rpc.notify('$/cancelRequest', { id = id }) + return rpc.notify(ms.dollar_cancelRequest, { id = id }) end -- Track this so that we can escalate automatically if we've already tried a @@ -1636,9 +1636,9 @@ function lsp.start_client(config) return end -- Sending a signal after a process has exited is acceptable. - rpc.request('shutdown', nil, function(err, _) + rpc.request(ms.shutdown, nil, function(err, _) if err == nil then - rpc.notify('exit') + rpc.notify(ms.exit) else -- If there was an error in the shutdown request, then term to be safe. rpc.terminate() -- cgit From c43c745a14dced87a23227d7be4f1c33d4455193 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 9 Aug 2023 11:06:13 +0200 Subject: fix(lua): improve annotations for stricter luals diagnostics (#24609) Problem: luals returns stricter diagnostics with bundled luarc.json Solution: Improve some function and type annotations: * use recognized uv.* types * disable diagnostic for global `vim` in shared.lua * docs: don't start comment lines with taglink (otherwise LuaLS will interpret it as a type) * add type alias for lpeg pattern * fix return annotation for `vim.secure.trust` * rename local Range object in vim.version (shadows `Range` in vim.treesitter) * fix some "missing fields" warnings * add missing required fields for test functions in eval.lua * rename lsp meta files for consistency --- runtime/lua/vim/lsp.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index bfba275c33..0c4290d067 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -364,7 +364,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_timer_t uv_timer + --- @field timer nil|uv.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 -- cgit From 72e64a1afef0df1bd244bfb31dfc8619d91ff709 Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 11 Aug 2023 20:20:05 +0800 Subject: fix(lsp): extra "." when completing with tsserver #24646 Problem: With tsserver LSP, omni completion after "." inserts an extra "." Solution: Apply adjust_start_col() regardless of `filterText`. adjust_start_col() is explained here: https://github.com/neovim/neovim/blob/0ea8dfeb3dc347579753169d9e3588f6306ab703/runtime/lua/vim/lsp.lua#L2334-L2351 The `filterText` field is used in the following situations rather than as a condition for obtaining column values: 1. Real-time filtering: When the user types characters in the editor, the language server can use the filterText field to filter the list of suggestions and only return suggestions that match the typed characters. This helps to provide more precise recommendations. 2. Fuzzy matching: The filterText field can be used to perform fuzzy matching, allowing matching in the middle or beginning of input characters, not limited to prefix matching. This can provide a more flexible code completion experience. Inspecting usage of `filtertext` in vim-lsp and coc and lsp-mode: - vim-lsp uses a `refresh_pattern` to judge filterText as completionitem word (although I think this is not the correct usage). - coc uses it for filtering. Fix #22803 --- runtime/lua/vim/lsp.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 0c4290d067..2a16bafbfc 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -2262,7 +2262,7 @@ end local function adjust_start_col(lnum, line, items, encoding) local min_start_char = nil for _, item in pairs(items) do - if item.filterText == nil and item.textEdit and item.textEdit.range.start.line == lnum - 1 then + if item.textEdit and item.textEdit.range.start.line == lnum - 1 then if min_start_char and min_start_char ~= item.textEdit.range.start.character then return nil end -- cgit From c235959fd909d75248c066a781475e207606c5aa Mon Sep 17 00:00:00 2001 From: Chris AtLee Date: Thu, 31 Aug 2023 04:00:24 -0400 Subject: fix(lsp): only disable inlay hints / diagnostics if no other clients are connected (#24535) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes the issue where the LspNotify handlers for inlay_hint / diagnostics would end up refreshing all attached clients. The handler would call util._refresh, which called vim.lsp.buf_request, which calls the method on all attached clients. Now util._refresh takes an optional client_id parameter, which is used to specify a specific client to update. This commit also fixes util._refresh's handling of the `only_visible` flag. Previously if `only_visible` was false, two requests would be made to the server: one for the visible region, and one for the entire file. Co-authored-by: Stanislav Asunkin <1353637+stasjok@users.noreply.github.com> Co-authored-by: Mathias Fußenegger --- runtime/lua/vim/lsp.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 2a16bafbfc..1990c09561 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -2118,7 +2118,7 @@ api.nvim_create_autocmd('VimLeavePre', { ---@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 handler lsp-handler See |lsp-handler| +---@param handler? lsp-handler See |lsp-handler| --- If nil, follows resolution strategy defined in |lsp-handler-configuration| --- ---@return table client_request_ids Map of client-id:request-id pairs -- cgit From 0e7e25af20cf98a19f4f50ee80774fce07cef12e Mon Sep 17 00:00:00 2001 From: Jongwook Choi Date: Thu, 31 Aug 2023 04:14:20 -0400 Subject: refactor(lsp): add type annotation for lsp.Client.server_capabilities (#24925) The class `lsp.Client` has a public member `server_capabilities`, which is assumed to be non-nil once initialized, as documented in `:help vim.lsp.client`. Due to the possibility that it may be nil before initialization, `lsp.Client` was not having a proper lua type annotations on the field `server_capabilities`. Instead of having a nil `server_capabilities` until initialized in the RPC response callback, we can have an initial value of empty table. This CHANGES the behavior of the `server_capabilities` field in a way that it is no longer `nil` until initialization. Note that, as already documented, `server_capabilities` should never be nil when it is once initialized and thus ready to be used in user configs. --- runtime/lua/vim/lsp.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 1990c09561..b0caf5af35 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -397,8 +397,7 @@ do ---@return CTGroup local function get_group(client) local allow_inc_sync = if_nil(client.config.flags.allow_incremental_sync, true) - local change_capability = - vim.tbl_get(client.server_capabilities or {}, 'textDocumentSync', 'change') + local change_capability = vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'change') local sync_kind = change_capability or protocol.TextDocumentSyncKind.None if not allow_inc_sync and change_capability == protocol.TextDocumentSyncKind.Incremental then sync_kind = protocol.TextDocumentSyncKind.Full @@ -1312,6 +1311,9 @@ function lsp.start_client(config) --- - lsp.WorkDoneProgressEnd (extended with title from Begin) progress = vim.ringbuf(50), + --- @type lsp.ServerCapabilities + server_capabilities = {}, + ---@deprecated use client.progress instead messages = { name = name, messages = {}, progress = {}, status = {} }, dynamic_capabilities = require('vim.lsp._dynamic').new(client_id), @@ -1401,7 +1403,7 @@ function lsp.start_client(config) if not required_capability then return true end - if vim.tbl_get(client.server_capabilities or {}, unpack(required_capability)) then + if vim.tbl_get(client.server_capabilities, unpack(required_capability)) then return true else if client.dynamic_capabilities:supports_registration(method) then -- cgit From a49924a318520a3b5c2f210f22a8d450165e89b5 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Thu, 14 Sep 2023 08:40:53 +0200 Subject: feat(lsp): remove notify from vim.lsp.buf_detach_client (#25140) Closes https://github.com/neovim/neovim/issues/19838 --- runtime/lua/vim/lsp.lua | 2 -- 1 file changed, 2 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index b0caf5af35..c25358e557 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1955,8 +1955,6 @@ function lsp.buf_detach_client(bufnr, 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)) end --- Checks if a buffer is attached for a particular client. -- cgit From 2e92065686f62851318150a315591c30b8306a4b Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Thu, 14 Sep 2023 08:23:01 -0500 Subject: docs: replace
 with ``` (#25136)

---
 runtime/lua/vim/lsp.lua | 17 +++++++----------
 1 file changed, 7 insertions(+), 10 deletions(-)

(limited to 'runtime/lua/vim/lsp.lua')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index c25358e557..f68ca7e88c 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -809,13 +809,14 @@ end
 --- Attaches the current buffer to the client.
 ---
 --- Example:
---- 
lua
+---
+--- ```lua
 --- vim.lsp.start({
 ---    name = 'my-server-name',
 ---    cmd = {'name-of-language-server-executable'},
 ---    root_dir = vim.fs.dirname(vim.fs.find({'pyproject.toml', 'setup.py'}, { upward = true })[1]),
 --- })
---- 
+--- ``` --- --- See |vim.lsp.start_client()| for all available options. The most important are: --- @@ -1988,9 +1989,10 @@ end --- --- You can also use the `stop()` function on a |vim.lsp.client| object. --- To stop all clients: ----
lua
+---
+--- ```lua
 --- vim.lsp.stop_client(vim.lsp.get_clients())
---- 
+--- ``` --- --- By default asks the server to shutdown, unless stop was requested --- already for this client, then force-shutdown is attempted. @@ -2502,12 +2504,7 @@ end ---@param bufnr integer Buffer number ---@param fn function Function to run on each client attached to buffer --- {bufnr}. The function takes the client, client ID, and ---- buffer number as arguments. Example: ----
lua
----               vim.lsp.for_each_buffer_client(0, function(client, client_id, bufnr)
----                 vim.print(client)
----               end)
----             
+--- buffer number as arguments. ---@deprecated use lsp.get_clients({ bufnr = bufnr }) with regular loop function lsp.for_each_buffer_client(bufnr, fn) return for_each_buffer_client(bufnr, fn) -- cgit From 2fde6295df8cdfb36ed02ef5f9a3b6a523b1ac23 Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Wed, 11 Oct 2023 13:38:58 -0700 Subject: fix(lsp): display initialization errors (#25409) --- runtime/lua/vim/lsp.lua | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index f68ca7e88c..4b1da94d77 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -189,7 +189,9 @@ lsp.client_errors = tbl_extend( 'error', lsp_rpc.client_errors, vim.tbl_add_reverse_lookup({ - ON_INIT_CALLBACK_ERROR = table.maxn(lsp_rpc.client_errors) + 1, + BEFORE_INIT_CALLBACK_ERROR = table.maxn(lsp_rpc.client_errors) + 1, + ON_INIT_CALLBACK_ERROR = table.maxn(lsp_rpc.client_errors) + 2, + ON_ATTACH_ERROR = table.maxn(lsp_rpc.client_errors) + 3, }) ) @@ -1172,6 +1174,16 @@ function lsp.start_client(config) return nil, lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound) end + --- Logs the given error to the LSP log and to the error buffer. + --- @param code integer Error code + --- @param err any Error arguments + local function write_error(code, 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)) + end + ---@private --- Invoked when the client operation throws an error. --- @@ -1180,10 +1192,7 @@ function lsp.start_client(config) ---@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) - 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)) + write_error(code, err) if config.on_error then local status, usererr = pcall(config.on_error, code, err) if not status then @@ -1391,8 +1400,10 @@ function lsp.start_client(config) trace = valid_traces[config.trace] or 'off', } if config.before_init then - -- TODO(ashkan) handle errors here. - pcall(config.before_init, initialize_params, config) + local status, err = pcall(config.before_init, initialize_params, config) + if not status then + write_error(lsp.client_errors.BEFORE_INIT_CALLBACK_ERROR, err) + end end --- @param method string @@ -1440,7 +1451,7 @@ function lsp.start_client(config) 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) + write_error(lsp.client_errors.ON_INIT_CALLBACK_ERROR, err) end end local _ = log.info() @@ -1714,8 +1725,10 @@ function lsp.start_client(config) }) if config.on_attach then - -- TODO(ashkan) handle errors. - pcall(config.on_attach, client, bufnr) + local status, err = pcall(config.on_attach, client, bufnr) + if not status then + write_error(lsp.client_errors.ON_ATTACH_ERROR, err) + end end -- schedule the initialization of semantic tokens to give the above -- cgit From 1e10310f4cc70cf95a68457c2be9e7459b5bbba6 Mon Sep 17 00:00:00 2001 From: Mathias Fussenegger Date: Sat, 21 Oct 2023 09:47:24 +0200 Subject: refactor(lsp): move completion logic into _completion module To reduce cross-chatter between modules and for https://github.com/neovim/neovim/issues/25272 Also preparing for https://github.com/neovim/neovim/issues/25714 --- runtime/lua/vim/lsp.lua | 95 +------------------------------------------------ 1 file changed, 1 insertion(+), 94 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 4b1da94d77..82a88772bd 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -2273,24 +2273,6 @@ function lsp.buf_notify(bufnr, method, params) return resp end ----@private -local function adjust_start_col(lnum, line, items, encoding) - local min_start_char = nil - for _, item in pairs(items) do - if item.textEdit and item.textEdit.range.start.line == lnum - 1 then - if min_start_char and min_start_char ~= item.textEdit.range.start.character then - return nil - end - min_start_char = item.textEdit.range.start.character - end - end - if min_start_char then - return util._str_byteindex_enc(line, min_start_char, encoding) - else - return nil - end -end - --- Implements 'omnifunc' compatible LSP completion. --- ---@see |complete-functions| @@ -2307,82 +2289,7 @@ function lsp.omnifunc(findstart, base) if log.debug() then log.debug('omnifunc.findstart', { findstart = findstart, base = base }) end - - local bufnr = resolve_bufnr() - local clients = lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_completion }) - local remaining = #clients - if remaining == 0 then - return findstart == 1 and -1 or {} - end - - -- Then, perform standard completion request - if log.info() then - log.info('base ', base) - end - - local win = api.nvim_get_current_win() - local pos = api.nvim_win_get_cursor(win) - local line = api.nvim_get_current_line() - local line_to_cursor = line:sub(1, pos[2]) - local _ = log.trace() and log.trace('omnifunc.line', pos, line) - - -- Get the start position of the current keyword - local match_pos = vim.fn.match(line_to_cursor, '\\k*$') + 1 - local items = {} - - local startbyte - - local function on_done() - local mode = api.nvim_get_mode()['mode'] - if mode == 'i' or mode == 'ic' then - vim.fn.complete(startbyte or match_pos, items) - end - end - - for _, client in ipairs(clients) do - local params = util.make_position_params(win, client.offset_encoding) - client.request(ms.textDocument_completion, params, function(err, result) - if err then - log.warn(err.message) - end - if result and vim.fn.mode() == 'i' then - -- Completion response items may be relative to a position different than `textMatch`. - -- Concrete example, with sumneko/lua-language-server: - -- - -- require('plenary.asy| - -- ▲ ▲ ▲ - -- │ │ └── cursor_pos: 20 - -- │ └────── textMatch: 17 - -- └────────────── textEdit.range.start.character: 9 - -- .newText = 'plenary.async' - -- ^^^ - -- prefix (We'd remove everything not starting with `asy`, - -- so we'd eliminate the `plenary.async` result - -- - -- `adjust_start_col` is used to prefer the language server boundary. - -- - local encoding = client.offset_encoding - local candidates = util.extract_completion_items(result) - local curstartbyte = adjust_start_col(pos[1], line, candidates, encoding) - if startbyte == nil then - startbyte = curstartbyte - elseif curstartbyte ~= nil and curstartbyte ~= startbyte then - startbyte = match_pos - end - local prefix = startbyte and line:sub(startbyte + 1) or line_to_cursor:sub(match_pos) - local matches = util.text_document_completion_list_to_complete_items(result, prefix) - vim.list_extend(items, matches) - end - remaining = remaining - 1 - if remaining == 0 then - vim.schedule(on_done) - end - end, bufnr) - end - - -- Return -2 to signal that we should continue completion so that we can - -- async complete. - return -2 + return require('vim.lsp._completion').omnifunc(findstart, base) end --- Provides an interface between the built-in client and a `formatexpr` function. -- cgit From 448907f65d6709fa234d8366053e33311a01bdb9 Mon Sep 17 00:00:00 2001 From: LW Date: Sun, 12 Nov 2023 04:54:27 -0800 Subject: feat(lsp)!: vim.lsp.inlay_hint.get(), enable(), is_enabled() #25512 refactor!: `vim.lsp.inlay_hint()` -> `vim.lsp.inlay_hint.enable()` Problem: The LSP specification allows inlay hints to include tooltips, clickable label parts, and code actions; but Neovim provides no API to query for these. Solution: Add minimal viable extension point from which plugins can query for inlay hints in a range, in order to build functionality on top of. Possible Next Steps --- - Add `virt_text_idx` field to `vim.fn.getmousepos()` return value, for usage in mappings of ``, ``, etc --- runtime/lua/vim/lsp.lua | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'runtime/lua/vim/lsp.lua') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 82a88772bd..261a3aa5de 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -24,6 +24,7 @@ local lsp = { buf = require('vim.lsp.buf'), diagnostic = require('vim.lsp.diagnostic'), codelens = require('vim.lsp.codelens'), + inlay_hint = require('vim.lsp.inlay_hint'), semantic_tokens = semantic_tokens, util = util, @@ -2439,13 +2440,6 @@ 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. -- cgit