Skip to content

Commit

Permalink
feat: format_on_save and format_after_save can be functions
Browse files Browse the repository at this point in the history
  • Loading branch information
stevearc committed Sep 10, 2023
1 parent 6b3dc33 commit dd5b2f2
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 42 deletions.
41 changes: 28 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,11 @@ require("conform").setup({
lsp_fallback = true,
timeout_ms = 500,
},
-- If this is set, Conform will run the formatter asynchronously after save.
-- It will pass the table to conform.format().
format_after_save = {
lsp_fallback = true,
},
-- Set the log level. Use `:ConformInfo` to see the location of the log file.
log_level = vim.log.levels.ERROR,
-- Conform will notify you when a formatter errors
Expand Down Expand Up @@ -301,7 +306,7 @@ require("conform").formatters.yamlfix = vim.tbl_deep_extend("force", require("co
## Autoformat on save

If you want more complex logic than the `format_on_save` option allows, you can write it yourself
using your own autocmd. For example:
using an autocmd. For example:

<!-- AUTOFORMAT -->

Expand All @@ -328,17 +333,27 @@ vim.api.nvim_create_autocmd("BufWritePre", {
end,
})

-- Format asynchronously on save
vim.api.nvim_create_autocmd("BufWritePost", {
pattern = "*",
callback = function(args)
require("conform").format({ async = true, lsp_fallback = true, bufnr = args.buf }, function(err)
if not err then
vim.api.nvim_buf_call(args.buf, function()
vim.cmd.update()
end)
end
end)
-- To eliminate the boilerplate, you can pass a function to format_on_save
-- and it will be called during the BufWritePre callback.
require("conform").setup({
format_on_save = function(bufnr)
if vim.g.disable_autoformat or vim.b[bufnr].disable_autoformat then
return
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
end
-- ...additional logic...
return { lsp_fallback = true }
end,
})
```
Expand All @@ -359,7 +374,7 @@ Format a buffer
| opts | `nil\|table` | | |
| | timeout_ms | `nil\|integer` | Time in milliseconds to block for formatting. Defaults to 1000. No effect if async = true. |
| | bufnr | `nil\|integer` | Format this buffer (default 0) |
| | async | `nil\|boolean` | If true the method won't block. Defaults to false. |
| | async | `nil\|boolean` | If true the method won't block. Defaults to false. If the buffer is modified before the formatter completes, the formatting will be discarded. |
| | formatters | `nil\|string[]` | List of formatters to run. Defaults to all formatters for the buffer filetype. |
| | lsp_fallback | `nil\|boolean\|"always"` | Attempt LSP formatting if no formatters are available. Defaults to false. If "always", will attempt LSP formatting even if formatters are available (useful if you set formatters for the "*" filetype) |
| | quiet | `nil\|boolean` | Don't show any notifications for warnings or failures. Defaults to false. |
Expand Down
40 changes: 28 additions & 12 deletions doc/conform.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ OPTIONS *conform-option
lsp_fallback = true,
timeout_ms = 500,
},
-- If this is set, Conform will run the formatter asynchronously after save.
-- It will pass the table to conform.format().
format_after_save = {
lsp_fallback = true,
},
-- Set the log level. Use `:ConformInfo` to see the location of the log file.
log_level = vim.log.levels.ERROR,
-- Conform will notify you when a formatter errors
Expand Down Expand Up @@ -96,7 +101,8 @@ format({opts}, {callback}): boolean *conform.forma
true.
{bufnr} `nil|integer` Format this buffer (default 0)
{async} `nil|boolean` If true the method won't block. Defaults
to false.
to false. If the buffer is modified before the
formatter completes, the formatting will be discarded.
{formatters} `nil|string[]` List of formatters to run. Defaults to
all formatters for the buffer filetype.
{lsp_fallback} `nil|boolean|"always"` Attempt LSP formatting if no
Expand Down Expand Up @@ -232,17 +238,27 @@ write it yourself using your own autocmd. For example:
end,
})

-- Format asynchronously on save
vim.api.nvim_create_autocmd("BufWritePost", {
pattern = "*",
callback = function(args)
require("conform").format({ async = true, lsp_fallback = true, bufnr = args.buf }, function(err)
if not err then
vim.api.nvim_buf_call(args.buf, function()
vim.cmd.update()
end)
end
end)
-- To eliminate the boilerplate, you can pass a function to format_on_save
-- and it will be called during the BufWritePre callback.
require("conform").setup({
format_on_save = function(bufnr)
if vim.g.disable_autoformat or vim.b[bufnr].disable_autoformat then
return
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
end
-- ...additional logic...
return { lsp_fallback = true }
end,
})
<
Expand Down
54 changes: 48 additions & 6 deletions lua/conform/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,61 @@ M.setup = function(opts)
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
opts.format_on_save = {}
end
local aug = vim.api.nvim_create_augroup("Conform", { clear = true })
vim.api.nvim_create_autocmd("BufWritePre", {
pattern = "*",
group = aug,
callback = function(args)
local format_opts = vim.tbl_deep_extend("keep", opts.format_on_save, {
buf = args.buf,
})
M.format(format_opts)
local format_args = opts.format_on_save
if type(format_args) == "function" then
format_args = format_args(args.buf)
end
if format_args then
M.format(vim.tbl_deep_extend("force", format_args, {
buf = args.buf,
async = false,
}))
end
end,
})
end

if opts.format_after_save then
if type(opts.format_after_save) == "boolean" then
opts.format_after_save = {}
end
vim.api.nvim_create_autocmd("BufWritePost", {
pattern = "*",
group = aug,
callback = function(args)
if vim.b[args.buf].conform_applying_formatting then
return
end
local format_args = opts.format_after_save
if type(format_args) == "function" then
format_args = format_args(args.buf)
end
if format_args then
M.format(
vim.tbl_deep_extend("force", format_args, {
buf = args.buf,
async = true,
}),
function(err)
if not err and vim.api.nvim_buf_is_valid(args.buf) then
vim.api.nvim_buf_call(args.buf, function()
vim.b[args.buf].conform_applying_formatting = true
vim.cmd.update()
vim.b[args.buf].conform_applying_formatting = false
end)
end
end
)
end
end,
})
end
Expand Down Expand Up @@ -211,7 +253,7 @@ end
---@param opts? table
--- timeout_ms nil|integer Time in milliseconds to block for formatting. Defaults to 1000. No effect if async = true.
--- bufnr nil|integer Format this buffer (default 0)
--- async nil|boolean If true the method won't block. Defaults to false.
--- async nil|boolean If true the method won't block. Defaults to false. If the buffer is modified before the formatter completes, the formatting will be discarded.
--- formatters nil|string[] List of formatters to run. Defaults to all formatters for the buffer filetype.
--- lsp_fallback nil|boolean|"always" Attempt LSP formatting if no formatters are available. Defaults to false. If "always", will attempt LSP formatting even if formatters are available (useful if you set formatters for the "*" filetype)
--- quiet nil|boolean Don't show any notifications for warnings or failures. Defaults to false.
Expand Down
32 changes: 21 additions & 11 deletions scripts/autoformat_doc.lua
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,26 @@ vim.api.nvim_create_autocmd("BufWritePre", {
end,
})

-- Format asynchronously on save
vim.api.nvim_create_autocmd("BufWritePost", {
pattern = "*",
callback = function(args)
require("conform").format({ async = true, lsp_fallback = true, bufnr = args.buf }, function(err)
if not err then
vim.api.nvim_buf_call(args.buf, function()
vim.cmd.update()
end)
end
end)
-- To eliminate the boilerplate, you can pass a function to format_on_save
-- and it will be called during the BufWritePre callback.
require("conform").setup({
format_on_save = function(bufnr)
if vim.g.disable_autoformat or vim.b[bufnr].disable_autoformat then
return
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
end
-- ...additional logic...
return { lsp_fallback = true }
end,
})
5 changes: 5 additions & 0 deletions scripts/options_doc.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ require("conform").setup({
lsp_fallback = true,
timeout_ms = 500,
},
-- If this is set, Conform will run the formatter asynchronously after save.
-- It will pass the table to conform.format().
format_after_save = {
lsp_fallback = true,
},
-- Set the log level. Use `:ConformInfo` to see the location of the log file.
log_level = vim.log.levels.ERROR,
-- Conform will notify you when a formatter errors
Expand Down

0 comments on commit dd5b2f2

Please sign in to comment.