Skip to content

Update neovim instructions for neovim 0.11+#2814

Open
erfanio wants to merge 5 commits intorust-lang:mainfrom
erfanio:main
Open

Update neovim instructions for neovim 0.11+#2814
erfanio wants to merge 5 commits intorust-lang:mainfrom
erfanio:main

Conversation

@erfanio
Copy link
Copy Markdown

@erfanio erfanio commented Mar 31, 2026

  • Adding a bit structure to the different options for neovim.
  • Adding a note about neovim 0.11 displaying a warning when using neoconf.nvim
  • Updated the custom lua script to work with HEAD of rust and neovim 0.11

This doc points to rust-analyzer book instructions for nvim lsp which is also being updated in rust-lang/rust-analyzer#21924

@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Mar 31, 2026

Thanks for the PR. If you have write access, feel free to merge this PR if it does not need reviews. You can request a review using r? rustc-dev-guide or r? <username>.

@rustbot rustbot added the S-waiting-on-review Status: this PR is waiting for a reviewer to verify its content label Mar 31, 2026
@erfanio
Copy link
Copy Markdown
Author

erfanio commented Mar 31, 2026

r? rustc-dev-guide

Copy link
Copy Markdown
Member

@reddevilmidzy reddevilmidzy left a comment

Choose a reason for hiding this comment

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

thanks! some nit and some question:)

View changes since this review

local default_root_dir = vim.lsp.config['rust_analyzer'].root_dir
local default_before_init = vim.lsp.config['rust_analyzer'].before_init

