Roslyn.nvim: Fix Diagnostics Only Showing In Open Buffers
Are you experiencing an issue where Roslyn.nvim only displays diagnostics for the files you currently have open, rather than the entire solution? This can be frustrating when you want a complete overview of all issues in your project. Let's dive into how to resolve this problem.
Understanding the Issue
The core problem is that the quickfix list isn't populated with all the diagnostics from your solution. Instead, it only shows errors, warnings, hints, and info for the files you're actively editing. This contradicts the expected behavior when you have configurations set to analyze the entire solution.
Common Misconceptions
Some users might assume that this is due to issues with their Unity solution, especially since Unity projects can sometimes have unique compilation quirks. However, the issue often lies within the Roslyn.nvim configuration itself, rather than the specifics of the project structure. Ensuring that your solution compiles correctly with all necessary DLLs and references is a good first step, but further configuration might be needed.
Identifying the Root Cause
The primary cause often boils down to how Roslyn.nvim is set up to handle diagnostics scope. The dotnet_analyzer_diagnostics_scope and dotnet_compiler_diagnostics_scope settings should be set to 'fullSolution' to ensure a comprehensive analysis. If these settings are not correctly applied or are being overridden, you'll likely only see diagnostics for open buffers.
Diagnosing the Problem
To effectively troubleshoot this, consider the following steps:
- Verify Your Neovim Version: Ensure you're running a compatible version of Neovim. The user in this case was using
v0.11.4-Release-LuaJIT-2.1.1741730670. While this version might work, it's always a good idea to keep your Neovim installation up to date. - Check Your Operating System: Confirm that your operating system (Windows 11 in this scenario) isn't interfering with file watching or other background processes that Roslyn.nvim relies on.
- Review Minimal Config: Ensure you load only the necessary plugins to reproduce the issue. This helps isolate whether other plugins are conflicting with Roslyn.nvim.
Configuration Deep Dive
Let's examine the provided minimal configuration and highlight key areas for troubleshooting.
Plugin Setup
The provided configuration includes a setup for installing plugins via Git. This ensures that Roslyn.nvim is installed correctly. It's crucial that the plugin is installed in the correct runtime path for Neovim to recognize it.
for name, url in pairs{
roslyn = "https://github.com/seblyng/roslyn.nvim",
-- Add other plugins to reproduce the issue
} do
local install_path = vim.fn.fnamemodify("roslyn_issue/"..name, ":p")
if vim.fn.isdirectory(install_path) == 0 then
vim.fn.system({ "git", "clone", "--depth=1", url, install_path })
end
vim.opt.runtimepath:append(install_path)
end
Language Server Configuration
The language server configuration is critical. It defines how Roslyn.nvim interacts with the Roslyn Language Server. Key configurations include the command (cmd) to start the server, the file types it handles (filetypes), and various handlers for different events.
require("roslyn").setup{
-- Add config options necessary to reproduce the issue
---@type vim.lsp.Config
name = 'roslyn_ls',
offset_encoding = 'utf-8',
cmd = {
'Microsoft.CodeAnalysis.LanguageServer',
'--logLevel',
'Information',
'--extensionLogDirectory',
fs.joinpath(uv.os_tmpdir(), 'roslyn_ls/logs'),
'--stdio',
},
filetypes = { 'cs' },
handlers = roslyn_handlers(),
filewatching = "roslyn",
commands = {
['roslyn.client.completionComplexEdit'] = function(command, ctx)
local client = assert(vim.lsp.get_client_by_id(ctx.client_id))
local args = command.arguments or {}
local uri, edit = args[1], args[2]
---@diagnostic disable: undefined-field
if uri and edit and edit.newText and edit.range then
local workspace_edit = {
changes = {
[uri.uri] = {
{
range = edit.range,
newText = edit.newText,
},
},
},
}
vim.lsp.util.apply_workspace_edit(workspace_edit, client.offset_encoding)
---@diagnostic enable: undefined-field
else
vim.notify('roslyn_ls: completionComplexEdit args not understood: ' .. vim.inspect(args), vim.log.levels.WARN)
end
end,
},
root_dir = function(bufnr, cb)
local bufname = vim.api.nvim_buf_get_name(bufnr)
-- don't try to find sln or csproj for files from libraries
-- outside of the project
if not bufname:match('^' .. fs.joinpath('/tmp/MetadataAsSource/') then
-- try find solutions root first
local root_dir = fs.root(bufnr, function(fname, _)
return fname:match('%.sln[x]?{{content}}#39;) ~= nil
end)
if not root_dir then
-- try find projects root
root_dir = fs.root(bufnr, function(fname, _)
return fname:match('%.csproj{{content}}#39;) ~= nil
end)
end
if root_dir then
cb(root_dir)
end
end
end,
on_init = {
function(client)
local root_dir = client.config.root_dir
-- try load first solution we find
for entry, type in fs.dir(root_dir) do
if type == 'file' and (vim.endswith(entry, '.sln') or vim.endswith(entry, '.slnx')) then
on_init_sln(client, fs.joinpath(root_dir, entry))
return
end
end
-- if no solution is found load project
for entry, type in fs.dir(root_dir) do
if type == 'file' and vim.endswith(entry, '.csproj') then
on_init_project(client, { fs.joinpath(root_dir, entry) })
end
end
end,
},
on_attach = function(client, bufnr)
-- avoid duplicate autocmds for same buffer
if vim.api.nvim_get_autocmds({ buffer = bufnr, group = group })[1] then
return
end
vim.api.nvim_create_autocmd({ 'BufWritePost', 'InsertLeave' }, {
group = group,
buffer = bufnr,
callback = function()
refresh_diagnostics(client)
end,
desc = 'roslyn_ls: refresh diagnostics',
})
end,
capabilities = {
-- HACK: Doesn't show any diagnostics if we do not set this to true
textDocument = {
diagnostic = {
dynamicRegistration = true,
},
},
},
settings = {
['csharp|background_analysis'] = {
dotnet_analyzer_diagnostics_scope = 'fullSolution',
dotnet_compiler_diagnostics_scope = 'fullSolution',
},
['csharp|inlay_hints'] = {
csharp_enable_inlay_hints_for_implicit_object_creation = true,
csharp_enable_inlay_hints_for_implicit_variable_types = true,
csharp_enable_inlay_hints_for_lambda_parameter_types = true,
csharp_enable_inlay_hints_for_types = true,
dotnet_enable_inlay_hints_for_indexer_parameters = true,
dotnet_enable_inlay_hints_for_literal_parameters = true,
dotnet_enable_inlay_hints_for_object_creation_parameters = true,
dotnet_enable_inlay_hints_for_other_parameters = true,
dotnet_enable_inlay_hints_for_parameters = true,
dotnet_suppress_inlay_hints_for_parameters_that_differ_only_by_suffix = true,
dotnet_suppress_inlay_hints_for_parameters_that_match_argument_name = true,
dotnet_suppress_inlay_hints_for_parameters_that_match_method_intent = true,
},
['csharp|symbol_search'] = {
dotnet_search_reference_assemblies = true,
},
['csharp|completion'] = {
dotnet_show_name_completion_suggestions = true,
dotnet_show_completion_items_from_unimported_namespaces = true,
dotnet_provide_regex_completions = true,
},
['csharp|code_lens'] = {
dotnet_enable_references_code_lens = true,
},
},
}
Key Settings
Pay close attention to these settings:
settings = {
['csharp|background_analysis'] = {
dotnet_analyzer_diagnostics_scope = 'fullSolution',
dotnet_compiler_diagnostics_scope = 'fullSolution',
},
}
These settings tell Roslyn.nvim to analyze the entire solution for both analyzer and compiler diagnostics. If these are missing or set to a different value (e.g., 'openFiles'), you'll only see diagnostics for open buffers.
File Watching
The `filewatching =