diff options
Diffstat (limited to 'runtime/lua/vim/lsp')
-rw-r--r-- | runtime/lua/vim/lsp/buf.lua | 19 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/diagnostic.lua | 141 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/handlers.lua | 33 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/util.lua | 1 |
4 files changed, 168 insertions, 26 deletions
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index ced1747ee0..29f8d6c3bc 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -422,6 +422,21 @@ function M.clear_references() util.buf_clear_references() end +--- Requests code actions from all clients and calls the handler exactly once +--- with all aggregated results +--@private +local function code_action_request(params) + local bufnr = vim.api.nvim_get_current_buf() + local method = 'textDocument/codeAction' + vim.lsp.buf_request_all(bufnr, method, params, function(results) + local actions = {} + for _, r in pairs(results) do + vim.list_extend(actions, r.result or {}) + end + vim.lsp.handlers[method](nil, method, actions, nil, bufnr) + end) +end + --- Selects a code action from the input list that is available at the current --- cursor position. -- @@ -432,7 +447,7 @@ function M.code_action(context) context = context or { diagnostics = vim.lsp.diagnostic.get_line_diagnostics() } local params = util.make_range_params() params.context = context - request('textDocument/codeAction', params) + code_action_request(params) end --- Performs |vim.lsp.buf.code_action()| for a given range. @@ -447,7 +462,7 @@ function M.range_code_action(context, start_pos, end_pos) context = context or { diagnostics = vim.lsp.diagnostic.get_line_diagnostics() } local params = util.make_given_range_params(start_pos, end_pos) params.context = context - request('textDocument/codeAction', params) + code_action_request(params) end --- Executes an LSP server command. diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 1342df529f..120320becc 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -208,6 +208,9 @@ local diagnostic_cache_lines = setmetatable({}, bufnr_and_client_cacher_mt) local diagnostic_cache_counts = setmetatable({}, bufnr_and_client_cacher_mt) local diagnostic_attached_buffers = {} +-- Disabled buffers and clients +local diagnostic_disabled = setmetatable({}, bufnr_and_client_cacher_mt) + local _bufs_waiting_to_update = setmetatable({}, bufnr_and_client_cacher_mt) --- Store Diagnostic[] by line @@ -816,10 +819,7 @@ end ---@param diagnostic_ns number|nil Associated diagnostic namespace ---@param sign_ns number|nil Associated sign namespace function M.clear(bufnr, client_id, diagnostic_ns, sign_ns) - validate { bufnr = { bufnr, 'n' } } - - bufnr = (bufnr == 0 and api.nvim_get_current_buf()) or bufnr - + bufnr = get_bufnr(bufnr) if client_id == nil then return vim.lsp.for_each_buffer_client(bufnr, function(_, iter_client_id, _) return M.clear(bufnr, iter_client_id) @@ -1092,6 +1092,10 @@ end --@private --- Display diagnostics for the buffer, given a configuration. function M.display(diagnostics, bufnr, client_id, config) + if diagnostic_disabled[bufnr][client_id] then + return + end + config = vim.lsp._with_extend('vim.lsp.diagnostic.on_publish_diagnostics', { signs = true, underline = true, @@ -1164,6 +1168,40 @@ function M.display(diagnostics, bufnr, client_id, config) save_extmarks(bufnr, client_id) end +--- Redraw diagnostics for the given buffer and client +--- +--- This calls the "textDocument/publishDiagnostics" handler manually using +--- the cached diagnostics already received from the server. This can be useful +--- for redrawing diagnostics after making changes in diagnostics +--- configuration. |lsp-handler-configuration| +--- +--- @param bufnr (optional, number): Buffer handle, defaults to current +--- @param client_id (optional, number): Redraw diagnostics for the given +--- client. The default is to redraw diagnostics for all attached +--- clients. +function M.redraw(bufnr, client_id) + bufnr = get_bufnr(bufnr) + if not client_id then + return vim.lsp.for_each_buffer_client(bufnr, function(client) + M.redraw(bufnr, client.id) + end) + end + + -- We need to invoke the publishDiagnostics handler directly instead of just + -- calling M.display so that we can preserve any custom configuration options + -- the user may have set with vim.lsp.with. + vim.lsp.handlers["textDocument/publishDiagnostics"]( + nil, + "textDocument/publishDiagnostics", + { + uri = vim.uri_from_bufnr(bufnr), + diagnostics = M.get(bufnr, client_id), + }, + client_id, + bufnr + ) +end + -- }}} -- Diagnostic User Functions {{{ @@ -1245,10 +1283,10 @@ function M.reset(client_id, buffer_client_map) end) end ---- Sets the location list +--- Gets diagnostics, converts them to quickfix/location list items, and applies the item_handler callback to the items. +---@param item_handler function Callback to apply to the diagnostic items +---@param command string|nil Command to execute after applying the item_handler ---@param opts table|nil Configuration table. Keys: ---- - {open_loclist}: (boolean, default true) ---- - Open loclist after set --- - {client_id}: (number) --- - If nil, will consider all clients attached to buffer. --- - {severity}: (DiagnosticSeverity) @@ -1257,9 +1295,8 @@ end --- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. --- - {workspace}: (boolean, default false) --- - Set the list with workspace diagnostics -function M.set_loclist(opts) +local function apply_to_diagnostic_items(item_handler, command, opts) opts = opts or {} - local open_loclist = if_nil(opts.open_loclist, true) local current_bufnr = api.nvim_get_current_buf() local diags = opts.workspace and M.get_all(opts.client_id) or { [current_bufnr] = M.get(current_bufnr, opts.client_id) @@ -1276,11 +1313,89 @@ function M.set_loclist(opts) return true end local items = util.diagnostics_to_items(diags, predicate) - local win_id = vim.api.nvim_get_current_win() - util.set_loclist(items, win_id) - if open_loclist then - vim.cmd [[lopen]] + item_handler(items) + if command then + vim.cmd(command) + end +end + +--- Sets the quickfix list +---@param opts table|nil Configuration table. Keys: +--- - {open}: (boolean, default true) +--- - Open quickfix list after set +--- - {client_id}: (number) +--- - If nil, will consider all clients attached to buffer. +--- - {severity}: (DiagnosticSeverity) +--- - Exclusive severity to consider. Overrides {severity_limit} +--- - {severity_limit}: (DiagnosticSeverity) +--- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. +--- - {workspace}: (boolean, default true) +--- - Set the list with workspace diagnostics +function M.set_qflist(opts) + opts = opts or {} + opts.workspace = if_nil(opts.workspace, true) + local open_qflist = if_nil(opts.open, true) + local command = open_qflist and [[copen]] or nil + apply_to_diagnostic_items(util.set_qflist, command, opts) +end + +--- Sets the location list +---@param opts table|nil Configuration table. Keys: +--- - {open}: (boolean, default true) +--- - Open loclist after set +--- - {client_id}: (number) +--- - If nil, will consider all clients attached to buffer. +--- - {severity}: (DiagnosticSeverity) +--- - Exclusive severity to consider. Overrides {severity_limit} +--- - {severity_limit}: (DiagnosticSeverity) +--- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. +--- - {workspace}: (boolean, default false) +--- - Set the list with workspace diagnostics +function M.set_loclist(opts) + opts = opts or {} + local open_loclist = if_nil(opts.open, true) + local command = open_loclist and [[lopen]] or nil + apply_to_diagnostic_items(util.set_loclist, command, opts) +end + +--- Disable diagnostics for the given buffer and client +--- @param bufnr (optional, number): Buffer handle, defaults to current +--- @param client_id (optional, number): Disable diagnostics for the given +--- client. The default is to disable diagnostics for all attached +--- clients. +-- Note that when diagnostics are disabled for a buffer, the server will still +-- send diagnostic information and the client will still process it. The +-- diagnostics are simply not displayed to the user. +function M.disable(bufnr, client_id) + if not client_id then + return vim.lsp.for_each_buffer_client(bufnr, function(client) + M.disable(bufnr, client.id) + end) end + + diagnostic_disabled[bufnr][client_id] = true + M.clear(bufnr, client_id) +end + +--- Enable diagnostics for the given buffer and client +--- @param bufnr (optional, number): Buffer handle, defaults to current +--- @param client_id (optional, number): Enable diagnostics for the given +--- client. The default is to enable diagnostics for all attached +--- clients. +function M.enable(bufnr, client_id) + if not client_id then + return vim.lsp.for_each_buffer_client(bufnr, function(client) + M.enable(bufnr, client.id) + end) + end + + if not diagnostic_disabled[bufnr][client_id] then + return + end + + diagnostic_disabled[bufnr][client_id] = nil + + M.redraw(bufnr, client_id) end -- }}} diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index acd20a3e0b..a77c88e2dc 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -190,30 +190,41 @@ end --@private ---- Return a function that converts LSP responses to quickfix items and opens the qflist --- ---@param map_result function `((resp, bufnr) -> list)` to convert the response ---@param entity name of the resource used in a `not found` error message -local function response_to_qflist(map_result, entity) - return function(_, _, result, _, bufnr) +--- 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) +--- +--- @param map_result function `((resp, bufnr) -> list)` to convert the response +--- @param entity name of the resource used in a `not found` error message +local function response_to_list(map_result, entity) + return function(_, _, result, _, bufnr, config) if not result or vim.tbl_isempty(result) then vim.notify('No ' .. entity .. ' found') else - util.set_qflist(map_result(result, bufnr)) - api.nvim_command("copen") + config = config or {} + if config.loclist then + util.set_loclist(map_result(result, bufnr)) + api.nvim_command("lopen") + else + util.set_qflist(map_result(result, bufnr)) + api.nvim_command("copen") + end end end end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references -M['textDocument/references'] = response_to_qflist(util.locations_to_items, 'references') +M['textDocument/references'] = response_to_list(util.locations_to_items, 'references') --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol -M['textDocument/documentSymbol'] = response_to_qflist(util.symbols_to_items, 'document symbols') +M['textDocument/documentSymbol'] = response_to_list(util.symbols_to_items, 'document symbols') --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_symbol -M['workspace/symbol'] = response_to_qflist(util.symbols_to_items, 'symbols') +M['workspace/symbol'] = response_to_list(util.symbols_to_items, 'symbols') --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rename M['textDocument/rename'] = function(_, _, result) diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 1ea974dffa..dc15d67e1c 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -990,6 +990,7 @@ function M.make_floating_popup_options(width, height, opts) style = 'minimal', width = width, border = opts.border or default_border, + zindex = opts.zindex or 50, } end |