Skip to content

Commit

Permalink
feat: allow formatters_by_ft to be a function (nvim-lua#174)
Browse files Browse the repository at this point in the history
  • Loading branch information
stevearc committed Nov 4, 2023
1 parent 893b139 commit 0bbe838
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 32 deletions.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -372,9 +372,17 @@ require("conform").setup({
formatters_by_ft = {
lua = { "stylua" },
-- Conform will run multiple formatters sequentially
python = { "isort", "black" },
go = { "goimports", "gofmt" },
-- Use a sub-list to run only the first available formatter
javascript = { { "prettierd", "prettier" } },
-- You can use a function here to determine the formatters dynamically
python = function(bufnr)
if require("conform").get_formatter_info("ruff_format", bufnr).available then
return { "ruff_format" }
else
return { "isort", "black" }
end
end,
-- Use the "*" filetype to run formatters on all filetypes.
["*"] = { "codespell" },
-- Use the "_" filetype to run formatters on filetypes that don't
Expand Down
10 changes: 9 additions & 1 deletion doc/conform.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,17 @@ OPTIONS *conform-option
formatters_by_ft = {
lua = { "stylua" },
-- Conform will run multiple formatters sequentially
python = { "isort", "black" },
go = { "goimports", "gofmt" },
-- Use a sub-list to run only the first available formatter
javascript = { { "prettierd", "prettier" } },
-- You can use a function here to determine the formatters dynamically
python = function(bufnr)
if require("conform").get_formatter_info("ruff_format", bufnr).available then
return { "ruff_format" }
else
return { "isort", "black" }
end
end,
-- Use the "*" filetype to run formatters on all filetypes.
["*"] = { "codespell" },
-- Use the "_" filetype to run formatters on filetypes that don't
Expand Down
8 changes: 7 additions & 1 deletion lua/conform/formatters/injected.lua
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,13 @@ return {
num_format = num_format + 1
last_start_lnum = start_lnum
local input_lines = util.tbl_slice(lines, start_lnum, end_lnum)
local formatter_names = conform.formatters_by_ft[lang]
local ft_formatters = conform.formatters_by_ft[lang]
local formatter_names
if type(ft_formatters) == "function" then
formatter_names = ft_formatters(ctx.buf)
else
formatter_names = require("conform").resolve_formatters(ft_formatters, ctx.buf, false)
end
local format_opts = { async = true, bufnr = ctx.buf, quiet = true }
local idx = num_format
log.debug("Injected format %s:%d:%d: %s", lang, start_lnum, end_lnum, formatter_names)
Expand Down
8 changes: 7 additions & 1 deletion lua/conform/health.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,14 @@ local function get_formatter_filetypes(name)
local conform = require("conform")
local filetypes = {}
for filetype, formatters in pairs(conform.formatters_by_ft) do
if type(formatters) == "function" then
formatters = formatters(0)
-- support the old structure where formatters could be a subkey
if not vim.tbl_islist(formatters) then
elseif not vim.tbl_islist(formatters) then
vim.notify_once(
"Using deprecated structure for formatters_by_ft. See :help conform-options for details.",
vim.log.levels.ERROR
)
---@diagnostic disable-next-line: undefined-field
formatters = formatters.formatters
end
Expand Down
55 changes: 28 additions & 27 deletions lua/conform/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ local M = {}
---@field end integer[]

---@alias conform.FormatterUnit string|string[]
---@alias conform.FiletypeFormatter conform.FormatterUnit[]|fun(bufnr: integer): string[]

---@type table<string, conform.FormatterUnit[]>
---@type table<string, conform.FiletypeFormatter>
M.formatters_by_ft = {}

---@type table<string, conform.FormatterConfigOverride|fun(bufnr: integer): nil|conform.FormatterConfigOverride>
Expand All @@ -78,20 +79,6 @@ M.setup = function(opts)
M.notify_on_error = opts.notify_on_error
end

for ft, formatters in pairs(M.formatters_by_ft) do
---@diagnostic disable-next-line: undefined-field
if formatters.format_on_save ~= nil then
vim.notify(
string.format(
'The "format_on_save" option for filetype "%s" is deprecated. It is recommended to put this logic in the autocmd, see :help conform-autoformat',
ft
),
vim.log.levels.WARN
)
break
end
end

local aug = vim.api.nvim_create_augroup("Conform", { clear = true })
if opts.format_on_save then
if type(opts.format_on_save) == "boolean" then
Expand Down Expand Up @@ -214,7 +201,6 @@ local function get_matching_filetype(bufnr)
local filetypes = vim.split(vim.bo[bufnr].filetype, ".", { plain = true })
table.insert(filetypes, "_")
for _, filetype in ipairs(filetypes) do
---@type conform.FormatterUnit[]
local ft_formatters = M.formatters_by_ft[filetype]
if ft_formatters then
return filetype
Expand Down Expand Up @@ -254,16 +240,23 @@ M.list_formatters_for_buffer = function(bufnr)
end
table.insert(filetypes, "*")
for _, ft in ipairs(filetypes) do
---@type conform.FormatterUnit[]
local ft_formatters = M.formatters_by_ft[ft]
if ft_formatters then
-- support the old structure where formatters could be a subkey
if not vim.tbl_islist(ft_formatters) then
---@diagnostic disable-next-line: undefined-field
ft_formatters = ft_formatters.formatters
end
if type(ft_formatters) == "function" then
dedupe_formatters(ft_formatters(bufnr), formatters)
else
-- support the old structure where formatters could be a subkey
if not vim.tbl_islist(ft_formatters) then
vim.notify_once(
"Using deprecated structure for formatters_by_ft. See :help conform-options for details.",
vim.log.levels.ERROR
)
---@diagnostic disable-next-line: undefined-field
ft_formatters = ft_formatters.formatters
end

dedupe_formatters(ft_formatters, formatters)
dedupe_formatters(ft_formatters, formatters)
end
end
end

Expand Down Expand Up @@ -301,11 +294,12 @@ local function range_from_selection(bufnr, mode)
}
end

---@private
---@param names conform.FormatterUnit[]
---@param bufnr integer
---@param warn_on_missing boolean
---@return conform.FormatterInfo[]
local function resolve_formatters(names, bufnr, warn_on_missing)
M.resolve_formatters = function(names, bufnr, warn_on_missing)
local all_info = {}
local function add_info(info, warn)
if info.available then
Expand Down Expand Up @@ -372,7 +366,7 @@ M.format = function(opts, callback)
local explicit_formatters = opts.formatters ~= nil
local formatter_names = opts.formatters or M.list_formatters_for_buffer(opts.bufnr)
local formatters =
resolve_formatters(formatter_names, opts.bufnr, not opts.quiet and explicit_formatters)
M.resolve_formatters(formatter_names, opts.bufnr, not opts.quiet and explicit_formatters)

local any_formatters = not vim.tbl_isempty(formatters)
if not explicit_formatters and opts.lsp_fallback == true and M.will_fallback_lsp(opts) then
Expand Down Expand Up @@ -464,7 +458,7 @@ M.format_lines = function(formatter_names, lines, opts, callback)
local errors = require("conform.errors")
local log = require("conform.log")
local runner = require("conform.runner")
local formatters = resolve_formatters(formatter_names, opts.bufnr, not opts.quiet)
local formatters = M.resolve_formatters(formatter_names, opts.bufnr, not opts.quiet)
if vim.tbl_isempty(formatters) then
callback(nil, lines)
return
Expand Down Expand Up @@ -499,16 +493,23 @@ M.list_formatters = function(bufnr)
bufnr = vim.api.nvim_get_current_buf()
end
local formatters = M.list_formatters_for_buffer(bufnr)
return resolve_formatters(formatters, bufnr, false)
return M.resolve_formatters(formatters, bufnr, false)
end

---List information about all filetype-configured formatters
---@return conform.FormatterInfo[]
M.list_all_formatters = function()
local formatters = {}
for _, ft_formatters in pairs(M.formatters_by_ft) do
if type(ft_formatters) == "function" then
ft_formatters = ft_formatters(0)
end
-- support the old structure where formatters could be a subkey
if not vim.tbl_islist(ft_formatters) then
vim.notify_once(
"Using deprecated structure for formatters_by_ft. See :help conform-options for details.",
vim.log.levels.ERROR
)
---@diagnostic disable-next-line: undefined-field
ft_formatters = ft_formatters.formatters
end
Expand Down
10 changes: 9 additions & 1 deletion scripts/options_doc.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,17 @@ require("conform").setup({
formatters_by_ft = {
lua = { "stylua" },
-- Conform will run multiple formatters sequentially
python = { "isort", "black" },
go = { "goimports", "gofmt" },
-- Use a sub-list to run only the first available formatter
javascript = { { "prettierd", "prettier" } },
-- You can use a function here to determine the formatters dynamically
python = function(bufnr)
if require("conform").get_formatter_info("ruff_format", bufnr).available then
return { "ruff_format" }
else
return { "isort", "black" }
end
end,
-- Use the "*" filetype to run formatters on all filetypes.
["*"] = { "codespell" },
-- Use the "_" filetype to run formatters on filetypes that don't
Expand Down

0 comments on commit 0bbe838

Please sign in to comment.