@@ -4,26 +4,8 @@ local log = require("null-ls.logger")
44local client = require (" null-ls.client" )
55
66local FORMATTING = methods .internal .FORMATTING
7- local NOTIFICATION_TITLE = " discovering `nix fmt` entrypoint"
8- local NOTIFICATION_TOKEN = " nix-flake-fmt-discovery"
9-
10- --- Asynchronously computes the command that `nix fmt` would run, or nil if
11- --- we're not in a flake with a formatter, or if we fail to discover the
12- --- formatter somehow. When finished, it invokes the `done` callback with a
13- --- single string|nil parameter identifier the `nix fmt` entrypoint if found.
14- ---
15- --- The formatter must follow treefmt's [formatter
16- --- spec](https://github.com/numtide/treefmt/blob/main/docs/formatter-spec.md).
17- ---
18- --- This basically re-implements the "entrypoint discovery" that `nix fmt` does.
19- --- So why are we doing this ourselves rather than just invoking `nix fmt`?
20- --- Unfortunately, it can take a few moments to evaluate all your nix code to
21- --- figure out the formatter entrypoint. It can even be slow enough to exceed
22- --- Neovim's default LSP timeout.
23- --- By doing this ourselves, we can cache the result.
24- local find_nix_fmt = function (opts , done )
25- done = vim .schedule_wrap (done )
267
8+ local run_job = function (opts )
279 local async = require (" plenary.async" )
2810 local Job = require (" plenary.job" )
2911
@@ -35,12 +17,30 @@ local find_nix_fmt = function(opts, done)
3517 Job :new (_opts ):start ()
3618 end , 2 )
3719
38- local tmpname = async .wrap (function (_done )
20+ return run_job (opts )
21+ end
22+
23+ local tmpname = function ()
24+ local async = require (" plenary.async" )
25+
26+ local mktemp = async .wrap (function (_done )
3927 vim .defer_fn (function ()
4028 _done (vim .fn .tempname ())
4129 end , 0 )
4230 end , 1 )
31+ return mktemp ()
32+ end
4333
34+ --- Asynchronously build and return the formatter for the flake located at {root},
35+ --- If {root} is not a flake, or does not have a formatter, or we cannot build the formatter, return `nil`.
36+ --- This legacy codepath is quite complicated, and unnecessary now that `nix` has core support for
37+ --- returning the fromatter command.
38+ --- TODO: remove after the `nix formatter` subcommand has been released for a while.
39+ --- The command was introduced in https://github.com/NixOS/nix/commit/d155bb901241441149c701b9efc92f5785c2e1c3
40+ ---
41+ --- @param root string
42+ --- @return string | nil
43+ local legacy_find_nix_fmt = function (root )
4444 local get_current_system = function ()
4545 local status , stdout_lines , stderr_lines = run_job ({
4646 command = " nix" ,
@@ -141,12 +141,6 @@ local find_nix_fmt = function(opts, done)
141141 builtins.toJSON result
142142 ]]
143143
144- client .send_progress_notification (NOTIFICATION_TOKEN , {
145- kind = " report" ,
146- title = NOTIFICATION_TITLE ,
147- message = " evaluating" ,
148- })
149-
150144 local status , stdout_lines , stderr_lines = run_job ({
151145 command = " nix" ,
152146 args = {
@@ -218,34 +212,97 @@ local find_nix_fmt = function(opts, done)
218212 return true
219213 end
220214
215+ local drv_path , nix_fmt_path = evaluate_flake_formatter (root )
216+ if drv_path == nil then
217+ return nil
218+ end
219+
220+ -- Build the derivation. This ensures that `nix_fmt_path` exists.
221+ if not build_derivation ({ drv = drv_path , out_link = tmpname () }) then
222+ return nil
223+ end
224+
225+ return nix_fmt_path
226+ end
227+
228+ local nix_has_formatter_subcommand = function ()
229+ local status , _ , _ = run_job ({
230+ command = " nix" ,
231+ args = {
232+ " --extra-experimental-features" ,
233+ " nix-command flakes" ,
234+ " formatter" ,
235+ " --help" ,
236+ },
237+ })
238+
239+ return status == 0
240+ end
241+
242+ --- Asynchronously computes the command that `nix fmt` would run, or nil if
243+ --- we're not in a flake with a formatter, or if we fail to discover the
244+ --- formatter somehow. When finished, it invokes the `done` callback with a
245+ --- single string|nil parameter identifier the `nix fmt` entrypoint if found.
246+ ---
247+ --- The formatter must follow treefmt's [formatter
248+ --- spec](https://github.com/numtide/treefmt/blob/main/docs/formatter-spec.md).
249+ ---
250+ --- This basically re-implements the "entrypoint discovery" that `nix fmt` does.
251+ --- So why are we doing this ourselves rather than just invoking `nix fmt`?
252+ --- Unfortunately, it can take a few moments to evaluate all your nix code to
253+ --- figure out the formatter entrypoint. It can even be slow enough to exceed
254+ --- Neovim's default LSP timeout.
255+ --- By doing this ourselves, we can cache the result.
256+ local find_nix_fmt = function (opts , done )
257+ done = vim .schedule_wrap (done )
258+
259+ local async = require (" plenary.async" )
260+
261+ local notification_title = " discovering `nix fmt` entrypoint"
262+ local notification_token = " nix-flake-fmt-discovery"
263+
221264 async .run (function ()
222- client .send_progress_notification (NOTIFICATION_TOKEN , {
265+ client .send_progress_notification (notification_token , {
223266 kind = " begin" ,
224- title = NOTIFICATION_TITLE ,
267+ title = notification_title ,
225268 })
226269
227270 local _done = function (result )
228271 done (result )
229- client .send_progress_notification (NOTIFICATION_TOKEN , {
272+ client .send_progress_notification (notification_token , {
230273 kind = " end" ,
231- title = NOTIFICATION_TITLE ,
274+ title = notification_title ,
232275 message = " done" ,
233276 })
234277 end
235278
236- local drv_path , nix_fmt_path = evaluate_flake_formatter (opts .root )
237- if drv_path == nil then
238- return _done (nil )
239- end
279+ local nix_fmt_path --- @type string | nil
280+ local is_legacy = not nix_has_formatter_subcommand ()
281+ if is_legacy then
282+ nix_fmt_path = legacy_find_nix_fmt ()
283+ else
284+ local status , stdout_lines , stderr_lines = run_job ({
285+ command = " nix" ,
286+ args = {
287+ " --extra-experimental-features" ,
288+ " nix-command" ,
289+ " formatter" ,
290+ " build" ,
291+ " --out-link" ,
292+ tmpname (),
293+ },
294+ })
240295
241- -- Build the derivation. This ensures that `nix_fmt_path` exists.
242- client .send_progress_notification (NOTIFICATION_TOKEN , {
243- kind = " report" ,
244- title = NOTIFICATION_TITLE ,
245- message = " building" ,
246- })
247- if not build_derivation ({ drv = drv_path , out_link = tmpname () }) then
248- return _done (nil )
296+ if status ~= 0 then
297+ local stderr = table.concat (stderr_lines , " \n " )
298+ vim .defer_fn (function ()
299+ log :warn (string.format (" unable to build 'nix fmt' entrypoint. stderr: %s" , stderr ))
300+ end , 0 )
301+ return false
302+ end
303+
304+ local stdout = table.concat (stdout_lines , " \n " )
305+ nix_fmt_path = stdout
249306 end
250307
251308 return _done (nix_fmt_path )
0 commit comments