Roslyn.nvim: Fix Diagnostics Only Showing In Open Buffers

by Alex Johnson 58 views

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:

  1. 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.
  2. 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.
  3. 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 =