Skip to content

Commit

Permalink
refactor!: formatter config functions take self as first argument (#233)
Browse files Browse the repository at this point in the history
This is a breaking API change, but there is a shim in place that will keep existing functions working, just with a warning notification. Most people should not encounter this at all. For anyone overriding a formatter config value with a function that takes `(ctx)` as the parameter, it will need to be updated to take `(self, ctx)`.
  • Loading branch information
stevearc authored Dec 7, 2023
1 parent 8d0421a commit 659838f
Show file tree
Hide file tree
Showing 19 changed files with 147 additions and 94 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ require("conform").formatters.shfmt = {
}
-- prepend_args can be a function, just like args
require("conform").formatters.shfmt = {
prepend_args = function(ctx)
prepend_args = function(self, ctx)
return { "-i", "2" }
end,
}
Expand Down
54 changes: 18 additions & 36 deletions doc/conform.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ CONTENTS *conform-content
1. Options |conform-options|
2. Api |conform-api|
3. Formatters |conform-formatters|
4. Autoformat |conform-autoformat|
4. Self argument migration |conform-self-args|

--------------------------------------------------------------------------------
OPTIONS *conform-options*
Expand Down Expand Up @@ -331,45 +331,27 @@ FORMATTERS *conform-formatter
`zprint` - Formatter for Clojure and EDN.

--------------------------------------------------------------------------------
AUTOFORMAT *conform-autoformat*
SELF ARGUMENT MIGRATION *conform-self-args*

If you want more complex logic than the `format_on_save` option allows, you can
write it yourself using your own autocmd. For example:
The function arguments for formatter config functions have changed. Previously,
they took a single `ctx` argument.
>lua
-- if format_on_save is a function, it will be called during BufWritePre
require("conform").setup({
format_on_save = function(bufnr)
-- Disable autoformat on certain filetypes
local ignore_filetypes = { "sql", "java" }
if vim.tbl_contains(ignore_filetypes, vim.bo[bufnr].filetype) then
return
end
-- Disable with a global or buffer-local variable
if vim.g.disable_autoformat or vim.b[bufnr].disable_autoformat then
return
end
-- Disable autoformat for files in a certain path
local bufname = vim.api.nvim_buf_get_name(bufnr)
if bufname:match("/node_modules/") then
return
{
command = "phpcbf",
args = function(ctx)
return { "-q", "--stdin-path=" .. ctx.filename, "-" }
end
-- ...additional logic...
return { timeout_ms = 500, lsp_fallback = true }
end,
})

-- There is a similar affordance for format_after_save, which uses BufWritePost.
-- This is good for formatters that are too slow to run synchronously.
require("conform").setup({
format_after_save = function(bufnr)
if vim.g.disable_autoformat or vim.b[bufnr].disable_autoformat then
return
}
<Now, they take `self` as the first argument, and `ctx` as the second.
>lua
{
command = "phpcbf",
args = function(self, ctx)
return { "-q", "--stdin-path=" .. ctx.filename, "-" }
end
-- ...additional logic...
return { lsp_fallback = true }
end,
})
<
}
<The config values that can be defined as functions are: `command`, `args`,
`range_args`, `cwd`, `env`, and `condition`.

================================================================================
vim:tw=80:ts=2:ft=help:norl:syntax=help:
2 changes: 1 addition & 1 deletion lua/conform/formatters/autopep8.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ return {
},
command = "autopep8",
args = { "-" },
range_args = function(ctx)
range_args = function(self, ctx)
return { "-", "--line-range", tostring(ctx.range.start[1]), tostring(ctx.range["end"][1]) }
end,
}
2 changes: 1 addition & 1 deletion lua/conform/formatters/clang_format.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ return {
},
command = "clang-format",
args = { "-assume-filename", "$FILENAME" },
range_args = function(ctx)
range_args = function(self, ctx)
local start_offset, end_offset = util.get_offsets_from_range(ctx.buf, ctx.range)
local length = end_offset - start_offset
return {
Expand Down
2 changes: 1 addition & 1 deletion lua/conform/formatters/darker.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ return {
description = "Run black only on changed lines.",
},
command = "darker",
args = function(ctx)
args = function(self, ctx)
-- make sure pre-save doesn't lose changes while post-save respects
-- the revision setting potentially set in pyproject.toml
if vim.bo[ctx.buf].modified then
Expand Down
2 changes: 1 addition & 1 deletion lua/conform/formatters/deno_fmt.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ return {
description = "Use [Deno](https://deno.land/) to format TypeScript, JavaScript/JSON and markdown.",
},
command = "deno",
args = function(ctx)
args = function(self, ctx)
return {
"fmt",
"-",
Expand Down
2 changes: 1 addition & 1 deletion lua/conform/formatters/markdown-toc.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ return {
},
command = "markdown-toc",
stdin = false,
args = function(ctx)
args = function(self, ctx)
-- use the indentation set in the current buffer, effectively allowing us to
-- use values from .editorconfig
local indent = vim.bo[ctx.buf].expandtab and (" "):rep(vim.bo[ctx.buf].tabstop) or "\t"
Expand Down
2 changes: 1 addition & 1 deletion lua/conform/formatters/phpcbf.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ return {
command = util.find_executable({
"vendor/bin/phpcbf",
}, "phpcbf"),
args = function(ctx)
args = function(self, ctx)
return { "-q", "--stdin-path=" .. ctx.filename, "-" }
end,
stdin = true,
Expand Down
2 changes: 1 addition & 1 deletion lua/conform/formatters/prettier.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ return {
},
command = util.from_node_modules("prettier"),
args = { "--stdin-filepath", "$FILENAME" },
range_args = function(ctx)
range_args = function(self, ctx)
local start_offset, end_offset = util.get_offsets_from_range(ctx.buf, ctx.range)
return { "$FILENAME", "--range-start=" .. start_offset, "--range-end=" .. end_offset }
end,
Expand Down
2 changes: 1 addition & 1 deletion lua/conform/formatters/prettierd.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ return {
},
command = util.from_node_modules("prettierd"),
args = { "$FILENAME" },
range_args = function(ctx)
range_args = function(self, ctx)
local start_offset, end_offset = util.get_offsets_from_range(ctx.buf, ctx.range)
return { "$FILENAME", "--range-start=" .. start_offset, "--range-end=" .. end_offset }
end,
Expand Down
2 changes: 1 addition & 1 deletion lua/conform/formatters/stylua.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ return {
},
command = "stylua",
args = { "--search-parent-directories", "--stdin-filepath", "$FILENAME", "-" },
range_args = function(ctx)
range_args = function(self, ctx)
local start_offset, end_offset = util.get_offsets_from_range(ctx.buf, ctx.range)
return {
"--search-parent-directories",
Expand Down
2 changes: 1 addition & 1 deletion lua/conform/formatters/uncrustify.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ return {
description = "A source code beautifier for C, C++, C#, ObjectiveC, D, Java, Pawn and Vala.",
},
command = "uncrustify",
args = function(ctx)
args = function(self, ctx)
return { "-q", "-l", vim.bo[ctx.buf].filetype:upper() }
end,
}
2 changes: 1 addition & 1 deletion lua/conform/formatters/yapf.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ return {
},
command = "yapf",
args = { "--quiet" },
range_args = function(ctx)
range_args = function(self, ctx)
return { "--quiet", "--lines", string.format("%d-%d", ctx.range.start[1], ctx.range["end"][1]) }
end,
}
2 changes: 1 addition & 1 deletion lua/conform/formatters/zprint.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ return {
description = "Formatter for Clojure and EDN.",
},
command = "zprint",
range_args = function(ctx)
range_args = function(self, ctx)
return {
string.format(
"{:input {:range {:start %d :end %d :use-previous-!zprint? true :continue-after-!zprint-error? true}}}",
Expand Down
26 changes: 15 additions & 11 deletions lua/conform/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ local M = {}
---@field available_msg? string

---@class (exact) conform.JobFormatterConfig
---@field command string|fun(ctx: conform.Context): string
---@field args? string|string[]|fun(ctx: conform.Context): string|string[]
---@field range_args? fun(ctx: conform.RangeContext): string|string[]
---@field cwd? fun(ctx: conform.Context): nil|string
---@field command string|fun(self: conform.JobFormatterConfig, ctx: conform.Context): string
---@field args? string|string[]|fun(self: conform.JobFormatterConfig, ctx: conform.Context): string|string[]
---@field range_args? fun(self: conform.JobFormatterConfig, ctx: conform.RangeContext): string|string[]
---@field cwd? fun(self: conform.JobFormatterConfig, ctx: conform.Context): nil|string
---@field require_cwd? boolean When cwd is not found, don't run the formatter (default false)
---@field stdin? boolean Send buffer contents to stdin (default true)
---@field condition? fun(ctx: conform.Context): boolean
---@field condition? fun(self: conform.JobFormatterConfig, ctx: conform.Context): boolean
---@field exit_codes? integer[] Exit codes that indicate success (default {0})
---@field env? table<string, any>|fun(ctx: conform.Context): table<string, any>
---@field env? table<string, any>|fun(self: conform.JobFormatterConfig, ctx: conform.Context): table<string, any>
---@field options? table

---@class (exact) conform.LuaFormatterConfig
---@field format fun(self: conform.LuaFormatterConfig, ctx: conform.Context, lines: string[], callback: fun(err: nil|string, new_lines: nil|string[]))
Expand All @@ -33,8 +34,8 @@ local M = {}

---@class (exact) conform.FormatterConfigOverride : conform.JobFormatterConfig
---@field inherit? boolean
---@field command? string|fun(ctx: conform.Context): string
---@field prepend_args? string|string[]|fun(ctx: conform.Context): string|string[]
---@field command? string|fun(self: conform.FormatterConfig, ctx: conform.Context): string
---@field prepend_args? string|string[]|fun(self: conform.FormatterConfig, ctx: conform.Context): string|string[]
---@field options? table

---@class (exact) conform.FormatterMeta
Expand Down Expand Up @@ -606,6 +607,7 @@ end
---@param bufnr? integer
---@return conform.FormatterInfo
M.get_formatter_info = function(formatter, bufnr)
local util = require("conform.util")
if not bufnr or bufnr == 0 then
bufnr = vim.api.nvim_get_current_buf()
end
Expand Down Expand Up @@ -639,19 +641,21 @@ M.get_formatter_info = function(formatter, bufnr)

local command = config.command
if type(command) == "function" then
command = command(ctx)
command = util.compat_call_with_self(formatter, config, command, ctx)
end

if vim.fn.executable(command) == 0 then
available = false
available_msg = "Command not found"
elseif config.condition and not config.condition(ctx) then
elseif
config.condition and not util.compat_call_with_self(formatter, config, config.condition, ctx)
then
available = false
available_msg = "Condition failed"
end
local cwd = nil
if config.cwd then
cwd = config.cwd(ctx)
cwd = util.compat_call_with_self(formatter, config, config.cwd, ctx)
if available and not cwd and config.require_cwd then
available = false
available_msg = "Root directory not found"
Expand Down
21 changes: 11 additions & 10 deletions lua/conform/runner.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,26 @@ local M = {}
---@class (exact) conform.RunOpts
---@field exclusive boolean If true, ensure only a single formatter is running per buffer

---@param formatter_name string
---@param ctx conform.Context
---@param config conform.JobFormatterConfig
---@return string|string[]
M.build_cmd = function(ctx, config)
M.build_cmd = function(formatter_name, ctx, config)
local command = config.command
if type(command) == "function" then
command = command(ctx)
command = util.compat_call_with_self(formatter_name, config, command, ctx)
end
---@type string|string[]
local args = {}
if ctx.range and config.range_args then
---@cast ctx conform.RangeContext
args = config.range_args(ctx)
args = util.compat_call_with_self(formatter_name, config, config.range_args, ctx)
elseif config.args then
if type(config.args) == "function" then
args = config.args(ctx)
local computed_args = config.args
if type(computed_args) == "function" then
args = util.compat_call_with_self(formatter_name, config, computed_args, ctx)
else
---@diagnostic disable-next-line: cast-local-type
args = config.args
args = computed_args
end
end

Expand Down Expand Up @@ -262,14 +263,14 @@ local function run_formatter(bufnr, formatter, config, ctx, input_lines, opts, c
return
end
---@cast config conform.JobFormatterConfig
local cmd = M.build_cmd(ctx, config)
local cmd = M.build_cmd(formatter.name, ctx, config)
local cwd = nil
if config.cwd then
cwd = config.cwd(ctx)
cwd = util.compat_call_with_self(formatter.name, config, config.cwd, ctx)
end
local env = config.env
if type(env) == "function" then
env = env(ctx)
env = util.compat_call_with_self(formatter.name, config, env, ctx)
end

local buffer_text
Expand Down
Loading

0 comments on commit 659838f

Please sign in to comment.