aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/lsp
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/lua/vim/lsp')
-rw-r--r--runtime/lua/vim/lsp/buf.lua19
-rw-r--r--runtime/lua/vim/lsp/diagnostic.lua141
-rw-r--r--runtime/lua/vim/lsp/handlers.lua33
-rw-r--r--runtime/lua/vim/lsp/util.lua1
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