I'm using NeoVim + LSP Zero plugin with Rust Analyzer.
If I just create new crate via cargo init my_program, then just open it via nvim my_program and try to go to the definition of println in std, LSP diagnostic starts to show me diagnostic from std, tons of this:
/home/user/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/lib.rs|100 col 1-32 error| `#![feature]` may not be used on the stable release channel
/home/user/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/lib.rs|100 col 12-30 error| `#![feature]` may not be used on the stable release channel
/home/user/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/lib.rs|101 col 12-30 error| `#![feature]` may not be used on the stable release channel
Same happens if I go to definitions in other external crates.
I also tried to use CoC instead of native LSP, but got exactly the same result.
On the other hand, if I try to use Helix editor or even VSCode, there is no such behavior.
What is correct way to disable diagnostic for crates outside your workspace in NeoVim?
I'm using rust-tool.vim and ChatGpt cooked up this solution for me based on telling RA that the root_dir can only be the current workspace. Should be portable as long as the lsp is rust-analyzer.
First define these helpers
local function ensure_uri_scheme(uri)
if not vim.startswith(uri, "file://") then
return "file://" .. uri
end
return uri
end
local function is_in_workspace(uri)
uri = ensure_uri_scheme(uri)
local path = vim.uri_to_fname(uri)
local workspace_dir = vim.fn.getcwd()
return vim.startswith(path, workspace_dir)
end
local function filter_diagnostics(diagnostics)
local filtered_diagnostics = {}
for _, diagnostic in ipairs(diagnostics) do
if is_in_workspace(diagnostic.source) then
table.insert(filtered_diagnostics, diagnostic)
end
end
return filtered_diagnostics
end
Then configure the root_dir option in lspconfig or lsp zero. In my case I just used rust-tools config directly and it worked.
local lspconfig = require('lspconfig')
let rt = require('rust-tools')
rt.setup({
--....
--....
server = {
root_dir = function(startpath)
local startpath_uri = vim.uri_from_fname(startpath)
if not is_in_workspace(startpath) then
return nil
end
return lspconfig.util.root_pattern("Cargo.toml", "rust-project.json")(startpath)
end,
}
})
It also gave me another option I can set with a function that does diagnostic filtering based on if the file is from the workspace. But I'm satisfied with RA being disabled all together with the above solution. Here is the filtering snippet
local lspconfig = require('lspconfig')
let rt = require('rust-tools')
rt.setup({
--....
--....
server = {
handlers = {
["textDocument/publishDiagnostics"] = function(err, method, result, client_id, bufnr, config)
if not result or not result.uri then
return
end
local uri = result.uri
local bufnr = vim.uri_to_bufnr(uri)
if not bufnr then
return
end
if is_in_workspace(uri) then
local diagnostics = vim.lsp.diagnostic.to_severity(result.diagnostics)
diagnostics = filter_diagnostics(diagnostics)
vim.lsp.diagnostic.save(diagnostics, bufnr, client_id)
if vim.api.nvim_buf_is_loaded(bufnr) then
vim.lsp.diagnostic.set_signs(diagnostics, bufnr, client_id)
end
end
end;
},
}
})
local util = require('lspconfig.util')
local rustPorjectDir = nil
local function rustRootPattern(fname)
local cargo_crate_dir = util.root_pattern 'Cargo.toml' (fname)
local cmd = { 'cargo', 'metadata', '--no-deps', '--format-version', '1' }
if cargo_crate_dir ~= nil then
cmd[#cmd + 1] = '--manifest-path'
cmd[#cmd + 1] = util.path.join(cargo_crate_dir, 'Cargo.toml')
end
local cargo_metadata = ''
local cargo_metadata_err = ''
local cm = vim.fn.jobstart(cmd, {
on_stdout = function(_, d, _)
cargo_metadata = table.concat(d, '\n')
end,
on_stderr = function(_, d, _)
cargo_metadata_err = table.concat(d, '\n')
end,
stdout_buffered = true,
stderr_buffered = true,
})
if cm > 0 then
cm = vim.fn.jobwait({ cm })[1]
else
cm = -1
end
local cargo_workspace_dir = nil
if cm == 0 then
cargo_workspace_dir = vim.json.decode(cargo_metadata)['workspace_root']
if cargo_workspace_dir ~= nil then
cargo_workspace_dir = util.path.sanitize(cargo_workspace_dir)
end
else
vim.notify(
string.format('[lspconfig] cmd (%q) failed:\n%s', table.concat(cmd, ' '), cargo_metadata_err),
vim.log.levels.WARN
)
end
return cargo_workspace_dir
or cargo_crate_dir
or util.root_pattern 'rust-project.json' (fname)
or util.find_git_ancestor(fname)
-- return cargo_crate_dir
-- or util.root_pattern 'rust-project.json' (fname)
-- or util.find_git_ancestor(fname)
end
local function rust_root_dir(fname)
local function get()
rustPorjectDir = rustRootPattern(fname)
vim.notify("Directory: " .. rustPorjectDir, "info", { title = "Rust project opened" })
return rustPorjectDir
end
return rustPorjectDir or get()
end
lsp.configure("rust_analyzer", {
root_dir = rust_root_dir,
})