From a74e869ffa503cc9c2d21836e24fec7a7ffca147 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Tue, 12 Mar 2024 06:51:53 +0100 Subject: docs: small fixes (#27364) Co-authored-by: C.D. MacEachern Co-authored-by: Ynda Jas Co-authored-by: Owen Hines Co-authored-by: Wanten <41904684+WantenMN@users.noreply.github.com> Co-authored-by: lukasvrenner <118417051+lukasvrenner@users.noreply.github.com> Co-authored-by: cuinix <915115094@qq.com> --- runtime/lua/vim/lsp/client.lua | 2 +- runtime/lua/vim/lsp/util.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index ff0db166d5..d48be131f3 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -15,7 +15,7 @@ local validate = vim.validate --- @inlinedoc --- --- Allow using incremental sync for buffer edits ---- (defailt: `true`) +--- (default: `true`) --- @field allow_incremental_sync? boolean --- --- Debounce `didChange` notifications to the server by the given number in milliseconds. diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index f8e5b6a90d..fc99f54d03 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -690,7 +690,7 @@ end --- --- It deletes existing buffers that conflict with the renamed file name only when --- * `opts` requests overwriting; or ---- * the conflicting buffers are not loaded, so that deleting thme does not result in data loss. +--- * the conflicting buffers are not loaded, so that deleting them does not result in data loss. --- --- @param old_fname string --- @param new_fname string -- cgit From 77a9f3395bd1e7184f4d735c01e50285e30477ab Mon Sep 17 00:00:00 2001 From: Takuya Tokuda Date: Mon, 18 Mar 2024 05:04:59 +0900 Subject: fix(lsp): create codelens request parameters for each buffer (#27699) --- runtime/lua/vim/lsp/codelens.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index 48c096c0c1..d2557ca9d7 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -299,12 +299,12 @@ function M.refresh(opts) local bufnr = opts.bufnr and resolve_bufnr(opts.bufnr) local buffers = bufnr and { bufnr } or vim.tbl_filter(api.nvim_buf_is_loaded, api.nvim_list_bufs()) - local params = { - textDocument = util.make_text_document_params(), - } for _, buf in ipairs(buffers) do if not active_refreshes[buf] then + local params = { + textDocument = util.make_text_document_params(buf), + } active_refreshes[buf] = true vim.lsp.buf_request(buf, ms.textDocument_codeLens, params, M.on_codelens) end -- cgit From 849d82b80b8584fe7918bffc6480f0ec0fda9b9b Mon Sep 17 00:00:00 2001 From: Jaehwang Jung Date: Sat, 23 Mar 2024 01:46:01 +0900 Subject: fix(lsp): handle stale bufnr on LspRequest autocmd trigger (#27981) continuation of https://github.com/neovim/neovim/pull/24013 --- runtime/lua/vim/lsp/client.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index d48be131f3..b06fab7319 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -693,7 +693,7 @@ function Client:_request(method, params, handler, bufnr) local request = { type = 'pending', bufnr = bufnr, method = method } self.requests[request_id] = request api.nvim_exec_autocmds('LspRequest', { - buffer = bufnr, + buffer = api.nvim_buf_is_valid(bufnr) and bufnr or nil, modeline = false, data = { client_id = self.id, request_id = request_id, request = request }, }) @@ -804,7 +804,7 @@ function Client:_cancel_request(id) if request and request.type == 'pending' then request.type = 'cancel' api.nvim_exec_autocmds('LspRequest', { - buffer = request.bufnr, + buffer = api.nvim_buf_is_valid(request.bufnr) and request.bufnr or nil, modeline = false, data = { client_id = self.id, request_id = id, request = request }, }) -- cgit From 3f238b39cfdf27657b2d9452c6ffd28f8209c95f Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 21 Mar 2024 15:15:20 +0000 Subject: refactor(lsp): simplify client tracking - Remove: - uninitialized_clients - active_clients - all_buffer_active_clients - Add: - all_clients - Use `lsp.get_clients()` to get buffer clients. --- runtime/lua/vim/lsp/client.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index b06fab7319..758d68e4b0 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -761,7 +761,7 @@ function Client:_request_sync(method, params, timeout_ms, bufnr) return request_result end ---- @private +--- @package --- Sends a notification to an LSP server. --- --- @param method string LSP method name. -- cgit From 934f38682afd5925df675485b96ac9a2d3b8dd57 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Mon, 25 Mar 2024 20:16:42 +0000 Subject: Revert "refactor(lsp): simplify client tracking" This reverts commit 3f238b39cfdf27657b2d9452c6ffd28f8209c95f. --- runtime/lua/vim/lsp/client.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index 758d68e4b0..b06fab7319 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -761,7 +761,7 @@ function Client:_request_sync(method, params, timeout_ms, bufnr) return request_result end ---- @package +--- @private --- Sends a notification to an LSP server. --- --- @param method string LSP method name. -- cgit From a7bbda121d035d050b449b4dc63bd6ae027e248d Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Mon, 25 Mar 2024 19:06:28 +0000 Subject: fix(test): typing --- runtime/lua/vim/lsp/client.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index b06fab7319..1259a2f3d1 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -225,7 +225,7 @@ local validate = vim.validate --- If {status} is `true`, the function returns {request_id} as the second --- result. You can use this with `client.cancel_request(request_id)` to cancel --- the request. ---- @field request fun(method: string, params: table?, handler: lsp.Handler?, bufnr: integer): boolean, integer? +--- @field request fun(method: string, params: table?, handler: lsp.Handler?, bufnr: integer?): boolean, integer? --- --- Sends a request to the server and synchronously waits for the response. --- This is a wrapper around {client.request} @@ -647,10 +647,10 @@ end --- checks for capabilities and handler availability. --- --- @param method string LSP method name. ---- @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 +--- @param params? table LSP request params. +--- @param handler? lsp.Handler Response |lsp-handler| for this method. +--- @param bufnr? integer Buffer handle (0 for current). +--- @return boolean status, integer? 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 -- cgit From 00e71d3da3464df2b4c4f33bfd5fac6d88e7c867 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 21 Mar 2024 15:15:20 +0000 Subject: refactor(lsp): simplify client tracking - Remove: - uninitialized_clients - active_clients - all_buffer_active_clients - Add: - all_clients - Use `lsp.get_clients()` to get buffer clients. --- runtime/lua/vim/lsp/client.lua | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index 1259a2f3d1..303fc55982 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -185,6 +185,10 @@ local validate = vim.validate --- @field root_dir string --- --- @field attached_buffers table +--- +--- Buffers that should be attached to upon initialize() +--- @field package _buffers_to_attach table +--- --- @field private _log_prefix string --- --- Track this so that we can escalate automatically if we've already tried a @@ -608,8 +612,16 @@ function Client:initialize() self:_notify(ms.workspace_didChangeConfiguration, { settings = self.settings }) end + -- If server is being restarted, make sure to re-attach to any previously attached buffers. + -- Save which buffers before on_init in case new buffers are attached. + local reattach_bufs = vim.deepcopy(self.attached_buffers) + self:_run_callbacks(self._on_init_cbs, lsp.client_errors.ON_INIT_CALLBACK_ERROR, self, result) + for buf in pairs(reattach_bufs) do + self:_on_attach(buf) + end + log.info( self._log_prefix, 'server_capabilities', @@ -761,7 +773,7 @@ function Client:_request_sync(method, params, timeout_ms, bufnr) return request_result end ---- @private +--- @package --- Sends a notification to an LSP server. --- --- @param method string LSP method name. -- cgit From 01691c5447d9222a0e84f77b7043251580de7dee Mon Sep 17 00:00:00 2001 From: Marcin Szamotulski Date: Sun, 31 Mar 2024 20:43:34 +0200 Subject: fix(lsp): abort callHierarchy on no result (#28102) The `callHierarchy` function should warn the user when `textDocument/prepareCallHierarchy` didn't resolve an entity and return, rather than calling the `callHierarchy/{incoming,outgoing}Calls` method with an empty object - which is encoded as an empty list (which doesn't respect language server specification for the `callHierarchy/incomingCalls` call). --- runtime/lua/vim/lsp/buf.lua | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 50121f30b2..43f52b8116 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -461,7 +461,14 @@ local function call_hierarchy(method) vim.notify(err.message, vim.log.levels.WARN) return end + if not result then + vim.notify('No item resolved', vim.log.levels.WARN) + return + end local call_hierarchy_item = pick_call_hierarchy_item(result) + if not call_hierarchy_item then + return + end local client = vim.lsp.get_client_by_id(ctx.client_id) if client then client.request(method, { item = call_hierarchy_item }, nil, ctx.bufnr) -- cgit From d9235efa76229708586d3c9db3dcbac46127ca0a Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 2 Apr 2024 11:56:29 +0100 Subject: refactor(lsp): move workspace folder logic into the client - Changed `reuse_client` to check workspace folders in addition to root_dir. --- runtime/lua/vim/lsp/buf.lua | 40 +++++---------------------------------- runtime/lua/vim/lsp/client.lua | 43 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 36 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 43f52b8116..17cc698b76 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -521,28 +521,9 @@ function M.add_workspace_folder(workspace_folder) print(workspace_folder, ' is not a valid directory') return end - local new_workspace = { - uri = vim.uri_from_fname(workspace_folder), - name = workspace_folder, - } - local params = { event = { added = { new_workspace }, removed = {} } } - local bufnr = vim.api.nvim_get_current_buf() + local bufnr = api.nvim_get_current_buf() for _, client in pairs(vim.lsp.get_clients({ bufnr = bufnr })) do - local found = false - for _, folder in pairs(client.workspace_folders or {}) do - if folder.name == workspace_folder then - found = true - print(workspace_folder, 'is already part of this workspace') - break - end - end - if not found then - client.notify(ms.workspace_didChangeWorkspaceFolders, params) - if not client.workspace_folders then - client.workspace_folders = {} - end - table.insert(client.workspace_folders, new_workspace) - end + client:_add_workspace_folder(workspace_folder) end end @@ -554,23 +535,12 @@ function M.remove_workspace_folder(workspace_folder) workspace_folder = workspace_folder or npcall(vim.fn.input, 'Workspace Folder: ', vim.fn.expand('%:p:h')) api.nvim_command('redraw') - if not (workspace_folder and #workspace_folder > 0) then + if not workspace_folder or #workspace_folder == 0 then return end - local workspace = { - uri = vim.uri_from_fname(workspace_folder), - name = workspace_folder, - } - local params = { event = { added = {}, removed = { workspace } } } - local bufnr = vim.api.nvim_get_current_buf() + local bufnr = api.nvim_get_current_buf() for _, client in pairs(vim.lsp.get_clients({ bufnr = bufnr })) do - for idx, folder in pairs(client.workspace_folders) do - if folder.name == workspace_folder then - client.notify(ms.workspace_didChangeWorkspaceFolders, params) - client.workspace_folders[idx] = nil - return - end - end + client:_remove_workspace_folder(workspace_folder) end print(workspace_folder, 'is not currently part of the workspace') end diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index 303fc55982..f73f97b8cd 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -420,7 +420,7 @@ local function get_workspace_folders(workspace_folders, root_dir) return { { uri = vim.uri_from_fname(root_dir), - name = string.format('%s', root_dir), + name = root_dir, }, } end @@ -1065,4 +1065,45 @@ function Client:_on_exit(code, signal) ) end +--- @package +--- Add a directory to the workspace folders. +--- @param dir string? +function Client:_add_workspace_folder(dir) + for _, folder in pairs(self.workspace_folders or {}) do + if folder.name == dir then + print(dir, 'is already part of this workspace') + return + end + end + + local wf = assert(get_workspace_folders(nil, dir)) + + self:_notify(ms.workspace_didChangeWorkspaceFolders, { + event = { added = wf, removed = {} }, + }) + + if not self.workspace_folders then + self.workspace_folders = {} + end + vim.list_extend(self.workspace_folders, wf) +end + +--- @package +--- Remove a directory to the workspace folders. +--- @param dir string? +function Client:_remove_workspace_folder(dir) + local wf = assert(get_workspace_folders(nil, dir)) + + self:_notify(ms.workspace_didChangeWorkspaceFolders, { + event = { added = {}, removed = wf }, + }) + + for idx, folder in pairs(self.workspace_folders) do + if folder.name == dir then + table.remove(self.workspace_folders, idx) + break + end + end +end + return Client -- cgit From 9af355964306977eab836ca5324ed70d685c0c8e Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Fri, 5 Apr 2024 13:24:39 +0200 Subject: feat(lsp): set workDoneToken in initialize request (#28182) Problem: Some servers don't report progress during initialize unless the client sets the `workDoneToken` See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initiatingWorkDoneProgress In particular: > There is no specific client capability signaling whether a client will > send a progress token per request. The reason for this is that this is > in many clients not a static aspect and might even change for every > request instance for the same request type. So the capability is signal > on every request instance by the presence of a workDoneToken property. And: > Servers can also initiate progress reporting using the > window/workDoneProgress/create request. This is useful if the server > needs to report progress outside of a request (for example the server > needs to re-index a database). The token can then be used to report > progress using the same notifications used as for client initiated > progress. So far progress report functionality was relying entirely on the latter. Solution: Set a `workDoneToken` Closes https://github.com/neovim/neovim/issues/27938 --- runtime/lua/vim/lsp/client.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index f73f97b8cd..09064b9510 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -579,6 +579,7 @@ function Client:initialize() initializationOptions = config.init_options, capabilities = self.capabilities, trace = self._trace, + workDoneToken = '1', } self:_run_callbacks( -- cgit From b95b6ed9753da8f157d3ba34408c4c2e0de9d744 Mon Sep 17 00:00:00 2001 From: Yi Ming Date: Wed, 10 Apr 2024 18:23:47 +0800 Subject: fix(lsp): empty commands should not be considered executable (#28216) According to the LSP specification, the CodeLens.command is optional but the CodeLens.command.command is not optional, which means the correct representation of a display-only code lens is indeed one with a command with a title to display and an empty string as command. --- runtime/lua/vim/lsp/codelens.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index d2557ca9d7..90ca83cd59 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -79,7 +79,7 @@ function M.run() local lenses_by_client = lens_cache_by_buf[bufnr] or {} for client, lenses in pairs(lenses_by_client) do for _, lens in pairs(lenses) do - if lens.range.start.line == (line - 1) then + if lens.range.start.line == (line - 1) and lens.command and lens.command.command ~= '' then table.insert(options, { client = client, lens = lens }) end end -- cgit From 1dacf2ecee36e2abd490df2ad5f9c24fa73205b7 Mon Sep 17 00:00:00 2001 From: Yi Ming Date: Wed, 10 Apr 2024 18:27:37 +0800 Subject: fix(lsp): prevent code-lens refresh from becoming a permanent no-op (#28228) To avoid repeatedly requesting a buffer multiple times before a request is completed, the current implementation puts the requested buffer into the active_refreshes table before requesting. But since we only remove the buffer from active_refreshes in the lsp-handler of textDocument/codeLens, this will cause if the user sends a request that cannot trigger lsp-handler (for example, if there is an LSP server attached to the current buffer, and especially when the user creates an autocmd which performs vim.lsp.codelens.refresh after the BufEnter event is triggered like in the document example), this buffer will be put into active_refreshes, and there is no way to remove it, which will result in all subsequent vim.lsp.codelens.refresh not requesting textDocument/codeLens. --- runtime/lua/vim/lsp/codelens.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index 90ca83cd59..a2a0c15b93 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -231,7 +231,7 @@ local function resolve_lenses(lenses, bufnr, client_id, callback) countdown() else assert(client) - client.request('codeLens/resolve', lens, function(_, result) + client.request(ms.codeLens_resolve, lens, function(_, result) if api.nvim_buf_is_loaded(bufnr) and result and result.command then lens.command = result.command -- Eager display to have some sort of incremental feedback @@ -306,7 +306,11 @@ function M.refresh(opts) textDocument = util.make_text_document_params(buf), } active_refreshes[buf] = true - vim.lsp.buf_request(buf, ms.textDocument_codeLens, params, M.on_codelens) + + local request_ids = vim.lsp.buf_request(buf, ms.textDocument_codeLens, params, M.on_codelens) + if vim.tbl_isempty(request_ids) then + active_refreshes[buf] = nil + end end end end -- cgit From 57adf8c6e01d9395eb52fe03571c535571efdc4b Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 15 Apr 2024 04:33:09 -0700 Subject: fix(vim.ui): open() may wait indefinitely #28325 Problem: vim.ui.open "locks up" Nvim if the spawned process does not terminate. #27986 Solution: - Change `vim.ui.open()`: - Do not call `wait()`. - Return a `SystemObj`. The caller can decide if it wants to `wait()`. - Change `gx` to `wait()` only a short time. - Allows `gx` to show a message if the command fails, without the risk of waiting forever. --- runtime/lua/vim/lsp/handlers.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index daf4fec8d2..1c5291e7fd 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -615,7 +615,8 @@ M[ms.window_showDocument] = function(_, result, ctx, _) if result.external then -- TODO(lvimuser): ask the user for confirmation - local ret, err = vim.ui.open(uri) + local cmd, err = vim.ui.open(uri) + local ret = cmd and cmd:wait(2000) or nil if ret == nil or ret.code ~= 0 then return { -- cgit From 97323d821be97deeb1a5797b4ca534156b9e9b0c Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Thu, 18 Apr 2024 15:34:10 +0200 Subject: refactor(lsp): merge rpc.domain_socket_connect into rpc.connect (#28398) See discussion in https://github.com/neovim/neovim/pull/26850 --- runtime/lua/vim/lsp/rpc.lua | 95 ++++++++++++++------------------------------- 1 file changed, 29 insertions(+), 66 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 984e4f040a..6748b32ec0 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -621,95 +621,53 @@ local function merge_dispatchers(dispatchers) return merged end ---- Create a LSP RPC client factory that connects via TCP to the given host and port. +--- Create a LSP RPC client factory that connects to either: +--- +--- - a named pipe (windows) +--- - a domain socket (unix) +--- - a host and port via TCP --- --- Return a function that can be passed to the `cmd` field for --- |vim.lsp.start_client()| or |vim.lsp.start()|. --- ----@param host string host to connect to ----@param port integer port to connect to +---@param host_or_path string host to connect to or path to a pipe/domain socket +---@param port integer? TCP port to connect to. If absent the first argument must be a pipe ---@return fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient -function M.connect(host, port) +function M.connect(host_or_path, port) return function(dispatchers) dispatchers = merge_dispatchers(dispatchers) - local tcp = assert(uv.new_tcp()) + local handle = ( + port == nil + and assert( + uv.new_pipe(false), + string.format('Pipe with name %s could not be opened.', host_or_path) + ) + or assert(uv.new_tcp(), 'Could not create new TCP socket') + ) local closing = false local transport = { write = function(msg) - tcp:write(msg) - end, - is_closing = function() - return closing - end, - terminate = function() - if not closing then - closing = true - tcp:shutdown() - tcp:close() - dispatchers.on_exit(0, 0) - end + handle:write(msg) end, - } - local client = new_client(dispatchers, transport) - tcp:connect(host, port, function(err) - if err then - vim.schedule(function() - vim.notify( - string.format('Could not connect to %s:%s, reason: %s', host, port, vim.inspect(err)), - vim.log.levels.WARN - ) - end) - return - end - local handle_body = function(body) - client:handle_body(body) - end - tcp:read_start(M.create_read_loop(handle_body, transport.terminate, function(read_err) - client:on_error(M.client_errors.READ_ERROR, read_err) - end)) - end) - - return public_client(client) - end -end - ---- Create a LSP RPC client factory that connects via named pipes (Windows) ---- or unix domain sockets (Unix) to the given pipe_path (file path on ---- Unix and name on Windows). ---- ---- Return a function that can be passed to the `cmd` field for ---- |vim.lsp.start_client()| or |vim.lsp.start()|. ---- ----@param pipe_path string file path of the domain socket (Unix) or name of the named pipe (Windows) to connect to ----@return fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient -function M.domain_socket_connect(pipe_path) - return function(dispatchers) - dispatchers = merge_dispatchers(dispatchers) - local pipe = - assert(uv.new_pipe(false), string.format('pipe with name %s could not be opened.', pipe_path)) - local closing = false - local transport = { - write = vim.schedule_wrap(function(msg) - pipe:write(msg) - end), is_closing = function() return closing end, terminate = function() if not closing then closing = true - pipe:shutdown() - pipe:close() + handle:shutdown() + handle:close() dispatchers.on_exit(0, 0) end end, } local client = new_client(dispatchers, transport) - pipe:connect(pipe_path, function(err) + local function on_connect(err) if err then + local address = port == nil and host_or_path or (host_or_path .. ':' .. port) vim.schedule(function() vim.notify( - string.format('Could not connect to :%s, reason: %s', pipe_path, vim.inspect(err)), + string.format('Could not connect to %s, reason: %s', address, vim.inspect(err)), vim.log.levels.WARN ) end) @@ -718,10 +676,15 @@ function M.domain_socket_connect(pipe_path) local handle_body = function(body) client:handle_body(body) end - pipe:read_start(M.create_read_loop(handle_body, transport.terminate, function(read_err) + handle:read_start(M.create_read_loop(handle_body, transport.terminate, function(read_err) client:on_error(M.client_errors.READ_ERROR, read_err) end)) - end) + end + if port == nil then + handle:connect(host_or_path, on_connect) + else + handle:connect(host_or_path, port, on_connect) + end return public_client(client) end -- cgit From f1dfe32bf5552197e0068298b0527526a4f918b1 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 18 Apr 2024 07:57:58 -0700 Subject: feat(lua): enable(enable:boolean, filter:table) #28374 Problem: We need to establish a pattern for `enable()`. Solution: - First `enable()` parameter is always `enable:boolean`. - Update `vim.diagnostic.enable()` - Update `vim.lsp.inlay_hint.enable()`. - It was not released yet, so no deprecation is needed. But to help HEAD users, it will show an informative error. - vim.deprecate(): - Improve message when the "removal version" is a *current or older* version. --- runtime/lua/vim/lsp/inlay_hint.lua | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index ec676ea97f..6305f82efb 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -349,7 +349,7 @@ api.nvim_set_decoration_provider(namespace, { end, }) ---- @param bufnr (integer|nil) Buffer handle, or 0 or nil for current +--- @param bufnr (integer|nil) Buffer handle, or 0 for current --- @return boolean --- @since 12 function M.is_enabled(bufnr) @@ -360,23 +360,39 @@ function M.is_enabled(bufnr) return bufstates[bufnr] and bufstates[bufnr].enabled or false end +--- Optional filters |kwargs|, or `nil` for all. +--- @class vim.lsp.inlay_hint.enable.Filter +--- @inlinedoc +--- Buffer number, or 0/nil for current buffer. +--- @field bufnr integer? + --- Enables or disables inlay hints for a buffer. --- --- To "toggle", pass the inverse of `is_enabled()`: --- --- ```lua ---- vim.lsp.inlay_hint.enable(0, not vim.lsp.inlay_hint.is_enabled()) +--- vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled()) --- ``` --- ---- @param bufnr (integer|nil) Buffer handle, or 0 or nil for current --- @param enable (boolean|nil) true/nil to enable, false to disable +--- @param filter vim.lsp.inlay_hint.enable.Filter? --- @since 12 -function M.enable(bufnr, enable) - vim.validate({ enable = { enable, 'boolean', true }, bufnr = { bufnr, 'number', true } }) +function M.enable(enable, filter) + if type(enable) == 'number' or type(filter) == 'boolean' then + vim.deprecate( + 'vim.lsp.inlay_hint.enable(bufnr:number, enable:boolean)', + 'vim.diagnostic.enable(enable:boolean, filter:table)', + '0.10-dev' + ) + error('see :help vim.lsp.inlay_hint.enable() for updated parameters') + end + + vim.validate({ enable = { enable, 'boolean', true }, filter = { filter, 'table', true } }) + filter = filter or {} if enable == false then - _disable(bufnr) + _disable(filter.bufnr) else - _enable(bufnr) + _enable(filter.bufnr) end end -- cgit From 97c0a52416b873e22aee41d7e1f2464c57c8be71 Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Thu, 18 Apr 2024 12:06:52 -0700 Subject: fix(lsp): correct deprecation message #28403 --- runtime/lua/vim/lsp/inlay_hint.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index 6305f82efb..b27795d797 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -381,7 +381,7 @@ function M.enable(enable, filter) if type(enable) == 'number' or type(filter) == 'boolean' then vim.deprecate( 'vim.lsp.inlay_hint.enable(bufnr:number, enable:boolean)', - 'vim.diagnostic.enable(enable:boolean, filter:table)', + 'vim.lsp.inlay_hint.enable(enable:boolean, filter:table)', '0.10-dev' ) error('see :help vim.lsp.inlay_hint.enable() for updated parameters') -- cgit From f190f758ac58d9cc955368e047b070e0a2261033 Mon Sep 17 00:00:00 2001 From: Yinzuo Jiang Date: Sat, 20 Apr 2024 21:40:01 +0800 Subject: feat(lsp): add vim.lsp.buf.subtypes(), vim.lsp.buf.supertypes() (#28388) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Mathias Fußenegger Co-authored-by: Maria José Solano --- runtime/lua/vim/lsp/buf.lua | 84 ++++++++++++++++++++++++++++++++++++++++ runtime/lua/vim/lsp/handlers.lua | 39 +++++++++++++++++++ 2 files changed, 123 insertions(+) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 17cc698b76..fa0cbab138 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -495,6 +495,90 @@ function M.outgoing_calls() call_hierarchy(ms.callHierarchy_outgoingCalls) end +--- @param method string +local function type_hierarchy(method) + --- Merge results from multiple clients into a single table. Client-ID is preserved. + --- + --- @param results table + local function merge_results(results) + local merged_results = {} + for client_id, client_result in pairs(results) do + if client_result.error then + vim.notify(client_result.error.message, vim.log.levels.WARN) + elseif client_result.result then + for _, item in pairs(client_result.result) do + table.insert(merged_results, { client_id, item }) + end + end + end + return merged_results + end + + local bufnr = api.nvim_get_current_buf() + local params = util.make_position_params() + --- @param results table + vim.lsp.buf_request_all(bufnr, ms.textDocument_prepareTypeHierarchy, params, function(results) + local merged_results = merge_results(results) + if #merged_results == 0 then + vim.notify('No items resolved', vim.log.levels.INFO) + return + end + + if #merged_results == 1 then + --- @type {integer, lsp.TypeHierarchyItem} + local item = merged_results[1] + local client = vim.lsp.get_client_by_id(item[1]) + if client then + --- @type lsp.TypeHierarchyItem + client.request(method, { item = item[2] }, nil, bufnr) + else + vim.notify( + string.format('Client with id=%d disappeared during call hierarchy request', item[1]), + vim.log.levels.WARN + ) + end + else + local opts = { + prompt = 'Select a type hierarchy item:', + kind = 'typehierarchy', + format_item = function(item) + if not item[2].detail or #item[2].detail == 0 then + return item[2].name + end + return string.format('%s %s', item[2].name, item[2].detail) + end, + } + + vim.ui.select(merged_results, opts, function(item) + local client = vim.lsp.get_client_by_id(item[1]) + if client then + --- @type lsp.TypeHierarchyItem + client.request(method, { item = item[2] }, nil, bufnr) + else + vim.notify( + string.format('Client with id=%d disappeared during call hierarchy request', item[1]), + vim.log.levels.WARN + ) + end + end) + end + end) +end + +--- Lists all the subtypes of the symbol under the +--- cursor in the |quickfix| window. If the symbol can resolve to +--- multiple items, the user can pick one using |vim.ui.select()|. +function M.subtypes() + type_hierarchy(ms.typeHierarchy_subtypes) +end + +--- Lists all the supertypes of the symbol under the +--- cursor in the |quickfix| window. If the symbol can resolve to +--- multiple items, the user can pick one using |vim.ui.select()|. +function M.supertypes() + type_hierarchy(ms.typeHierarchy_supertypes) +end + --- List workspace folders. --- function M.list_workspace_folders() diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 1c5291e7fd..a15096fdad 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -565,6 +565,45 @@ M[ms.callHierarchy_incomingCalls] = make_call_hierarchy_handler('from') --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#callHierarchy_outgoingCalls M[ms.callHierarchy_outgoingCalls] = make_call_hierarchy_handler('to') +--- Displays type hierarchy in the quickfix window. +local function make_type_hierarchy_handler() + --- @param result lsp.TypeHierarchyItem[] + return function(_, result, ctx, _) + if not result then + return + end + local function format_item(item) + if not item.detail or #item.detail == 0 then + return item.name + end + return string.format('%s %s', item.name, item.detail) + end + local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) + local items = {} + for _, type_hierarchy_item in pairs(result) do + local col = util._get_line_byte_from_position( + ctx.bufnr, + type_hierarchy_item.range.start, + client.offset_encoding + ) + table.insert(items, { + filename = assert(vim.uri_to_fname(type_hierarchy_item.uri)), + text = format_item(type_hierarchy_item), + lnum = type_hierarchy_item.range.start.line + 1, + col = col + 1, + }) + end + vim.fn.setqflist({}, ' ', { title = 'LSP type hierarchy', items = items }) + api.nvim_command('botright copen') + end +end + +--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#typeHierarchy_incomingCalls +M[ms.typeHierarchy_subtypes] = make_type_hierarchy_handler() + +--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#typeHierarchy_outgoingCalls +M[ms.typeHierarchy_supertypes] = make_type_hierarchy_handler() + --- @see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_logMessage --- @param result lsp.LogMessageParams M[ms.window_logMessage] = function(_, result, ctx, _) -- cgit From d9d890562e43493c999f8a6ff2b848959686f5b6 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 21 Apr 2024 15:19:43 +0200 Subject: refactor(lua): rename tbl_islist => islist ref #24572 --- runtime/lua/vim/lsp/handlers.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index a15096fdad..d6579cf4b3 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -428,7 +428,7 @@ local function location_handler(_, result, ctx, config) -- textDocument/definition can return Location or Location[] -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition - if not vim.tbl_islist(result) then + if not vim.islist(result) then result = { result } end -- cgit From 9912a4c81b0856200f44a38e99d38eae44cef5c9 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 22 Apr 2024 00:58:48 +0200 Subject: refactor(lua): deprecate tbl_flatten Problem: Besides being redundant with vim.iter():flatten(), `tbl_flatten` has these problems: - Has `tbl_` prefix but only accepts lists. - Discards some results! Compare the following: - iter.flatten(): ``` vim.iter({1, { { a = 2 } }, { 3 } }):flatten():totable() ``` - tbl_flatten: ``` vim.tbl_flatten({1, { { a = 2 } }, { 3 } }) ``` Solution: Deprecate tbl_flatten. Note: iter:flatten() currently fails ("flatten() requires a list-like table") on this code from gen_lsp.lua: local anonym = vim.iter({ -- remove nil anonymous_num > 1 and '' or nil, '---@class ' .. anonymous_classname, }):flatten():totable() Should we enhance :flatten() to work for arrays? --- runtime/lua/vim/lsp/client.lua | 2 +- runtime/lua/vim/lsp/handlers.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index 09064b9510..98b3cfc762 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -722,7 +722,7 @@ local wait_result_reason = { [-1] = 'timeout', [-2] = 'interrupted', [-3] = 'err --- --- @param ... string List to write to the buffer local function err_message(...) - local message = table.concat(vim.tbl_flatten({ ... })) + local message = table.concat(vim.iter({ ... }):flatten():totable()) if vim.in_fast_event() then vim.schedule(function() api.nvim_err_writeln(message) diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index d6579cf4b3..4672d94105 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -12,7 +12,7 @@ local M = {} --- Writes to error buffer. ---@param ... string Will be concatenated before being written local function err_message(...) - vim.notify(table.concat(vim.tbl_flatten({ ... })), vim.log.levels.ERROR) + vim.notify(table.concat(vim.iter({ ... }):flatten():totable()), vim.log.levels.ERROR) api.nvim_command('redraw') end -- cgit From 39fc340276a4fdbe1f1bb4bfbe7328267ad7f9d6 Mon Sep 17 00:00:00 2001 From: Yi Ming Date: Tue, 23 Apr 2024 02:18:49 +0800 Subject: fix(lsp): avoid assertion when `client_hints` do not exist (#28461) --- runtime/lua/vim/lsp/inlay_hint.lua | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index b27795d797..37b330f5f6 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -311,6 +311,10 @@ api.nvim_set_decoration_provider(namespace, { if bufstate.version ~= util.buf_versions[bufnr] then return end + + if not bufstate.client_hints then + return + end local hints_by_client = assert(bufstate.client_hints) for lnum = topline, botline do -- cgit From c81b7849a0f677164c01cf84ecfb25c1f47acf21 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Tue, 23 Apr 2024 19:05:01 +0200 Subject: refactor(lsp): merge subtypes and supertypes into typehierarchy (#28467) Both methods had pretty much the same documentation and shared the implementation. --- runtime/lua/vim/lsp/buf.lua | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index fa0cbab138..79c1aeb53d 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -495,11 +495,17 @@ function M.outgoing_calls() call_hierarchy(ms.callHierarchy_outgoingCalls) end ---- @param method string -local function type_hierarchy(method) +--- Lists all the subtypes or supertypes of the symbol under the +--- cursor in the |quickfix| window. If the symbol can resolve to +--- multiple items, the user can pick one using |vim.ui.select()|. +---@param kind "subtypes"|"supertypes" +function M.typehierarchy(kind) + local method = kind == 'subtypes' and ms.typeHierarchy_subtypes or ms.typeHierarchy_supertypes + --- Merge results from multiple clients into a single table. Client-ID is preserved. --- --- @param results table + --- @return [integer, lsp.TypeHierarchyItem][] local function merge_results(results) local merged_results = {} for client_id, client_result in pairs(results) do @@ -525,11 +531,9 @@ local function type_hierarchy(method) end if #merged_results == 1 then - --- @type {integer, lsp.TypeHierarchyItem} local item = merged_results[1] local client = vim.lsp.get_client_by_id(item[1]) if client then - --- @type lsp.TypeHierarchyItem client.request(method, { item = item[2] }, nil, bufnr) else vim.notify( @@ -565,20 +569,6 @@ local function type_hierarchy(method) end) end ---- Lists all the subtypes of the symbol under the ---- cursor in the |quickfix| window. If the symbol can resolve to ---- multiple items, the user can pick one using |vim.ui.select()|. -function M.subtypes() - type_hierarchy(ms.typeHierarchy_subtypes) -end - ---- Lists all the supertypes of the symbol under the ---- cursor in the |quickfix| window. If the symbol can resolve to ---- multiple items, the user can pick one using |vim.ui.select()|. -function M.supertypes() - type_hierarchy(ms.typeHierarchy_supertypes) -end - --- List workspace folders. --- function M.list_workspace_folders() -- cgit From e0d92b9cc20b58179599f53dfa74ca821935a539 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 25 Apr 2024 04:15:58 -0700 Subject: fix(vim.ui)!: change open() to return pcall-like values #28502 Problem: `vim.ui.open` unnecessarily invents a different success/failure convention. Its return type was changed in 57adf8c6e01d, so we might as well change it to have a more conventional form. Solution: Change the signature to use the `pcall` convention of `status, result`. --- runtime/lua/vim/lsp/handlers.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 4672d94105..ab4fa52c40 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -654,15 +654,15 @@ M[ms.window_showDocument] = function(_, result, ctx, _) if result.external then -- TODO(lvimuser): ask the user for confirmation - local cmd, err = vim.ui.open(uri) - local ret = cmd and cmd:wait(2000) or nil + local ok, cmd_or_err = vim.ui.open(uri) + local ret = ok and (cmd_or_err --[[@as vim.SystemObj]]):wait(2000) or nil if ret == nil or ret.code ~= 0 then return { success = false, error = { code = protocol.ErrorCodes.UnknownErrorCode, - message = ret and ret.stderr or err, + message = ret and ret.stderr or cmd_or_err, }, } end -- cgit From 47dbda97d2f40729733b1c0d1d13d914065af23c Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Fri, 26 Apr 2024 09:57:59 +0200 Subject: fix(lsp): buffer messages until connected to server (#28507) `handle:write(msg)` can fail if the socket is not yet connected to the server. Should address https://github.com/neovim/neovim/pull/28398#issuecomment-2078152491 --- runtime/lua/vim/lsp/rpc.lua | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 6748b32ec0..d73e46edfb 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -645,9 +645,23 @@ function M.connect(host_or_path, port) or assert(uv.new_tcp(), 'Could not create new TCP socket') ) local closing = false + -- Connect returns a PublicClient synchronously so the caller + -- can immediately send messages before the connection is established + -- -> Need to buffer them until that happens + local connected = false + -- size should be enough because the client can't really do anything until initialization is done + -- which required a response from the server - implying the connection got established + local msgbuf = vim.ringbuf(10) local transport = { write = function(msg) - handle:write(msg) + if connected then + local _, err = handle:write(msg) + if err and not closing then + log.error('Error on handle:write: %q', err) + end + else + msgbuf:push(msg) + end end, is_closing = function() return closing @@ -679,6 +693,10 @@ function M.connect(host_or_path, port) handle:read_start(M.create_read_loop(handle_body, transport.terminate, function(read_err) client:on_error(M.client_errors.READ_ERROR, read_err) end)) + connected = true + for msg in msgbuf do + handle:write(msg) + end end if port == nil then handle:connect(host_or_path, on_connect) -- cgit From 567f8a300b2c12fbf5a8bf7d85c5714e8dcde79d Mon Sep 17 00:00:00 2001 From: Yi Ming Date: Fri, 26 Apr 2024 19:25:55 +0800 Subject: refactor(lsp): rename foos_by_bar to bar_foos #28505 --- runtime/lua/vim/lsp/inlay_hint.lua | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index 37b330f5f6..3d8b54ee3d 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -41,13 +41,13 @@ function M.on_inlayhint(err, result, ctx, _) bufstate.client_hints = vim.defaulttable() bufstate.version = ctx.version end - local hints_by_client = bufstate.client_hints + local client_hints = bufstate.client_hints local client = assert(vim.lsp.get_client_by_id(client_id)) - local new_hints_by_lnum = vim.defaulttable() + local new_lnum_hints = vim.defaulttable() local num_unprocessed = #result if num_unprocessed == 0 then - hints_by_client[client_id] = {} + client_hints[client_id] = {} bufstate.version = ctx.version api.nvim__buf_redraw_range(bufnr, 0, -1) return @@ -73,10 +73,10 @@ function M.on_inlayhint(err, result, ctx, _) for _, hint in ipairs(result) do local lnum = hint.position.line hint.position.character = pos_to_byte(hint.position) - table.insert(new_hints_by_lnum[lnum], hint) + table.insert(new_lnum_hints[lnum], hint) end - hints_by_client[client_id] = new_hints_by_lnum + client_hints[client_id] = new_lnum_hints bufstate.version = ctx.version api.nvim__buf_redraw_range(bufnr, 0, -1) end @@ -175,19 +175,19 @@ function M.get(filter) end --- @type vim.lsp.inlay_hint.get.ret[] - local hints = {} + local result = {} for _, client in pairs(clients) do - local hints_by_lnum = bufstate.client_hints[client.id] - if hints_by_lnum then + local lnum_hints = bufstate.client_hints[client.id] + if lnum_hints then for lnum = range.start.line, range['end'].line do - local line_hints = hints_by_lnum[lnum] or {} - for _, hint in pairs(line_hints) do + local hints = lnum_hints[lnum] or {} + for _, hint in pairs(hints) do local line, char = hint.position.line, hint.position.character if (line > range.start.line or char >= range.start.character) and (line < range['end'].line or char <= range['end'].character) then - table.insert(hints, { + table.insert(result, { bufnr = bufnr, client_id = client.id, inlay_hint = hint, @@ -197,7 +197,7 @@ function M.get(filter) end end end - return hints + return result end --- Clear inlay hints @@ -315,14 +315,14 @@ api.nvim_set_decoration_provider(namespace, { if not bufstate.client_hints then return end - local hints_by_client = assert(bufstate.client_hints) + local client_hints = assert(bufstate.client_hints) for lnum = topline, botline do if bufstate.applied[lnum] ~= bufstate.version then api.nvim_buf_clear_namespace(bufnr, namespace, lnum, lnum + 1) - for _, hints_by_lnum in pairs(hints_by_client) do - local line_hints = hints_by_lnum[lnum] or {} - for _, hint in pairs(line_hints) do + for _, lnum_hints in pairs(client_hints) do + local hints = lnum_hints[lnum] or {} + for _, hint in pairs(hints) do local text = '' local label = hint.label if type(label) == 'string' then -- cgit From 37d8e504593646c81542f8c66f0d608e0a59f036 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Fri, 26 Apr 2024 08:15:44 -0500 Subject: fix(lsp): add "silent" option to vim.lsp.start (#28478) vim.notify cannot be suppressed and it is not always necessary to display a visible warning to the user if the RPC process fails to start. For instance, a user may have the same LSP configuration across systems, some of which may not have all of the LSP server executables installed. In that case, the user receives a notification every time a file is opened that they cannot suppress. Instead of using vim.notify in vim.lsp.rpc, propagate a normal error up through the call stack and use vim.notify in vim.lsp.start() only if the "silent" option is not set. This also updates lsp.start_client() to return an error message as its second return value if an error occurred, rather than calling vim.notify directly. Callers of lsp.start_client() will need to update call sites appropriately if they wish to report errors to the user (or even better, switch to vim.lsp.start). --- runtime/lua/vim/lsp/client.lua | 14 +++----------- runtime/lua/vim/lsp/rpc.lua | 5 ++--- 2 files changed, 5 insertions(+), 14 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index 98b3cfc762..448f986cd3 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -37,7 +37,7 @@ local validate = vim.validate --- `is_closing` and `terminate`. --- See |vim.lsp.rpc.request()|, |vim.lsp.rpc.notify()|. --- For TCP there is a builtin RPC client factory: |vim.lsp.rpc.connect()| ---- @field cmd string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient? +--- @field cmd string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient --- --- Directory to launch the `cmd` process. Not related to `root_dir`. --- (default: cwd) @@ -506,25 +506,17 @@ function Client.create(config) } -- Start the RPC client. - local rpc --- @type vim.lsp.rpc.PublicClient? local config_cmd = config.cmd if type(config_cmd) == 'function' then - rpc = config_cmd(dispatchers) + self.rpc = config_cmd(dispatchers) else - rpc = lsp.rpc.start(config_cmd, dispatchers, { + self.rpc = lsp.rpc.start(config_cmd, dispatchers, { cwd = config.cmd_cwd, env = config.cmd_env, detached = config.detached, }) end - -- Return nil if the rpc client fails to start - if not rpc then - return - end - - self.rpc = rpc - setmetatable(self, Client) return self diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index d73e46edfb..3c63a12da2 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -722,7 +722,7 @@ end --- @param cmd string[] Command to start the LSP server. --- @param dispatchers? vim.lsp.rpc.Dispatchers --- @param extra_spawn_params? vim.lsp.rpc.ExtraSpawnParams ---- @return vim.lsp.rpc.PublicClient? : Client RPC object, with these methods: +--- @return vim.lsp.rpc.PublicClient : Client RPC object, with these methods: --- - `notify()` |vim.lsp.rpc.notify()| --- - `request()` |vim.lsp.rpc.request()| --- - `is_closing()` returns a boolean indicating if the RPC is closing. @@ -797,8 +797,7 @@ function M.start(cmd, dispatchers, extra_spawn_params) end local msg = string.format('Spawning language server with cmd: `%s` failed%s', vim.inspect(cmd), sfx) - vim.notify(msg, vim.log.levels.WARN) - return nil + error(msg) end sysobj = sysobj_or_err --[[@as vim.SystemObj]] -- cgit From d5063f4b290e1c4262f7ced6d425ff2d7a2e2045 Mon Sep 17 00:00:00 2001 From: Yi Ming Date: Thu, 2 May 2024 21:16:20 +0800 Subject: feat(lsp): vim.lsp.inlay_hint.enable(nil) applies to all buffers #28543 Problem: Inlay hints `enable()` does not fully implement the `:help dev-lua` guidelines: Interface conventions ~ - When accepting a buffer id, etc., 0 means "current buffer", nil means "all buffers". Likewise for window id, tabpage id, etc. - Examples: |vim.lsp.codelens.clear()| |vim.diagnostic.enable()| Solution: Implement globally enabling inlay hints. * refactor(lsp): do not rely on `enable` to create autocmds * refactor(lsp): make `bufstates` a defaulttable * refactor(lsp): make `bufstate` inherit values from `globalstate` * feat(lsp): `vim.lsp.inlay_hints` now take effect on all buffers by default * test(lsp): add basic tests for enable inlay hints for all buffers * test(lsp): add test cases cover more than one buffer --- runtime/lua/vim/lsp/inlay_hint.lua | 160 ++++++++++++++++++++++--------------- 1 file changed, 94 insertions(+), 66 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index 3d8b54ee3d..985cbef5ff 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -4,13 +4,26 @@ local ms = require('vim.lsp.protocol').Methods local api = vim.api local M = {} ----@class (private) vim.lsp.inlay_hint.bufstate +---@class (private) vim.lsp.inlay_hint.globalstate Global state for inlay hints +---@field enabled boolean Whether inlay hints are enabled for this scope +---@type vim.lsp.inlay_hint.globalstate +local globalstate = { + enabled = false, +} + +---@class (private) vim.lsp.inlay_hint.bufstate: vim.lsp.inlay_hint.globalstate Buffer local state for inlay hints ---@field version? integer ---@field client_hints? table> client_id -> (lnum -> hints) ---@field applied table Last version of hints applied to this line ----@field enabled boolean Whether inlay hints are enabled for this buffer ---@type table -local bufstates = {} +local bufstates = vim.defaulttable(function(_) + return setmetatable({ applied = {} }, { + __index = globalstate, + __newindex = function(state, key, value) + rawset(state, key, (globalstate[key] ~= value) and value or nil) + end, + }) +end) local namespace = api.nvim_create_namespace('vim_lsp_inlayhint') local augroup = api.nvim_create_augroup('vim_lsp_inlayhint', {}) @@ -34,7 +47,7 @@ function M.on_inlayhint(err, result, ctx, _) return end local bufstate = bufstates[bufnr] - if not bufstate or not bufstate.enabled then + if not bufstate.enabled then return end if not (bufstate.client_hints and bufstate.version) then @@ -91,11 +104,7 @@ function M.on_refresh(err, _, ctx, _) for _, bufnr in ipairs(vim.lsp.get_buffers_by_client_id(ctx.client_id)) do for _, winid in ipairs(api.nvim_list_wins()) do if api.nvim_win_get_buf(winid) == bufnr then - local bufstate = bufstates[bufnr] - if bufstate then - util._refresh(ms.textDocument_inlayHint, { bufnr = bufnr }) - break - end + util._refresh(ms.textDocument_inlayHint, { bufnr = bufnr }) end end end @@ -154,7 +163,7 @@ function M.get(filter) end local bufstate = bufstates[bufnr] - if not (bufstate and bufstate.client_hints) then + if not bufstate.client_hints then return {} end @@ -203,12 +212,9 @@ end --- Clear inlay hints ---@param bufnr (integer) Buffer handle, or 0 for current local function clear(bufnr) - if bufnr == nil or bufnr == 0 then + if bufnr == 0 then bufnr = api.nvim_get_current_buf() end - if not bufstates[bufnr] then - return - end local bufstate = bufstates[bufnr] local client_lens = (bufstate or {}).client_hints or {} local client_ids = vim.tbl_keys(client_lens) --- @type integer[] @@ -222,15 +228,14 @@ local function clear(bufnr) end --- Disable inlay hints for a buffer ----@param bufnr (integer|nil) Buffer handle, or 0 or nil for current +---@param bufnr (integer) Buffer handle, or 0 for current local function _disable(bufnr) - if bufnr == nil or bufnr == 0 then + if bufnr == 0 then bufnr = api.nvim_get_current_buf() end clear(bufnr) - if bufstates[bufnr] then - bufstates[bufnr] = { enabled = false, applied = {} } - end + bufstates[bufnr] = nil + bufstates[bufnr].enabled = false end --- Refresh inlay hints, only if we have attached clients that support it @@ -244,30 +249,38 @@ local function _refresh(bufnr, opts) end --- Enable inlay hints for a buffer ----@param bufnr (integer|nil) Buffer handle, or 0 or nil for current +---@param bufnr (integer) Buffer handle, or 0 for current local function _enable(bufnr) - if bufnr == nil or bufnr == 0 then + if bufnr == 0 then bufnr = api.nvim_get_current_buf() end - local bufstate = bufstates[bufnr] - if not bufstate then - bufstates[bufnr] = { applied = {}, enabled = true } - api.nvim_create_autocmd('LspNotify', { - buffer = bufnr, - callback = function(opts) - if - opts.data.method ~= ms.textDocument_didChange - and opts.data.method ~= ms.textDocument_didOpen - then - return - end - if bufstates[bufnr] and bufstates[bufnr].enabled then - _refresh(bufnr, { client_id = opts.data.client_id }) - end - end, - group = augroup, - }) - _refresh(bufnr) + bufstates[bufnr] = nil + bufstates[bufnr].enabled = true + _refresh(bufnr) +end + +api.nvim_create_autocmd('LspNotify', { + callback = function(args) + ---@type integer + local bufnr = args.buf + + if + args.data.method ~= ms.textDocument_didChange + and args.data.method ~= ms.textDocument_didOpen + then + return + end + if bufstates[bufnr].enabled then + _refresh(bufnr, { client_id = args.data.client_id }) + end + end, + group = augroup, +}) +api.nvim_create_autocmd('LspAttach', { + callback = function(args) + ---@type integer + local bufnr = args.buf + api.nvim_buf_attach(bufnr, false, { on_reload = function(_, cb_bufnr) clear(cb_bufnr) @@ -278,32 +291,30 @@ local function _enable(bufnr) end, on_detach = function(_, cb_bufnr) _disable(cb_bufnr) + bufstates[cb_bufnr] = nil end, }) - api.nvim_create_autocmd('LspDetach', { - buffer = bufnr, - callback = function(args) - local clients = vim.lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_inlayHint }) - - if - not vim.iter(clients):any(function(c) - return c.id ~= args.data.client_id - end) - then - _disable(bufnr) - end - end, - group = augroup, - }) - else - bufstate.enabled = true - _refresh(bufnr) - end -end + end, + group = augroup, +}) +api.nvim_create_autocmd('LspDetach', { + callback = function(args) + ---@type integer + local bufnr = args.buf + local clients = vim.lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_inlayHint }) + if not vim.iter(clients):any(function(c) + return c.id ~= args.data.client_id + end) then + _disable(bufnr) + end + end, + group = augroup, +}) api.nvim_set_decoration_provider(namespace, { on_win = function(_, _, bufnr, topline, botline) - local bufstate = bufstates[bufnr] + ---@type vim.lsp.inlay_hint.bufstate + local bufstate = rawget(bufstates, bufnr) if not bufstate then return end @@ -361,13 +372,13 @@ function M.is_enabled(bufnr) if bufnr == nil or bufnr == 0 then bufnr = api.nvim_get_current_buf() end - return bufstates[bufnr] and bufstates[bufnr].enabled or false + return bufstates[bufnr].enabled end --- Optional filters |kwargs|, or `nil` for all. --- @class vim.lsp.inlay_hint.enable.Filter --- @inlinedoc ---- Buffer number, or 0/nil for current buffer. +--- Buffer number, or 0 for current buffer, or nil for all. --- @field bufnr integer? --- Enables or disables inlay hints for a buffer. @@ -392,11 +403,28 @@ function M.enable(enable, filter) end vim.validate({ enable = { enable, 'boolean', true }, filter = { filter, 'table', true } }) + enable = enable == nil or enable filter = filter or {} - if enable == false then - _disable(filter.bufnr) + + if filter.bufnr == nil then + globalstate.enabled = enable + for bufnr, _ in pairs(bufstates) do + if api.nvim_buf_is_loaded(bufnr) then + if enable == false then + _disable(bufnr) + else + _enable(bufnr) + end + else + bufstates[bufnr] = nil + end + end else - _enable(filter.bufnr) + if enable == false then + _disable(filter.bufnr) + else + _enable(filter.bufnr) + end end end -- cgit From 037ea6e786b5d05f4a8965e4c2ba6aa60ec7c01a Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 10 Apr 2024 11:42:46 +0200 Subject: feat(api): add nvim__redraw for more granular redrawing Experimental and subject to future changes. Add a way to redraw certain elements that are not redrawn while Nvim is waiting for input, or currently have no API to do so. This API covers all that can be done with the :redraw* commands, in addition to the following new features: - Immediately move the cursor to a (non-current) window. - Target a specific window or buffer to mark for redraw. - Mark a buffer range for redraw (replaces nvim__buf_redraw_range()). - Redraw the 'statuscolumn'. --- runtime/lua/vim/lsp/inlay_hint.lua | 6 +++--- runtime/lua/vim/lsp/semantic_tokens.lua | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index 985cbef5ff..c9e485f474 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -62,7 +62,7 @@ function M.on_inlayhint(err, result, ctx, _) if num_unprocessed == 0 then client_hints[client_id] = {} bufstate.version = ctx.version - api.nvim__buf_redraw_range(bufnr, 0, -1) + api.nvim__redraw({ buf = bufnr, valid = true }) return end @@ -91,7 +91,7 @@ function M.on_inlayhint(err, result, ctx, _) client_hints[client_id] = new_lnum_hints bufstate.version = ctx.version - api.nvim__buf_redraw_range(bufnr, 0, -1) + api.nvim__redraw({ buf = bufnr, valid = true }) end --- |lsp-handler| for the method `textDocument/inlayHint/refresh` @@ -224,7 +224,7 @@ local function clear(bufnr) end end api.nvim_buf_clear_namespace(bufnr, namespace, 0, -1) - api.nvim__buf_redraw_range(bufnr, 0, -1) + api.nvim__redraw({ buf = bufnr, valid = true }) end --- Disable inlay hints for a buffer diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index 20ac0a125f..be2d6ee0ae 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -394,7 +394,7 @@ function STHighlighter:process_response(response, client, version) current_result.namespace_cleared = false -- redraw all windows displaying buffer - api.nvim__buf_redraw_range(self.bufnr, 0, -1) + api.nvim__redraw({ buf = self.bufnr, valid = true }) end --- on_win handler for the decoration provider (see |nvim_set_decoration_provider|) -- cgit From 350d81856473b45100d6b0e5920b757df1b4ad27 Mon Sep 17 00:00:00 2001 From: Yi Ming Date: Thu, 2 May 2024 22:26:07 +0800 Subject: feat(lsp): inlay_hint.is_enabled({filter}) #28523 vim.diagnostic.enable and vim.diagnostic.is_enabled() use the same pattern. --- runtime/lua/vim/lsp/inlay_hint.lua | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index 985cbef5ff..dc0b6d7037 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -364,12 +364,29 @@ api.nvim_set_decoration_provider(namespace, { end, }) ---- @param bufnr (integer|nil) Buffer handle, or 0 for current +--- @param filter vim.lsp.inlay_hint.enable.Filter --- @return boolean --- @since 12 -function M.is_enabled(bufnr) +function M.is_enabled(filter) + ---@type integer + local bufnr + if type(filter) == 'number' then + vim.deprecate( + 'vim.lsp.inlay_hint.is_enabled(bufnr:number)', + 'vim.lsp.inlay_hint.is_enabled(filter:table)', + '0.10-dev' + ) + bufnr = filter + else + vim.validate({ filter = { filter, 'table', true } }) + filter = filter or {} + bufnr = filter.bufnr + end + vim.validate({ bufnr = { bufnr, 'number', true } }) - if bufnr == nil or bufnr == 0 then + if bufnr == nil then + return globalstate.enabled + elseif bufnr == 0 then bufnr = api.nvim_get_current_buf() end return bufstates[bufnr].enabled -- cgit From 40ce8577977fcdce8ad76863c70eb522e4cefd4d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 3 May 2024 03:20:03 -0700 Subject: fix(vim.ui)!: change open() to return `result|nil, errmsg|nil` #28612 reverts e0d92b9cc20b58179599f53dfa74ca821935a539 #28502 Problem: `vim.ui.open()` has a `pcall()` like signature, under the assumption that this is the Lua idiom for returning result-or-error. However, the `result|nil, errmsg|nil` pattern: - has precedent in: - `io.open` - `vim.uv` (`:help luv-error-handling`) - has these advantages: - Can be used with `assert()`: ``` local result, err = assert(foobar()) ``` - Allows LuaLS to infer the type of `result`: ``` local result, err = foobar() if err then ... elseif result then ... end ``` Solution: - Revert to the `result|nil, errmsg|nil` pattern. - Document the pattern in our guidelines. --- runtime/lua/vim/lsp/handlers.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index ab4fa52c40..4672d94105 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -654,15 +654,15 @@ M[ms.window_showDocument] = function(_, result, ctx, _) if result.external then -- TODO(lvimuser): ask the user for confirmation - local ok, cmd_or_err = vim.ui.open(uri) - local ret = ok and (cmd_or_err --[[@as vim.SystemObj]]):wait(2000) or nil + local cmd, err = vim.ui.open(uri) + local ret = cmd and cmd:wait(2000) or nil if ret == nil or ret.code ~= 0 then return { success = false, error = { code = protocol.ErrorCodes.UnknownErrorCode, - message = ret and ret.stderr or cmd_or_err, + message = ret and ret.stderr or err, }, } end -- cgit From 52823616bc4d77898ddc03da7629280841d3bced Mon Sep 17 00:00:00 2001 From: Yi Ming Date: Fri, 3 May 2024 22:18:55 +0800 Subject: fix(lsp): replace bug-prone ternary operation #28627 ref #28624 --- runtime/lua/vim/lsp/inlay_hint.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index 2c81a3f465..c6be54e65f 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -20,7 +20,11 @@ local bufstates = vim.defaulttable(function(_) return setmetatable({ applied = {} }, { __index = globalstate, __newindex = function(state, key, value) - rawset(state, key, (globalstate[key] ~= value) and value or nil) + if globalstate[key] == value then + rawset(state, key, nil) + else + rawset(state, key, value) + end end, }) end) -- cgit From c3c673cdeca25594454f8721e725e6ff1127e2a8 Mon Sep 17 00:00:00 2001 From: Yi Ming Date: Tue, 7 May 2024 17:30:19 +0800 Subject: fix(lsp): enable() does not activate inlay hints on open buffers #28629 Problem: inlay_hint `enable()` does not activate inlay hints on open buffers. If a buffer does not have a corresponding `bufstate` in `bufstates`, then `enable` all buffers will not take effect on it. Solution: Make the effective range determined by the loaded buffers. Fix #28624 --- runtime/lua/vim/lsp/inlay_hint.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index c6be54e65f..d983357a2c 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -429,7 +429,7 @@ function M.enable(enable, filter) if filter.bufnr == nil then globalstate.enabled = enable - for bufnr, _ in pairs(bufstates) do + for _, bufnr in ipairs(api.nvim_list_bufs()) do if api.nvim_buf_is_loaded(bufnr) then if enable == false then _disable(bufnr) -- cgit From 4e5086a67e8916d9a5c5c5cb1933633b3e200eee Mon Sep 17 00:00:00 2001 From: tom-anders <13141438+tom-anders@users.noreply.github.com> Date: Mon, 29 Apr 2024 19:20:31 +0200 Subject: refactor(lsp): s/options/opts for parameters in vim.lsp.buf See https://github.com/neovim/neovim/pull/28483#discussion_r1583344120 --- runtime/lua/vim/lsp/buf.lua | 116 ++++++++++++++++++++++---------------------- 1 file changed, 58 insertions(+), 58 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 79c1aeb53d..820ff9c2ca 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -35,13 +35,13 @@ function M.hover() request(ms.textDocument_hover, params) end -local function request_with_options(name, params, options) +local function request_with_opts(name, params, opts) local req_handler --- @type function? - if options then + if opts then req_handler = function(err, result, ctx, config) local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) local handler = client.handlers[name] or vim.lsp.handlers[name] - handler(err, result, ctx, vim.tbl_extend('force', config or {}, options)) + handler(err, result, ctx, vim.tbl_extend('force', config or {}, opts)) end end request(name, params, req_handler) @@ -83,32 +83,32 @@ end --- Jumps to the declaration of the symbol under the cursor. --- @note Many servers do not implement this method. Generally, see |vim.lsp.buf.definition()| instead. ---- @param options? vim.lsp.LocationOpts -function M.declaration(options) +--- @param opts? vim.lsp.LocationOpts +function M.declaration(opts) local params = util.make_position_params() - request_with_options(ms.textDocument_declaration, params, options) + request_with_opts(ms.textDocument_declaration, params, opts) end --- Jumps to the definition of the symbol under the cursor. ---- @param options? vim.lsp.LocationOpts -function M.definition(options) +--- @param opts? vim.lsp.LocationOpts +function M.definition(opts) local params = util.make_position_params() - request_with_options(ms.textDocument_definition, params, options) + request_with_opts(ms.textDocument_definition, params, opts) end --- Jumps to the definition of the type of the symbol under the cursor. ---- @param options? vim.lsp.LocationOpts -function M.type_definition(options) +--- @param opts? vim.lsp.LocationOpts +function M.type_definition(opts) local params = util.make_position_params() - request_with_options(ms.textDocument_typeDefinition, params, options) + request_with_opts(ms.textDocument_typeDefinition, params, opts) end --- Lists all the implementations for the symbol under the cursor in the --- quickfix window. ---- @param options? vim.lsp.LocationOpts -function M.implementation(options) +--- @param opts? vim.lsp.LocationOpts +function M.implementation(opts) local params = util.make_position_params() - request_with_options(ms.textDocument_implementation, params, options) + request_with_opts(ms.textDocument_implementation, params, opts) end --- Displays signature information about the symbol under the cursor in a @@ -213,25 +213,25 @@ end --- Formats a buffer using the attached (and optionally filtered) language --- server clients. --- ---- @param options? vim.lsp.buf.format.Opts -function M.format(options) - options = options or {} - local bufnr = options.bufnr or api.nvim_get_current_buf() +--- @param opts? vim.lsp.buf.format.Opts +function M.format(opts) + opts = opts or {} + local bufnr = opts.bufnr or api.nvim_get_current_buf() local mode = api.nvim_get_mode().mode - local range = options.range + local range = opts.range if not range and mode == 'v' or mode == 'V' then range = range_from_selection(bufnr, mode) end local method = range and ms.textDocument_rangeFormatting or ms.textDocument_formatting local clients = vim.lsp.get_clients({ - id = options.id, + id = opts.id, bufnr = bufnr, - name = options.name, + name = opts.name, method = method, }) - if options.filter then - clients = vim.tbl_filter(options.filter, clients) + if opts.filter then + clients = vim.tbl_filter(opts.filter, clients) end if #clients == 0 then @@ -250,12 +250,12 @@ function M.format(options) return params end - if options.async then + if opts.async then local function do_format(idx, client) if not client then return end - local params = set_range(client, util.make_formatting_params(options.formatting_options)) + local params = set_range(client, util.make_formatting_params(opts.formatting_options)) client.request(method, params, function(...) local handler = client.handlers[method] or vim.lsp.handlers[method] handler(...) @@ -264,9 +264,9 @@ function M.format(options) end do_format(next(clients)) else - local timeout_ms = options.timeout_ms or 1000 + local timeout_ms = opts.timeout_ms or 1000 for _, client in pairs(clients) do - local params = set_range(client, util.make_formatting_params(options.formatting_options)) + local params = set_range(client, util.make_formatting_params(opts.formatting_options)) local result, err = client.request_sync(method, params, timeout_ms, bufnr) if result and result.result then util.apply_text_edits(result.result, bufnr, client.offset_encoding) @@ -295,18 +295,18 @@ end --- ---@param new_name string|nil If not provided, the user will be prompted for a new --- name using |vim.ui.input()|. ----@param options? vim.lsp.buf.rename.Opts Additional options: -function M.rename(new_name, options) - options = options or {} - local bufnr = options.bufnr or api.nvim_get_current_buf() +---@param opts? vim.lsp.buf.rename.Opts Additional options: +function M.rename(new_name, opts) + opts = opts or {} + local bufnr = opts.bufnr or api.nvim_get_current_buf() local clients = vim.lsp.get_clients({ bufnr = bufnr, - name = options.name, + name = opts.name, -- Clients must at least support rename, prepareRename is optional method = ms.textDocument_rename, }) - if options.filter then - clients = vim.tbl_filter(options.filter, clients) + if opts.filter then + clients = vim.tbl_filter(opts.filter, clients) end if #clients == 0 then @@ -415,21 +415,21 @@ end --- ---@param context (table|nil) Context for the request ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references ----@param options? vim.lsp.ListOpts -function M.references(context, options) +---@param opts? vim.lsp.ListOpts +function M.references(context, opts) validate({ context = { context, 't', true } }) local params = util.make_position_params() params.context = context or { includeDeclaration = true, } - request_with_options(ms.textDocument_references, params, options) + request_with_opts(ms.textDocument_references, params, opts) end --- Lists all symbols in the current buffer in the quickfix window. ---- @param options? vim.lsp.ListOpts -function M.document_symbol(options) +--- @param opts? vim.lsp.ListOpts +function M.document_symbol(opts) local params = { textDocument = util.make_text_document_params() } - request_with_options(ms.textDocument_documentSymbol, params, options) + request_with_opts(ms.textDocument_documentSymbol, params, opts) end --- @param call_hierarchy_items lsp.CallHierarchyItem[]? @@ -542,7 +542,7 @@ function M.typehierarchy(kind) ) end else - local opts = { + local select_opts = { prompt = 'Select a type hierarchy item:', kind = 'typehierarchy', format_item = function(item) @@ -553,7 +553,7 @@ function M.typehierarchy(kind) end, } - vim.ui.select(merged_results, opts, function(item) + vim.ui.select(merged_results, select_opts, function(item) local client = vim.lsp.get_client_by_id(item[1]) if client then --- @type lsp.TypeHierarchyItem @@ -626,14 +626,14 @@ end --- string means no filtering is done. --- --- @param query string? optional ---- @param options? vim.lsp.ListOpts -function M.workspace_symbol(query, options) +--- @param opts? vim.lsp.ListOpts +function M.workspace_symbol(query, opts) query = query or npcall(vim.fn.input, 'Query: ') if query == nil then return end local params = { query = query } - request_with_options(ms.workspace_symbol, params, options) + request_with_opts(ms.workspace_symbol, params, opts) end --- Send request to the server to resolve document highlights for the current @@ -825,19 +825,19 @@ end --- Selects a code action available at the current --- cursor position. --- ----@param options? vim.lsp.buf.code_action.Opts +---@param opts? vim.lsp.buf.code_action.Opts ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction ---@see vim.lsp.protocol.CodeActionTriggerKind -function M.code_action(options) - validate({ options = { options, 't', true } }) - options = options or {} +function M.code_action(opts) + validate({ options = { opts, 't', true } }) + opts = opts or {} -- Detect old API call code_action(context) which should now be -- code_action({ context = context} ) --- @diagnostic disable-next-line:undefined-field - if options.diagnostics or options.only then - options = { options = options } + if opts.diagnostics or opts.only then + opts = { options = opts } end - local context = options.context or {} + local context = opts.context or {} if not context.triggerKind then context.triggerKind = vim.lsp.protocol.CodeActionTriggerKind.Invoked end @@ -867,17 +867,17 @@ function M.code_action(options) results[ctx.client_id] = { error = err, result = result, ctx = ctx } remaining = remaining - 1 if remaining == 0 then - on_code_action_results(results, options) + on_code_action_results(results, opts) end end for _, client in ipairs(clients) do ---@type lsp.CodeActionParams local params - if options.range then - assert(type(options.range) == 'table', 'code_action range must be a table') - local start = assert(options.range.start, 'range must have a `start` property') - local end_ = assert(options.range['end'], 'range must have a `end` property') + if opts.range then + assert(type(opts.range) == 'table', 'code_action range must be a table') + local start = assert(opts.range.start, 'range must have a `start` property') + local end_ = assert(opts.range['end'], 'range must have a `end` property') params = util.make_given_range_params(start, end_, bufnr, client.offset_encoding) elseif mode == 'v' or mode == 'V' then local range = range_from_selection(bufnr, mode) -- cgit From 3da251efc6329c4d5e6b94780815dce8d6e24999 Mon Sep 17 00:00:00 2001 From: tom-anders <13141438+tom-anders@users.noreply.github.com> Date: Sat, 4 May 2024 09:56:18 +0200 Subject: refactor(lsp): use vim.is_callable() --- runtime/lua/vim/lsp/handlers.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 4672d94105..11818b6a89 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -257,7 +257,7 @@ M[ms.textDocument_references] = function(_, result, ctx, config) vim.fn.setloclist(0, {}, ' ', { title = title, items = items, context = ctx }) api.nvim_command('lopen') elseif config.on_list then - assert(type(config.on_list) == 'function', 'on_list is not a function') + assert(vim.is_callable(config.on_list), 'on_list is not a function') config.on_list({ title = title, items = items, context = ctx }) else vim.fn.setqflist({}, ' ', { title = title, items = items, context = ctx }) @@ -290,7 +290,7 @@ local function response_to_list(map_result, entity, title_fn) vim.fn.setloclist(0, {}, ' ', { title = title, items = items, context = ctx }) api.nvim_command('lopen') elseif config.on_list then - assert(type(config.on_list) == 'function', 'on_list is not a function') + assert(vim.is_callable(config.on_list), 'on_list is not a function') config.on_list({ title = title, items = items, context = ctx }) else vim.fn.setqflist({}, ' ', { title = title, items = items, context = ctx }) @@ -436,7 +436,7 @@ local function location_handler(_, result, ctx, config) local items = util.locations_to_items(result, client.offset_encoding) if config.on_list then - assert(type(config.on_list) == 'function', 'on_list is not a function') + assert(vim.is_callable(config.on_list), 'on_list is not a function') config.on_list({ title = title, items = items }) return end -- cgit From e14e75099883e6904cf3575b612f3820cd122938 Mon Sep 17 00:00:00 2001 From: Jongwook Choi Date: Tue, 7 May 2024 14:09:27 -0400 Subject: fix(lsp): rename LspProgress data.result => data.params #28632 Rename the field `result` to `params` in the `data` table for `LspProgress` autocmds. This aligns with LspNotify. The previous name was chosen because the initial handler implementation mistakenly had a parameter name `result` instead of `params` for the `$/progress` LSP "notification" handler. However, `params` would be a more appropriate name that is more consistent with the underlying LSP type (`ProgressParams`). See also: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress --- runtime/lua/vim/lsp/handlers.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 4672d94105..620eebeeb4 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -22,16 +22,16 @@ M[ms.workspace_executeCommand] = function(_, _, _, _) end --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress ----@param result lsp.ProgressParams +---@param params lsp.ProgressParams ---@param ctx lsp.HandlerContext -M[ms.dollar_progress] = function(_, result, ctx) +M[ms.dollar_progress] = function(_, params, ctx) local client = vim.lsp.get_client_by_id(ctx.client_id) if not client then err_message('LSP[id=', tostring(ctx.client_id), '] client has shut down during progress update') return vim.NIL end local kind = nil - local value = result.value + local value = params.value if type(value) == 'table' then kind = value.kind @@ -39,21 +39,21 @@ M[ms.dollar_progress] = function(_, result, ctx) -- So that consumers always have it available, even if they consume a -- subset of the full sequence if kind == 'begin' then - client.progress.pending[result.token] = value.title + client.progress.pending[params.token] = value.title else - value.title = client.progress.pending[result.token] + value.title = client.progress.pending[params.token] if kind == 'end' then - client.progress.pending[result.token] = nil + client.progress.pending[params.token] = nil end end end - client.progress:push(result) + client.progress:push(params) api.nvim_exec_autocmds('LspProgress', { pattern = kind, modeline = false, - data = { client_id = ctx.client_id, result = result }, + data = { client_id = ctx.client_id, params = params }, }) end -- cgit From 6ffc209a8a84c6d627dde39d27283d98a451b105 Mon Sep 17 00:00:00 2001 From: tom-anders <13141438+tom-anders@users.noreply.github.com> Date: Sat, 4 May 2024 09:58:07 +0200 Subject: refactor(lsp): move repeated table construction into a variable As suggested in https://github.com/neovim/neovim/pull/28483#discussion_r1581712828 --- runtime/lua/vim/lsp/handlers.lua | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 11818b6a89..de146ab1ef 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -253,14 +253,15 @@ M[ms.textDocument_references] = function(_, result, ctx, config) local title = 'References' local items = util.locations_to_items(result, client.offset_encoding) + local list = { title = title, items = items, context = ctx } if config.loclist then - vim.fn.setloclist(0, {}, ' ', { title = title, items = items, context = ctx }) + vim.fn.setloclist(0, {}, ' ', list) api.nvim_command('lopen') elseif config.on_list then assert(vim.is_callable(config.on_list), 'on_list is not a function') - config.on_list({ title = title, items = items, context = ctx }) + config.on_list(list) else - vim.fn.setqflist({}, ' ', { title = title, items = items, context = ctx }) + vim.fn.setqflist({}, ' ', list) api.nvim_command('botright copen') end end @@ -286,14 +287,15 @@ local function response_to_list(map_result, entity, title_fn) local title = title_fn(ctx) local items = map_result(result, ctx.bufnr) + local list = { title = title, items = items, context = ctx } if config.loclist then - vim.fn.setloclist(0, {}, ' ', { title = title, items = items, context = ctx }) + vim.fn.setloclist(0, {}, ' ', list) api.nvim_command('lopen') elseif config.on_list then assert(vim.is_callable(config.on_list), 'on_list is not a function') - config.on_list({ title = title, items = items, context = ctx }) + config.on_list(list) else - vim.fn.setqflist({}, ' ', { title = title, items = items, context = ctx }) + vim.fn.setqflist({}, ' ', list) api.nvim_command('botright copen') end end -- cgit From cdc0974063fd18199040e783df5826af5786aee3 Mon Sep 17 00:00:00 2001 From: tom-anders <13141438+tom-anders@users.noreply.github.com> Date: Sat, 4 May 2024 10:02:56 +0200 Subject: docs(lsp): fix type annotations in response_to_list(...) --- runtime/lua/vim/lsp/handlers.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index de146ab1ef..aff98dc5cb 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -273,7 +273,7 @@ end --- --- loclist: (boolean) use the location list (default is to use the quickfix list) --- ----@param map_result function `((resp, bufnr) -> list)` to convert the response +---@param map_result fun(resp, bufnr: integer): table to convert the response ---@param entity string name of the resource used in a `not found` error message ---@param title_fn fun(ctx: lsp.HandlerContext): string Function to call to generate list title ---@return lsp.Handler -- cgit From 80d108eeee89c095e0c1226a558a690a708abaa1 Mon Sep 17 00:00:00 2001 From: tom-anders <13141438+tom-anders@users.noreply.github.com> Date: Sat, 4 May 2024 10:04:47 +0200 Subject: refactor(lsp): use vim.cmd instead of api.nvim_command As suggested in https://github.com/neovim/neovim/pull/28483#discussion_r1586878457 and https://github.com/neovim/neovim/pull/28483#discussion_r1586878226 --- runtime/lua/vim/lsp/handlers.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index aff98dc5cb..c639a317cf 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -256,13 +256,13 @@ M[ms.textDocument_references] = function(_, result, ctx, config) local list = { title = title, items = items, context = ctx } if config.loclist then vim.fn.setloclist(0, {}, ' ', list) - api.nvim_command('lopen') + vim.cmd.lopen() elseif config.on_list then assert(vim.is_callable(config.on_list), 'on_list is not a function') config.on_list(list) else vim.fn.setqflist({}, ' ', list) - api.nvim_command('botright copen') + vim.cmd('botright copen') end end @@ -290,13 +290,13 @@ local function response_to_list(map_result, entity, title_fn) local list = { title = title, items = items, context = ctx } if config.loclist then vim.fn.setloclist(0, {}, ' ', list) - api.nvim_command('lopen') + vim.cmd.lopen() elseif config.on_list then assert(vim.is_callable(config.on_list), 'on_list is not a function') config.on_list(list) else vim.fn.setqflist({}, ' ', list) - api.nvim_command('botright copen') + vim.cmd('botright copen') end end end @@ -447,7 +447,7 @@ local function location_handler(_, result, ctx, config) return end vim.fn.setqflist({}, ' ', { title = title, items = items }) - api.nvim_command('botright copen') + vim.cmd('botright copen') end --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_declaration @@ -557,7 +557,7 @@ local function make_call_hierarchy_handler(direction) end end vim.fn.setqflist({}, ' ', { title = 'LSP call hierarchy', items = items }) - api.nvim_command('botright copen') + vim.cmd('botright copen') end end @@ -596,7 +596,7 @@ local function make_type_hierarchy_handler() }) end vim.fn.setqflist({}, ' ', { title = 'LSP type hierarchy', items = items }) - api.nvim_command('botright copen') + vim.cmd('botright copen') end end -- cgit From b0cc85c00504dc5530ab5d6c5fa03f3fa96d5a1a Mon Sep 17 00:00:00 2001 From: tom-anders <13141438+tom-anders@users.noreply.github.com> Date: Sat, 4 May 2024 10:10:44 +0200 Subject: docs(lsp): document vim.lsp.ListOpts.loclist --- runtime/lua/vim/lsp/buf.lua | 9 ++++----- runtime/lua/vim/lsp/handlers.lua | 5 +---- 2 files changed, 5 insertions(+), 9 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 820ff9c2ca..e05acff3df 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -62,14 +62,13 @@ end --- vim.lsp.buf.references(nil, { on_list = on_list }) --- ``` --- ---- If you prefer loclist do something like this: +--- If you prefer loclist instead of qflist: --- ```lua ---- local function on_list(options) ---- vim.fn.setloclist(0, {}, ' ', options) ---- vim.cmd.lopen() ---- end +--- vim.lsp.buf.definition({ loclist = true }) +--- vim.lsp.buf.references(nil, { loclist = true }) --- ``` --- @field on_list? fun(t: vim.lsp.LocationOpts.OnList) +--- @field loclist? boolean --- @class vim.lsp.LocationOpts.OnList --- @field items table[] Structured like |setqflist-what| diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index c639a317cf..205de68101 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -268,10 +268,7 @@ end --- Return a function that converts LSP responses to list items and opens the list --- ---- The returned function has an optional {config} parameter that accepts a table ---- with the following keys: ---- ---- loclist: (boolean) use the location list (default is to use the quickfix list) +--- The returned function has an optional {config} parameter that accepts |vim.lsp.ListOpts| --- ---@param map_result fun(resp, bufnr: integer): table to convert the response ---@param entity string name of the resource used in a `not found` error message -- cgit From 5c40f3e86a81f78f821b8c75dafc3ce2ce67e1c5 Mon Sep 17 00:00:00 2001 From: tom-anders <13141438+tom-anders@users.noreply.github.com> Date: Sat, 4 May 2024 10:13:08 +0200 Subject: feat(lsp): support vim.lsp.ListOpts.loclist in location_handler() --- runtime/lua/vim/lsp/handlers.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 205de68101..eec16d7298 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -443,8 +443,13 @@ local function location_handler(_, result, ctx, config) util.jump_to_location(result[1], client.offset_encoding, config.reuse_win) return end - vim.fn.setqflist({}, ' ', { title = title, items = items }) - vim.cmd('botright copen') + if config.loclist then + vim.fn.setloclist(0, {}, ' ', { title = title, items = items }) + vim.cmd.lopen() + else + vim.fn.setqflist({}, ' ', { title = title, items = items }) + vim.cmd('botright copen') + end end --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_declaration -- cgit From c1a95d9653f39c5e118d030270e4b77ebd20139e Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 10 May 2024 13:47:30 +0100 Subject: fix(lsp): disable didChangeWatchedFiles on Linux Problem: The file watcher backends for Linux have too many limitations and doesn't work reliably. Solution: disable didChangeWatchedFiles on Linux Ref: #27807, #28058, #23291, #26520 --- runtime/lua/vim/lsp/protocol.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 599f02425e..419c2ff644 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -10,6 +10,8 @@ local function get_value_set(tbl) return value_set end +local sysname = vim.uv.os_uname().sysname + -- Protocol for the Microsoft Language Server Protocol (mslsp) local protocol = {} @@ -835,7 +837,10 @@ function protocol.make_client_capabilities() refreshSupport = true, }, didChangeWatchedFiles = { - dynamicRegistration = true, + -- TODO(lewis6991): do not advertise didChangeWatchedFiles on Linux + -- or BSD since all the current backends are too limited. + -- Ref: #27807, #28058, #23291, #26520 + dynamicRegistration = sysname == 'Darwin' or sysname == 'Windows_NT', relativePatternSupport = true, }, inlayHint = { -- cgit From e3ec974324bd73b63f54503480a4e48d1887f8d9 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 13 May 2024 05:00:39 -0700 Subject: refactor(lua): remove deprecated features #28725 --- runtime/lua/vim/lsp/inlay_hint.lua | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index d983357a2c..effc69c67e 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -372,20 +372,9 @@ api.nvim_set_decoration_provider(namespace, { --- @return boolean --- @since 12 function M.is_enabled(filter) - ---@type integer - local bufnr - if type(filter) == 'number' then - vim.deprecate( - 'vim.lsp.inlay_hint.is_enabled(bufnr:number)', - 'vim.lsp.inlay_hint.is_enabled(filter:table)', - '0.10-dev' - ) - bufnr = filter - else - vim.validate({ filter = { filter, 'table', true } }) - filter = filter or {} - bufnr = filter.bufnr - end + vim.validate({ filter = { filter, 'table', true } }) + filter = filter or {} + local bufnr = filter.bufnr vim.validate({ bufnr = { bufnr, 'number', true } }) if bufnr == nil then @@ -414,15 +403,6 @@ end --- @param filter vim.lsp.inlay_hint.enable.Filter? --- @since 12 function M.enable(enable, filter) - if type(enable) == 'number' or type(filter) == 'boolean' then - vim.deprecate( - 'vim.lsp.inlay_hint.enable(bufnr:number, enable:boolean)', - 'vim.lsp.inlay_hint.enable(enable:boolean, filter:table)', - '0.10-dev' - ) - error('see :help vim.lsp.inlay_hint.enable() for updated parameters') - end - vim.validate({ enable = { enable, 'boolean', true }, filter = { filter, 'table', true } }) enable = enable == nil or enable filter = filter or {} -- cgit From abd2352bd8b896417af75ac85caad9a7f849841b Mon Sep 17 00:00:00 2001 From: Jongwook Choi Date: Tue, 14 May 2024 09:33:03 -0400 Subject: feat(lsp): update LSP protocol 3.18 typings to date (#28730) Make the LSP protocol typings up-to-date with LSP protocol (upcoming) version 3.18, before and in preparation for the Nvim 0.10.0 release. --- runtime/lua/vim/lsp/_meta/protocol.lua | 187 +++++++++++++++++++-------------- 1 file changed, 106 insertions(+), 81 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/_meta/protocol.lua b/runtime/lua/vim/lsp/_meta/protocol.lua index a5da5ac6b7..9a11972007 100644 --- a/runtime/lua/vim/lsp/_meta/protocol.lua +++ b/runtime/lua/vim/lsp/_meta/protocol.lua @@ -2534,8 +2534,14 @@ error('Cannot require a meta file') ---@proposed ---@field inlineCompletionProvider? boolean|lsp.InlineCompletionOptions --- +---Text document specific server capabilities. +--- +---@since 3.18.0 +---@proposed +---@field textDocument? lsp._anonym12.textDocument +--- ---Workspace specific server capabilities. ----@field workspace? lsp._anonym12.workspace +---@field workspace? lsp._anonym14.workspace --- ---Experimental server capabilities. ---@field experimental? lsp.LSPAny @@ -2598,8 +2604,10 @@ error('Cannot require a meta file') ---appears in the user interface. ---@field source? string --- ----The diagnostic's message. It usually appears in the user interface ----@field message string +---The diagnostic's message. It usually appears in the user interface. +--- +---@since 3.18.0 - support for `MarkupContent`. This is guarded by the client capability `textDocument.diagnostic.markupMessageSupport`. +---@field message string|lsp.MarkupContent --- ---Additional metadata about the diagnostic. --- @@ -2684,7 +2692,7 @@ error('Cannot require a meta file') ---capabilities. --- ---@since 3.17.0 ----@field completionItem? lsp._anonym13.completionItem +---@field completionItem? lsp._anonym15.completionItem ---Hover options. ---@class lsp.HoverOptions: lsp.WorkDoneProgressOptions @@ -2811,6 +2819,8 @@ error('Cannot require a meta file') ---errors are currently presented to the user for the given range. There is no guarantee ---that these accurately reflect the error state of the resource. The primary parameter ---to compute code actions is the provided range. +--- +---Note that the client should check the `textDocument.diagnostic.markupMessageSupport` server capability before sending diagnostics with markup messages to a server. ---@field diagnostics lsp.Diagnostic[] --- ---Requested kind of actions to return. @@ -3146,7 +3156,7 @@ error('Cannot require a meta file') ---@class lsp.NotebookDocumentSyncOptions --- ---The notebooks to be synced ----@field notebookSelector (lsp._anonym14.notebookSelector|lsp._anonym16.notebookSelector)[] +---@field notebookSelector (lsp._anonym16.notebookSelector|lsp._anonym18.notebookSelector)[] --- ---Whether save notification should be forwarded to ---the server. Will only be honored if mode === `notebook`. @@ -3514,7 +3524,7 @@ error('Cannot require a meta file') ---anymore since the information is outdated). --- ---@since 3.17.0 ----@field staleRequestSupport? lsp._anonym18.staleRequestSupport +---@field staleRequestSupport? lsp._anonym20.staleRequestSupport --- ---Client capabilities specific to regular expressions. --- @@ -3590,7 +3600,7 @@ error('Cannot require a meta file') ---create file, rename file and delete file changes. --- ---@since 3.16.0 ----@field changeAnnotationSupport? lsp._anonym19.changeAnnotationSupport +---@field changeAnnotationSupport? lsp._anonym21.changeAnnotationSupport ---@class lsp.DidChangeConfigurationClientCapabilities --- @@ -3617,20 +3627,20 @@ error('Cannot require a meta file') ---@field dynamicRegistration? boolean --- ---Specific capabilities for the `SymbolKind` in the `workspace/symbol` request. ----@field symbolKind? lsp._anonym20.symbolKind +---@field symbolKind? lsp._anonym22.symbolKind --- ---The client supports tags on `SymbolInformation`. ---Clients supporting tags have to handle unknown tags gracefully. --- ---@since 3.16.0 ----@field tagSupport? lsp._anonym21.tagSupport +---@field tagSupport? lsp._anonym23.tagSupport --- ---The client support partial workspace symbols. The client will send the ---request `workspaceSymbol/resolve` to the server to resolve additional ---properties. --- ---@since 3.17.0 ----@field resolveSupport? lsp._anonym22.resolveSupport +---@field resolveSupport? lsp._anonym24.resolveSupport ---The client capabilities of a {@link ExecuteCommandRequest}. ---@class lsp.ExecuteCommandClientCapabilities @@ -3775,9 +3785,9 @@ error('Cannot require a meta file') --- ---The client supports the following `CompletionItem` specific ---capabilities. ----@field completionItem? lsp._anonym23.completionItem +---@field completionItem? lsp._anonym25.completionItem --- ----@field completionItemKind? lsp._anonym27.completionItemKind +---@field completionItemKind? lsp._anonym29.completionItemKind --- ---Defines how the client handles whitespace and indentation ---when accepting a completion item that uses multi line @@ -3794,7 +3804,7 @@ error('Cannot require a meta file') ---capabilities. --- ---@since 3.17.0 ----@field completionList? lsp._anonym28.completionList +---@field completionList? lsp._anonym30.completionList ---@class lsp.HoverClientCapabilities --- @@ -3813,7 +3823,7 @@ error('Cannot require a meta file') --- ---The client supports the following `SignatureInformation` ---specific properties. ----@field signatureInformation? lsp._anonym29.signatureInformation +---@field signatureInformation? lsp._anonym31.signatureInformation --- ---The client supports to send additional context information for a ---`textDocument/signatureHelp` request. A client that opts into @@ -3891,7 +3901,7 @@ error('Cannot require a meta file') --- ---Specific capabilities for the `SymbolKind` in the ---`textDocument/documentSymbol` request. ----@field symbolKind? lsp._anonym31.symbolKind +---@field symbolKind? lsp._anonym33.symbolKind --- ---The client supports hierarchical document symbols. ---@field hierarchicalDocumentSymbolSupport? boolean @@ -3901,7 +3911,7 @@ error('Cannot require a meta file') ---Clients supporting tags have to handle unknown tags gracefully. --- ---@since 3.16.0 ----@field tagSupport? lsp._anonym32.tagSupport +---@field tagSupport? lsp._anonym34.tagSupport --- ---The client supports an additional label presented in the UI when ---registering a document symbol provider. @@ -3920,7 +3930,7 @@ error('Cannot require a meta file') ---set the request can only return `Command` literals. --- ---@since 3.8.0 ----@field codeActionLiteralSupport? lsp._anonym33.codeActionLiteralSupport +---@field codeActionLiteralSupport? lsp._anonym35.codeActionLiteralSupport --- ---Whether code action supports the `isPreferred` property. --- @@ -3943,7 +3953,7 @@ error('Cannot require a meta file') ---properties via a separate `codeAction/resolve` request. --- ---@since 3.16.0 ----@field resolveSupport? lsp._anonym35.resolveSupport +---@field resolveSupport? lsp._anonym37.resolveSupport --- ---Whether the client honors the change annotations in ---text edits and resource operations returned via the @@ -4051,12 +4061,12 @@ error('Cannot require a meta file') ---Specific options for the folding range kind. --- ---@since 3.17.0 ----@field foldingRangeKind? lsp._anonym36.foldingRangeKind +---@field foldingRangeKind? lsp._anonym38.foldingRangeKind --- ---Specific options for the folding range. --- ---@since 3.17.0 ----@field foldingRange? lsp._anonym37.foldingRange +---@field foldingRange? lsp._anonym39.foldingRange ---@class lsp.SelectionRangeClientCapabilities --- @@ -4075,7 +4085,7 @@ error('Cannot require a meta file') ---Clients supporting tags have to handle unknown tags gracefully. --- ---@since 3.15.0 ----@field tagSupport? lsp._anonym38.tagSupport +---@field tagSupport? lsp._anonym40.tagSupport --- ---Whether the client interprets the version property of the ---`textDocument/publishDiagnostics` notification's parameter. @@ -4119,7 +4129,7 @@ error('Cannot require a meta file') ---`request.range` are both set to true but the server only provides a ---range provider the client might not render a minimap correctly or might ---even decide to not show any semantic tokens at all. ----@field requests lsp._anonym39.requests +---@field requests lsp._anonym41.requests --- ---The token types that the client supports. ---@field tokenTypes string[] @@ -4202,7 +4212,7 @@ error('Cannot require a meta file') --- ---Indicates which properties a client can resolve lazily on an inlay ---hint. ----@field resolveSupport? lsp._anonym42.resolveSupport +---@field resolveSupport? lsp._anonym44.resolveSupport ---Client capabilities specific to diagnostic pull requests. --- @@ -4216,6 +4226,9 @@ error('Cannot require a meta file') --- ---Whether the clients supports related documents for document diagnostic pulls. ---@field relatedDocumentSupport? boolean +--- +---Whether the client supports `MarkupContent` in diagnostic messages. +---@field markupMessageSupport? boolean ---Client capabilities specific to inline completions. --- @@ -4244,7 +4257,7 @@ error('Cannot require a meta file') ---@class lsp.ShowMessageRequestClientCapabilities --- ---Capabilities specific to the `MessageActionItem` type. ----@field messageActionItem? lsp._anonym43.messageActionItem +---@field messageActionItem? lsp._anonym45.messageActionItem ---Client capabilities for the showDocument request. --- @@ -4671,7 +4684,7 @@ error('Cannot require a meta file') ---@since 3.17.0 ---@alias lsp.DocumentDiagnosticReport lsp.RelatedFullDocumentDiagnosticReport|lsp.RelatedUnchangedDocumentDiagnosticReport ----@alias lsp.PrepareRenameResult lsp.Range|lsp._anonym44.PrepareRenameResult|lsp._anonym45.PrepareRenameResult +---@alias lsp.PrepareRenameResult lsp.Range|lsp._anonym46.PrepareRenameResult|lsp._anonym47.PrepareRenameResult ---A document selector is the combination of one or many document filters. --- @@ -4692,7 +4705,7 @@ error('Cannot require a meta file') ---An event describing a change to a text document. If only a text is provided ---it is considered to be the full content of the document. ----@alias lsp.TextDocumentContentChangeEvent lsp._anonym46.TextDocumentContentChangeEvent|lsp._anonym47.TextDocumentContentChangeEvent +---@alias lsp.TextDocumentContentChangeEvent lsp._anonym48.TextDocumentContentChangeEvent|lsp._anonym49.TextDocumentContentChangeEvent ---MarkedString can be used to render human readable text. It is either a markdown string ---or a code-block that provides a language and a code snippet. The language identifier @@ -4706,7 +4719,7 @@ error('Cannot require a meta file') --- ---Note that markdown strings will be sanitized - that means html will be escaped. ---@deprecated use MarkupContent instead. ----@alias lsp.MarkedString string|lsp._anonym48.MarkedString +---@alias lsp.MarkedString string|lsp._anonym50.MarkedString ---A document filter describes a top level text document or ---a notebook cell document. @@ -4739,14 +4752,14 @@ error('Cannot require a meta file') ---\@sample A language filter that applies to all package.json paths: `{ language: 'json', pattern: '**package.json' }` --- ---@since 3.17.0 ----@alias lsp.TextDocumentFilter lsp._anonym49.TextDocumentFilter|lsp._anonym50.TextDocumentFilter|lsp._anonym51.TextDocumentFilter +---@alias lsp.TextDocumentFilter lsp._anonym51.TextDocumentFilter|lsp._anonym52.TextDocumentFilter|lsp._anonym53.TextDocumentFilter ---A notebook document filter denotes a notebook document by ---different properties. The properties will be match ---against the notebook's URI (same as with documents) --- ---@since 3.17.0 ----@alias lsp.NotebookDocumentFilter lsp._anonym52.NotebookDocumentFilter|lsp._anonym53.NotebookDocumentFilter|lsp._anonym54.NotebookDocumentFilter +---@alias lsp.NotebookDocumentFilter lsp._anonym54.NotebookDocumentFilter|lsp._anonym55.NotebookDocumentFilter|lsp._anonym56.NotebookDocumentFilter ---The glob pattern to watch relative to the base path. Glob patterns can have the following syntax: ---- `*` to match one or more characters in a path segment @@ -4856,7 +4869,19 @@ error('Cannot require a meta file') ---The client's version as defined by the client. ---@field version? string ----@class lsp._anonym12.workspace +---@class lsp._anonym13.textDocument.diagnostic +--- +---Whether the server supports `MarkupContent` in diagnostic messages. +---@field markupMessageSupport? boolean + +---@class lsp._anonym12.textDocument +--- +---Capabilities specific to the diagnostic pull model. +--- +---@since 3.18.0 +---@field diagnostic? lsp._anonym13.textDocument.diagnostic + +---@class lsp._anonym14.workspace --- ---The server supports workspace folder. --- @@ -4868,7 +4893,7 @@ error('Cannot require a meta file') ---@since 3.16.0 ---@field fileOperations? lsp.FileOperationOptions ----@class lsp._anonym13.completionItem +---@class lsp._anonym15.completionItem --- ---The server has support for completion item label ---details (see also `CompletionItemLabelDetails`) when @@ -4877,11 +4902,11 @@ error('Cannot require a meta file') ---@since 3.17.0 ---@field labelDetailsSupport? boolean ----@class lsp._anonym15.notebookSelector.cells +---@class lsp._anonym17.notebookSelector.cells --- ---@field language string ----@class lsp._anonym14.notebookSelector +---@class lsp._anonym16.notebookSelector --- ---The notebook to be synced If a string ---value is provided it matches against the @@ -4889,13 +4914,13 @@ error('Cannot require a meta file') ---@field notebook string|lsp.NotebookDocumentFilter --- ---The cells of the matching notebook to be synced. ----@field cells? lsp._anonym15.notebookSelector.cells[] +---@field cells? lsp._anonym17.notebookSelector.cells[] ----@class lsp._anonym17.notebookSelector.cells +---@class lsp._anonym19.notebookSelector.cells --- ---@field language string ----@class lsp._anonym16.notebookSelector +---@class lsp._anonym18.notebookSelector --- ---The notebook to be synced If a string ---value is provided it matches against the @@ -4903,9 +4928,9 @@ error('Cannot require a meta file') ---@field notebook? string|lsp.NotebookDocumentFilter --- ---The cells of the matching notebook to be synced. ----@field cells lsp._anonym17.notebookSelector.cells[] +---@field cells lsp._anonym19.notebookSelector.cells[] ----@class lsp._anonym18.staleRequestSupport +---@class lsp._anonym20.staleRequestSupport --- ---The client will actively cancel the request. ---@field cancel boolean @@ -4915,14 +4940,14 @@ error('Cannot require a meta file') ---response with error code `ContentModified` ---@field retryOnContentModified string[] ----@class lsp._anonym19.changeAnnotationSupport +---@class lsp._anonym21.changeAnnotationSupport --- ---Whether the client groups edits with equal labels into tree nodes, ---for instance all edits labelled with "Changes in Strings" would ---be a tree node. ---@field groupsOnLabel? boolean ----@class lsp._anonym20.symbolKind +---@class lsp._anonym22.symbolKind --- ---The symbol kind values the client supports. When this ---property exists the client also guarantees that it will @@ -4934,32 +4959,32 @@ error('Cannot require a meta file') ---the initial version of the protocol. ---@field valueSet? lsp.SymbolKind[] ----@class lsp._anonym21.tagSupport +---@class lsp._anonym23.tagSupport --- ---The tags supported by the client. ---@field valueSet lsp.SymbolTag[] ----@class lsp._anonym22.resolveSupport +---@class lsp._anonym24.resolveSupport --- ---The properties that a client can resolve lazily. Usually ---`location.range` ---@field properties string[] ----@class lsp._anonym24.completionItem.tagSupport +---@class lsp._anonym26.completionItem.tagSupport --- ---The tags supported by the client. ---@field valueSet lsp.CompletionItemTag[] ----@class lsp._anonym25.completionItem.resolveSupport +---@class lsp._anonym27.completionItem.resolveSupport --- ---The properties that a client can resolve lazily. ---@field properties string[] ----@class lsp._anonym26.completionItem.insertTextModeSupport +---@class lsp._anonym28.completionItem.insertTextModeSupport --- ---@field valueSet lsp.InsertTextMode[] ----@class lsp._anonym23.completionItem +---@class lsp._anonym25.completionItem --- ---Client supports snippets as insert text. --- @@ -4988,7 +5013,7 @@ error('Cannot require a meta file') ---a resolve call. --- ---@since 3.15.0 ----@field tagSupport? lsp._anonym24.completionItem.tagSupport +---@field tagSupport? lsp._anonym26.completionItem.tagSupport --- ---Client support insert replace edit to control different behavior if a ---completion item is inserted in the text or should replace text. @@ -5001,14 +5026,14 @@ error('Cannot require a meta file') ---and `details` could be resolved lazily. --- ---@since 3.16.0 ----@field resolveSupport? lsp._anonym25.completionItem.resolveSupport +---@field resolveSupport? lsp._anonym27.completionItem.resolveSupport --- ---The client supports the `insertTextMode` property on ---a completion item to override the whitespace handling mode ---as defined by the client (see `insertTextMode`). --- ---@since 3.16.0 ----@field insertTextModeSupport? lsp._anonym26.completionItem.insertTextModeSupport +---@field insertTextModeSupport? lsp._anonym28.completionItem.insertTextModeSupport --- ---The client has support for completion item label ---details (see also `CompletionItemLabelDetails`). @@ -5016,7 +5041,7 @@ error('Cannot require a meta file') ---@since 3.17.0 ---@field labelDetailsSupport? boolean ----@class lsp._anonym27.completionItemKind +---@class lsp._anonym29.completionItemKind --- ---The completion item kind values the client supports. When this ---property exists the client also guarantees that it will @@ -5028,7 +5053,7 @@ error('Cannot require a meta file') ---the initial version of the protocol. ---@field valueSet? lsp.CompletionItemKind[] ----@class lsp._anonym28.completionList +---@class lsp._anonym30.completionList --- ---The client supports the following itemDefaults on ---a completion list. @@ -5040,7 +5065,7 @@ error('Cannot require a meta file') ---@since 3.17.0 ---@field itemDefaults? string[] ----@class lsp._anonym30.signatureInformation.parameterInformation +---@class lsp._anonym32.signatureInformation.parameterInformation --- ---The client supports processing label offsets instead of a ---simple label string. @@ -5048,14 +5073,14 @@ error('Cannot require a meta file') ---@since 3.14.0 ---@field labelOffsetSupport? boolean ----@class lsp._anonym29.signatureInformation +---@class lsp._anonym31.signatureInformation --- ---Client supports the following content formats for the documentation ---property. The order describes the preferred format of the client. ---@field documentationFormat? lsp.MarkupKind[] --- ---Client capabilities specific to parameter information. ----@field parameterInformation? lsp._anonym30.signatureInformation.parameterInformation +---@field parameterInformation? lsp._anonym32.signatureInformation.parameterInformation --- ---The client supports the `activeParameter` property on `SignatureInformation` ---literal. @@ -5070,7 +5095,7 @@ error('Cannot require a meta file') ---@since 3.18.0 ---@field noActiveParameterSupport? boolean ----@class lsp._anonym31.symbolKind +---@class lsp._anonym33.symbolKind --- ---The symbol kind values the client supports. When this ---property exists the client also guarantees that it will @@ -5082,12 +5107,12 @@ error('Cannot require a meta file') ---the initial version of the protocol. ---@field valueSet? lsp.SymbolKind[] ----@class lsp._anonym32.tagSupport +---@class lsp._anonym34.tagSupport --- ---The tags supported by the client. ---@field valueSet lsp.SymbolTag[] ----@class lsp._anonym34.codeActionLiteralSupport.codeActionKind +---@class lsp._anonym36.codeActionLiteralSupport.codeActionKind --- ---The code action kind values the client supports. When this ---property exists the client also guarantees that it will @@ -5095,18 +5120,18 @@ error('Cannot require a meta file') ---to a default value when unknown. ---@field valueSet lsp.CodeActionKind[] ----@class lsp._anonym33.codeActionLiteralSupport +---@class lsp._anonym35.codeActionLiteralSupport --- ---The code action kind is support with the following value ---set. ----@field codeActionKind lsp._anonym34.codeActionLiteralSupport.codeActionKind +---@field codeActionKind lsp._anonym36.codeActionLiteralSupport.codeActionKind ----@class lsp._anonym35.resolveSupport +---@class lsp._anonym37.resolveSupport --- ---The properties that a client can resolve lazily. ---@field properties string[] ----@class lsp._anonym36.foldingRangeKind +---@class lsp._anonym38.foldingRangeKind --- ---The folding range kind values the client supports. When this ---property exists the client also guarantees that it will @@ -5114,7 +5139,7 @@ error('Cannot require a meta file') ---to a default value when unknown. ---@field valueSet? lsp.FoldingRangeKind[] ----@class lsp._anonym37.foldingRange +---@class lsp._anonym39.foldingRange --- ---If set, the client signals that it supports setting collapsedText on ---folding ranges to display custom labels instead of the default text. @@ -5122,52 +5147,52 @@ error('Cannot require a meta file') ---@since 3.17.0 ---@field collapsedText? boolean ----@class lsp._anonym38.tagSupport +---@class lsp._anonym40.tagSupport --- ---The tags supported by the client. ---@field valueSet lsp.DiagnosticTag[] ----@class lsp._anonym40.requests.range +---@class lsp._anonym42.requests.range ----@class lsp._anonym41.requests.full +---@class lsp._anonym43.requests.full --- ---The client will send the `textDocument/semanticTokens/full/delta` request if ---the server provides a corresponding handler. ---@field delta? boolean ----@class lsp._anonym39.requests +---@class lsp._anonym41.requests --- ---The client will send the `textDocument/semanticTokens/range` request if ---the server provides a corresponding handler. ----@field range? boolean|lsp._anonym40.requests.range +---@field range? boolean|lsp._anonym42.requests.range --- ---The client will send the `textDocument/semanticTokens/full` request if ---the server provides a corresponding handler. ----@field full? boolean|lsp._anonym41.requests.full +---@field full? boolean|lsp._anonym43.requests.full ----@class lsp._anonym42.resolveSupport +---@class lsp._anonym44.resolveSupport --- ---The properties that a client can resolve lazily. ---@field properties string[] ----@class lsp._anonym43.messageActionItem +---@class lsp._anonym45.messageActionItem --- ---Whether the client supports additional attributes which ---are preserved and send back to the server in the ---request's response. ---@field additionalPropertiesSupport? boolean ----@class lsp._anonym44.PrepareRenameResult +---@class lsp._anonym46.PrepareRenameResult --- ---@field range lsp.Range --- ---@field placeholder string ----@class lsp._anonym45.PrepareRenameResult +---@class lsp._anonym47.PrepareRenameResult --- ---@field defaultBehavior boolean ----@class lsp._anonym46.TextDocumentContentChangeEvent +---@class lsp._anonym48.TextDocumentContentChangeEvent --- ---The range of the document that changed. ---@field range lsp.Range @@ -5180,18 +5205,18 @@ error('Cannot require a meta file') ---The new text for the provided range. ---@field text string ----@class lsp._anonym47.TextDocumentContentChangeEvent +---@class lsp._anonym49.TextDocumentContentChangeEvent --- ---The new text of the whole document. ---@field text string ----@class lsp._anonym48.MarkedString +---@class lsp._anonym50.MarkedString --- ---@field language string --- ---@field value string ----@class lsp._anonym49.TextDocumentFilter +---@class lsp._anonym51.TextDocumentFilter --- ---A language id, like `typescript`. ---@field language string @@ -5202,7 +5227,7 @@ error('Cannot require a meta file') ---A glob pattern, like **/*.{ts,js}. See TextDocumentFilter for examples. ---@field pattern? string ----@class lsp._anonym50.TextDocumentFilter +---@class lsp._anonym52.TextDocumentFilter --- ---A language id, like `typescript`. ---@field language? string @@ -5213,7 +5238,7 @@ error('Cannot require a meta file') ---A glob pattern, like **/*.{ts,js}. See TextDocumentFilter for examples. ---@field pattern? string ----@class lsp._anonym51.TextDocumentFilter +---@class lsp._anonym53.TextDocumentFilter --- ---A language id, like `typescript`. ---@field language? string @@ -5224,7 +5249,7 @@ error('Cannot require a meta file') ---A glob pattern, like **/*.{ts,js}. See TextDocumentFilter for examples. ---@field pattern string ----@class lsp._anonym52.NotebookDocumentFilter +---@class lsp._anonym54.NotebookDocumentFilter --- ---The type of the enclosing notebook. ---@field notebookType string @@ -5235,7 +5260,7 @@ error('Cannot require a meta file') ---A glob pattern. ---@field pattern? string ----@class lsp._anonym53.NotebookDocumentFilter +---@class lsp._anonym55.NotebookDocumentFilter --- ---The type of the enclosing notebook. ---@field notebookType? string @@ -5246,7 +5271,7 @@ error('Cannot require a meta file') ---A glob pattern. ---@field pattern? string ----@class lsp._anonym54.NotebookDocumentFilter +---@class lsp._anonym56.NotebookDocumentFilter --- ---The type of the enclosing notebook. ---@field notebookType? string -- cgit From 6818ba271cb43b1430f019b832d7e26671e0f5f4 Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Tue, 14 May 2024 07:08:13 -0700 Subject: fix(health): clients may not support watchfiles #28710 --- runtime/lua/vim/lsp/health.lua | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua index 797a1097f9..a79ae76eb9 100644 --- a/runtime/lua/vim/lsp/health.lua +++ b/runtime/lua/vim/lsp/health.lua @@ -51,6 +51,29 @@ end local function check_watcher() vim.health.start('vim.lsp: File watcher') + + -- Only run the check if file watching has been enabled by a client. + local clients = vim.lsp.get_clients() + if + --- @param client vim.lsp.Client + vim.iter(clients):all(function(client) + local has_capability = vim.tbl_get( + client.capabilities, + 'workspace', + 'didChangeWatchedFiles', + 'dynamicRegistration' + ) + local has_dynamic_capability = + client.dynamic_capabilities:get(vim.lsp.protocol.Methods.workspace_didChangeWatchedFiles) + return has_capability == nil + or has_dynamic_capability == nil + or client.workspace_folders == nil + end) + then + report_info('file watching "(workspace/didChangeWatchedFiles)" disabled on all clients') + return + end + local watchfunc = vim.lsp._watchfiles._watchfunc assert(watchfunc) local watchfunc_name --- @type string -- cgit From 7acf39ddab8ebdb63ebf78ec980149d20783fd4b Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Wed, 15 May 2024 01:18:33 +0200 Subject: docs: misc (#28609) Closes https://github.com/neovim/neovim/issues/28484. Closes https://github.com/neovim/neovim/issues/28719. Co-authored-by: Chris Co-authored-by: Gregory Anders Co-authored-by: Jake B <16889000+jakethedev@users.noreply.github.com> Co-authored-by: Jonathan Raines Co-authored-by: Yi Ming Co-authored-by: Zane Dufour Co-authored-by: zeertzjq --- runtime/lua/vim/lsp/buf.lua | 4 ++-- runtime/lua/vim/lsp/inlay_hint.lua | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index e05acff3df..49833eaeec 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -503,7 +503,7 @@ function M.typehierarchy(kind) --- Merge results from multiple clients into a single table. Client-ID is preserved. --- - --- @param results table + --- @param results table --- @return [integer, lsp.TypeHierarchyItem][] local function merge_results(results) local merged_results = {} @@ -521,7 +521,7 @@ function M.typehierarchy(kind) local bufnr = api.nvim_get_current_buf() local params = util.make_position_params() - --- @param results table + --- @param results table vim.lsp.buf_request_all(bufnr, ms.textDocument_prepareTypeHierarchy, params, function(results) local merged_results = merge_results(results) if #merged_results == 0 then diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index effc69c67e..8d22859a80 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -98,7 +98,7 @@ function M.on_inlayhint(err, result, ctx, _) api.nvim__redraw({ buf = bufnr, valid = true }) end ---- |lsp-handler| for the method `textDocument/inlayHint/refresh` +--- |lsp-handler| for the method `workspace/inlayHint/refresh` ---@param ctx lsp.HandlerContext ---@private function M.on_refresh(err, _, ctx, _) @@ -368,6 +368,7 @@ api.nvim_set_decoration_provider(namespace, { end, }) +--- Query whether inlay hint is enabled in the {filter}ed scope --- @param filter vim.lsp.inlay_hint.enable.Filter --- @return boolean --- @since 12 @@ -391,7 +392,7 @@ end --- Buffer number, or 0 for current buffer, or nil for all. --- @field bufnr integer? ---- Enables or disables inlay hints for a buffer. +--- Enables or disables inlay hints for the {filter}ed scope. --- --- To "toggle", pass the inverse of `is_enabled()`: --- -- cgit From 01b6bff7e9bc751be20ce7bb68e7ebe3037441de Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 2 May 2024 15:57:21 +0200 Subject: docs: news Set dev_xx.txt help files to use "flow" layout. --- runtime/lua/vim/lsp/inlay_hint.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index 8d22859a80..f98496456b 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -136,7 +136,8 @@ end --- local hint = vim.lsp.inlay_hint.get({ bufnr = 0 })[1] -- 0 for current buffer --- --- local client = vim.lsp.get_client_by_id(hint.client_id) ---- resolved_hint = client.request_sync('inlayHint/resolve', hint.inlay_hint, 100, 0).result +--- local resp = client.request_sync('inlayHint/resolve', hint.inlay_hint, 100, 0) +--- local resolved_hint = assert(resp and resp.result, resp.err) --- vim.lsp.util.apply_text_edits(resolved_hint.textEdits, 0, client.encoding) --- --- location = resolved_hint.label[1].location -- cgit From a664246171569209698c0b17b1d7af831f6603d2 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Thu, 16 May 2024 15:22:46 +0200 Subject: feat: remove deprecated features Remove following functions: - vim.lsp.util.extract_completion_items - vim.lsp.util.get_progress_messages - vim.lsp.util.parse_snippet() - vim.lsp.util.text_document_completion_list_to_complete_items - LanguageTree:for_each_child - health#report_error - health#report_info - health#report_ok - health#report_start - health#report_warn - vim.health.report_error - vim.health.report_info - vim.health.report_ok - vim.health.report_start - vim.health.report_warn --- runtime/lua/vim/lsp/handlers.lua | 3 +- runtime/lua/vim/lsp/util.lua | 127 --------------------------------------- 2 files changed, 2 insertions(+), 128 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 7018b9f61b..f9d394642c 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -3,6 +3,7 @@ local protocol = require('vim.lsp.protocol') local ms = protocol.Methods local util = require('vim.lsp.util') local api = vim.api +local completion = require('vim.lsp._completion') --- @type table local M = {} @@ -353,7 +354,7 @@ M[ms.textDocument_completion] = function(_, result, _, _) local textMatch = vim.fn.match(line_to_cursor, '\\k*$') local prefix = line_to_cursor:sub(textMatch + 1) - local matches = util.text_document_completion_list_to_complete_items(result, prefix) + local matches = completion._lsp_to_complete_items(result, prefix) vim.fn.complete(textMatch + 1, matches) end diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index fc99f54d03..5a229a1169 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1,5 +1,4 @@ local protocol = require('vim.lsp.protocol') -local snippet = require('vim.lsp._snippet_grammar') local validate = vim.validate local api = vim.api local list_extend = vim.list_extend @@ -343,68 +342,6 @@ local function get_line_byte_from_position(bufnr, position, offset_encoding) return col end ---- Process and return progress reports from lsp server ----@private ----@deprecated Use vim.lsp.status() or access client.progress directly -function M.get_progress_messages() - vim.deprecate('vim.lsp.util.get_progress_messages()', 'vim.lsp.status()', '0.11') - local new_messages = {} - local progress_remove = {} - - for _, client in ipairs(vim.lsp.get_clients()) do - local groups = {} - 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 = { - done = false, - progress = true, - title = 'empty title', - } - groups[progress.token] = group - end - group.title = value.title or group.title - group.cancellable = value.cancellable or group.cancellable - if value.kind == 'end' then - group.done = true - end - group.message = value.message or group.message - group.percentage = value.percentage or group.percentage - end - end - - for _, group in pairs(groups) do - table.insert(new_messages, group) - end - - local messages = client.messages - local data = messages - for token, ctx in pairs(data.progress) do - local new_report = { - name = data.name, - title = ctx.title or 'empty title', - message = ctx.message, - percentage = ctx.percentage, - done = ctx.done, - progress = true, - } - table.insert(new_messages, new_report) - - if ctx.done then - table.insert(progress_remove, { client = client, token = token }) - end - end - end - - for _, item in ipairs(progress_remove) do - item.client.messages.progress[item.token] = nil - end - - return new_messages -end - --- Applies a list of text edits to a buffer. ---@param text_edits table list of `TextEdit` objects ---@param bufnr integer Buffer id @@ -541,38 +478,6 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) end end --- local valid_windows_path_characters = "[^<>:\"/\\|?*]" --- local valid_unix_path_characters = "[^/]" --- https://github.com/davidm/lua-glob-pattern --- https://stackoverflow.com/questions/1976007/what-characters-are-forbidden-in-windows-and-linux-directory-names --- function M.glob_to_regex(glob) --- end - ---- Can be used to extract the completion items from a ---- `textDocument/completion` request, which may return one of ---- `CompletionItem[]`, `CompletionList` or null. ---- ---- Note that this method doesn't apply `itemDefaults` to `CompletionList`s, and hence the returned ---- results might be incorrect. ---- ----@deprecated ----@param result table The result of a `textDocument/completion` request ----@return lsp.CompletionItem[] List of completion items ----@see https://microsoft.github.io/language-server-protocol/specification#textDocument_completion -function M.extract_completion_items(result) - vim.deprecate('vim.lsp.util.extract_completion_items()', nil, '0.11') - if type(result) == 'table' and result.items then - -- result is a `CompletionList` - return result.items - elseif result ~= nil then - -- result is `CompletionItem[]` - return result - else - -- result is `null` - return {} - end -end - --- Applies a `TextDocumentEdit`, which is a list of changes to a single --- document. --- @@ -615,38 +520,6 @@ function M.apply_text_document_edit(text_document_edit, index, offset_encoding) M.apply_text_edits(text_document_edit.edits, bufnr, offset_encoding) end ---- Parses snippets in a completion entry. ---- ----@deprecated ----@param input string unparsed snippet ----@return string parsed snippet -function M.parse_snippet(input) - vim.deprecate('vim.lsp.util.parse_snippet()', nil, '0.11') - local ok, parsed = pcall(function() - return snippet.parse(input) - end) - if not ok then - return input - end - - return tostring(parsed) -end - ---- Turns the result of a `textDocument/completion` request into vim-compatible ---- |complete-items|. ---- ----@deprecated ----@param result table The result of a `textDocument/completion` call, e.g. ---- from |vim.lsp.buf.completion()|, which may be one of `CompletionItem[]`, ---- `CompletionList` or `null` ----@param prefix (string) the prefix to filter the completion items ----@return table[] items ----@see complete-items -function M.text_document_completion_list_to_complete_items(result, prefix) - vim.deprecate('vim.lsp.util.text_document_completion_list_to_complete_items()', nil, '0.11') - return vim.lsp._completion._lsp_to_complete_items(result, prefix) -end - local function path_components(path) return vim.split(path, '/', { plain = true }) end -- cgit From d9a2acdab3bfd33584ad468f4d559b0e94f84471 Mon Sep 17 00:00:00 2001 From: Mango The Fourth <40720523+MangoIV@users.noreply.github.com> Date: Tue, 21 May 2024 18:23:37 +0200 Subject: fix(lsp): hide layout in codelenses in virtual text (#28794) (#28807) Problem: layout i.e. whitespace that is part of codelenses is currently displayed as weird symbols and large amounts of spaces Solution: replace all consecutive whitespace symbols with a single space character when trying to display codelenses as virtual text --- runtime/lua/vim/lsp/codelens.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index a2a0c15b93..c85bb6aa32 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -164,7 +164,7 @@ function M.display(lenses, bufnr, client_id) return a.range.start.character < b.range.start.character end) for j, lens in ipairs(line_lenses) do - local text = lens.command and lens.command.title or 'Unresolved lens ...' + local text = (lens.command and lens.command.title or 'Unresolved lens ...'):gsub('%s+', ' ') table.insert(chunks, { text, 'LspCodeLens' }) if j < num_line_lenses then table.insert(chunks, { ' | ', 'LspCodeLensSeparator' }) -- cgit From a108852b008b2cb1fe38b1f66cffd78b33bd9a70 Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Tue, 21 May 2024 09:25:54 -0700 Subject: fix(lsp): semantic token functions allow "0" bufnr #28849 aligns with ":help dev-patterns" --- runtime/lua/vim/lsp/semantic_tokens.lua | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index be2d6ee0ae..ef2502b12e 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -570,9 +570,9 @@ local M = {} --- client.server_capabilities.semanticTokensProvider = nil --- ``` --- ----@param bufnr integer ----@param client_id integer ----@param opts? table Optional keyword arguments +---@param bufnr (integer) Buffer number, or `0` for current buffer +---@param client_id (integer) The ID of the |vim.lsp.Client| +---@param opts? (table) Optional keyword arguments --- - debounce (integer, default: 200): Debounce token requests --- to the server by the given number in milliseconds function M.start(bufnr, client_id, opts) @@ -581,6 +581,10 @@ function M.start(bufnr, client_id, opts) client_id = { client_id, 'n', false }, }) + if bufnr == 0 then + bufnr = api.nvim_get_current_buf() + end + opts = opts or {} assert( (not opts.debounce or type(opts.debounce) == 'number'), @@ -626,14 +630,18 @@ end --- of `start()`, so you should only need this function to manually disengage the semantic --- token engine without fully detaching the LSP client from the buffer. --- ----@param bufnr integer ----@param client_id integer +---@param bufnr (integer) Buffer number, or `0` for current buffer +---@param client_id (integer) The ID of the |vim.lsp.Client| function M.stop(bufnr, client_id) vim.validate({ bufnr = { bufnr, 'n', false }, client_id = { client_id, 'n', false }, }) + if bufnr == 0 then + bufnr = api.nvim_get_current_buf() + end + local highlighter = STHighlighter.active[bufnr] if not highlighter then return @@ -741,12 +749,15 @@ end --- mark will be deleted by the semantic token engine when appropriate; for --- example, when the LSP sends updated tokens. This function is intended for --- use inside |LspTokenUpdate| callbacks. ----@param token (table) a semantic token, found as `args.data.token` in |LspTokenUpdate|. ----@param bufnr (integer) the buffer to highlight +---@param token (table) A semantic token, found as `args.data.token` in |LspTokenUpdate| +---@param bufnr (integer) The buffer to highlight, or `0` for current buffer ---@param client_id (integer) The ID of the |vim.lsp.Client| ---@param hl_group (string) Highlight group name ---@param opts? vim.lsp.semantic_tokens.highlight_token.Opts Optional parameters: function M.highlight_token(token, bufnr, client_id, hl_group, opts) + if bufnr == 0 then + bufnr = api.nvim_get_current_buf() + end local highlighter = STHighlighter.active[bufnr] if not highlighter then return -- cgit From 339129ebc9503883a3f060d3eff620d67a9eadaf Mon Sep 17 00:00:00 2001 From: Ilia Choly Date: Sun, 19 May 2024 13:03:06 -0400 Subject: refactor(lsp): use supports_method where applicable --- runtime/lua/vim/lsp/client.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index 448f986cd3..8fb5879e9b 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -905,7 +905,7 @@ end --- @param bufnr integer Number of the buffer, or 0 for current function Client:_text_document_did_open_handler(bufnr) changetracking.init(self, bufnr) - if not vim.tbl_get(self.server_capabilities, 'textDocumentSync', 'openClose') then + if not self.supports_method(ms.textDocument_didOpen) then return end if not api.nvim_buf_is_loaded(bufnr) then -- cgit From af200c10cf9d117a14ebf9f2e9c666721a1090d6 Mon Sep 17 00:00:00 2001 From: Ilia Choly Date: Thu, 23 May 2024 09:17:53 -0400 Subject: fix(lsp): check if buffer was detached in on_init callback (#28914) Co-authored-by: Jongwook Choi --- runtime/lua/vim/lsp/client.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp') diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index 8fb5879e9b..4beb7fefda 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -612,7 +612,10 @@ function Client:initialize() self:_run_callbacks(self._on_init_cbs, lsp.client_errors.ON_INIT_CALLBACK_ERROR, self, result) for buf in pairs(reattach_bufs) do - self:_on_attach(buf) + -- The buffer may have been detached in the on_init callback. + if self.attached_buffers[buf] then + self:_on_attach(buf) + end end log.info( -- cgit