From d15abd1be4ae85b10174e3ee139d3b7605e87577 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sun, 12 Mar 2023 09:45:28 +0100 Subject: fix(lsp): use line start/end for visual line selection (#22632) Fixes https://github.com/neovim/neovim/issues/22629 --- runtime/lua/vim/lsp/buf.lua | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 6ac885c78f..0e16e8f820 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -118,8 +118,10 @@ function M.completion(context) end ---@private +---@param bufnr integer +---@param mode "v"|"V" ---@return table {start={row, col}, end={row, col}} using (1, 0) indexing -local function range_from_selection() +local function range_from_selection(bufnr, mode) -- TODO: Use `vim.region()` instead https://github.com/neovim/neovim/pull/13896 -- [bufnum, lnum, col, off]; both row and column 1-indexed @@ -138,6 +140,11 @@ local function range_from_selection() start_row, end_row = end_row, start_row start_col, end_col = end_col, start_col end + if mode == 'V' then + start_col = 1 + local lines = api.nvim_buf_get_lines(bufnr, end_row - 1, end_row, true) + end_col = #lines[1] + end return { ['start'] = { start_row, start_col - 1 }, ['end'] = { end_row, end_col - 1 }, @@ -200,7 +207,7 @@ function M.format(options) local mode = api.nvim_get_mode().mode local range = options.range if not range and mode == 'v' or mode == 'V' then - range = range_from_selection() + range = range_from_selection(bufnr, mode) end local method = range and 'textDocument/rangeFormatting' or 'textDocument/formatting' @@ -772,7 +779,7 @@ function M.code_action(options) local end_ = assert(options.range['end'], 'range must have a `end` property') params = util.make_given_range_params(start, end_) elseif mode == 'v' or mode == 'V' then - local range = range_from_selection() + local range = range_from_selection(0, mode) params = util.make_given_range_params(range.start, range['end']) else params = util.make_range_params() -- cgit From f01f18cdf4ffc3ce035db5fde2f45493eebb7fd9 Mon Sep 17 00:00:00 2001 From: Dan Strokirk Date: Mon, 13 Mar 2023 14:01:34 +0100 Subject: fix(lsp): remove_workspace_folders fails if client has no workspace_folders #22633 When a client has no workspace_folders, (e.g., copilot), `pairs` code would crash. --- runtime/lua/vim/lsp/buf.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 0e16e8f820..8bf3764f5e 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -530,7 +530,7 @@ function M.remove_workspace_folder(workspace_folder) { { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } } ) for _, client in pairs(vim.lsp.get_active_clients({ bufnr = 0 })) do - for idx, folder in pairs(client.workspace_folders) do + for idx, folder in pairs(client.workspace_folders or {}) do if folder.name == workspace_folder then vim.lsp.buf_notify(0, 'workspace/didChangeWorkspaceFolders', params) client.workspace_folders[idx] = nil -- cgit From bfb28b62dab756ec76a73506c2070ddf491a0cdd Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Thu, 13 Apr 2023 15:29:13 -0600 Subject: refactor: remove modelines from Lua files Now that we have builtin EditorConfig support and a formatting check in CI, these are not necessary. --- runtime/lua/vim/lsp/buf.lua | 1 - 1 file changed, 1 deletion(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 8bf3764f5e..3d9011656f 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -806,4 +806,3 @@ function M.execute_command(command_params) end return M --- vim:sw=2 ts=2 et -- cgit From 512a90520e3287b9962ed7853d1f1021b564a133 Mon Sep 17 00:00:00 2001 From: Raphael Date: Sat, 13 May 2023 17:27:05 +0800 Subject: refactor(lsp): mark server_ready function as deprecated (#23520) --- runtime/lua/vim/lsp/buf.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 3d9011656f..a307dea673 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -31,7 +31,9 @@ end --- ready. --- ---@returns `true` if server responds. +---@deprecated function M.server_ready() + vim.deprecate('vim.lsp.buf.server_ready', nil, '0.10.0') return not not vim.lsp.buf_notify(0, 'window/progress', {}) end -- cgit From ddd92a70d2aab5247895e89abaaa79c62ba7dbb4 Mon Sep 17 00:00:00 2001 From: Folke Lemaitre Date: Sun, 28 May 2023 07:51:28 +0200 Subject: feat(lsp): initial support for dynamic capabilities (#23681) - `client.dynamic_capabilities` is an object that tracks client register/unregister - `client.supports_method` will additionally check if a dynamic capability supports the method, taking document filters into account. But only if the client enabled `dynamicRegistration` for the capability - updated the default client capabilities to include dynamicRegistration for: - formatting - rangeFormatting - hover - codeAction - hover - rename --- runtime/lua/vim/lsp/buf.lua | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index a307dea673..b2f202c4ba 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -683,11 +683,7 @@ local function on_code_action_results(results, ctx, options) -- local client = vim.lsp.get_client_by_id(action_tuple[1]) local action = action_tuple[2] - if - not action.edit - and client - and vim.tbl_get(client.server_capabilities, 'codeActionProvider', 'resolveProvider') - then + if not action.edit and client and client.supports_method('codeAction/resolve') then client.request('codeAction/resolve', action, function(err, resolved_action) if err then vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR) -- cgit From dd3fa645735539c75b72dc1b0114278b5fa57f7f Mon Sep 17 00:00:00 2001 From: Folke Lemaitre Date: Tue, 30 May 2023 19:15:07 +0200 Subject: fix(lsp): fix dynamic registration of code actions (#23826) --- runtime/lua/vim/lsp/buf.lua | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index b2f202c4ba..bb3ca0e6d6 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -681,9 +681,16 @@ local function on_code_action_results(results, ctx, options) -- command: string -- arguments?: any[] -- + ---@type lsp.Client local client = vim.lsp.get_client_by_id(action_tuple[1]) local action = action_tuple[2] - if not action.edit and client and client.supports_method('codeAction/resolve') then + + local reg = client.dynamic_capabilities:get('textDocument/codeAction', { bufnr = ctx.bufnr }) + + local supports_resolve = vim.tbl_get(reg or {}, 'registerOptions', 'resolveProvider') + or client.supports_method('codeAction/resolve') + + if not action.edit and client and supports_resolve then client.request('codeAction/resolve', action, function(err, resolved_action) if err then vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR) -- cgit From c07dceba335c56c9a356395ad0d1e5a14d416752 Mon Sep 17 00:00:00 2001 From: Jonas Strittmatter <40792180+smjonas@users.noreply.github.com> Date: Sat, 17 Jun 2023 08:01:31 +0200 Subject: fix(lsp): allow Lua pattern chars in code action filter (#24041) Previously, filtering code actions with the "only" option failed if the code action kind contained special Lua pattern chars such as "-" (e.g. the ocaml language server supports a "type-annotate" code action). Solution: use string comparison instead of string.find --- runtime/lua/vim/lsp/buf.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index bb3ca0e6d6..e0034cf86e 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -608,9 +608,9 @@ local function on_code_action_results(results, ctx, options) end local found = false for _, o in ipairs(options.context.only) do - -- action kinds are hierarchical with . as a separator: when requesting only - -- 'quickfix' this filter allows both 'quickfix' and 'quickfix.foo', for example - if a.kind:find('^' .. o .. '$') or a.kind:find('^' .. o .. '%.') then + -- action kinds are hierarchical with . as a separator: when requesting only 'type-annotate' + -- this filter allows both 'type-annotate' and 'type-annotate.foo', for example + if a.kind == o or vim.startswith(a.kind, o .. '.') then found = true break end -- cgit From ca5de9306c00d07cce1daef1f0038c937098bc66 Mon Sep 17 00:00:00 2001 From: Chinmay Dalal Date: Tue, 20 Jun 2023 11:36:54 +0530 Subject: feat(lsp): inlay hints #23984 Add automatic refresh and a public interface on top of #23736 * add on_reload, on_detach handlers in `enable()` buf_attach, and LspDetach autocommand in case of manual detach * unify `__buffers` and `hint_cache_by_buf` * use callback bufnr in `on_lines` callback, bufstate: remove __index override * move user-facing functions into vim.lsp.buf, unify enable/disable/toggle Closes #18086 --- runtime/lua/vim/lsp/buf.lua | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index e0034cf86e..c3deffc1f9 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -810,4 +810,19 @@ function M.execute_command(command_params) request('workspace/executeCommand', command_params) end +--- Enable/disable/toggle inlay hints for a buffer +---@param bufnr (integer) Buffer handle, or 0 for current +---@param enable (boolean|nil) true/false to enable/disable, nil to toggle +function M.inlay_hint(bufnr, enable) + vim.validate({ enable = { enable, { 'boolean', 'nil' } }, bufnr = { bufnr, 'number' } }) + local inlay_hint = require('vim.lsp._inlay_hint') + if enable then + inlay_hint.enable(bufnr) + elseif enable == false then + inlay_hint.disable(bufnr) + else + inlay_hint.toggle(bufnr) + end +end + return M -- cgit From 64f2691a984a5b1e2958d5656a910054982a6f0e Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Tue, 20 Jun 2023 18:36:18 +0200 Subject: refactor(lsp): extract common execute command functionality (#24065) --- runtime/lua/vim/lsp/buf.lua | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index c3deffc1f9..45056cf272 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -646,21 +646,7 @@ local function on_code_action_results(results, ctx, options) end if action.command then local command = type(action.command) == 'table' and action.command or action - local fn = client.commands[command.command] or vim.lsp.commands[command.command] - if fn then - local enriched_ctx = vim.deepcopy(ctx) - enriched_ctx.client_id = client.id - fn(command, enriched_ctx) - else - -- Not using command directly to exclude extra properties, - -- see https://github.com/python-lsp/python-lsp-server/issues/146 - local params = { - command = command.command, - arguments = command.arguments, - workDoneToken = command.workDoneToken, - } - client.request('workspace/executeCommand', params, nil, ctx.bufnr) - end + client._exec_cmd(command, ctx) end end @@ -697,7 +683,7 @@ local function on_code_action_results(results, ctx, options) return end apply_action(resolved_action, client) - end) + end, ctx.bufnr) else apply_action(action, client) end -- cgit From 4e6356559c8cd44dbcaa765d1f39e176064526ec Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 22 Jun 2023 03:44:51 -0700 Subject: test: spellcheck :help (vimdoc) files #24109 Enforce consistent terminology (defined in `gen_help_html.lua:spell_dict`) for common misspellings. This does not spellcheck English in general (perhaps a future TODO, though it may be noisy). --- runtime/lua/vim/lsp/buf.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 45056cf272..17b444a6e8 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -159,7 +159,7 @@ end --- @param options table|nil Optional table which holds the following optional fields: --- - formatting_options (table|nil): --- Can be used to specify FormattingOptions. Some unspecified options will be ---- automatically derived from the current Neovim options. +--- automatically derived from the current Nvim options. --- See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#formattingOptions --- - timeout_ms (integer|nil, default 1000): --- Time in milliseconds to block for formatting requests. No effect if async=true -- cgit From 036da0d07921e67090d1a62c9a4e382ca09d8584 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 24 Jun 2023 13:47:10 +0200 Subject: fix(docs): vimdoc syntax errors gen_help_html: truncate parse-error sample text --- runtime/lua/vim/lsp/buf.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 17b444a6e8..c2e0179cc4 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -122,7 +122,7 @@ end ---@private ---@param bufnr integer ---@param mode "v"|"V" ----@return table {start={row, col}, end={row, col}} using (1, 0) indexing +---@return table {start={row,col}, end={row,col}} using (1, 0) indexing local function range_from_selection(bufnr, mode) -- TODO: Use `vim.region()` instead https://github.com/neovim/neovim/pull/13896 @@ -189,7 +189,7 @@ end --- Restrict formatting to the client with name (client.name) matching this field. --- --- - range (table|nil) Range to format. ---- Table must contain `start` and `end` keys with {row, col} tuples using +--- Table must contain `start` and `end` keys with {row,col} tuples using --- (1,0) indexing. --- Defaults to current selection in visual mode --- Defaults to `nil` in other modes, formatting the full buffer @@ -741,7 +741,7 @@ end --- - range: (table|nil) --- Range for which code actions should be requested. --- If in visual mode this defaults to the active selection. ---- Table must contain `start` and `end` keys with {row, col} tuples +--- Table must contain `start` and `end` keys with {row,col} tuples --- using mark-like indexing. See |api-indexing| --- ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction -- cgit From 37079fca58f396fd866dc7b7d87a0100c17ee760 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Fri, 30 Jun 2023 11:33:28 +0200 Subject: feat(lsp): move inlay_hint() to vim.lsp (#24130) Allows to keep more functions hidden and gives a path forward for further inlay_hint related functions - like applying textEdits. See https://github.com/neovim/neovim/pull/23984#pullrequestreview-1486624668 --- runtime/lua/vim/lsp/buf.lua | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index c2e0179cc4..0369725216 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -796,19 +796,4 @@ function M.execute_command(command_params) request('workspace/executeCommand', command_params) end ---- Enable/disable/toggle inlay hints for a buffer ----@param bufnr (integer) Buffer handle, or 0 for current ----@param enable (boolean|nil) true/false to enable/disable, nil to toggle -function M.inlay_hint(bufnr, enable) - vim.validate({ enable = { enable, { 'boolean', 'nil' } }, bufnr = { bufnr, 'number' } }) - local inlay_hint = require('vim.lsp._inlay_hint') - if enable then - inlay_hint.enable(bufnr) - elseif enable == false then - inlay_hint.disable(bufnr) - else - inlay_hint.toggle(bufnr) - end -end - return M -- cgit From ba8f19ebb67ca27d746f4b1cd902ab3d807eace3 Mon Sep 17 00:00:00 2001 From: Raphael Date: Sat, 1 Jul 2023 18:42:37 +0800 Subject: fix(lsp): lint warnings, default offset_encoding #24046 - fix lint / analysis warnings - locations_to_items(): get default offset_encoding from active client - character_offset(): get default offset_encoding from active client --- runtime/lua/vim/lsp/buf.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 0369725216..c742505ec6 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -581,9 +581,9 @@ function M.document_highlight() end --- Removes document highlights from current buffer. ---- -function M.clear_references() - util.buf_clear_references() +--- @param bufnr integer|nil +function M.clear_references(bufnr) + util.buf_clear_references(bufnr or 0) end ---@private -- cgit From cf5f1492d702f940934b0b40024d1741e4474542 Mon Sep 17 00:00:00 2001 From: Raphael Date: Tue, 4 Jul 2023 20:30:31 +0800 Subject: fix(lsp): revert change to buf.clear_references() #24238 Problem: in #24046 the signature of buf.clear_references() changed, which indirectly breaks callers that were passing "ignored" args. Solution: because util.buf_clear_references() already defaulted to "current buffer", the change to buf.clear_references() isn't actually needed, so just revert it. --- runtime/lua/vim/lsp/buf.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index c742505ec6..bac66b671d 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -581,9 +581,8 @@ function M.document_highlight() end --- Removes document highlights from current buffer. ---- @param bufnr integer|nil -function M.clear_references(bufnr) - util.buf_clear_references(bufnr or 0) +function M.clear_references() + util.buf_clear_references() end ---@private -- cgit From 766f4978d6cb146511cf0b676c01e5327db46647 Mon Sep 17 00:00:00 2001 From: Raphael Date: Mon, 10 Jul 2023 19:38:15 +0800 Subject: fix(lint): lint warnings #24226 --- runtime/lua/vim/lsp/buf.lua | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index bac66b671d..8ae0e0cde3 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -13,10 +13,11 @@ local M = {} ---@param params (table|nil) Parameters to send to the server ---@param handler (function|nil) See |lsp-handler|. Follows |lsp-handler-resolution| -- ----@returns 2-tuple: ---- - Map of client-id:request-id pairs for all successful requests. ---- - Function which can be used to cancel all the requests. You could instead ---- iterate all clients and call their `cancel_request()` methods. +---@return table client_request_ids Map of client-id:request-id pairs +---for all successful requests. +---@return function _cancel_all_requests Function which can be used to +---cancel all the requests. You could instead +---iterate all clients and call their `cancel_request()` methods. --- ---@see |vim.lsp.buf_request()| local function request(method, params, handler) @@ -30,7 +31,7 @@ end --- Checks whether the language servers attached to the current buffer are --- ready. --- ----@returns `true` if server responds. +---@return boolean if server responds. ---@deprecated function M.server_ready() vim.deprecate('vim.lsp.buf.server_ready', nil, '0.10.0') @@ -108,7 +109,7 @@ end --- Retrieves the completion items at the current cursor position. Can only be --- called in Insert mode. --- ----@param context (context support not yet implemented) Additional information +---@param context table (context support not yet implemented) Additional information --- about the context in which a completion was triggered (how it was triggered, --- and by which trigger character, if applicable) --- @@ -549,7 +550,7 @@ end --- call, the user is prompted to enter a string on the command line. An empty --- string means no filtering is done. --- ----@param query (string, optional) +---@param query string|nil optional ---@param options table|nil additional options --- - on_list: (function) handler for list results. See |lsp-on-list-handler| function M.workspace_symbol(query, options) -- cgit From 317c80f460a7826421f40f57ee8bdbd736b0f225 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Wed, 12 Jul 2023 14:48:21 +0200 Subject: feat(lsp): add method filter to get_active_clients (#24319) --- runtime/lua/vim/lsp/buf.lua | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 8ae0e0cde3..b238b5c221 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -197,15 +197,6 @@ end function M.format(options) options = options or {} local bufnr = options.bufnr or api.nvim_get_current_buf() - local clients = vim.lsp.get_active_clients({ - id = options.id, - bufnr = bufnr, - name = options.name, - }) - - if options.filter then - clients = vim.tbl_filter(options.filter, clients) - end local mode = api.nvim_get_mode().mode local range = options.range @@ -214,9 +205,15 @@ function M.format(options) end local method = range and 'textDocument/rangeFormatting' or 'textDocument/formatting' - clients = vim.tbl_filter(function(client) - return client.supports_method(method) - end, clients) + local clients = vim.lsp.get_active_clients({ + id = options.id, + bufnr = bufnr, + name = options.name, + method = method, + }) + if options.filter then + clients = vim.tbl_filter(options.filter, clients) + end if #clients == 0 then vim.notify('[LSP] Format request failed, no matching language servers.') @@ -277,16 +274,13 @@ function M.rename(new_name, options) local clients = vim.lsp.get_active_clients({ bufnr = bufnr, name = options.name, + -- Clients must at least support rename, prepareRename is optional + method = 'textDocument/rename', }) if options.filter then clients = vim.tbl_filter(options.filter, clients) end - -- Clients must at least support rename, prepareRename is optional - clients = vim.tbl_filter(function(client) - return client.supports_method('textDocument/rename') - end, clients) - if #clients == 0 then vim.notify('[LSP] Rename, no matching language servers with rename capability.') end -- cgit From 1b9ccd38a12f8fdbdff51ef0b3ff363540f745ec Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Mon, 17 Jul 2023 18:27:16 +0200 Subject: feat(lsp)!: rename vim.lsp.get_active_clients to get_clients (#24113) --- runtime/lua/vim/lsp/buf.lua | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index b238b5c221..2140d3ae37 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -197,7 +197,6 @@ end function M.format(options) options = options or {} local bufnr = options.bufnr or api.nvim_get_current_buf() - local mode = api.nvim_get_mode().mode local range = options.range if not range and mode == 'v' or mode == 'V' then @@ -205,7 +204,7 @@ function M.format(options) end local method = range and 'textDocument/rangeFormatting' or 'textDocument/formatting' - local clients = vim.lsp.get_active_clients({ + local clients = vim.lsp.get_clients({ id = options.id, bufnr = bufnr, name = options.name, @@ -271,7 +270,7 @@ end function M.rename(new_name, options) options = options or {} local bufnr = options.bufnr or api.nvim_get_current_buf() - local clients = vim.lsp.get_active_clients({ + local clients = vim.lsp.get_clients({ bufnr = bufnr, name = options.name, -- Clients must at least support rename, prepareRename is optional @@ -468,7 +467,7 @@ end --- function M.list_workspace_folders() local workspace_folders = {} - for _, client in pairs(vim.lsp.get_active_clients({ bufnr = 0 })) do + for _, client in pairs(vim.lsp.get_clients({ bufnr = 0 })) do for _, folder in pairs(client.workspace_folders or {}) do table.insert(workspace_folders, folder.name) end @@ -493,7 +492,7 @@ function M.add_workspace_folder(workspace_folder) { { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } }, {} ) - for _, client in pairs(vim.lsp.get_active_clients({ bufnr = 0 })) do + for _, client in pairs(vim.lsp.get_clients({ bufnr = 0 })) do local found = false for _, folder in pairs(client.workspace_folders or {}) do if folder.name == workspace_folder then @@ -526,7 +525,7 @@ function M.remove_workspace_folder(workspace_folder) { {} }, { { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } } ) - for _, client in pairs(vim.lsp.get_active_clients({ bufnr = 0 })) do + for _, client in pairs(vim.lsp.get_clients({ bufnr = 0 })) do for idx, folder in pairs(client.workspace_folders or {}) do if folder.name == workspace_folder then vim.lsp.buf_notify(0, 'workspace/didChangeWorkspaceFolders', params) -- cgit From be74807eef13ff8c90d55cf8b22b01d6d33b1641 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 18 Jul 2023 15:42:30 +0100 Subject: docs(lua): more improvements (#24387) * docs(lua): teach lua2dox how to table * docs(lua): teach gen_vimdoc.py about local functions No more need to mark local functions with @private * docs(lua): mention @nodoc and @meta in dev-lua-doc * fixup! Co-authored-by: Justin M. Keyes --------- Co-authored-by: Justin M. Keyes --- runtime/lua/vim/lsp/buf.lua | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 2140d3ae37..46f582dc7e 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -5,7 +5,6 @@ local npcall = vim.F.npcall local M = {} ----@private --- Sends an async request to all active clients attached to the current --- buffer. --- @@ -45,7 +44,6 @@ function M.hover() request('textDocument/hover', params) end ----@private local function request_with_options(name, params, options) local req_handler if options then @@ -120,7 +118,6 @@ function M.completion(context) return request('textDocument/completion', params) end ----@private ---@param bufnr integer ---@param mode "v"|"V" ---@return table {start={row,col}, end={row,col}} using (1, 0) indexing @@ -218,7 +215,6 @@ function M.format(options) vim.notify('[LSP] Format request failed, no matching language servers.') end - ---@private local function set_range(client, params) if range then local range_params = @@ -289,7 +285,6 @@ function M.rename(new_name, options) -- Compute early to account for cursor movements after going async local cword = vim.fn.expand('') - ---@private local function get_text_at_range(range, offset_encoding) return api.nvim_buf_get_text( bufnr, @@ -307,7 +302,6 @@ function M.rename(new_name, options) return end - ---@private local function rename(name) local params = util.make_position_params(win, client.offset_encoding) params.newName = name @@ -408,7 +402,6 @@ function M.document_symbol(options) request_with_options('textDocument/documentSymbol', params, options) end ----@private local function pick_call_hierarchy_item(call_hierarchy_items) if not call_hierarchy_items then return @@ -428,7 +421,6 @@ local function pick_call_hierarchy_item(call_hierarchy_items) return choice end ----@private local function call_hierarchy(method) local params = util.make_position_params() request('textDocument/prepareCallHierarchy', params, function(err, result, ctx) @@ -579,8 +571,6 @@ function M.clear_references() util.buf_clear_references() end ----@private --- --- This is not public because the main extension point is --- vim.ui.select which can be overridden independently. --- @@ -592,7 +582,6 @@ end local function on_code_action_results(results, ctx, options) local action_tuples = {} - ---@private local function action_filter(a) -- filter by specified action kind if options and options.context and options.context.only then @@ -632,7 +621,6 @@ local function on_code_action_results(results, ctx, options) return end - ---@private local function apply_action(action, client) if action.edit then util.apply_workspace_edit(action.edit, client.offset_encoding) @@ -643,7 +631,6 @@ local function on_code_action_results(results, ctx, options) end end - ---@private local function on_user_choice(action_tuple) if not action_tuple then return @@ -701,7 +688,6 @@ end --- Requests code actions from all clients and calls the handler exactly once --- with all aggregated results ----@private local function code_action_request(params, options) local bufnr = api.nvim_get_current_buf() local method = 'textDocument/codeAction' -- cgit From a37d568082ad2a6fd479bc0584f1088b51019b7f Mon Sep 17 00:00:00 2001 From: Mitchell Hanberg Date: Mon, 24 Jul 2023 12:09:53 -0400 Subject: fix(lsp): send empty "added" list when removing workspace folder #24440 When adding `workspace/didChangeWorkspaceFolders` support to my [language server](https://github.com/elixir-tools/next-ls), I noticed that when neovim removes a workspace, it sends an empty table (which is serialized to an empty JSON array) for the value in the `added` field. This does not follow the spec; the `added` table should just be empty. The following error led me to this discovery. Note the payload includes `"added" => [[]]`: ``` 22:46:48.476 [error] LSP Exited. Last message received: handle_notification %{"jsonrpc" => "2.0", "method" => "workspace/didChangeWorkspaceFolders", "params" => %{"event" => %{"added" => [[]], "removed" => [%{"name" => "/Users/mitchell/src/gen_lsp", "uri" => "file:///Users/mitchell/src/gen_lsp"}]}}} ** (MatchError) no match of right hand side value: {:error, %{"params" => %{"event" => %{"added" => [error: "expected a map"]}}}} (gen_lsp 0.4.0) lib/gen_lsp.ex:265: anonymous fn/4 in GenLSP.loop/3 (gen_lsp 0.4.0) lib/gen_lsp.ex:292: GenLSP.attempt/3 (stdlib 5.0.2) proc_lib.erl:241: :proc_lib.init_p_do_apply/3 ``` --- runtime/lua/vim/lsp/buf.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 46f582dc7e..02d8d0fa7b 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -514,7 +514,7 @@ function M.remove_workspace_folder(workspace_folder) return end local params = util.make_workspace_params( - { {} }, + {}, { { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } } ) for _, client in pairs(vim.lsp.get_clients({ bufnr = 0 })) do -- cgit From 74bd4aba57d2f1b224abe46a6de82911d14ef6c1 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Tue, 25 Jul 2023 16:57:19 +0200 Subject: fix(lsp): fix multi client handling workspace_folder methods (#18839) `buf_notify` sends the notification to all clients of a buffer, calling that inside a loop over clients multiplies the amount of notifications. --- runtime/lua/vim/lsp/buf.lua | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 02d8d0fa7b..5e0e429021 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -480,11 +480,13 @@ function M.add_workspace_folder(workspace_folder) print(workspace_folder, ' is not a valid directory') return end - local params = util.make_workspace_params( - { { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } }, - {} - ) - for _, client in pairs(vim.lsp.get_clients({ bufnr = 0 })) do + 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() + 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 @@ -494,11 +496,11 @@ function M.add_workspace_folder(workspace_folder) end end if not found then - vim.lsp.buf_notify(0, 'workspace/didChangeWorkspaceFolders', params) + client.notify('workspace/didChangeWorkspaceFolders', params) if not client.workspace_folders then client.workspace_folders = {} end - table.insert(client.workspace_folders, params.event.added[1]) + table.insert(client.workspace_folders, new_workspace) end end end @@ -513,14 +515,16 @@ function M.remove_workspace_folder(workspace_folder) if not (workspace_folder and #workspace_folder > 0) then return end - local params = util.make_workspace_params( - {}, - { { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } } - ) - for _, client in pairs(vim.lsp.get_clients({ bufnr = 0 })) do - for idx, folder in pairs(client.workspace_folders or {}) do + 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() + 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 - vim.lsp.buf_notify(0, 'workspace/didChangeWorkspaceFolders', params) + client.notify('workspace/didChangeWorkspaceFolders', params) client.workspace_folders[idx] = nil return end -- cgit From f1772272b4fda43c093fc495f54b5e7c11968d62 Mon Sep 17 00:00:00 2001 From: Raphael Date: Thu, 3 Aug 2023 19:03:48 +0800 Subject: refactor(lsp): use protocol.Methods instead of strings #24537 --- runtime/lua/vim/lsp/buf.lua | 54 ++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 27 deletions(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 5e0e429021..59afaf6fa0 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -2,6 +2,7 @@ local api = vim.api local validate = vim.validate local util = require('vim.lsp.util') local npcall = vim.F.npcall +local ms = require('vim.lsp.protocol').Methods local M = {} @@ -41,7 +42,7 @@ end --- window. Calling the function twice will jump into the floating window. function M.hover() local params = util.make_position_params() - request('textDocument/hover', params) + request(ms.textDocument_hover, params) end local function request_with_options(name, params, options) @@ -64,7 +65,7 @@ end --- - on_list: (function) handler for list results. See |lsp-on-list-handler| function M.declaration(options) local params = util.make_position_params() - request_with_options('textDocument/declaration', params, options) + request_with_options(ms.textDocument_declaration, params, options) end --- Jumps to the definition of the symbol under the cursor. @@ -74,7 +75,7 @@ end --- - on_list: (function) handler for list results. See |lsp-on-list-handler| function M.definition(options) local params = util.make_position_params() - request_with_options('textDocument/definition', params, options) + request_with_options(ms.textDocument_definition, params, options) end --- Jumps to the definition of the type of the symbol under the cursor. @@ -84,7 +85,7 @@ end --- - on_list: (function) handler for list results. See |lsp-on-list-handler| function M.type_definition(options) local params = util.make_position_params() - request_with_options('textDocument/typeDefinition', params, options) + request_with_options(ms.textDocument_typeDefinition, params, options) end --- Lists all the implementations for the symbol under the cursor in the @@ -94,14 +95,14 @@ end --- - on_list: (function) handler for list results. See |lsp-on-list-handler| function M.implementation(options) local params = util.make_position_params() - request_with_options('textDocument/implementation', params, options) + request_with_options(ms.textDocument_implementation, params, options) end --- Displays signature information about the symbol under the cursor in a --- floating window. function M.signature_help() local params = util.make_position_params() - request('textDocument/signatureHelp', params) + request(ms.textDocument_signatureHelp, params) end --- Retrieves the completion items at the current cursor position. Can only be @@ -115,7 +116,7 @@ end function M.completion(context) local params = util.make_position_params() params.context = context - return request('textDocument/completion', params) + return request(ms.textDocument_completion, params) end ---@param bufnr integer @@ -199,7 +200,7 @@ function M.format(options) if not range and mode == 'v' or mode == 'V' then range = range_from_selection(bufnr, mode) end - local method = range and 'textDocument/rangeFormatting' or 'textDocument/formatting' + local method = range and ms.textDocument_rangeFormatting or ms.textDocument_formatting local clients = vim.lsp.get_clients({ id = options.id, @@ -270,7 +271,7 @@ function M.rename(new_name, options) bufnr = bufnr, name = options.name, -- Clients must at least support rename, prepareRename is optional - method = 'textDocument/rename', + method = ms.textDocument_rename, }) if options.filter then clients = vim.tbl_filter(options.filter, clients) @@ -305,17 +306,17 @@ function M.rename(new_name, options) local function rename(name) local params = util.make_position_params(win, client.offset_encoding) params.newName = name - local handler = client.handlers['textDocument/rename'] - or vim.lsp.handlers['textDocument/rename'] - client.request('textDocument/rename', params, function(...) + local handler = client.handlers[ms.textDocument_rename] + or vim.lsp.handlers[ms.textDocument_rename] + client.request(ms.textDocument_rename, params, function(...) handler(...) try_use_client(next(clients, idx)) end, bufnr) end - if client.supports_method('textDocument/prepareRename') then + if client.supports_method(ms.textDocument_prepareRename) then local params = util.make_position_params(win, client.offset_encoding) - client.request('textDocument/prepareRename', params, function(err, result) + client.request(ms.textDocument_prepareRename, params, function(err, result) if err or result == nil then if next(clients, idx) then try_use_client(next(clients, idx)) @@ -354,7 +355,7 @@ function M.rename(new_name, options) end, bufnr) else assert( - client.supports_method('textDocument/rename'), + client.supports_method(ms.textDocument_rename), 'Client must support textDocument/rename' ) if new_name then @@ -390,7 +391,7 @@ function M.references(context, options) params.context = context or { includeDeclaration = true, } - request_with_options('textDocument/references', params, options) + request_with_options(ms.textDocument_references, params, options) end --- Lists all symbols in the current buffer in the quickfix window. @@ -399,7 +400,7 @@ end --- - on_list: (function) handler for list results. See |lsp-on-list-handler| function M.document_symbol(options) local params = { textDocument = util.make_text_document_params() } - request_with_options('textDocument/documentSymbol', params, options) + request_with_options(ms.textDocument_documentSymbol, params, options) end local function pick_call_hierarchy_item(call_hierarchy_items) @@ -423,7 +424,7 @@ end local function call_hierarchy(method) local params = util.make_position_params() - request('textDocument/prepareCallHierarchy', params, function(err, result, ctx) + request(ms.textDocument_prepareCallHierarchy, params, function(err, result, ctx) if err then vim.notify(err.message, vim.log.levels.WARN) return @@ -496,7 +497,7 @@ function M.add_workspace_folder(workspace_folder) end end if not found then - client.notify('workspace/didChangeWorkspaceFolders', params) + client.notify(ms.workspace_didChangeWorkspaceFolders, params) if not client.workspace_folders then client.workspace_folders = {} end @@ -524,7 +525,7 @@ function M.remove_workspace_folder(workspace_folder) 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('workspace/didChangeWorkspaceFolders', params) + client.notify(ms.workspace_didChangeWorkspaceFolders, params) client.workspace_folders[idx] = nil return end @@ -548,7 +549,7 @@ function M.workspace_symbol(query, options) return end local params = { query = query } - request_with_options('workspace/symbol', params, options) + request_with_options(ms.workspace_symbol, params, options) end --- Send request to the server to resolve document highlights for the current @@ -567,7 +568,7 @@ end --- |hl-LspReferenceWrite| function M.document_highlight() local params = util.make_position_params() - request('textDocument/documentHighlight', params) + request(ms.textDocument_documentHighlight, params) end --- Removes document highlights from current buffer. @@ -655,7 +656,7 @@ local function on_code_action_results(results, ctx, options) local client = vim.lsp.get_client_by_id(action_tuple[1]) local action = action_tuple[2] - local reg = client.dynamic_capabilities:get('textDocument/codeAction', { bufnr = ctx.bufnr }) + local reg = client.dynamic_capabilities:get(ms.textDocument_codeAction, { bufnr = ctx.bufnr }) local supports_resolve = vim.tbl_get(reg or {}, 'registerOptions', 'resolveProvider') or client.supports_method('codeAction/resolve') @@ -694,9 +695,8 @@ end --- with all aggregated results local function code_action_request(params, options) local bufnr = api.nvim_get_current_buf() - local method = 'textDocument/codeAction' - vim.lsp.buf_request_all(bufnr, method, params, function(results) - local ctx = { bufnr = bufnr, method = method, params = params } + vim.lsp.buf_request_all(bufnr, ms.textDocument_codeAction, params, function(results) + local ctx = { bufnr = bufnr, method = ms.textDocument_codeAction, params = params } on_code_action_results(results, ctx, options) end) end @@ -776,7 +776,7 @@ function M.execute_command(command_params) arguments = command_params.arguments, workDoneToken = command_params.workDoneToken, } - request('workspace/executeCommand', command_params) + request(ms.workspace_executeCommand, command_params) end return M -- cgit From 42630923fc00633d806af97c1792b2ed4a71e1cc Mon Sep 17 00:00:00 2001 From: Raphael Date: Sat, 5 Aug 2023 17:03:57 +0800 Subject: refactor(lsp): use protocol.Methods instead of strings #24570 --- runtime/lua/vim/lsp/buf.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 59afaf6fa0..3b1654d11f 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -446,14 +446,14 @@ end --- |quickfix| window. If the symbol can resolve to multiple --- items, the user can pick one in the |inputlist()|. function M.incoming_calls() - call_hierarchy('callHierarchy/incomingCalls') + call_hierarchy(ms.callHierarchy_incomingCalls) end --- Lists all the items that are called by the symbol under the --- cursor in the |quickfix| window. If the symbol can resolve to --- multiple items, the user can pick one in the |inputlist()|. function M.outgoing_calls() - call_hierarchy('callHierarchy/outgoingCalls') + call_hierarchy(ms.callHierarchy_outgoingCalls) end --- List workspace folders. @@ -659,10 +659,10 @@ local function on_code_action_results(results, ctx, options) local reg = client.dynamic_capabilities:get(ms.textDocument_codeAction, { bufnr = ctx.bufnr }) local supports_resolve = vim.tbl_get(reg or {}, 'registerOptions', 'resolveProvider') - or client.supports_method('codeAction/resolve') + or client.supports_method(ms.codeAction_resolve) if not action.edit and client and supports_resolve then - client.request('codeAction/resolve', action, function(err, resolved_action) + client.request(ms.codeAction_resolve, action, function(err, resolved_action) if err then vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR) return -- cgit From 832459219b4cbf3151e48b43187f1ab0c94ea285 Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Mon, 7 Aug 2023 06:35:06 -0700 Subject: docs(lsp): fix references to protocol.constants #24578 --- runtime/lua/vim/lsp/buf.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 3b1654d11f..6cd0aa1e95 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -112,7 +112,7 @@ end --- about the context in which a completion was triggered (how it was triggered, --- and by which trigger character, if applicable) --- ----@see vim.lsp.protocol.constants.CompletionTriggerKind +---@see vim.lsp.protocol.CompletionTriggerKind function M.completion(context) local params = util.make_position_params() params.context = context @@ -728,7 +728,7 @@ end --- using mark-like indexing. See |api-indexing| --- ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction ----@see vim.lsp.protocol.constants.CodeActionTriggerKind +---@see vim.lsp.protocol.CodeActionTriggerKind function M.code_action(options) validate({ options = { options, 't', true } }) options = options or {} -- cgit From 2e92065686f62851318150a315591c30b8306a4b Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Thu, 14 Sep 2023 08:23:01 -0500 Subject: docs: replace
 with ``` (#25136)

---
 runtime/lua/vim/lsp/buf.lua | 23 +++++++++++------------
 1 file changed, 11 insertions(+), 12 deletions(-)

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

diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index 6cd0aa1e95..8a29fac2b5 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -168,13 +168,11 @@ end
 ---
 ---     - filter (function|nil):
 ---         Predicate used to filter clients. Receives a client as argument and must return a
----         boolean. Clients matching the predicate are included. Example:
----
----         
lua
----           -- Never request typescript-language-server for formatting
----           vim.lsp.buf.format {
----             filter = function(client) return client.name ~= "tsserver" end
----           }
+---         boolean. Clients matching the predicate are included. Example: 
lua
+---                     -- Never request typescript-language-server for formatting
+---                     vim.lsp.buf.format {
+---                       filter = function(client) return client.name ~= "tsserver" end
+---                     }
 ---         
--- --- - async boolean|nil @@ -555,11 +553,12 @@ end --- Send request to the server to resolve document highlights for the current --- text document position. This request can be triggered by a key mapping or --- by events such as `CursorHold`, e.g.: ----
vim
----   autocmd CursorHold   lua vim.lsp.buf.document_highlight()
----   autocmd CursorHoldI  lua vim.lsp.buf.document_highlight()
----   autocmd CursorMoved  lua vim.lsp.buf.clear_references()
---- 
+--- +--- ```vim +--- autocmd CursorHold lua vim.lsp.buf.document_highlight() +--- autocmd CursorHoldI lua vim.lsp.buf.document_highlight() +--- autocmd CursorMoved lua vim.lsp.buf.clear_references() +--- ``` --- --- Note: Usage of |vim.lsp.buf.document_highlight()| requires the following highlight groups --- to be defined or you won't be able to see the actual highlights. -- cgit From 4a09c178a19097c295521892c889f1f196fff100 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Mon, 2 Oct 2023 22:14:19 +0200 Subject: feat(lsp): fallback to code-action command on resolve failure (#25464) The haskell-language-server supports resolve only for a subset of code actions. For many code actions trying to resolve the `edit` property results in an error, but the unresolved action already contains a command that can be executed without issue. The protocol specification is unfortunately a bit vague about this, and what the haskell-language-server does seems to be valid. Example: newtype Dummy = Dummy Int instance Num Dummy where Triggering code actions on "Num Dummy" and choosing "Add placeholders for all missing methods" resulted in: -32601: No plugin enabled for SMethod_CodeActionResolve, potentially available: explicit-fields, importLens, hlint, overloaded-record-dot With this change it will insert the missing methods: instance Num Dummy where (+) = _ (-) = _ (*) = _ negate = _ abs = _ signum = _ fromInteger = _ --- runtime/lua/vim/lsp/buf.lua | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 8a29fac2b5..a906512e24 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -652,7 +652,7 @@ local function on_code_action_results(results, ctx, options) -- arguments?: any[] -- ---@type lsp.Client - local client = vim.lsp.get_client_by_id(action_tuple[1]) + local client = assert(vim.lsp.get_client_by_id(action_tuple[1])) local action = action_tuple[2] local reg = client.dynamic_capabilities:get(ms.textDocument_codeAction, { bufnr = ctx.bufnr }) @@ -663,10 +663,14 @@ local function on_code_action_results(results, ctx, options) if not action.edit and client and supports_resolve then client.request(ms.codeAction_resolve, action, function(err, resolved_action) if err then - vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR) - return + if action.command then + apply_action(action, client) + else + vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR) + end + else + apply_action(resolved_action, client) end - apply_action(resolved_action, client) end, ctx.bufnr) else apply_action(action, client) -- cgit From c46a6c065e8d830adb8a2f410d3c92cf5bd4455b Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Mon, 16 Oct 2023 08:13:37 -0700 Subject: docs: do not hardcode LSP version in URL #25648 --- runtime/lua/vim/lsp/buf.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index a906512e24..9436fbbf56 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -159,7 +159,7 @@ end --- - formatting_options (table|nil): --- Can be used to specify FormattingOptions. Some unspecified options will be --- automatically derived from the current Nvim options. ---- See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#formattingOptions +--- See https://microsoft.github.io/language-server-protocol/specification/#formattingOptions --- - timeout_ms (integer|nil, default 1000): --- Time in milliseconds to block for formatting requests. No effect if async=true --- - bufnr (number|nil): -- cgit From adbe7f368397da21465f27181e254dd3694820e9 Mon Sep 17 00:00:00 2001 From: Evgeni Chasnovski Date: Tue, 31 Oct 2023 14:18:44 +0200 Subject: fix(lsp): call `on_list()` even for single location (#25830) Problem: Currently there is no way of customizing behavior of `declaration`, `definition`, `typeDefinition`, and `implementation` methods in `vim.lsp.buf` when LSP server returns `Location`. Instead, cursor jumps to that location directly. Solution: Normalize LSP response to be `Location[]` for those four cases. --- runtime/lua/vim/lsp/buf.lua | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 9436fbbf56..b9b7aefae6 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -62,7 +62,8 @@ end --- ---@param options table|nil additional options --- - reuse_win: (boolean) Jump to existing window if buffer is already open. ---- - on_list: (function) handler for list results. See |lsp-on-list-handler| +--- - on_list: (function) |lsp-on-list-handler| replacing the default handler. +--- Called for any non-empty result. function M.declaration(options) local params = util.make_position_params() request_with_options(ms.textDocument_declaration, params, options) @@ -72,7 +73,8 @@ end --- ---@param options table|nil additional options --- - reuse_win: (boolean) Jump to existing window if buffer is already open. ---- - on_list: (function) handler for list results. See |lsp-on-list-handler| +--- - on_list: (function) |lsp-on-list-handler| replacing the default handler. +--- Called for any non-empty result. function M.definition(options) local params = util.make_position_params() request_with_options(ms.textDocument_definition, params, options) @@ -82,7 +84,8 @@ end --- ---@param options table|nil additional options --- - reuse_win: (boolean) Jump to existing window if buffer is already open. ---- - on_list: (function) handler for list results. See |lsp-on-list-handler| +--- - on_list: (function) |lsp-on-list-handler| replacing the default handler. +--- Called for any non-empty result. function M.type_definition(options) local params = util.make_position_params() request_with_options(ms.textDocument_typeDefinition, params, options) @@ -92,7 +95,8 @@ end --- quickfix window. --- ---@param options table|nil additional options ---- - on_list: (function) handler for list results. See |lsp-on-list-handler| +--- - on_list: (function) |lsp-on-list-handler| replacing the default handler. +--- Called for any non-empty result. function M.implementation(options) local params = util.make_position_params() request_with_options(ms.textDocument_implementation, params, options) -- cgit From 9281edb334a374e7753d4a6b7a05e31120e39772 Mon Sep 17 00:00:00 2001 From: Mathias Fussenegger Date: Thu, 2 Nov 2023 09:59:41 +0100 Subject: fix(lsp): create per client params in lsp.buf.code_action `code_action` used the same parameters for all clients, which led to the following warning and incorrect start/end column locations if using clients with mixed encodings: warning: multiple different client offset_encodings detected for buffer, this is not supported yet --- runtime/lua/vim/lsp/buf.lua | 141 ++++++++++++++++++++++++++++---------------- 1 file changed, 91 insertions(+), 50 deletions(-) (limited to 'runtime/lua/vim/lsp/buf.lua') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index b9b7aefae6..cf9acc0808 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -579,6 +579,17 @@ function M.clear_references() util.buf_clear_references() end +---@class vim.lsp.CodeActionResultEntry +---@field error? lsp.ResponseError +---@field result? (lsp.Command|lsp.CodeAction)[] +---@field ctx lsp.HandlerContext + +---@class vim.lsp.buf.code_action.opts +---@field context? lsp.CodeActionContext +---@field filter? fun(x: lsp.CodeAction|lsp.Command):boolean +---@field apply? boolean +---@field range? {start: integer[], end: integer[]} + --- This is not public because the main extension point is --- vim.ui.select which can be overridden independently. --- @@ -587,17 +598,18 @@ end --- from multiple clients to have 1 single UI prompt for the user, yet we still --- need to be able to link a `CodeAction|Command` to the right client for --- `codeAction/resolve` -local function on_code_action_results(results, ctx, options) - local action_tuples = {} - +---@param results table +---@param opts? vim.lsp.buf.code_action.opts +local function on_code_action_results(results, opts) + ---@param a lsp.Command|lsp.CodeAction local function action_filter(a) -- filter by specified action kind - if options and options.context and options.context.only then + if opts and opts.context and opts.context.only then if not a.kind then return false end local found = false - for _, o in ipairs(options.context.only) do + for _, o in ipairs(opts.context.only) do -- action kinds are hierarchical with . as a separator: when requesting only 'type-annotate' -- this filter allows both 'type-annotate' and 'type-annotate.foo', for example if a.kind == o or vim.startswith(a.kind, o .. '.') then @@ -610,26 +622,31 @@ local function on_code_action_results(results, ctx, options) end end -- filter by user function - if options and options.filter and not options.filter(a) then + if opts and opts.filter and not opts.filter(a) then return false end -- no filter removed this action return true end - for client_id, result in pairs(results) do + ---@type {action: lsp.Command|lsp.CodeAction, ctx: lsp.HandlerContext}[] + local actions = {} + for _, result in pairs(results) do for _, action in pairs(result.result or {}) do if action_filter(action) then - table.insert(action_tuples, { client_id, action }) + table.insert(actions, { action = action, ctx = result.ctx }) end end end - if #action_tuples == 0 then + if #actions == 0 then vim.notify('No code actions available', vim.log.levels.INFO) return end - local function apply_action(action, client) + ---@param action lsp.Command|lsp.CodeAction + ---@param client lsp.Client + ---@param ctx lsp.HandlerContext + local function apply_action(action, client, ctx) if action.edit then util.apply_workspace_edit(action.edit, client.offset_encoding) end @@ -639,8 +656,9 @@ local function on_code_action_results(results, ctx, options) end end - local function on_user_choice(action_tuple) - if not action_tuple then + ---@param choice {action: lsp.Command|lsp.CodeAction, ctx: lsp.HandlerContext} + local function on_user_choice(choice) + if not choice then return end -- textDocument/codeAction can return either Command[] or CodeAction[] @@ -656,10 +674,11 @@ local function on_code_action_results(results, ctx, options) -- arguments?: any[] -- ---@type lsp.Client - local client = assert(vim.lsp.get_client_by_id(action_tuple[1])) - local action = action_tuple[2] + local client = assert(vim.lsp.get_client_by_id(choice.ctx.client_id)) + local action = choice.action + local bufnr = assert(choice.ctx.bufnr, 'Must have buffer number') - local reg = client.dynamic_capabilities:get(ms.textDocument_codeAction, { bufnr = ctx.bufnr }) + local reg = client.dynamic_capabilities:get(ms.textDocument_codeAction, { bufnr = bufnr }) local supports_resolve = vim.tbl_get(reg or {}, 'registerOptions', 'resolveProvider') or client.supports_method(ms.codeAction_resolve) @@ -668,44 +687,37 @@ local function on_code_action_results(results, ctx, options) client.request(ms.codeAction_resolve, action, function(err, resolved_action) if err then if action.command then - apply_action(action, client) + apply_action(action, client, choice.ctx) else vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR) end else - apply_action(resolved_action, client) + apply_action(resolved_action, client, choice.ctx) end - end, ctx.bufnr) + end, bufnr) else - apply_action(action, client) + apply_action(action, client, choice.ctx) end end -- If options.apply is given, and there are just one remaining code action, -- apply it directly without querying the user. - if options and options.apply and #action_tuples == 1 then - on_user_choice(action_tuples[1]) + if opts and opts.apply and #actions == 1 then + on_user_choice(actions[1]) return end - vim.ui.select(action_tuples, { + ---@param item {action: lsp.Command|lsp.CodeAction} + local function format_item(item) + local title = item.action.title:gsub('\r\n', '\\r\\n') + return title:gsub('\n', '\\n') + end + local select_opts = { prompt = 'Code actions:', kind = 'codeaction', - format_item = function(action_tuple) - local title = action_tuple[2].title:gsub('\r\n', '\\r\\n') - return title:gsub('\n', '\\n') - end, - }, on_user_choice) -end - ---- Requests code actions from all clients and calls the handler exactly once ---- with all aggregated results -local function code_action_request(params, options) - local bufnr = api.nvim_get_current_buf() - vim.lsp.buf_request_all(bufnr, ms.textDocument_codeAction, params, function(results) - local ctx = { bufnr = bufnr, method = ms.textDocument_codeAction, params = params } - on_code_action_results(results, ctx, options) - end) + format_item = format_item, + } + vim.ui.select(actions, select_opts, on_user_choice) end --- Selects a code action available at the current @@ -752,21 +764,50 @@ function M.code_action(options) local bufnr = api.nvim_get_current_buf() context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics(bufnr) end - local params local mode = api.nvim_get_mode().mode - 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') - params = util.make_given_range_params(start, end_) - elseif mode == 'v' or mode == 'V' then - local range = range_from_selection(0, mode) - params = util.make_given_range_params(range.start, range['end']) - else - params = util.make_range_params() + local bufnr = api.nvim_get_current_buf() + local win = api.nvim_get_current_win() + local clients = vim.lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_codeAction }) + local remaining = #clients + if remaining == 0 then + if next(vim.lsp.get_clients({ bufnr = bufnr })) then + vim.notify(vim.lsp._unsupported_method(ms.textDocument_codeAction), vim.log.levels.WARN) + end + return + end + + ---@type table + local results = {} + + ---@param err? lsp.ResponseError + ---@param result? (lsp.Command|lsp.CodeAction)[] + ---@param ctx lsp.HandlerContext + local function on_result(err, result, ctx) + results[ctx.client_id] = { error = err, result = result, ctx = ctx } + remaining = remaining - 1 + if remaining == 0 then + on_code_action_results(results, options) + 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') + 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) + params = + util.make_given_range_params(range.start, range['end'], bufnr, client.offset_encoding) + else + params = util.make_range_params(win, client.offset_encoding) + end + params.context = context + client.request(ms.textDocument_codeAction, params, on_result, bufnr) end - params.context = context - code_action_request(params, options) end --- Executes an LSP server command. -- cgit