mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 03:18:16 +00:00
feat(lsp): workspace diagnostic support (#34262)
* refactor(lsp): remove underscore prefix from local variables * feat(lsp): workspace diagnostic support
This commit is contained in:

committed by
GitHub

parent
d75ffa5934
commit
cb4559bc32
@@ -1855,6 +1855,18 @@ typehierarchy({kind}) *vim.lsp.buf.typehierarchy()*
|
|||||||
Parameters: ~
|
Parameters: ~
|
||||||
• {kind} (`"subtypes"|"supertypes"`)
|
• {kind} (`"subtypes"|"supertypes"`)
|
||||||
|
|
||||||
|
workspace_diagnostics({opts}) *vim.lsp.buf.workspace_diagnostics()*
|
||||||
|
Request workspace-wide diagnostics.
|
||||||
|
|
||||||
|
Parameters: ~
|
||||||
|
• {opts} (`table?`) A table with the following fields:
|
||||||
|
• {client_id}? (`integer`) Only request diagnostics from the
|
||||||
|
indicated client. If nil, the request is sent to all
|
||||||
|
clients.
|
||||||
|
|
||||||
|
See also: ~
|
||||||
|
• https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_dagnostics
|
||||||
|
|
||||||
workspace_symbol({query}, {opts}) *vim.lsp.buf.workspace_symbol()*
|
workspace_symbol({query}, {opts}) *vim.lsp.buf.workspace_symbol()*
|
||||||
Lists all symbols in the current workspace in the quickfix window.
|
Lists all symbols in the current workspace in the quickfix window.
|
||||||
|
|
||||||
|
@@ -165,6 +165,8 @@ LSP
|
|||||||
non-applicable LSP clients.
|
non-applicable LSP clients.
|
||||||
• |vim.lsp.is_enabled()| checks if a LSP config is enabled (without
|
• |vim.lsp.is_enabled()| checks if a LSP config is enabled (without
|
||||||
"resolving" it).
|
"resolving" it).
|
||||||
|
• Support for `workspace/diagnostic`: |vim.lsp.buf.workspace_diagnostics()|
|
||||||
|
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_dagnostics
|
||||||
|
|
||||||
LUA
|
LUA
|
||||||
|
|
||||||
|
@@ -1015,6 +1015,21 @@ function M.workspace_symbol(query, opts)
|
|||||||
request_with_opts(ms.workspace_symbol, params, opts)
|
request_with_opts(ms.workspace_symbol, params, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- @class vim.lsp.WorkspaceDiagnosticsOpts
|
||||||
|
--- @inlinedoc
|
||||||
|
---
|
||||||
|
--- Only request diagnostics from the indicated client. If nil, the request is sent to all clients.
|
||||||
|
--- @field client_id? integer
|
||||||
|
|
||||||
|
--- Request workspace-wide diagnostics.
|
||||||
|
--- @param opts? vim.lsp.WorkspaceDiagnosticsOpts
|
||||||
|
--- @see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_dagnostics
|
||||||
|
function M.workspace_diagnostics(opts)
|
||||||
|
vim.validate('opts', opts, 'table', true)
|
||||||
|
|
||||||
|
lsp.diagnostic._workspace_diagnostics(opts or {})
|
||||||
|
end
|
||||||
|
|
||||||
--- Send request to the server to resolve document highlights for the current
|
--- 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
|
--- text document position. This request can be triggered by a key mapping or
|
||||||
--- by events such as `CursorHold`, e.g.:
|
--- by events such as `CursorHold`, e.g.:
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
local protocol = require('vim.lsp.protocol')
|
local lsp = vim.lsp
|
||||||
|
local protocol = lsp.protocol
|
||||||
local ms = protocol.Methods
|
local ms = protocol.Methods
|
||||||
local util = vim.lsp.util
|
local util = lsp.util
|
||||||
|
|
||||||
local api = vim.api
|
local api = vim.api
|
||||||
|
|
||||||
@@ -9,10 +10,10 @@ local M = {}
|
|||||||
local augroup = api.nvim_create_augroup('nvim.lsp.diagnostic', {})
|
local augroup = api.nvim_create_augroup('nvim.lsp.diagnostic', {})
|
||||||
|
|
||||||
---@class (private) vim.lsp.diagnostic.BufState
|
---@class (private) vim.lsp.diagnostic.BufState
|
||||||
---@field enabled boolean Whether diagnostics are enabled for this buffer
|
---@field pull_kind 'document'|'workspace'|'disabled' Whether diagnostics are being updated via document pull, workspace pull, or disabled.
|
||||||
---@field client_result_id table<integer, string?> Latest responded `resultId`
|
---@field client_result_id table<integer, string?> Latest responded `resultId`
|
||||||
|
|
||||||
---@type table<integer,vim.lsp.diagnostic.BufState>
|
---@type table<integer, vim.lsp.diagnostic.BufState>
|
||||||
local bufstates = {}
|
local bufstates = {}
|
||||||
|
|
||||||
local DEFAULT_CLIENT_ID = -1
|
local DEFAULT_CLIENT_ID = -1
|
||||||
@@ -38,11 +39,11 @@ end
|
|||||||
---@param bufnr integer
|
---@param bufnr integer
|
||||||
---@return string[]?
|
---@return string[]?
|
||||||
local function get_buf_lines(bufnr)
|
local function get_buf_lines(bufnr)
|
||||||
if vim.api.nvim_buf_is_loaded(bufnr) then
|
if api.nvim_buf_is_loaded(bufnr) then
|
||||||
return vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
return api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
local filename = vim.api.nvim_buf_get_name(bufnr)
|
local filename = api.nvim_buf_get_name(bufnr)
|
||||||
local f = io.open(filename)
|
local f = io.open(filename)
|
||||||
if not f then
|
if not f then
|
||||||
return
|
return
|
||||||
@@ -74,7 +75,7 @@ local function tags_lsp_to_vim(diagnostic, client_id)
|
|||||||
tags = tags or {}
|
tags = tags or {}
|
||||||
tags.deprecated = true
|
tags.deprecated = true
|
||||||
else
|
else
|
||||||
vim.lsp.log.info(string.format('Unknown DiagnosticTag %d from LSP client %d', tag, client_id))
|
lsp.log.info(string.format('Unknown DiagnosticTag %d from LSP client %d', tag, client_id))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return tags
|
return tags
|
||||||
@@ -86,7 +87,7 @@ end
|
|||||||
---@return vim.Diagnostic.Set[]
|
---@return vim.Diagnostic.Set[]
|
||||||
local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)
|
local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)
|
||||||
local buf_lines = get_buf_lines(bufnr)
|
local buf_lines = get_buf_lines(bufnr)
|
||||||
local client = vim.lsp.get_client_by_id(client_id)
|
local client = lsp.get_client_by_id(client_id)
|
||||||
local position_encoding = client and client.offset_encoding or 'utf-16'
|
local position_encoding = client and client.offset_encoding or 'utf-16'
|
||||||
--- @param diagnostic lsp.Diagnostic
|
--- @param diagnostic lsp.Diagnostic
|
||||||
--- @return vim.Diagnostic.Set
|
--- @return vim.Diagnostic.Set
|
||||||
@@ -97,7 +98,7 @@ local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)
|
|||||||
if type(message) ~= 'string' then
|
if type(message) ~= 'string' then
|
||||||
vim.notify_once(
|
vim.notify_once(
|
||||||
string.format('Unsupported Markup message from LSP client %d', client_id),
|
string.format('Unsupported Markup message from LSP client %d', client_id),
|
||||||
vim.lsp.log_levels.ERROR
|
lsp.log_levels.ERROR
|
||||||
)
|
)
|
||||||
--- @diagnostic disable-next-line: undefined-field,no-unknown
|
--- @diagnostic disable-next-line: undefined-field,no-unknown
|
||||||
message = diagnostic.message.value
|
message = diagnostic.message.value
|
||||||
@@ -174,10 +175,10 @@ function M.from(diagnostics)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@type table<integer, integer>
|
---@type table<integer, integer>
|
||||||
local _client_push_namespaces = {}
|
local client_push_namespaces = {}
|
||||||
|
|
||||||
---@type table<string, integer>
|
---@type table<string, integer>
|
||||||
local _client_pull_namespaces = {}
|
local client_pull_namespaces = {}
|
||||||
|
|
||||||
--- Get the diagnostic namespace associated with an LSP client |vim.diagnostic| for diagnostics
|
--- Get the diagnostic namespace associated with an LSP client |vim.diagnostic| for diagnostics
|
||||||
---
|
---
|
||||||
@@ -186,7 +187,7 @@ local _client_pull_namespaces = {}
|
|||||||
function M.get_namespace(client_id, is_pull)
|
function M.get_namespace(client_id, is_pull)
|
||||||
vim.validate('client_id', client_id, 'number')
|
vim.validate('client_id', client_id, 'number')
|
||||||
|
|
||||||
local client = vim.lsp.get_client_by_id(client_id)
|
local client = lsp.get_client_by_id(client_id)
|
||||||
if is_pull then
|
if is_pull then
|
||||||
local server_id =
|
local server_id =
|
||||||
vim.tbl_get((client or {}).server_capabilities or {}, 'diagnosticProvider', 'identifier')
|
vim.tbl_get((client or {}).server_capabilities or {}, 'diagnosticProvider', 'identifier')
|
||||||
@@ -196,19 +197,19 @@ function M.get_namespace(client_id, is_pull)
|
|||||||
client_id,
|
client_id,
|
||||||
server_id or 'nil'
|
server_id or 'nil'
|
||||||
)
|
)
|
||||||
local ns = _client_pull_namespaces[key]
|
local ns = client_pull_namespaces[key]
|
||||||
if not ns then
|
if not ns then
|
||||||
ns = api.nvim_create_namespace(name)
|
ns = api.nvim_create_namespace(name)
|
||||||
_client_pull_namespaces[key] = ns
|
client_pull_namespaces[key] = ns
|
||||||
end
|
end
|
||||||
return ns
|
return ns
|
||||||
end
|
end
|
||||||
|
|
||||||
local ns = _client_push_namespaces[client_id]
|
local ns = client_push_namespaces[client_id]
|
||||||
if not ns then
|
if not ns then
|
||||||
local name = ('nvim.lsp.%s.%d'):format(client and client.name or 'unknown', client_id)
|
local name = ('nvim.lsp.%s.%d'):format(client and client.name or 'unknown', client_id)
|
||||||
ns = api.nvim_create_namespace(name)
|
ns = api.nvim_create_namespace(name)
|
||||||
_client_push_namespaces[client_id] = ns
|
client_push_namespaces[client_id] = ns
|
||||||
end
|
end
|
||||||
return ns
|
return ns
|
||||||
end
|
end
|
||||||
@@ -257,7 +258,7 @@ end
|
|||||||
function M.on_diagnostic(error, result, ctx)
|
function M.on_diagnostic(error, result, ctx)
|
||||||
if error ~= nil and error.code == protocol.ErrorCodes.ServerCancelled then
|
if error ~= nil and error.code == protocol.ErrorCodes.ServerCancelled then
|
||||||
if error.data == nil or error.data.retriggerRequest ~= false then
|
if error.data == nil or error.data.retriggerRequest ~= false then
|
||||||
local client = assert(vim.lsp.get_client_by_id(ctx.client_id))
|
local client = assert(lsp.get_client_by_id(ctx.client_id))
|
||||||
client:request(ctx.method, ctx.params)
|
client:request(ctx.method, ctx.params)
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
@@ -271,7 +272,7 @@ function M.on_diagnostic(error, result, ctx)
|
|||||||
handle_diagnostics(ctx.params.textDocument.uri, client_id, result.items, true)
|
handle_diagnostics(ctx.params.textDocument.uri, client_id, result.items, true)
|
||||||
|
|
||||||
local bufnr = assert(ctx.bufnr)
|
local bufnr = assert(ctx.bufnr)
|
||||||
local bufstate = assert(bufstates[bufnr])
|
local bufstate = bufstates[bufnr]
|
||||||
bufstate.client_result_id[client_id] = result.resultId
|
bufstate.client_result_id[client_id] = result.resultId
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -329,7 +330,7 @@ end
|
|||||||
|
|
||||||
--- Clear diagnostics from pull based clients
|
--- Clear diagnostics from pull based clients
|
||||||
local function clear(bufnr)
|
local function clear(bufnr)
|
||||||
for _, namespace in pairs(_client_pull_namespaces) do
|
for _, namespace in pairs(client_pull_namespaces) do
|
||||||
vim.diagnostic.reset(namespace, bufnr)
|
vim.diagnostic.reset(namespace, bufnr)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -339,7 +340,7 @@ end
|
|||||||
local function disable(bufnr)
|
local function disable(bufnr)
|
||||||
local bufstate = bufstates[bufnr]
|
local bufstate = bufstates[bufnr]
|
||||||
if bufstate then
|
if bufstate then
|
||||||
bufstate.enabled = false
|
bufstate.pull_kind = 'disabled'
|
||||||
end
|
end
|
||||||
clear(bufnr)
|
clear(bufnr)
|
||||||
end
|
end
|
||||||
@@ -348,7 +349,7 @@ end
|
|||||||
---@param bufnr integer buffer number
|
---@param bufnr integer buffer number
|
||||||
---@param client_id? integer Client ID to refresh (default: all clients)
|
---@param client_id? integer Client ID to refresh (default: all clients)
|
||||||
---@param only_visible? boolean Whether to only refresh for the visible regions of the buffer (default: false)
|
---@param only_visible? boolean Whether to only refresh for the visible regions of the buffer (default: false)
|
||||||
local function _refresh(bufnr, client_id, only_visible)
|
local function refresh(bufnr, client_id, only_visible)
|
||||||
if
|
if
|
||||||
only_visible
|
only_visible
|
||||||
and vim.iter(api.nvim_list_wins()):all(function(window)
|
and vim.iter(api.nvim_list_wins()):all(function(window)
|
||||||
@@ -359,8 +360,8 @@ local function _refresh(bufnr, client_id, only_visible)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local method = ms.textDocument_diagnostic
|
local method = ms.textDocument_diagnostic
|
||||||
local clients = vim.lsp.get_clients({ bufnr = bufnr, method = method, id = client_id })
|
local clients = lsp.get_clients({ bufnr = bufnr, method = method, id = client_id })
|
||||||
local bufstate = assert(bufstates[bufnr])
|
local bufstate = bufstates[bufnr]
|
||||||
|
|
||||||
util._cancel_requests({
|
util._cancel_requests({
|
||||||
bufnr = bufnr,
|
bufnr = bufnr,
|
||||||
@@ -383,54 +384,130 @@ end
|
|||||||
function M._enable(bufnr)
|
function M._enable(bufnr)
|
||||||
bufnr = vim._resolve_bufnr(bufnr)
|
bufnr = vim._resolve_bufnr(bufnr)
|
||||||
|
|
||||||
if not bufstates[bufnr] then
|
if bufstates[bufnr] then
|
||||||
bufstates[bufnr] = { enabled = true, client_result_id = {} }
|
-- If we're already pulling diagnostics for this buffer, nothing to do here.
|
||||||
|
if bufstates[bufnr].pull_kind == 'document' then
|
||||||
api.nvim_create_autocmd('LspNotify', {
|
return
|
||||||
buffer = bufnr,
|
end
|
||||||
callback = function(opts)
|
-- Else diagnostics were disabled or we were using workspace diagnostics.
|
||||||
if
|
bufstates[bufnr].pull_kind = 'document'
|
||||||
opts.data.method ~= ms.textDocument_didChange
|
|
||||||
and opts.data.method ~= ms.textDocument_didOpen
|
|
||||||
then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if bufstates[bufnr] and bufstates[bufnr].enabled then
|
|
||||||
local client_id = opts.data.client_id --- @type integer?
|
|
||||||
_refresh(bufnr, client_id, true)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
group = augroup,
|
|
||||||
})
|
|
||||||
|
|
||||||
api.nvim_buf_attach(bufnr, false, {
|
|
||||||
on_reload = function()
|
|
||||||
if bufstates[bufnr] and bufstates[bufnr].enabled then
|
|
||||||
_refresh(bufnr)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
on_detach = function()
|
|
||||||
disable(bufnr)
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
api.nvim_create_autocmd('LspDetach', {
|
|
||||||
buffer = bufnr,
|
|
||||||
callback = function(args)
|
|
||||||
local clients = vim.lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_diagnostic })
|
|
||||||
|
|
||||||
if
|
|
||||||
not vim.iter(clients):any(function(c)
|
|
||||||
return c.id ~= args.data.client_id
|
|
||||||
end)
|
|
||||||
then
|
|
||||||
disable(bufnr)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
group = augroup,
|
|
||||||
})
|
|
||||||
else
|
else
|
||||||
bufstates[bufnr].enabled = true
|
bufstates[bufnr] = { pull_kind = 'document', client_result_id = {} }
|
||||||
|
end
|
||||||
|
|
||||||
|
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].pull_kind == 'document' then
|
||||||
|
local client_id = opts.data.client_id --- @type integer?
|
||||||
|
refresh(bufnr, client_id, true)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
group = augroup,
|
||||||
|
})
|
||||||
|
|
||||||
|
api.nvim_buf_attach(bufnr, false, {
|
||||||
|
on_reload = function()
|
||||||
|
if bufstates[bufnr] and bufstates[bufnr].pull_kind == 'document' then
|
||||||
|
refresh(bufnr)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
on_detach = function()
|
||||||
|
disable(bufnr)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
api.nvim_create_autocmd('LspDetach', {
|
||||||
|
buffer = bufnr,
|
||||||
|
callback = function(args)
|
||||||
|
local clients = lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_diagnostic })
|
||||||
|
|
||||||
|
if
|
||||||
|
not vim.iter(clients):any(function(c)
|
||||||
|
return c.id ~= args.data.client_id
|
||||||
|
end)
|
||||||
|
then
|
||||||
|
disable(bufnr)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
group = augroup,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the result IDs from the reports provided by the given client.
|
||||||
|
--- @return lsp.PreviousResultId[]
|
||||||
|
local function previous_result_ids(client_id)
|
||||||
|
local results = {}
|
||||||
|
|
||||||
|
for bufnr, state in pairs(bufstates) do
|
||||||
|
if state.pull_kind ~= 'disabled' then
|
||||||
|
for buf_client_id, result_id in pairs(state.client_result_id) do
|
||||||
|
if buf_client_id == client_id then
|
||||||
|
table.insert(results, {
|
||||||
|
textDocument = util.make_text_document_params(bufnr),
|
||||||
|
previousResultId = result_id,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return results
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Request workspace-wide diagnostics.
|
||||||
|
--- @param opts vim.lsp.WorkspaceDiagnosticsOpts
|
||||||
|
function M._workspace_diagnostics(opts)
|
||||||
|
local clients = lsp.get_clients({ method = ms.workspace_diagnostic, id = opts.client_id })
|
||||||
|
|
||||||
|
--- @param error lsp.ResponseError?
|
||||||
|
--- @param result lsp.WorkspaceDiagnosticReport
|
||||||
|
--- @param ctx lsp.HandlerContext
|
||||||
|
local function handler(error, result, ctx)
|
||||||
|
-- Check for retrigger requests on cancellation errors.
|
||||||
|
-- Unless `retriggerRequest` is explicitly disabled, try again.
|
||||||
|
if error ~= nil and error.code == lsp.protocol.ErrorCodes.ServerCancelled then
|
||||||
|
if error.data == nil or error.data.retriggerRequest ~= false then
|
||||||
|
local client = assert(lsp.get_client_by_id(ctx.client_id))
|
||||||
|
client:request(ms.workspace_diagnostic, ctx.params, handler)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if error == nil and result ~= nil then
|
||||||
|
for _, report in ipairs(result.items) do
|
||||||
|
local bufnr = vim.uri_to_bufnr(report.uri)
|
||||||
|
|
||||||
|
-- Start tracking the buffer (but don't send "textDocument/diagnostic" requests for it).
|
||||||
|
if not bufstates[bufnr] then
|
||||||
|
bufstates[bufnr] = { pull_kind = 'workspace', client_result_id = {} }
|
||||||
|
end
|
||||||
|
|
||||||
|
-- We favor document pull requests over workspace results, so only update the buffer
|
||||||
|
-- state if we're not pulling document diagnostics for this buffer.
|
||||||
|
if bufstates[bufnr].pull_kind == 'workspace' and report.kind == 'full' then
|
||||||
|
handle_diagnostics(report.uri, ctx.client_id, report.items, true)
|
||||||
|
bufstates[bufnr].client_result_id[ctx.client_id] = report.resultId
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, client in ipairs(clients) do
|
||||||
|
--- @type lsp.WorkspaceDiagnosticParams
|
||||||
|
local params = {
|
||||||
|
identifier = vim.tbl_get(client, 'server_capabilities, diagnosticProvider', 'identifier'),
|
||||||
|
previousResultIds = previous_result_ids(client.id),
|
||||||
|
}
|
||||||
|
|
||||||
|
client:request(ms.workspace_diagnostic, params, handler)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@@ -566,6 +566,9 @@ function protocol.make_client_capabilities()
|
|||||||
inlayHint = {
|
inlayHint = {
|
||||||
refreshSupport = true,
|
refreshSupport = true,
|
||||||
},
|
},
|
||||||
|
workspace = {
|
||||||
|
refreshSupport = false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
experimental = nil,
|
experimental = nil,
|
||||||
window = {
|
window = {
|
||||||
|
@@ -6801,4 +6801,128 @@ describe('LSP', function()
|
|||||||
eq(false, exec_lua([[return vim.lsp.is_enabled('foo')]]))
|
eq(false, exec_lua([[return vim.lsp.is_enabled('foo')]]))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe('vim.lsp.buf.workspace_diagnostics()', function()
|
||||||
|
local fake_uri = 'file:///fake/uri'
|
||||||
|
|
||||||
|
--- @param kind lsp.DocumentDiagnosticReportKind
|
||||||
|
--- @param msg string
|
||||||
|
--- @param pos integer
|
||||||
|
--- @return lsp.WorkspaceDocumentDiagnosticReport
|
||||||
|
local function make_report(kind, msg, pos)
|
||||||
|
return {
|
||||||
|
kind = kind,
|
||||||
|
uri = fake_uri,
|
||||||
|
items = {
|
||||||
|
{
|
||||||
|
range = {
|
||||||
|
start = { line = pos, character = pos },
|
||||||
|
['end'] = { line = pos, character = pos },
|
||||||
|
},
|
||||||
|
message = msg,
|
||||||
|
severity = 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @param items lsp.WorkspaceDocumentDiagnosticReport[]
|
||||||
|
--- @return integer
|
||||||
|
local function setup_server(items)
|
||||||
|
exec_lua(create_server_definition)
|
||||||
|
return exec_lua(function()
|
||||||
|
_G.server = _G._create_server({
|
||||||
|
capabilities = {
|
||||||
|
diagnosticProvider = { workspaceDiagnostics = true },
|
||||||
|
},
|
||||||
|
handlers = {
|
||||||
|
['workspace/diagnostic'] = function(_, _, callback)
|
||||||
|
callback(nil, { items = items })
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
local client_id = assert(vim.lsp.start({ name = 'dummy', cmd = _G.server.cmd }))
|
||||||
|
vim.lsp.buf.workspace_diagnostics()
|
||||||
|
return client_id
|
||||||
|
end, { items })
|
||||||
|
end
|
||||||
|
|
||||||
|
it('updates diagnostics obtained with vim.diagnostic.get()', function()
|
||||||
|
setup_server({ make_report('full', 'Error here', 1) })
|
||||||
|
|
||||||
|
retry(nil, nil, function()
|
||||||
|
eq(
|
||||||
|
1,
|
||||||
|
exec_lua(function()
|
||||||
|
return #vim.diagnostic.get()
|
||||||
|
end)
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
|
||||||
|
eq(
|
||||||
|
'Error here',
|
||||||
|
exec_lua(function()
|
||||||
|
return vim.diagnostic.get()[1].message
|
||||||
|
end)
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('ignores unchanged diagnostic reports', function()
|
||||||
|
setup_server({ make_report('unchanged', '', 1) })
|
||||||
|
|
||||||
|
eq(
|
||||||
|
0,
|
||||||
|
exec_lua(function()
|
||||||
|
-- Wait for diagnostics to be processed.
|
||||||
|
vim.uv.sleep(50)
|
||||||
|
|
||||||
|
return #vim.diagnostic.get()
|
||||||
|
end)
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('favors document diagnostics over workspace diagnostics', function()
|
||||||
|
local client_id = setup_server({ make_report('full', 'Workspace error', 1) })
|
||||||
|
local diagnostic_bufnr = exec_lua(function()
|
||||||
|
return vim.uri_to_bufnr(fake_uri)
|
||||||
|
end)
|
||||||
|
|
||||||
|
exec_lua(function()
|
||||||
|
vim.lsp.diagnostic.on_diagnostic(nil, {
|
||||||
|
kind = 'full',
|
||||||
|
items = {
|
||||||
|
{
|
||||||
|
range = {
|
||||||
|
start = { line = 2, character = 2 },
|
||||||
|
['end'] = { line = 2, character = 2 },
|
||||||
|
},
|
||||||
|
message = 'Document error',
|
||||||
|
severity = 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
method = 'textDocument/diagnostic',
|
||||||
|
params = {
|
||||||
|
textDocument = { uri = fake_uri },
|
||||||
|
},
|
||||||
|
client_id = client_id,
|
||||||
|
bufnr = diagnostic_bufnr,
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
|
eq(
|
||||||
|
1,
|
||||||
|
exec_lua(function()
|
||||||
|
return #vim.diagnostic.get(diagnostic_bufnr)
|
||||||
|
end)
|
||||||
|
)
|
||||||
|
|
||||||
|
eq(
|
||||||
|
'Document error',
|
||||||
|
exec_lua(function()
|
||||||
|
return vim.diagnostic.get(vim.uri_to_bufnr(fake_uri))[1].message
|
||||||
|
end)
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
Reference in New Issue
Block a user