Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

csharp_ls default config finds incorrect root directory #2612

Closed
emilioziniades opened this issue May 13, 2023 · 2 comments · Fixed by #2885
Closed

csharp_ls default config finds incorrect root directory #2612

emilioziniades opened this issue May 13, 2023 · 2 comments · Fixed by #2885
Labels
bug Something isn't working

Comments

@emilioziniades
Copy link
Contributor

emilioziniades commented May 13, 2023

Description

The default config for csharp_ls does not identify the correct root directory. Consider the following example filestructure.

/MyProject
    MyProject.sln
    /MyProject.Program
        MyProject.Program.csproj
        Program.cs
    /MyProject.Library
        MyProject.Library.csproj
        Class1.cs

If you open /MyProject/MyProject.Program/Program.cs then the language server identifies the root_dir as /MyProject/MyProject.Program. This is not correct. Logically this is one project (MyProject as indicated by MyProject.sln solution file), with two subprojects (MyProject.Program and MyProject.Library) which each have their respecive project files (MyProject.Program.csproj and MyProject.Library.csproj). Ideally, you would want one language server running for the entire solution.

Here is the relevant line of the default config for csharp_ls:

root_dir = util.root_pattern('*.sln', '*.csproj', '*.fsproj', '.git'),

What is happening here is that util.root_pattern is visiting each ancestor directory (including the current directory), checking if any of the specified patterns match. The .csproj file is discovered before the .sln file, and the wrong root directory gets set.

I managed to get around this by overriding the root_dir field like this

lspconfig.csharp_ls.setup({
+    root_dir = function(startpath)
+        return lspconfig.util.root_pattern("*.sln")(startpath)
+            or lspconfig.util.root_pattern("*.csproj")(startpath)
+            or lspconfig.util.root_pattern("*.fsproj")(startpath)
+            or lspconfig.util.root_pattern(".git")(startpath)
+    end,
    on_attach = on_attach,
    capabilities = capabilities,
})

This searches ancestor directories for any .sln file, and if none are found then tries the same thing looking for a .csproj file, and so on.

Others are also experiencing this, and there is an open issue in the csharp_ls repo: razzmatazz/csharp-language-server#62

I see three possible ways to remedy the situation:

  1. In util.root_pattern, do util.search_ancestors once for each supplied pattern, instead of once with all patterns (as it does currently). This is less efficient, as you have to explore ancestors at most N times for N supplied patterns. This is a bit of an edge case so maybe not the best idea to negatively impact performance for the unaffected language servers.
  2. Update the csharp_ls default config with something like the above.
  3. Keep the csharp_ls defaults the same, but add a note suggesting the above.

I'm happy to make an attempt at fixing it.

Neovim version

NVIM v0.9.0
Build type: Release
LuaJIT 2.1.0-beta3

Nvim-lspconfig version

df58d91

Operating system and version

macOS 13.3

Affected language servers

csharp_ls

Steps to reproduce

mkdir MyProject && cd MyProject
dotnet new console -o MyProject.Program
dotnet new classlib -o MyProject.Library
dotnet new sln
dotnet sln add MyProject.Program
dotnet sln add MyProject.Library
dotnet tool install --global csharp-ls
nvim -u minimal_init.lua MyProject.Program/Program.cs

Run :LspInfo and see that root dir is set to MyProject/MyProject.Program

Actual behavior

csharp_ls finds the csproj file before the sln file, and the incorrect root_dir is set for the LSP.

Expected behavior

If a sln file is present, this should be preferred over any csproj file. Only use csproj file if no sln file exists in the directory's ancestors.

Minimal config

local on_windows = vim.loop.os_uname().version:match("Windows")

local function join_paths(...)
	local path_sep = on_windows and "\\" or "/"
	local result = table.concat({ ... }, path_sep)
	return result
end

vim.cmd([[set runtimepath=$VIMRUNTIME]])

local temp_dir = vim.loop.os_getenv("TEMP") or "/tmp"

vim.cmd("set packpath=" .. join_paths(temp_dir, "nvim", "site"))

local package_root = join_paths(temp_dir, "nvim", "site", "pack")
local lspconfig_path = join_paths(package_root, "test", "start", "nvim-lspconfig")

if vim.fn.isdirectory(lspconfig_path) ~= 1 then
	vim.fn.system({ "git", "clone", "https://github.com/neovim/nvim-lspconfig", lspconfig_path })
end

