conform.nvim icon indicating copy to clipboard operation
conform.nvim copied to clipboard

bug: inconsistent formatting when using `rustfmt` through `conform` as opposed to the shell

Open tvsfx opened this issue 1 year ago • 2 comments

Neovim version (nvim -v)

v0.10.0 (tag commit)

Operating system/version

Ubuntu 20.04

Add the debug logs

  • [X] I have set log_level = vim.log.levels.DEBUG and pasted the log contents below.

Log file

Logs when running require("conform").format({ async = true, lsp_fallback = false }) on this file look normal:

02:30:13[DEBUG] Running formatters on /home/thomas/Documents/Coding/openraft/openraft/src/storage/v2.rs: { "rustfmt" }
02:30:13[INFO] Run rustfmt on /home/thomas/Documents/Coding/openraft/openraft/src/storage/v2.rs
02:30:13[DEBUG] Run command: { "/home/thomas/.cargo/bin/rustfmt", "--emit=stdout", "--edition=2021" }
02:30:13[DEBUG] rustfmt exited with code 0

Describe the bug

When I run the command /home/thomas/.cargo/bin/rustfmt -emit=stdout -edition=2021 in the shell, I get correct formatting. When this same command is run in nvim through conform.nvim, the result is inconsistent; formatting is correct at times, but not at others. I thought it was a matter of finding the rustfmt.toml at first, but when I set --config-path to the rustfmt.toml file, the issue persists. The differences in formatting are all to do with function signatures, e.g. (green is the incorrect output of conform), which makes me think it's a matter of using the wrong rustfmt.toml (even though I explicitly pass its path):

-    async fn try_get_log_entry(&mut self, log_index: u64) -> Result<Option<C::Entry>, StorageError<C::NodeId>> {
+    async fn try_get_log_entry(
+        &mut self,
+        log_index: u64,
+    ) -> Result<Option<C::Entry>, StorageError<C::NodeId>> {

What is the severity of this bug?

breaking (some functionality is broken)

Steps To Reproduce

The keybind that triggers the bug is:

{
    "<leader>bf",
    function()
        require("conform").format({ async = false, lsp_fallback = false })
    end,
    mode = "",
    desc = "[B]uffer (or selection) [F]ormat",
},

I pointed rustfmt to a specific binary to rule out shell sourcing shenanigans, using the following config in conform.nvim:

formatters_by_ft = {
    rust = { "rustfmt" },
},
formatters = {
    rustfmt = {
        command = vim.fn.expand("~") .. "/.cargo/bin/rustfmt",
        prepends_args = { "--config-path", "/home/thomas/Documents/Coding/openraft/rustfmt.toml" },
    },
},

but this did not fix the bug consistently.

Contents of rustfmt.toml: https://github.com/datafuselabs/openraft/blob/main/rustfmt.toml

Expected Behavior

Formatting should be consistent between the shell and conform invocations of rustfmt

Minimal example file

No response

Minimal init.lua

-- DO NOT change the paths and don't remove the colorscheme
local root = vim.fn.fnamemodify("./.repro", ":p")

-- set stdpaths to use .repro
for _, name in ipairs({ "config", "data", "state", "cache" }) do
  vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name
end

-- bootstrap lazy
local lazypath = root .. "/plugins/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
  vim.fn.system({
    "git",
    "clone",
    "--filter=blob:none",
    "--single-branch",
    "https://github.com/folke/lazy.nvim.git",
    lazypath,
  })
end
vim.opt.runtimepath:prepend(lazypath)

-- install plugins
local plugins = {
  "folke/tokyonight.nvim",
  {
    "stevearc/conform.nvim",
    config = function()
      require("conform").setup({
        log_level = vim.log.levels.DEBUG,
        -- add your config here
      })
    end,
  },
  -- add any other plugins here
{
    "stevearc/conform.nvim",
    lazy = false,
    keys = {
        {
            "<leader>bf",
            function()
                require("conform").format({ async = false, lsp_fallback = false })
            end,
            mode = "",
            desc = "[B]uffer (or selection) [F]ormat",
        },
    },
    opts = {
        notify_on_error = false,
        formatters_by_ft = {
            lua = { "stylua" },
            rust = { "rustfmt" },
        },
        formatters = {
            rustfmt = {
                command = vim.fn.expand("~") .. "/.cargo/bin/rustfmt",
            },
        },
        log_level = 1,
    },
}
}
require("lazy").setup(plugins, {
  root = root .. "/plugins",
})

vim.cmd.colorscheme("tokyonight")
-- add anything else here

Additional context

No response

tvsfx avatar May 23 '24 01:05 tvsfx

Check that you are running the command line program from the same cwd that you're opening vim from. If the same binary is producing different output when fed the same stdin, arguments, and when run in the same cwd, then something very weird is going on. I suspect that instead there is something different in how the commands are run.

stevearc avatar May 23 '24 02:05 stevearc

Yep, you're right, changing the cwd in nvim fixes the issue. Before, I was running from ~, which triggers wrong formatting. If I cd to the basedir of the file, all works fine.

The weird thing though is that in the terminal, the cwd seems to have no impact on the result; running /home/thomas/.cargo/bin/rustfmt --edition=2021 [--emit=stdout]* ~/Documents/Coding/openraft/openraft/src/storage/v2.rs from ~ does work, whereas through conform it does not. The only other thing I can think of (with low confidence, given that I just diagonally read some of the conform code) is that using relative file paths here in the codebase somehow confuses rustfmt into checking the wrong folder for the rustfmt.toml file, and coming up empty.

Adding the following workaround to the config of rustfmt does fix the issue for me, it seems:

cwd = function(self, ctx)
    local root = require("conform.util").root_file({ "rustfmt.toml", ".rustfmt.toml" })
    print("root found:", vim.inspect(root(self, ctx)))
    return root(self, ctx)
end,

tvsfx avatar May 23 '24 10:05 tvsfx

Great! I've added that as part of the default config

stevearc avatar Jun 05 '24 20:06 stevearc