Skip to content

Commit 820ece1

Browse files
committed
feat(conform): resolve formatters discovery-first
Resolve formatter_deps through the shared resolver against conform's own registry (deferred off the BufWritePre tick), classifying broken vs unknown vs per-buffer-unverifiable configs honestly. Function-form commands are evaluated at probe time so a node_modules copy passes and the bare-name fallback stays in the install/warn contract. autoformat_allowed groups the save-time gates.
1 parent 5ac9139 commit 820ece1

1 file changed

Lines changed: 74 additions & 16 deletions

File tree

lua/modules/configs/completion/conform.lua

Lines changed: 74 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,19 @@ return function()
4040
return false
4141
end
4242

43+
---The gates a buffer must pass before an automatic format, grouped into one
44+
---named predicate for the format_on_save callback's readability. The
45+
---format_on_save SETTING is not re-checked here: its gate lives at the
46+
---single place the callback is installed (format_on_save = enabled and …).
47+
---@param bufnr integer
48+
---@return boolean
49+
local function autoformat_allowed(bufnr)
50+
return block_list[vim.bo[bufnr].filetype] ~= true
51+
and not is_disabled_workspace(bufnr)
52+
and not vim.g.disable_autoformat
53+
and not vim.b[bufnr].disable_autoformat
54+
end
55+
4356
---Format only git-modified lines using gitsigns hunks + conform range format
4457
---@param bufnr integer
4558
---@return boolean @true if modifications were formatted
@@ -87,6 +100,8 @@ return function()
87100
return true
88101
end
89102

103+
local tools = require("modules.utils.tools")
104+
90105
require("modules.utils").load_plugin("conform", {
91106
default_format_opts = {
92107
timeout_ms = format_timeout,
@@ -132,31 +147,22 @@ return function()
132147
args = { "fix", "--stdin" },
133148
stdin = true,
134149
},
135-
-- prettier: stdin mode does not work under bun's node shim,
136-
-- so use --write (file-based) mode instead.
150+
-- prettier: the --write-on-temp-copy shape dates from the bun
151+
-- node-shim era (stdin was broken); mise ships real node now, so
152+
-- stdin likely works again — kept pending re-evaluation. `stdin =
153+
-- false` points $FILENAME at a `.conform.$RANDOM.*` copy; the real
154+
-- file is never touched.
137155
prettier = {
138156
command = "prettier",
139157
args = { "--write", "$FILENAME" },
140158
stdin = false,
141159
},
142160
},
143161
format_on_save = format_on_save_enabled and function(bufnr)
144-
-- Check disabled filetypes
145-
if block_list[vim.bo[bufnr].filetype] == true then
146-
return
147-
end
148-
149-
-- Check disabled workspaces
150-
if is_disabled_workspace(bufnr) then
151-
return
152-
end
153-
154-
-- Check global toggle
155-
if vim.g.disable_autoformat or vim.b[bufnr].disable_autoformat then
162+
if not autoformat_allowed(bufnr) then
156163
return
157164
end
158165

159-
-- Format only modified lines if enabled
160166
if format_modifications_only then
161167
if format_modifications(bufnr) then
162168
return
@@ -168,6 +174,58 @@ return function()
168174
end or false,
169175
})
170176

177+
-- Resolve `formatter_deps` (conform formatter names) discovery-first against
178+
-- conform's own registry, so a missing formatter is installed / reported.
179+
-- The probe only drives install/warn — nothing on the save path reads it —
180+
-- so `defer` moves the resolve off the BufWritePre tick that lazy-loaded
181+
-- conform; the resolver itself keeps the same-tick guarantee that Mason's
182+
-- bin dir is on $PATH before the replayed save's spawns.
183+
tools.resolve_runtime_tools("conform.nvim", settings.formatter_deps, function(name)
184+
-- get_formatter_config is conform's @private API; if it vanishes, every
185+
-- formatter is UNVERIFIABLE — report unresolved with the reason (missing
186+
-- bucket, immediate flush) instead of silently classifying them all as
187+
-- self-resolving, which would turn off installs and warnings wholesale.
188+
local conform = require("conform")
189+
if type(conform.get_formatter_config) ~= "function" then
190+
return {
191+
unresolved = true,
192+
reason = "conform.get_formatter_config is unavailable (conform API drift?) — formatters cannot be verified",
193+
}
194+
end
195+
-- get_formatter_config runs a function-form override directly, so pcall keeps a
196+
-- throwing override (a broken config) from being misread as an unknown name.
197+
local ok, config, err = pcall(conform.get_formatter_config, name)
198+
if not ok then
199+
return { broken = tostring(config) }
200+
end
201+
if config then
202+
-- A function-form command resolves per buffer at format time (e.g. the
203+
-- builtin from_node_modules): treat it as self-resolving rather than
204+
-- evaluating it for a representative binary — a node_modules command
205+
-- shouldn't map to a Mason install anyway.
206+
if type(config.command) == "function" then
207+
return { binary = nil }
208+
end
209+
return { binary = config.command }
210+
end
211+
-- (nil, err) is a real formatter with a broken config; bare nil is an unknown name.
212+
if type(err) == "string" then
213+
return { broken = err }
214+
end
215+
-- A function-form override may legitimately return nil for the
216+
-- probe-time buffer (this probe runs on a scheduled tick against
217+
-- whatever buffer happens to be current): its existence proves the
218+
-- name real, but nothing is verifiable — report it unresolved
219+
-- (missing bucket, tailored reason) instead of a typo or a silent pass.
220+
local overrides = conform.formatters
221+
if type(overrides) == "table" and type(overrides[name]) == "function" then
222+
-- The reason rides on the probe result: the phrasing is conform's,
223+
-- not the shared resolver's (nvim-lint shares resolve_runtime_tools).
224+
return { unresolved = true, reason = "config resolves per buffer and could not be verified at startup" }
225+
end
226+
return nil
227+
end, nil, { defer = true })
228+
171229
-- User commands
172230
vim.api.nvim_create_user_command("Format", function(args)
173231
local range = nil
@@ -226,7 +284,7 @@ return function()
226284
end
227285
end, { nargs = 1, complete = "filetype" })
228286

229-
-- Auto stop shell LSPs for .env files (migrated from null-ls config).
287+
-- Auto stop shell LSPs for .env files.
230288
-- Both bashls and shuck attach to .env's `sh` filetype and only add noise there.
231289
vim.api.nvim_create_autocmd("LspAttach", {
232290
callback = function(event)

0 commit comments

Comments
 (0)