vim.lsp.config('rust_analyzer', {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not very familiar with Lua, so is it correct that there is a difference between _ and - in vim.lsp.config('rust_analyzer', {, cmd = { 'rust-analyzer' },, config.settings["rust-analyzer"] = json.lsp["rust-analyzer"] and vim.lsp.enable('rust_analyzer')?

Copy link
Copy Markdown
Author

@erfanio erfanio Apr 6, 2026

Choose a reason for hiding this comment

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

Yeah the _ and - here do matter but I agree it's confusing which is which but unfortunately we have to follow the pattern used in https://github.com/neovim/nvim-lspconfig/blob/master/lsp/rust_analyzer.lua unless we want to reimplement everything there.

  • rust_analyzer is the name of the LSP config. They're using _ for consistency across all the nvim-lspconfig configs.
  • cmd = { 'rust-analyzer' }, is the name of the binary
  • config.settings["rust-analyzer"] does feel arbitrary and looking at the some of the other configs in nvim-lspconfig I think it's probably evolved organically without a real convention.

EDIT: config.settings key mirrors the name rust-analyzer uses to query for client configurations https://rust-analyzer.github.io/book/configuration.html

@reddevilmidzy reddevilmidzy added S-waiting-on-author Status: this PR is waiting for additional action by the OP and removed S-waiting-on-review Status: this PR is waiting for a reviewer to verify its content labels Apr 1, 2026
@khyperia
Copy link
Copy Markdown
Contributor

khyperia commented Apr 5, 2026

fyi, neovim 0.12 (released last week) added skip_comments, so you don't have to manually strip out comments out of the zed json file. I made #2822 before I realized this PR existed! sorry

@erfanio
Copy link
Copy Markdown
Author

erfanio commented Apr 6, 2026

Thanks @khyperia. I've incorporated your suggestion as well but since 0.12 is very recent, I left a comment for people still running the older version.

@erfanio
Copy link
Copy Markdown
Author

erfanio commented Apr 6, 2026

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: this PR is waiting for a reviewer to verify its content and removed S-waiting-on-author Status: this PR is waiting for additional action by the OP labels Apr 6, 2026
Copy link
Copy Markdown
Member

@reddevilmidzy reddevilmidzy left a comment

Choose a reason for hiding this comment

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

LGTM! but let's wait for confirmation from a Neovim user @ShoyuVanilla, thanks

View changes since this review

@khyperia
Copy link
Copy Markdown
Contributor

khyperia commented Apr 6, 2026

LGTM! but let's wait for confirmation from a Neovim user @ShoyuVanilla, thanks

(I'm not ShoyuVanilla, but) notes from my own testing:

  • rust-analyzer stable is used, not nightly. I think this is due to this commit in general, not due to this PR. I don't know what was intended in this PR, but at least for my own personal setup, I will continue to use rust-analyzer nightly. (I don't thiiiink there's a stage0 rust-analyzer?)
  • rustfmt does not seem to work: I think rustfmt.overrideCommand must be an absolute path, and it is no longer an absolute path, done in rust-lang/rust@a6bf524 (I don't know why or how this works for zed, tbh). I remember looking into this a few months ago and being confused why procMacro.server is parsed/invoked in a completely different codepath in rust-analyzer than rustfmt.overrideCommand - the former allows relative paths, but the latter seems to not.

For the second one, slapping this in seems to fix it (probably want to clean this up a bit though to do it a bit more nicely)

json.lsp["rust-analyzer"].initialization_options.rustfmt.overrideCommand[1] = init_params.rootPath .. "/" .. json.lsp["rust-analyzer"].initialization_options.rustfmt.overrideCommand[1]

for what it's worth, here's my personal neovim config - it has some things that shouldn't be included by default (such as disabling flycheck, due to personal preference of mine), but might be good inspiration for anyone wanting to further customize:

click to expand
local old_rust_before_init = vim.lsp.config.rust_analyzer.before_init
vim.lsp.config('rust_analyzer', {
    cmd = function(dispatchers, config)
        local exe = "rust-analyzer"
        if config.root_dir:find("^/d/rust") then
            if not config.cmd_env then
                config.cmd_env = {}
            end
            config.cmd_env["RUSTUP_TOOLCHAIN"] = "nightly"
        end
        return vim.lsp.rpc.start({ exe }, dispatchers, {
            cwd = config.cmd_cwd,
            env = config.cmd_env,
            detached = config.detached,
        })
    end,
    root_dir = function(bufnr, on_dir)
        -- fully override root dir here, do not call the old root_dir
        -- reuse existing RA if it exists
        local clients = vim.lsp.get_clients { name = 'rust_analyzer' }
        if #clients > 0 then
            local existing_root = clients[#clients].config.root_dir
            if existing_root then
                on_dir(existing_root)
                return
            end
        end
        -- only scan for .git
        local gitroot = vim.fs.root(bufnr, '.git')
        if gitroot then
            on_dir(gitroot)
        end
    end,
    before_init = function(init_params, config)
        if init_params and init_params.rootPath and init_params.rootPath:find("^/d/rust") then
            if not config.settings then
                config.settings = {}
            end
            if not config.settings['rust-analyzer'] then
                config.settings['rust-analyzer'] = {}
            end
            local config_path = vim.fs.joinpath(init_params.rootPath, "src/etc/rust_analyzer_zed.json")
            local file = io.open(config_path)
            if file == nil then
                vim.print("missing config file: " .. config_path)
            else
                local file_contents = file:read("*a")
                local json = vim.json.decode(file_contents, { skip_comments = true })
                json = json.lsp["rust-analyzer"].initialization_options
                json.check.overrideCommand = { "DISABLE" };
                json.checkOnSave = false
                json.rustfmt.overrideCommand[1] = init_params.rootPath .. "/" .. json.rustfmt.overrideCommand[1]
                config.settings['rust-analyzer'] = vim.tbl_deep_extend('force', config.settings['rust-analyzer'],
                    json)
            end
        end

        if old_rust_before_init ~= nil then
            old_rust_before_init(init_params, config)
        end
    end
})

@erfanio
Copy link
Copy Markdown
Author

erfanio commented Apr 10, 2026

I think that's a good point about the toolchain. We could include something like this to override the default toolchain.

vim.lsp.config("rust_analyzer", {
    cmd_env = { RUSTUP_TOOLCHAIN = "nightly" },

But I guess a meta point, wouldn't this affect other editors too? I just assumed everyone is using rustup default nightly. There is note about overriding the tools to use nightly but that only works if your cwd is inside the repo but I'm not sure if GUI editors (like vscode) cd into the repo before starting rust-analyzer.

Re rustfm: I'm a bit confused because this working for me on both macos and linux. The logic seems to be if there are path separators in the rustfmt cmd, join with the root_path. So rustfmt.overrideCommand=["build/host/rustfmt/bin/rustfmt",...] should work.

@khyperia
Copy link
Copy Markdown
Contributor

khyperia commented Apr 10, 2026

Re rustfm: I'm a bit confused because this working for me on both macos and linux. The logic seems to be if there are path separators in the rustfmt cmd, join with the root_path. So rustfmt.overrideCommand=["build/host/rustfmt/bin/rustfmt",...] should work.

added some debug printlns to figure out what's going on my system (linux, fwiw) - target_spec_for_file() is returning None

https://github.com/rust-lang/rust/blob/f5eca4fcfa908d1e038afd19c6e746f075859130/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs#L2436

my debug log prints target_spec_for_file(file_id: FileId(18586), crate_id: Crate(Id(10577))) = None

which then causes this line to be hit

https://github.com/rust-lang/rust/blob/f5eca4fcfa908d1e038afd19c6e746f075859130/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs#L2452

and root_path is not prepended.


attempting to trace the execution of target_spec_for_file to figure out why it's returning None


when rustfmt is not an absolute path (i.e. removing json.rustfmt.overrideCommand[1] = init_params.rootPath .. "/" .. json.rustfmt.overrideCommand[1] from my neovim config)

  • autoformatting /d/rust/compiler/rustc_trait_selection/src/traits/normalize.rs fails
  • autoformatting /d/rust/compiler/rustc_trait_selection/src/lib.rs succeeds

@erfanio
Copy link
Copy Markdown
Author

erfanio commented Apr 10, 2026

Ah yeah that totally makes sense. I was using compiler/rustc_driver_impl/src/lib.rs 🙃 Using a different file I can repro rustfmt not working.

I have a PR rust-lang/rust-analyzer#22010 to fix this. Although while looking at this I also discovered that rust-analyzer is not working for any of the files in tests/. I assumed that's a known issue but rust-lang/rust-analyzer#18279 seems to indicate some people can use rust-analyzer for tests but I can't figure out how. I get "This file is not included in any crates, so rust-analyzer can't offer IDE services."

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review Status: this PR is waiting for a reviewer to verify its content

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants