stevearc / conform.nvim

Lightweight yet powerful formatter plugin for Neovim
MIT License
2.55k stars 136 forks source link

Mason support #104

Open zapling opened 9 months ago

zapling commented 9 months ago

I'm wondering if there is any plan to integrate with mason, such integration would allow auto-downloading of formatters depending on which ones the user has registered. Unsure how much work this would entail or if such feature is better of in a separate plugin.

Edit:

I have created a mason-conform.nvim plugin that detects the formatters registered with conform and automatically downloads them via mason. I added a conform to mason mapping for all the formatters I could find in the mason registry, I might have missed something but feel free to create a PR if that's the case.

stevearc commented 9 months ago

My interest in this is pretty low, as I don't use mason. I'm not sure exactly what this would entail, but from skimming the docs it seems like you would just need a list of mason packages. If someone wants to do this, I think the way would be to add a new optional mason_package field to the metadata of formatters, and then plumb that through to the FormatterInfo object. Then it would be pretty easy to get your list of mason packages from the list_all_formatters() API.

xfzv commented 9 months ago

auto-downloading of formatters depending on which ones the user has registered

I'm using WhoIsSethDaniel/mason-tool-installer.nvim to do that.

zapling commented 9 months ago

auto-downloading of formatters depending on which ones the user has registered

I'm using WhoIsSethDaniel/mason-tool-installer.nvim to do that.

Have you manually specified the formatters to download, or is there a convenient way to fetch the ones that have been registered with the plugin?

xfzv commented 9 months ago

Have you manually specified the formatters to download or is there a convenient way to fetch the ones that have been registered with the plugin?

Yes, I set them manually in ensure_installed (mason-tool-installer) and also in formatters_by_ft (conform). I don't think there's another way to specify the formatters only once for both plug-ins (hence your original request).

I mentioned mason-tool-installer because you talked about auto-downloading formatters. Very useful plug-in in my opinion.

