stevearc / conform.nvim

Lightweight yet powerful formatter plugin for Neovim
MIT License
2.72k stars 142 forks source link

Question: How to customize the formatter prepend_args per filetype? #339

Closed polirritmico closed 4 months ago

polirritmico commented 4 months ago

Hi, first want to thank you for sharing this great plugin!

My question is if there's a 'proper way' to pass custom prepends_args to a formatter only for a certain filetype?

For example, I use prettier to format my json, css, yaml and markdown files. I want to set --prose-wrap to always and --print-width to 80 only for markdown. How should I do it?

I have fully read the documentation but I've only seen ways to fully customize the formatter that would affect all the associated filetypes.

I've got this working, but seems too hacky (lazy.nvim config):

return {
  "stevearc/conform.nvim",
  dependencies = { "mason.nvim" },
  event = { "BufWritePre" },
  cmd = { "ConformInfo" },
  opts = {
    formatters_by_ft = {
      ["*"] = { "trim_whitespace" },
      json = { "prettier" },
      markdown = { "prettier_markdown" },
    },
    format_on_save = function(bufnr)
      return { timeout_ms = 500, lsp_fallback = true }
    end,
    formatters = {
      prettier = { prepend_args = { "--tab-width", "2" } },
    },
  },
  init = function() vim.o.formatexpr = [[v:lua.require("conform").formatexpr()]] end,
  config = function(_, opts)
    require("conform").setup(opts)

    require("conform").formatters.prettier_markdown = function()
      return {
        command = require("conform.util").from_node_modules("prettier"),
        cwd = require("conform.formatters.prettier").cwd,
        args = {
          "--prose-wrap",
          "always",
          "--print-width",
          "80",
          "--stdin-filepath",
          "$FILENAME",
        },
      }
    end
  end,
}

Regards

stevearc commented 4 months ago

Defining a separate formatter is the way to go. You don't have to duplicate everything, you can just copy the original definition and modify it. Something like this:

require("conform").formatters.prettier_markdown = vim.deepcopy(require("conform.formatters.prettier"))
require("conform").formatters.prettier_markdown.prepend_args = {"--prose-wrap",
          "always",
          "--print-width",
          "80",
}
polirritmico commented 4 months ago

Thanks for taking the time to respond. Really appreciated!

polirritmico commented 4 months ago

Hi, I couldn't get it working with that approach because the prepend_args never get merged into args in the runner.run_formatter call. Also, I got a assign-type-mismatch LSP warning:

Diagnostics:
1. Cannot assign `conform.FileFormatterConfig|unknown` to `conform.FormatterConfigOverride|fun(bufnr: integer):conform.FormatterConfigOverride|nil`.
   - `conform.FileFormatterConfig` cannot match `conform.FormatterConfigOverride|fun(bufnr: integer):conform.FormatterConfigOverride|nil`
   - `conform.FileFormatterConfig` cannot match any subtypes in `conform.FormatterConfigOverride|fun(bufnr: integer):conform.FormatterConfigOverride|nil`
   - Type `conform.FileFormatterConfig` cannot match `fun(bufnr: integer):conform.FormatterConfigOverride|nil`
   - Type `conform.JobFormatterConfig` cannot match `fun(bufnr: integer):conform.FormatterConfigOverride|nil`
   - Type `conform.FileFormatterConfig` cannot match `conform.FormatterConfigOverride`
   - Type `conform.JobFormatterConfig` cannot match `conform.FormatterConfigOverride` [assign-type-mismatch]

Digging into the code I managed to get it running with util.add_formatter_args.

Since I couldn't find this in the documentation I share my conclusion here:

require("conform").setup(opts)

local markdown_formatter = vim.deepcopy(require("conform.formatters.prettier"))
require("conform.util").add_formatter_args(markdown_formatter, {
  "--prose-wrap",
  "always",
  "--print-width",
  "80",
}, { append = false })
---@cast markdown_formatter conform.FormatterConfigOverride
require("conform").formatters.prettier_markdown = markdown_formatter

Thanks!

Edit: Added cast