Skip to content

Commit afce105

Browse files
committed
feat: formatter config can specify base formatter to extend (#800)
1 parent 60df284 commit afce105

File tree

5 files changed

+46
-23
lines changed

5 files changed

+46
-23
lines changed

README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,15 @@ require("conform").formatters.shfmt = {
492492
}
493493
```
494494

495+
You can also specify which formatter to inherit from. This can be useful if you want to define multiple variants of a single formatter with slightly different options.
496+
497+
```lua
498+
require("conform").formatters.deno_fmt_markdown = {
499+
inherit = "deno_fmt",
500+
append_args = { "--indent-width", "4" },
501+
}
502+
```
503+
495504
### Magic strings
496505

497506
The following magic strings are available in `args` and `range_args`. They will be dynamically replaced at runtime with the relevant value.
@@ -623,7 +632,8 @@ require("conform").setup({
623632
env = {
624633
VAR = "value",
625634
},
626-
-- Set to false to disable merging the config with the base definition
635+
-- Set to false to disable merging the config with the base definition.
636+
-- Can also be set to the name of the formatter to merge with (e.g. inherit = "black")
627637
inherit = true,
628638
-- When inherit = true, add these additional arguments to the beginning of the command.
629639
-- This can also be a function, like args
@@ -734,7 +744,7 @@ conform.format({ lsp_format = "fallback" })
734744
-- Asynchronously format the current buffer; will not block the UI
735745
conform.format({ async = true }, function(err, did_edit)
736746
-- called after formatting
737-
end
747+
end)
738748
-- Format the current buffer with a specific formatter
739749
conform.format({ formatters = { "ruff_fix" } })
740750
```

doc/conform.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ OPTIONS *conform-option
9191
env = {
9292
VAR = "value",
9393
},
94-
-- Set to false to disable merging the config with the base definition
94+
-- Set to false to disable merging the config with the base definition.
95+
-- Can also be set to the name of the formatter to merge with (e.g. inherit = "black")
9596
inherit = true,
9697
-- When inherit = true, add these additional arguments to the beginning of the command.
9798
-- This can also be a function, like args
@@ -227,7 +228,7 @@ format({opts}, {callback}): boolean *conform.forma
227228
-- Asynchronously format the current buffer; will not block the UI
228229
conform.format({ async = true }, function(err, did_edit)
229230
-- called after formatting
230-
end
231+
end)
231232
-- Format the current buffer with a specific formatter
232233
conform.format({ formatters = { "ruff_fix" } })
233234
<

lua/conform/init.lua

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ local has_notified_ft_no_formatters = {}
402402
--- -- Asynchronously format the current buffer; will not block the UI
403403
--- conform.format({ async = true }, function(err, did_edit)
404404
--- -- called after formatting
405-
--- end
405+
--- end)
406406
--- -- Format the current buffer with a specific formatter
407407
--- conform.format({ formatters = { "ruff_fix" } })
408408
M.format = function(opts, callback)
@@ -700,6 +700,7 @@ end
700700
---@param formatter string
701701
---@param bufnr? integer
702702
---@return nil|conform.FormatterConfig
703+
---@return nil|string error_msg
703704
M.get_formatter_config = function(formatter, bufnr)
704705
if not bufnr or bufnr == 0 then
705706
bufnr = vim.api.nvim_get_current_buf()
@@ -710,32 +711,41 @@ M.get_formatter_config = function(formatter, bufnr)
710711
override = override(bufnr)
711712
end
712713
if override and override.command and override.format then
713-
local msg =
714-
string.format("Formatter '%s' cannot define both 'command' and 'format' function", formatter)
715-
notify_once(msg, vim.log.levels.ERROR)
716-
return nil
714+
return nil, "Cannot define both 'command' and 'format' function"
717715
end
718716

719717
---@type nil|conform.FormatterConfig
720-
local config = override
721-
if not override or override.inherit ~= false then
722-
local ok, mod_config = pcall(require, "conform.formatters." .. formatter)
718+
local config
719+
local inherit = (override or {}).inherit
720+
if inherit == nil then
721+
inherit = true
722+
end
723+
if inherit then
724+
local parent_formatter_name = formatter
725+
if type(inherit) == "string" then
726+
parent_formatter_name = inherit
727+
end
728+
local ok, mod_config = pcall(require, "conform.formatters." .. parent_formatter_name)
723729
if ok then
724730
if override then
725731
config = require("conform.util").merge_formatter_configs(mod_config, override)
726732
else
727733
config = mod_config
728734
end
729735
elseif override then
730-
if override.command or override.format then
736+
if override.inherit then
737+
-- We attempted to explicitly inherit, but the `require` failed
738+
return nil,
739+
string.format(
740+
"Attempting to inherit from non-existent built-in formatter '%s'",
741+
parent_formatter_name
742+
)
743+
elseif override.command or override.format then
744+
-- No built-in formatter to inherit from, but this is a complete definition
731745
config = override
732746
else
733-
local msg = string.format(
734-
"Formatter '%s' missing built-in definition\nSet `command` to get rid of this error.",
735-
formatter
736-
)
737-
notify_once(msg, vim.log.levels.ERROR)
738-
return nil
747+
-- No built-in formatter to inherit from, and this is missing a `command` or `format` function
748+
return nil, "Missing built-in definition. Set `command` to get rid of this error."
739749
end
740750
else
741751
return nil
@@ -756,13 +766,14 @@ M.get_formatter_info = function(formatter, bufnr)
756766
if not bufnr or bufnr == 0 then
757767
bufnr = vim.api.nvim_get_current_buf()
758768
end
759-
local config = M.get_formatter_config(formatter, bufnr)
769+
local config, missing_config_err = M.get_formatter_config(formatter, bufnr)
760770
if not config then
761771
return {
762772
name = formatter,
763773
command = formatter,
764774
available = false,
765-
available_msg = "Unknown formatter. Formatter config missing or incomplete",
775+
available_msg = missing_config_err
776+
or "Unknown formatter. Formatter config missing or incomplete",
766777
error = true,
767778
}
768779
end

lua/conform/types.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
---@alias conform.FormatterConfig conform.JobFormatterConfig|conform.LuaFormatterConfig
3434

3535
---@class (exact) conform.FormatterConfigOverride : conform.JobFormatterConfig
36-
---@field inherit? boolean
36+
---@field inherit? boolean|string False creates an entirely new formatter. True will merge the config with the formatter defined in conform. Use a string name (e.g. "prettier") to inherit from that conform formatter.
3737
---@field command? string|fun(self: conform.FormatterConfig, ctx: conform.Context): string
3838
---@field prepend_args? string|string[]|fun(self: conform.FormatterConfig, ctx: conform.Context): string|string[]
3939
---@field append_args? string|string[]|fun(self: conform.FormatterConfig, ctx: conform.Context): string|string[]

scripts/options_doc.lua

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ require("conform").setup({
7878
env = {
7979
VAR = "value",
8080
},
81-
-- Set to false to disable merging the config with the base definition
81+
-- Set to false to disable merging the config with the base definition.
82+
-- Can also be set to the name of the formatter to merge with (e.g. inherit = "black")
8283
inherit = true,
8384
-- When inherit = true, add these additional arguments to the beginning of the command.
8485
-- This can also be a function, like args

0 commit comments

Comments
 (0)