Skip to content
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ Other dedicated linters that are built-in are:
| [quick-lint-js][quick-lint-js] | `quick-lint-js` |
| [redocly][redocly] | `redolcy` |
| [regal][regal] | `regal` |
| [resharper][resharper] (*) | `resharper` |
| [Revive][14] | `revive` |
| [rflint][rflint] | `rflint` |
| [robocop][robocop] | `robocop` |
Expand Down Expand Up @@ -262,6 +263,8 @@ Other dedicated linters that are built-in are:
| [zlint][zlint] | `zlint` |
| [zsh][zsh] | `zsh` |

(*) resharper requires version 2025.2 or newer of command line tools (earlier versions don't support stdout)

## Custom Linters

You can register custom linters by adding them to the `linters` table, but
Expand Down Expand Up @@ -641,6 +644,7 @@ vimcats -t -f lua/lint.lua lua/lint/parser.lua > doc/lint.txt
[opa_check]: https://www.openpolicyagent.org/
[oxlint]: https://oxc-project.github.io/
[regal]: https://github.com/StyraInc/regal
[resharper]: https://www.jetbrains.com/help/resharper/ReSharper_Command_Line_Tools.html
[vala-lint]: https://github.com/vala-lang/vala-lint
[systemdlint]: https://github.com/priv-kweihmann/systemdlint
[htmlhint]: https://htmlhint.com/
Expand Down
60 changes: 60 additions & 0 deletions lua/lint/linters/resharper.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
local function find_upward(extension)
local dir = vim.fn.getcwd()
while dir and dir ~= "/" do
local files = vim.fn.globpath(dir, "*" .. extension, false, true)
if not vim.tbl_isempty(files) then
return files[1]
end
dir = vim.fn.fnamemodify(dir, ":h")
end
return nil
end

return function()
local root_file = find_upward(".sln")
if not root_file then
root_file = find_upward(".csproj")
end

if not root_file then
vim.notify("No solution file or csproj found in parent directories", vim.log.levels.WARN)
return {}
end

local current_buffer = vim.api.nvim_get_current_buf()
local filepath = vim.api.nvim_buf_get_name(current_buffer)

if filepath == "" then
vim.notify("Current buffer has no file associated", vim.log.levels.WARN)
return {}
end

local solution_directory = vim.fn.fnamemodify(root_file, ":h")

local relative_filepath = filepath:sub(#solution_directory + 2)
local cache_directory = vim.fn.stdpath("cache") .. "/nvim-lint-resharper"
Comment on lines +1 to +35
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be changed to only search .sln or .csproj relative to the current dir?
Adding root dir heuristics as part of the linter is out of scope for nvim-dap. cwd can be parameterized as part of the try_lint call for users who have cwd != project_dir.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the resharper cli cannot lint a single file. It has to run against an entire project or solution. So I guess it's fine as long the user always has csproj or sln in the cwd. For me, that rarely is the case because .NET projects are often nested in various folders. .NET has solution groups to organize files virtually that is irrespective of file tree structure on disk, which makes it even a little more confusing. I have a plugin I wrote for this problem, but basically .NET projects aren't always structured relative on disk; sometimes it's by the "logical" solution groups.
https://github.com/xentropic-dev/explorer.dotnet.nvim

I think for nvim-lint, I should just do whatever makes sense for the good of the rest of the plugin's ecosystem, but I'm really starting to think I need to just make a separate plugin for just the resharper cli. There's too many ways in which the tool is different than the standard linters in this project.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I guess it's fine as long the user always has csproj or sln in the cwd. For me, that rarely is the case because

You could still do something as described in #482 (comment)

And at some point in the future there might be a solution based on neovim/neovim#34622

So for now I'd restrict resharper to only search downward from cwd


-- Ensure cache directory exists
vim.fn.mkdir(cache_directory, "p")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this have to be specified?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be honest, I'm not sure. In my tests, it could take 7 or 8 seconds or more for the linter to run on an entire project and caching seemed to have minor effects to the point where I couldn't even be sure if it was working. This was an attempt by me to be as explicit as possible to try to ensure caching was working, and it also gave me a way to check to make sure the cache was actually being saved. I still didn't see any performance increase really by enabling caching and/or specifying the cache directory. I'm fine with removing the cache_directory completely tbh.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there wasn't any difference in your tests I'd be in favor of removing it.


return {
cmd = "jb",
stdin = false,
append_fname = false,
args = {
"inspectcode",
"--no-swea",
"--no-build",
"--jobs=0",
"--severity=INFO",
"--output=-",
"--absolute-paths",
"--include=" .. relative_filepath,
"--caches-home=" .. cache_directory,
root_file,
},
stream = nil,
ignore_exitcode = true,
parser = require("lint.parser").for_sarif(),
}
end