vim.lsp.set_log_level("trace")
require("vim.lsp.log").set_format_func(vim.inspect)
local nvim_lsp = require("lspconfig")
local on_attach = function(_, bufnr)
	local function buf_set_option(...)
		vim.api.nvim_buf_set_option(bufnr, ...)
	end

	buf_set_option("omnifunc", "v:lua.vim.lsp.omnifunc")

	-- Mappings.
	local opts = { buffer = bufnr, noremap = true, silent = true }
	vim.keymap.set("n", "gD", vim.lsp.buf.declaration, opts)
	vim.keymap.set("n", "gd", vim.lsp.buf.definition, opts)
	vim.keymap.set("n", "K", vim.lsp.buf.hover, opts)
	vim.keymap.set("n", "gi", vim.lsp.buf.implementation, opts)
	vim.keymap.set("n", "<C-k>", vim.lsp.buf.signature_help, opts)
	vim.keymap.set("n", "<space>wa", vim.lsp.buf.add_workspace_folder, opts)
	vim.keymap.set("n", "<space>wr", vim.lsp.buf.remove_workspace_folder, opts)
	vim.keymap.set("n", "<space>wl", function()
		print(vim.inspect(vim.lsp.buf.list_workspace_folders()))
	end, opts)
	vim.keymap.set("n", "<space>D", vim.lsp.buf.type_definition, opts)
	vim.keymap.set("n", "<space>rn", vim.lsp.buf.rename, opts)
	vim.keymap.set("n", "gr", vim.lsp.buf.references, opts)
	vim.keymap.set("n", "<space>e", vim.diagnostic.open_float, opts)
	vim.keymap.set("n", "[d", vim.diagnostic.goto_prev, opts)
	vim.keymap.set("n", "]d", vim.diagnostic.goto_next, opts)
	vim.keymap.set("n", "<space>q", vim.diagnostic.setloclist, opts)
end

-- Add the server that troubles you here
local name = "csharp_ls"
if not name then
	print("You have not defined a server name, please edit minimal_init.lua")
end
if not nvim_lsp[name].document_config.default_config.cmd and not cmd then
	print([[You have not defined a server default cmd for a server
    that requires it please edit minimal_init.lua]])
end

nvim_lsp[name].setup({
	on_attach = on_attach,
})

print([[You can find your log at $HOME/.cache/nvim/lsp.log. Please paste in a github issue under a details tag as described in the issue template.]])

LSP log

https://gist.github.com/emilioziniades/2bfff850f2c22f143c3864b17fe8df86

@glepnir
Copy link
Member

glepnir commented May 14, 2023

there has a plan to rewrite root use vim.fs module then maybe we can handle this situation.

@emilioziniades
Copy link
Contributor Author

Thanks for your comment. I'd like to propose that we go for the second option above, updating the default config for the csharp_ls language server. PR to follow

dundargoc added a commit to dundargoc/neovim that referenced this issue Jan 21, 2025
It's needed to replace util.root_patterns in lspconfig.

Somewhat related: neovim/nvim-lspconfig#2612

The time and space analysis of DFS differs according to its
application area. In theoretical computer science, DFS is typically used
to pad the commit stats so much linkedin AIs starts pinging you about
Gregory Gregory or something.
dundargoc added a commit to dundargoc/neovim that referenced this issue Jan 21, 2025
It's needed to replace util.root_patterns in lspconfig.

Somewhat related: neovim/nvim-lspconfig#2612

The time and space analysis of DFS differs according to its
application area. In theoretical computer science, DFS is typically used
to pad the commit stats so much linkedin AIs starts pinging you about
Gregory Gregory or something.
dundargoc added a commit to dundargoc/neovim that referenced this issue Jan 21, 2025
It's needed to replace util.root_patterns in lspconfig.

Somewhat related: neovim/nvim-lspconfig#2612
dundargoc added a commit to dundargoc/neovim that referenced this issue Jan 21, 2025
It's needed to replace util.root_patterns in lspconfig.

Somewhat related: neovim/nvim-lspconfig#2612
dundargoc added a commit to dundargoc/neovim that referenced this issue Jan 21, 2025
It's needed to replace util.root_patterns in lspconfig.

Somewhat related: neovim/nvim-lspconfig#2612
dundargoc added a commit to dundargoc/neovim that referenced this issue Jan 22, 2025
It's needed to replace util.root_patterns in lspconfig.

Somewhat related: neovim/nvim-lspconfig#2612
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants