I like the conform.nvim plugin - it helps me automatically format different file types.
But I had two common cases where I wanted a bit more flexibility:
- Sometimes I need to use different formatters per project.
- Sometimes I want to pass extra arguments to a formatter, especially if the project doesn’t already include a formatter-specific config file.
At first, I hardcoded all of this directly in my conform.lua
setup.
But then I thought - what if I could store the configuration inside each project repository?
This is where .conform.json
comes in.
Example .conform.json
file
{
"formatters_by_ft": {
"python": ["isort", "yapf"],
"json": ["prettier"],
"yaml": ["prettier"]
},
"args": {
"yapf": ["--style", "{based_on_style: pep8, indent_width: 4}"],
"isort": ["--profile", "open_stack", "--line-length", "79"]
}
}
If the project contains .conform.json
, conform.nvim
will read it and use whatever formatters and arguments you define there.
If not, it will just fall back to the defaults.
-
formatters_by_ft
— which formatter(s) to use per filetype. -
args
— optional arguments to pass to each formatter.
Tip: If you don’t want to commit .conform.json
to your repository, you can add it to your global gitignore file:
echo ".conform.json" >> ~/.gitignore_global
Plugin config
Here’s the conform.nvim
configuration that automatically reads .conform.json
from the git root if it exists:
return {
"stevearc/conform.nvim",
config = function()
local conform = require("conform")
-- Constant path to the config file (relative to git root)
local config_filename = ".conform.json"
-- Default formatters if no config file present
local defaults = {
formatters = {
lua = { "stylua" },
python = { "isort", "yapf" },
json = { "prettier" },
yaml = { "prettier" },
markdown = { "prettier" },
},
args = {},
}
--- Load configuration from `.conform.json` in the git repository root
--- Example `.conform.json`:
-- {
-- "formatters_by_ft": {
-- "python": ["isort", "yapf"],
-- "json": ["prettier"],
-- "yaml": ["prettier"]
-- },
-- "args": {
-- "yapf": ["--style", "{based_on_style: pep8, indent_width: 4}"],
-- "isort": ["--profile", "open_stack", "--line-length", "79"]
-- }
-- }
local load_config = function(bufnr)
local path = vim.fn.fnamemodify(vim.api.nvim_buf_get_name(bufnr), ":h")
local git_root = vim.fn.systemlist(
"git -C " .. vim.fn.fnameescape(path) .. " rev-parse --show-toplevel"
)[1]
if vim.v.shell_error ~= 0 or not git_root then
vim.notify("No git root found, using defaults", vim.log.levels.DEBUG)
return nil
end
local cfg_path = git_root .. "/" .. config_filename
if vim.fn.filereadable(cfg_path) == 0 then
vim.notify("No " .. config_filename .. " found, using defaults", vim.log.levels.DEBUG)
return nil
end
local ok_read, content = pcall(vim.fn.readfile, cfg_path)
if not ok_read or not content then
vim.notify("Failed to read " .. cfg_path, vim.log.levels.ERROR)
return nil
end
local ok_parse, parsed = pcall(vim.fn.json_decode, table.concat(content, "\n"))
if not ok_parse then
vim.notify("Invalid JSON in " .. cfg_path, vim.log.levels.ERROR)
return nil
end
return {
formatters = parsed.formatters_by_ft or {},
args = parsed.args or {},
}
end
local orig_format = conform.format
conform.format = function(opts)
local bufnr = opts.buf or vim.api.nvim_get_current_buf()
local cfg = load_config(bufnr) or defaults
vim.notify("Using conform config: " .. vim.inspect(cfg), vim.log.levels.DEBUG)
conform.formatters_by_ft = cfg.formatters
for name, arg_list in pairs(cfg.args) do
conform.formatters[name] = conform.formatters[name] or {}
conform.formatters[name].prepend_args = function()
return arg_list
end
end
orig_format(opts)
end
vim.keymap.set({ "n", "v" }, "<leader>cf", function()
conform.format({
lsp_fallback = true,
async = false,
timeout_ms = 5000,
})
end, { desc = "Format current file with conform" })
end,
}
Why this is useful
This small setup makes conform.nvim
much more flexible: each project can define its own formatter set and options, without touching your main neovim config.
Link to my config
You can check out my dotfiles here: https://github.com/art-vasilyev/.dotfiles
Top comments (0)