From 4c67568df62e905c6d4194a602a65490f78ffc35 Mon Sep 17 00:00:00 2001 From: Steven Arcangeli Date: Thu, 7 Sep 2023 17:57:32 -0700 Subject: refactor: make get_formatter_info a public method --- doc/conform.txt | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'doc/conform.txt') diff --git a/doc/conform.txt b/doc/conform.txt index 46c971f..8a8d60b 100644 --- a/doc/conform.txt +++ b/doc/conform.txt @@ -121,6 +121,14 @@ list_formatters({bufnr}): conform.FormatterInfo[] *conform.list_formatter list_all_formatters(): conform.FormatterInfo[] *conform.list_all_formatters* List information about all filetype-configured formatters + +get_formatter_info({formatter}, {bufnr}): conform.FormatterInfo *conform.get_formatter_info* + Get information about a formatter (including availability) + + Parameters: + {formatter} `string` + {bufnr} `nil|integer` + -------------------------------------------------------------------------------- FORMATTERS *conform-formatters* -- cgit v1.2.3-70-g09d2 From bd1aa02ef191410b2ea0b3ef5caabe06592d9c51 Mon Sep 17 00:00:00 2001 From: Steven Arcangeli Date: Thu, 7 Sep 2023 20:28:07 -0700 Subject: refactor!: remove run_all_formatters config option run_all_formatters is now the default. To only run the first formatter (the previous behavior), see the next commit --- README.md | 22 ++++------------------ doc/conform.txt | 12 ++---------- lua/conform/init.lua | 41 ++++++++++++++--------------------------- scripts/options_doc.lua | 12 ++---------- 4 files changed, 22 insertions(+), 65 deletions(-) (limited to 'doc/conform.txt') diff --git a/README.md b/README.md index 8331dae..8d6cd45 100644 --- a/README.md +++ b/README.md @@ -110,14 +110,8 @@ At a minimum, you will need to set up some formatters by filetype require("conform").setup({ formatters_by_ft = { lua = { "stylua" }, - -- Conform will use the first available formatter in the list - javascript = { "prettier_d", "prettier" }, - -- Formatters can also be specified with additional options - python = { - formatters = { "isort", "black" }, - -- Run formatters one after another instead of stopping at the first success - run_all_formatters = true, - }, + -- Conform will run multiple formatters sequentially + python = { "isort", "black" }, }, }) ``` @@ -214,16 +208,8 @@ require("conform").setup({ -- Map of filetype to formatters formatters_by_ft = { lua = { "stylua" }, - -- Conform will use the first available formatter in the list - javascript = { "prettierd", "prettier" }, - -- Formatters can also be specified with additional options - python = { - formatters = { "isort", "black" }, - -- Run formatters one after another instead of stopping at the first success - run_all_formatters = true, - -- Don't run these formatters as part of the format_on_save autocmd (see below) - format_on_save = false, - }, + -- Conform will run multiple formatters sequentially + python = { "isort", "black" }, }, -- If this is set, Conform will run the formatter on save. -- It will pass the table to conform.format(). diff --git a/doc/conform.txt b/doc/conform.txt index 8a8d60b..5995f99 100644 --- a/doc/conform.txt +++ b/doc/conform.txt @@ -16,16 +16,8 @@ OPTIONS *conform-option -- Map of filetype to formatters formatters_by_ft = { lua = { "stylua" }, - -- Conform will use the first available formatter in the list - javascript = { "prettierd", "prettier" }, - -- Formatters can also be specified with additional options - python = { - formatters = { "isort", "black" }, - -- Run formatters one after another instead of stopping at the first success - run_all_formatters = true, - -- Don't run these formatters as part of the format_on_save autocmd (see below) - format_on_save = false, - }, + -- Conform will run multiple formatters sequentially + python = { "isort", "black" }, }, -- If this is set, Conform will run the formatter on save. -- It will pass the table to conform.format(). diff --git a/lua/conform/init.lua b/lua/conform/init.lua index 3b7c415..ef6f94a 100644 --- a/lua/conform/init.lua +++ b/lua/conform/init.lua @@ -38,13 +38,9 @@ local M = {} ---@field start integer[] ---@field end integer[] ----@class (exact) conform.RunOptions ----@field run_all_formatters nil|boolean Run all listed formatters instead of stopping at the first one. +---@alias conform.FormatterUnit string|string[] ----@class (exact) conform.FormatterList : conform.RunOptions ----@field formatters string[] - ----@type table +---@type table M.formatters_by_ft = {} ---@type table @@ -104,30 +100,23 @@ end ---@private ---@param bufnr? integer ---@return conform.FormatterInfo[] ----@return conform.RunOptions M.list_formatters_for_buffer = function(bufnr) if not bufnr or bufnr == 0 then bufnr = vim.api.nvim_get_current_buf() end local formatters = {} local seen = {} - local run_options = { - run_all_formatters = false, - format_on_save = true, - } local filetypes = vim.split(vim.bo[bufnr].filetype, ".", { plain = true }) table.insert(filetypes, "*") for _, filetype in ipairs(filetypes) do local ft_formatters = M.formatters_by_ft[filetype] if ft_formatters then + -- support the old structure where formatters could be a subkey if not vim.tbl_islist(ft_formatters) then - for k, v in pairs(ft_formatters) do - if k ~= "formatters" then - run_options[k] = v - end - end + ---@diagnostic disable-next-line: undefined-field ft_formatters = ft_formatters.formatters end + for _, formatter in ipairs(ft_formatters) do if not seen[formatter] then table.insert(formatters, formatter) @@ -142,21 +131,17 @@ M.list_formatters_for_buffer = function(bufnr) return M.get_formatter_info(f, bufnr) end, formatters) - return all_info, run_options + return all_info end ---@param formatters conform.FormatterInfo[] ----@param run_options conform.RunOptions ---@return conform.FormatterInfo[] -local function filter_formatters(formatters, run_options) +local function filter_formatters(formatters) ---@type conform.FormatterInfo[] local all_info = {} for _, info in ipairs(formatters) do if info.available then table.insert(all_info, info) - if not run_options.run_all_formatters then - break - end end end @@ -239,10 +224,9 @@ M.format = function(opts, callback) end end else - local run_info - formatters, run_info = M.list_formatters_for_buffer(opts.bufnr) + formatters = M.list_formatters_for_buffer(opts.bufnr) any_formatters_configured = not vim.tbl_isempty(formatters) - formatters = filter_formatters(formatters, run_info) + formatters = filter_formatters(formatters) end local formatter_names = vim.tbl_map(function(f) return f.name @@ -303,8 +287,8 @@ end ---@param bufnr? integer ---@return conform.FormatterInfo[] M.list_formatters = function(bufnr) - local formatters, run_options = M.list_formatters_for_buffer(bufnr) - return filter_formatters(formatters, run_options) + local formatters = M.list_formatters_for_buffer(bufnr) + return filter_formatters(formatters) end ---List information about all filetype-configured formatters @@ -312,9 +296,12 @@ end M.list_all_formatters = function() local formatters = {} for _, ft_formatters in pairs(M.formatters_by_ft) do + -- 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 + for _, formatter in ipairs(ft_formatters) do formatters[formatter] = true end diff --git a/scripts/options_doc.lua b/scripts/options_doc.lua index a501e38..a796501 100644 --- a/scripts/options_doc.lua +++ b/scripts/options_doc.lua @@ -2,16 +2,8 @@ require("conform").setup({ -- Map of filetype to formatters formatters_by_ft = { lua = { "stylua" }, - -- Conform will use the first available formatter in the list - javascript = { "prettierd", "prettier" }, - -- Formatters can also be specified with additional options - python = { - formatters = { "isort", "black" }, - -- Run formatters one after another instead of stopping at the first success - run_all_formatters = true, - -- Don't run these formatters as part of the format_on_save autocmd (see below) - format_on_save = false, - }, + -- Conform will run multiple formatters sequentially + python = { "isort", "black" }, }, -- If this is set, Conform will run the formatter on save. -- It will pass the table to conform.format(). -- cgit v1.2.3-70-g09d2 From 2568d746abbadf66a03c62b568ee73d874cd8617 Mon Sep 17 00:00:00 2001 From: Steven Arcangeli Date: Fri, 8 Sep 2023 08:48:33 -0700 Subject: feat: syntax for using first available formatter Alternations are now supported. You can specify a sub-list in place of a formatter name and conform will use the first formatter in that list that is available. For example, this will use either prettierd or prettier (whichever is available), and then always trim whitespace afterwards: conform.format(formatters = { { "prettierd", "prettier" }, "trim_whitespace" }) This syntax is available both in the formatters_by_ft config option and in the `formatters` argument of the `format` method. --- README.md | 14 ++++-- doc/conform.txt | 4 +- lua/conform/health.lua | 92 ++++++++++++++++++++++++---------- lua/conform/init.lua | 130 +++++++++++++++++++++++++++--------------------- scripts/options_doc.lua | 2 + 5 files changed, 153 insertions(+), 89 deletions(-) (limited to 'doc/conform.txt') diff --git a/README.md b/README.md index 8d6cd45..66b50dc 100644 --- a/README.md +++ b/README.md @@ -110,8 +110,10 @@ At a minimum, you will need to set up some formatters by filetype require("conform").setup({ formatters_by_ft = { lua = { "stylua" }, - -- Conform will run multiple formatters sequentially + -- Conform will run multiple formatters sequentially python = { "isort", "black" }, + -- Use a sub-list to run only the first available formatter + javascript = { { "prettierd", "prettier" } }, }, }) ``` @@ -210,6 +212,8 @@ require("conform").setup({ lua = { "stylua" }, -- Conform will run multiple formatters sequentially python = { "isort", "black" }, + -- Use a sub-list to run only the first available formatter + javascript = { { "prettierd", "prettier" } }, }, -- If this is set, Conform will run the formatter on save. -- It will pass the table to conform.format(). @@ -384,10 +388,10 @@ List information about all filetype-configured formatters `get_formatter_info(formatter, bufnr): conform.FormatterInfo` \ Get information about a formatter (including availability) -| Param | Type | Desc | -| --------- | -------------- | ---- | -| formatter | `string` | | -| bufnr | `nil\|integer` | | +| Param | Type | Desc | +| --------- | -------------- | ------------------------- | +| formatter | `string` | The name of the formatter | +| bufnr | `nil\|integer` | | ## Acknowledgements diff --git a/doc/conform.txt b/doc/conform.txt index 5995f99..66754a6 100644 --- a/doc/conform.txt +++ b/doc/conform.txt @@ -18,6 +18,8 @@ OPTIONS *conform-option lua = { "stylua" }, -- Conform will run multiple formatters sequentially python = { "isort", "black" }, + -- Use a sub-list to run only the first available formatter + javascript = { { "prettierd", "prettier" } }, }, -- If this is set, Conform will run the formatter on save. -- It will pass the table to conform.format(). @@ -118,7 +120,7 @@ get_formatter_info({formatter}, {bufnr}): conform.FormatterInfo *conform.get_for Get information about a formatter (including availability) Parameters: - {formatter} `string` + {formatter} `string` The name of the formatter {bufnr} `nil|integer` -------------------------------------------------------------------------------- diff --git a/lua/conform/health.lua b/lua/conform/health.lua index d36ef03..18085bf 100644 --- a/lua/conform/health.lua +++ b/lua/conform/health.lua @@ -6,11 +6,24 @@ local function get_formatter_filetypes(name) local conform = require("conform") local filetypes = {} for filetype, formatters in pairs(conform.formatters_by_ft) do + -- support the old structure where formatters could be a subkey if not vim.tbl_islist(formatters) then + ---@diagnostic disable-next-line: undefined-field formatters = formatters.formatters end - if vim.tbl_contains(formatters, name) then - table.insert(filetypes, filetype) + + for _, ft_name in ipairs(formatters) do + if type(ft_name) == "string" then + if ft_name == name then + table.insert(filetypes, filetype) + break + end + else + if vim.tbl_contains(ft_name, name) then + table.insert(filetypes, filetype) + break + end + end end end return filetypes @@ -38,6 +51,22 @@ M.check = function() end end +---@param formatters conform.FormatterUnit[] +---@return string[] +local function flatten_formatters(formatters) + local flat = {} + for _, name in ipairs(formatters) do + if type(name) == "string" then + table.insert(flat, name) + else + for _, f in ipairs(flatten_formatters(name)) do + table.insert(flat, f) + end + end + end + return flat +end + M.show_window = function() local conform = require("conform") local lines = {} @@ -60,35 +89,43 @@ M.show_window = function() end table.insert(lines, "") - ---@param formatters conform.FormatterInfo[] + ---@param formatter conform.FormatterInfo + local function append_formatter_info(formatter) + if not formatter.available then + local line = string.format("%s unavailable: %s", formatter.name, formatter.available_msg) + table.insert(lines, line) + table.insert( + highlights, + { "DiagnosticWarn", #lines, formatter.name:len(), formatter.name:len() + 12 } + ) + else + local filetypes = get_formatter_filetypes(formatter.name) + local line = string.format("%s ready (%s)", formatter.name, table.concat(filetypes, ", ")) + table.insert(lines, line) + table.insert( + highlights, + { "DiagnosticInfo", #lines, formatter.name:len(), formatter.name:len() + 6 } + ) + end + end + + local seen = {} + ---@param formatters string[] local function append_formatters(formatters) - for _, formatter in ipairs(formatters) do - if not formatter.available then - local line = string.format("%s unavailable: %s", formatter.name, formatter.available_msg) - table.insert(lines, line) - table.insert( - highlights, - { "DiagnosticWarn", #lines, formatter.name:len(), formatter.name:len() + 12 } - ) + for _, name in ipairs(formatters) do + if type(name) == "table" then + append_formatters(name) else - local filetypes = get_formatter_filetypes(formatter.name) - local line = string.format("%s ready (%s)", formatter.name, table.concat(filetypes, ", ")) - table.insert(lines, line) - table.insert( - highlights, - { "DiagnosticInfo", #lines, formatter.name:len(), formatter.name:len() + 6 } - ) + seen[name] = true + local formatter = conform.get_formatter_info(name) + append_formatter_info(formatter) end end end table.insert(lines, "Formatters for this buffer:") table.insert(highlights, { "Title", #lines, 0, -1 }) - local seen = {} - local buf_formatters = conform.list_formatters_for_buffer() - for _, formatter in ipairs(buf_formatters) do - seen[formatter.name] = true - end + local buf_formatters = flatten_formatters(conform.list_formatters_for_buffer()) append_formatters(buf_formatters) if vim.tbl_isempty(buf_formatters) then table.insert(lines, "") @@ -97,10 +134,11 @@ M.show_window = function() table.insert(lines, "") table.insert(lines, "Other formatters:") table.insert(highlights, { "Title", #lines, 0, -1 }) - local all_formatters = vim.tbl_filter(function(f) - return not seen[f.name] - end, conform.list_all_formatters()) - append_formatters(all_formatters) + for _, formatter in ipairs(conform.list_all_formatters()) do + if not seen[formatter.name] then + append_formatter_info(formatter) + end + end local bufnr = vim.api.nvim_create_buf(false, true) local winid = vim.api.nvim_open_win(bufnr, true, { diff --git a/lua/conform/init.lua b/lua/conform/init.lua index ef6f94a..2761b45 100644 --- a/lua/conform/init.lua +++ b/lua/conform/init.lua @@ -99,7 +99,7 @@ end ---@private ---@param bufnr? integer ----@return conform.FormatterInfo[] +---@return conform.FormatterUnit[] M.list_formatters_for_buffer = function(bufnr) if not bufnr or bufnr == 0 then bufnr = vim.api.nvim_get_current_buf() @@ -107,8 +107,25 @@ M.list_formatters_for_buffer = function(bufnr) local formatters = {} local seen = {} local filetypes = vim.split(vim.bo[bufnr].filetype, ".", { plain = true }) + + local function dedupe_formatters(names, collect) + for _, name in ipairs(names) do + if type(name) == "table" then + local alternation = {} + dedupe_formatters(name, alternation) + if not vim.tbl_isempty(alternation) then + table.insert(collect, alternation) + end + elseif not seen[name] then + table.insert(collect, name) + seen[name] = true + end + end + end + table.insert(filetypes, "*") for _, filetype in ipairs(filetypes) do + ---@type conform.FormatterUnit[] local ft_formatters = M.formatters_by_ft[filetype] if ft_formatters then -- support the old structure where formatters could be a subkey @@ -117,35 +134,11 @@ M.list_formatters_for_buffer = function(bufnr) ft_formatters = ft_formatters.formatters end - for _, formatter in ipairs(ft_formatters) do - if not seen[formatter] then - table.insert(formatters, formatter) - seen[formatter] = true - end - end + dedupe_formatters(ft_formatters, formatters) end end - ---@type conform.FormatterInfo[] - local all_info = vim.tbl_map(function(f) - return M.get_formatter_info(f, bufnr) - end, formatters) - - return all_info -end - ----@param formatters conform.FormatterInfo[] ----@return conform.FormatterInfo[] -local function filter_formatters(formatters) - ---@type conform.FormatterInfo[] - local all_info = {} - for _, info in ipairs(formatters) do - if info.available then - table.insert(all_info, info) - end - end - - return all_info + return formatters end ---@param bufnr integer @@ -179,6 +172,41 @@ local function range_from_selection(bufnr, mode) } end +---@param names conform.FormatterUnit[] +---@param bufnr integer +---@param warn_on_missing boolean +---@return conform.FormatterInfo[] +local function resolve_formatters(names, bufnr, warn_on_missing) + local all_info = {} + local function add_info(info, warn) + if info.available then + table.insert(all_info, info) + elseif warn then + vim.notify( + string.format("Formatter '%s' unavailable: %s", info.name, info.available_msg), + vim.log.levels.WARN + ) + end + return info.available + end + + for _, name in ipairs(names) do + if type(name) == "string" then + local info = M.get_formatter_info(name, bufnr) + add_info(info, warn_on_missing) + else + -- If this is an alternation, take the first one that's available + for i, v in ipairs(name) do + local info = M.get_formatter_info(v, bufnr) + if add_info(info, i == #name) then + break + end + end + end + end + return all_info +end + ---Format a buffer ---@param opts? table --- timeout_ms nil|integer Time in milliseconds to block for formatting. Defaults to 1000. No effect if async = true. @@ -204,34 +232,15 @@ M.format = function(opts, callback) local lsp_format = require("conform.lsp_format") local runner = require("conform.runner") - local formatters = {} - local any_formatters_configured - if opts.formatters then - any_formatters_configured = true - for _, formatter in ipairs(opts.formatters) do - local info = M.get_formatter_info(formatter) - if info.available then - table.insert(formatters, info) - else - if opts.quiet then - log.warn("Formatter '%s' unavailable: %s", info.name, info.available_msg) - else - vim.notify( - string.format("Formatter '%s' unavailable: %s", info.name, info.available_msg), - vim.log.levels.WARN - ) - end - end - end - else - formatters = M.list_formatters_for_buffer(opts.bufnr) - any_formatters_configured = not vim.tbl_isempty(formatters) - formatters = filter_formatters(formatters) - end - local formatter_names = vim.tbl_map(function(f) + local formatter_names = opts.formatters or M.list_formatters_for_buffer(opts.bufnr) + local any_formatters_configured = formatter_names ~= nil and not vim.tbl_isempty(formatter_names) + local formatters = + resolve_formatters(formatter_names, opts.bufnr, not opts.quiet and opts.formatters ~= nil) + + local resolved_names = vim.tbl_map(function(f) return f.name end, formatters) - log.debug("Running formatters on %s: %s", vim.api.nvim_buf_get_name(opts.bufnr), formatter_names) + log.debug("Running formatters on %s: %s", vim.api.nvim_buf_get_name(opts.bufnr), resolved_names) local any_formatters = not vim.tbl_isempty(formatters) if any_formatters then @@ -287,8 +296,11 @@ end ---@param bufnr? integer ---@return conform.FormatterInfo[] M.list_formatters = function(bufnr) + if not bufnr or bufnr == 0 then + bufnr = vim.api.nvim_get_current_buf() + end local formatters = M.list_formatters_for_buffer(bufnr) - return filter_formatters(formatters) + return resolve_formatters(formatters, bufnr, false) end ---List information about all filetype-configured formatters @@ -303,7 +315,13 @@ M.list_all_formatters = function() end for _, formatter in ipairs(ft_formatters) do - formatters[formatter] = true + if type(formatter) == "table" then + for _, v in ipairs(formatter) do + formatters[v] = true + end + else + formatters[formatter] = true + end end end @@ -348,7 +366,7 @@ M.get_formatter_config = function(formatter, bufnr) end ---Get information about a formatter (including availability) ----@param formatter string +---@param formatter string The name of the formatter ---@param bufnr? integer ---@return conform.FormatterInfo M.get_formatter_info = function(formatter, bufnr) diff --git a/scripts/options_doc.lua b/scripts/options_doc.lua index a796501..47eccd9 100644 --- a/scripts/options_doc.lua +++ b/scripts/options_doc.lua @@ -4,6 +4,8 @@ require("conform").setup({ lua = { "stylua" }, -- Conform will run multiple formatters sequentially python = { "isort", "black" }, + -- Use a sub-list to run only the first available formatter + javascript = { { "prettierd", "prettier" } }, }, -- If this is set, Conform will run the formatter on save. -- It will pass the table to conform.format(). -- cgit v1.2.3-70-g09d2 From 0db858075b4d8214a34a6836d994e2100fed51bf Mon Sep 17 00:00:00 2001 From: Steven Arcangeli Date: Fri, 8 Sep 2023 09:13:06 -0700 Subject: doc: document the "*" filetype option --- README.md | 4 ++++ doc/conform.txt | 4 ++++ scripts/options_doc.lua | 4 ++++ 3 files changed, 12 insertions(+) (limited to 'doc/conform.txt') diff --git a/README.md b/README.md index 66b50dc..72514ef 100644 --- a/README.md +++ b/README.md @@ -214,6 +214,10 @@ require("conform").setup({ python = { "isort", "black" }, -- Use a sub-list to run only the first available formatter javascript = { { "prettierd", "prettier" } }, + -- Use the "*" filetype to run formatters on all files. + -- Note that if you use this, you may want to set lsp_fallback = "always" + -- (see :help conform.format) + ["*"] = { "trim_whitespace" }, }, -- If this is set, Conform will run the formatter on save. -- It will pass the table to conform.format(). diff --git a/doc/conform.txt b/doc/conform.txt index 66754a6..fada19a 100644 --- a/doc/conform.txt +++ b/doc/conform.txt @@ -20,6 +20,10 @@ OPTIONS *conform-option python = { "isort", "black" }, -- Use a sub-list to run only the first available formatter javascript = { { "prettierd", "prettier" } }, + -- Use the "*" filetype to run formatters on all files. + -- Note that if you use this, you may want to set lsp_fallback = "always" + -- (see :help conform.format) + ["*"] = { "trim_whitespace" }, }, -- If this is set, Conform will run the formatter on save. -- It will pass the table to conform.format(). diff --git a/scripts/options_doc.lua b/scripts/options_doc.lua index 47eccd9..1a6921c 100644 --- a/scripts/options_doc.lua +++ b/scripts/options_doc.lua @@ -6,6 +6,10 @@ require("conform").setup({ python = { "isort", "black" }, -- Use a sub-list to run only the first available formatter javascript = { { "prettierd", "prettier" } }, + -- Use the "*" filetype to run formatters on all files. + -- Note that if you use this, you may want to set lsp_fallback = "always" + -- (see :help conform.format) + ["*"] = { "trim_whitespace" }, }, -- If this is set, Conform will run the formatter on save. -- It will pass the table to conform.format(). -- cgit v1.2.3-70-g09d2 From c3028b327bc44335cc2b5c3014cd6d5c12a54ee4 Mon Sep 17 00:00:00 2001 From: Steven Arcangeli Date: Fri, 8 Sep 2023 09:20:44 -0700 Subject: feat: format() can always fall back to LSP formatting --- README.md | 22 +++++++++++----------- doc/conform.txt | 7 +++++-- lua/conform/init.lua | 17 ++++++++++++++--- 3 files changed, 30 insertions(+), 16 deletions(-) (limited to 'doc/conform.txt') diff --git a/README.md b/README.md index 72514ef..86cef63 100644 --- a/README.md +++ b/README.md @@ -354,17 +354,17 @@ vim.api.nvim_create_autocmd("BufWritePost", { `format(opts, callback): boolean` \ Format a buffer -| Param | Type | Desc | | -| -------- | ---------------------------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | -| 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. | -| | formatters | `nil\|string[]` | List of formatters to run. Defaults to all formatters for the buffer filetype. | -| | lsp_fallback | `nil\|boolean` | Attempt LSP formatting if no formatters are available. Defaults to false. | -| | quiet | `nil\|boolean` | Don't show any notifications for warnings or failures. Defaults to false. | -| | range | `nil\|table` | Range to format. Table must contain `start` and `end` keys with {row, col} tuples using (1,0) indexing. Defaults to current selection in visual mode | -| callback | `nil\|fun(err: nil\|string)` | Called once formatting has completed | | +| Param | Type | Desc | | +| -------- | ---------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 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. | +| | 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. | +| | range | `nil\|table` | Range to format. Table must contain `start` and `end` keys with {row, col} tuples using (1,0) indexing. Defaults to current selection in visual mode | +| callback | `nil\|fun(err: nil\|string)` | Called once formatting has completed | | Returns: diff --git a/doc/conform.txt b/doc/conform.txt index fada19a..149db88 100644 --- a/doc/conform.txt +++ b/doc/conform.txt @@ -99,8 +99,11 @@ format({opts}, {callback}): boolean *conform.forma to false. {formatters} `nil|string[]` List of formatters to run. Defaults to all formatters for the buffer filetype. - {lsp_fallback} `nil|boolean` Attempt LSP formatting if no formatters - are available. Defaults to false. + {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. {range} `nil|table` Range to format. Table must contain `start` diff --git a/lua/conform/init.lua b/lua/conform/init.lua index 2761b45..9f844b1 100644 --- a/lua/conform/init.lua +++ b/lua/conform/init.lua @@ -213,13 +213,13 @@ end --- bufnr nil|integer Format this buffer (default 0) --- async nil|boolean If true the method won't block. Defaults to false. --- formatters nil|string[] List of formatters to run. Defaults to all formatters for the buffer filetype. ---- lsp_fallback nil|boolean Attempt LSP formatting if no formatters are available. Defaults to false. +--- 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. --- range nil|table Range to format. Table must contain `start` and `end` keys with {row, col} tuples using (1,0) indexing. Defaults to current selection in visual mode ---@param callback? fun(err: nil|string) Called once formatting has completed ---@return boolean True if any formatters were attempted M.format = function(opts, callback) - ---@type {timeout_ms: integer, bufnr: integer, async: boolean, lsp_fallback: boolean, quiet: boolean, formatters?: string[], range?: conform.Range} + ---@type {timeout_ms: integer, bufnr: integer, async: boolean, lsp_fallback: boolean|"always", quiet: boolean, formatters?: string[], range?: conform.Range} opts = vim.tbl_extend("keep", opts or {}, { timeout_ms = 1000, bufnr = 0, @@ -269,7 +269,18 @@ M.format = function(opts, callback) if not err_message and not vim.api.nvim_buf_is_valid(opts.bufnr) then err_message = "buffer was deleted" end - callback(err_message) + if err_message then + return callback(err_message) + end + + if + opts.lsp_fallback == "always" and not vim.tbl_isempty(lsp_format.get_format_clients(opts)) + then + log.debug("Running LSP formatter on %s", vim.api.nvim_buf_get_name(opts.bufnr)) + lsp_format.format(opts, callback) + else + callback() + end end if opts.async then -- cgit v1.2.3-70-g09d2