I don't mind setting all my tools in both plug-ins personally, it's pretty much "set-and-forget", unless you often add/remove LSP/linters/formatters (which isn't my case).

chrisgrieser commented 9 months ago

@xfzv @zapling I have written a small function that takes the formatters_by_ft from conform.nvim and turns them into a list that mason-tool-installer can use. That way, I only have to specify any formatter in one location.

https://github.com/chrisgrieser/.config/blob/7dc36c350976010b32ece078edd581687634811a/nvim/lua/plugins/linter-formatter.lua#L27-L82 https://github.com/chrisgrieser/.config/blob/7dc36c350976010b32ece078edd581687634811a/nvim/lua/plugins/linter-formatter.lua#L214-L234

cwrau commented 6 months ago

This would be really amazing, manually installing these tools is really annoying and errorprone

spflaumer commented 5 months ago

I have thus far used this snippet, although it might not be the most compatible with each formatter:

local mason_reg = require "mason-registry"

local formatters = {}
local formatters_by_ft = {}

for _, pkg in pairs(mason_reg.get_installed_packages()) do
    for _, type in pairs(pkg.spec.categories) do
        -- only act upon a formatter
        if type == "Formatter" then
            -- if formatter doesn't have a builtin config, create our own from a generic template
            if not require "conform".get_formatter_config(pkg.spec.name) then
                -- the key of the entry to this table
                -- is the name of the bare executable
                -- the actual value may not be the absolute path
                -- in some cases
                local bin = next(pkg.spec.bin)
                -- this should be replaced by a function
                -- that quieries the configured mason install path
                local prefix = vim.fn.stdpath("data") .. "/mason/bin/"

                formatters[pkg.spec.name] = {
                    command = prefix .. bin,
                    args = { "$FILENAME" },
                    stdin = true,
                    require_cwd = false
                }
            end

            -- finally add the formatter to it's compatible filetype(s)
            for _, ft in pairs(pkg.spec.languages) do
                local ftl = string.lower(ft)
                formatters_by_ft[ftl] = formatters_by_ft[ftl] or {}
                table.insert(formatters_by_ft[ftl], pkg.spec.name)
            end
        end
    end
end

return {
    format_on_save = {
        lsp_fallback = true,
        timeout_ms = 500
    },
    formatters = formatters,
    formatters_by_ft = formatters_by_ft
}
spflaumer commented 5 months ago

Although in some cases, some formatters tend to overwrite recent changes, like commenting something out for example. This mostly happens with formatters that didn't have a builtin configuration, as such I would assume that there is some issue with the configuration that I am providing.

Another thing to point out is the fact that using the prefix variable before bin might not be necessary, if mason.nvim is configured to append/prepend the formatter to the PATH

chrisgrieser commented 5 months ago

I think a solution that is simple to implement on conform.nvim's side, yet still useful for users, would be to expose a function that simply returns a list of all CLIs that conform.nvim requires. Basically everything listed in formatters_by_ft, minus the special formatters like trim_whitespace.

If conform.nvim had such a function, users like us could feed the list to a mason-tools-installer. Would also solve the "only need to list a CLI once" problem, and also make the config of everyone using mason + conform.nvim much cleaner.

LeonardoMor commented 5 months ago

Mason already has ensure_installed. Whenever I want to add a new formatter, I add it to that table first.

SamuelBorn commented 4 months ago

ensure_installed

This is part of the mason-lspconfig package and is not able to install formatters to my knowledge.

zapling commented 4 months ago

I have created a mason-conform.nvim plugin that detects the formatters registered with conform and automatically downloads them via mason. I added a conform to mason mapping for all the formatters I could find in the mason registry, I might have missed something but feel free to create a PR if that's the case.

SamuelBorn commented 4 months ago

I have created a mason-conform.nvim plugin that detects the formatters registered with conform and automatically downloads them via mason. I added a conform to mason mapping for all the formatters I could find in the mason registry, I might have missed something but feel free to create a PR if that's the case.

This is awesome! Takes a few steps away instead of using mason-tool-installer. Thank you.

mikesmithgh commented 4 months ago

I have created a mason-conform.nvim plugin that detects the formatters registered with conform and automatically downloads them via mason. I added a conform to mason mapping for all the formatters I could find in the mason registry, I might have missed something but feel free to create a PR if that's the case.

Very nice, works like a charm 👏

cwrau commented 4 months ago

I have created a mason-conform.nvim plugin that detects the formatters registered with conform and automatically downloads them via mason. I added a conform to mason mapping for all the formatters I could find in the mason registry, I might have missed something but feel free to create a PR if that's the case.

Awesome, works perfectly!

serranomorante commented 4 months ago

I do the inverse. I created a table that is the source of truth of everything that needs an installed package. Everything else on my config relies on this table. This allows me to just use mason.nvim, mason-tools-installer.nvim and nothing else, not even mason-lspconfig.nvim.

tools.lua

tools_by_filetype = {
javascript = {
formatters = { "prettierd" },
linters = { "eslint_d" },
dap = { "js-debug-adapter" },
lsp = { "typescript-language-server" }
},
python = { ... }
}

Then I can easily use this on plugins:

---On nvim-lint
config = function()
    ...linters_by_ft = {
      javascript = tools_by_filetype.javascript.linters, -- is { "eslint_d" }
      python = { tools_by_filetype.python.linters }, -- is { { "mypy", "pylint" } }
    }

---On conform.nvim
config = function()
    ...formatters_by_ft = {
      javascript = tools_by_filetype.javascript.formatters, -- is { "prettierd", "eslint_d" }
      typescript = { tools_by_filetype.javascript.formatters }, -- is { { "prettierd", "eslint_d" } }

---On mason-tools-installer.nvim
config = function()
    ....ensure_installed = some_utility_function(tools_by_filetype)

---On nvim-lspconfig
config = function()
    ....for _, server in ipairs(some_utility_function(tools_by_filetype, "LSP")) do
            lspconfig[server].setup({
...

This has been working fine for me. I just needed to create 2 utility functions and copy the nvim-lspconfig mappings from here: mason-lspconfig mappings

LeonardoMor commented 1 month ago

I have created a mason-conform.nvim plugin that detects the formatters registered with conform and automatically downloads them via mason. I added a conform to mason mapping for all the formatters I could find in the mason registry, I might have missed something but feel free to create a PR if that's the case.

This is awesome, thank you. And yes I was very wrong.

What if there's a formatter that is already installed system-wide? for example, what if I installed prettier not using Mason? In that case I'll add it to the conform config, but this does not need to be downloaded. Will mason-conform.nvim still try to download it?

zapling commented 1 month ago

I have created a mason-conform.nvim plugin that detects the formatters registered with conform and automatically downloads them via mason. I added a conform to mason mapping for all the formatters I could find in the mason registry, I might have missed something but feel free to create a PR if that's the case.

This is awesome, thank you. And yes I was very wrong.

What if there's a formatter that is already installed system-wide? for example, what if I installed prettier not using Mason? In that case I'll add it to the conform config, but this does not need to be downloaded. Will mason-conform.nvim still try to download it?

Right now there is no such functionality implemented. Feel free to open a ticket for it and I'll get to it when I have time.

pojokcodeid commented 1 week ago

this is my mapping config just do MasonInstall, ready to use

return {
  "stevearc/conform.nvim",
  event = { "BufReadPre", "BufNewFile" },
  opts = function()
    local mason_reg = require("mason-registry")

    local formatters = {}
    local formatters_by_ft = {}

    -- add diff langue vs filetype
    local keymap = {
      ["c++"] = "cpp",
      ["c#"] = "cs",
    }

    -- add dif conform vs mason
    local name_map = {
      ["cmakelang"] = "cmake_format",
      ["deno"] = "deno_fmt",
      ["elm-format"] = "elm_format",
      ["gdtoolkit"] = "gdformat",
      ["nixpkgs-fmt"] = "nixpkgs_fmt",
      ["opa"] = "opa_fmt",
      ["php-cs-fixer"] = "php_cs_fixer",
      ["ruff"] = "ruff_format",
      ["sql-formatter"] = "sql_formatter",
      ["xmlformatter"] = "xmlformat",
    }

    for _, pkg in pairs(mason_reg.get_installed_packages()) do
      for _, type in pairs(pkg.spec.categories) do
        -- only act upon a formatter
        if type == "Formatter" then
          -- if formatter doesn't have a builtin config, create our own from a generic template
          if not require("conform").get_formatter_config(pkg.spec.name) then
            -- the key of the entry to this table
            -- is the name of the bare executable
            -- the actual value may not be the absolute path
            -- in some cases
            local bin = next(pkg.spec.bin)
            -- this should be replaced by a function
            -- that quieries the configured mason install path
            local prefix = vim.fn.stdpath("data") .. "/mason/bin/"

            formatters[pkg.spec.name] = {
              command = prefix .. bin,
              args = { "$FILENAME" },
              stdin = true,
              require_cwd = false,
            }
          end

          -- finally add the formatter to it's compatible filetype(s)
          for _, ft in pairs(pkg.spec.languages) do
            local ftl = string.lower(ft)
            local ready = mason_reg.get_package(pkg.spec.name):is_installed()
            if ready then
              if keymap[ftl] ~= nil then
                ftl = keymap[ftl]
              end
              if name_map[pkg.spec.name] ~= nil then
                pkg.spec.name = name_map[pkg.spec.name]
              end
              formatters_by_ft[ftl] = formatters_by_ft[ftl] or {}
              table.insert(formatters_by_ft[ftl], pkg.spec.name)
            end
          end
        end
      end
    end

    return {
      format_on_save = {
        lsp_fallback = true,
        timeout_ms = 500,
      },
      formatters = formatters,
      formatters_by_ft = formatters_by_ft,
    }
  end,
  config = function(_, opts)
    local conform = require("conform")
    conform.setup(opts)
    vim.keymap.set({ "n", "v" }, "<leader>lF", function()
      conform.format({
        lsp_fallback = true,
        async = false,
        timeout_ms = 500,
      })
    end, { desc = "Format file or range (in visual mode)" })
  end,
}