@@ -4,43 +4,43 @@ local log = require("null-ls.logger")
4
4
local client = require (" null-ls.client" )
5
5
6
6
local 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 )
26
7
8
+ local run_job = function (opts )
27
9
local async = require (" plenary.async" )
28
10
local Job = require (" plenary.job" )
29
11
30
- local run_job = async .wrap (function (_opts , _done )
12
+ local _run_job = async .wrap (function (_opts , _done )
31
13
_opts .on_exit = function (j , status )
32
14
_done (status , j :result (), j :stderr_result ())
33
15
end
34
16
35
17
Job :new (_opts ):start ()
36
18
end , 2 )
37
19
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 )
39
27
vim .defer_fn (function ()
40
28
_done (vim .fn .tempname ())
41
29
end , 0 )
42
30
end , 1 )
31
+ return mktemp ()
32
+ end
43
33
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 )
44
44
local get_current_system = function ()
45
45
local status , stdout_lines , stderr_lines = run_job ({
46
46
command = " nix" ,
@@ -65,7 +65,7 @@ local find_nix_fmt = function(opts, done)
65
65
return nix_current_system
66
66
end
67
67
68
- local get_flake_ref = function (root )
68
+ local get_flake_ref = function (_root )
69
69
local status , stdout_lines , stderr_lines = run_job ({
70
70
command = " nix" ,
71
71
args = {
@@ -74,7 +74,7 @@ local find_nix_fmt = function(opts, done)
74
74
" flake" ,
75
75
" metadata" ,
76
76
" --json" ,
77
- root ,
77
+ _root ,
78
78
},
79
79
})
80
80
@@ -101,12 +101,12 @@ local find_nix_fmt = function(opts, done)
101
101
return flake_ref
102
102
end
103
103
104
- local evaluate_flake_formatter = function (root )
104
+ local evaluate_flake_formatter = function (_root )
105
105
local nix_current_system = get_current_system ()
106
106
if nix_current_system == nil then
107
107
return
108
108
end
109
- local flake_ref = get_flake_ref (root )
109
+ local flake_ref = get_flake_ref (_root )
110
110
local eval_nix_formatter = [[
111
111
let
112
112
system = "]] .. nix_current_system .. [[ ";
@@ -141,12 +141,6 @@ local find_nix_fmt = function(opts, done)
141
141
builtins.toJSON result
142
142
]]
143
143
144
- client .send_progress_notification (NOTIFICATION_TOKEN , {
145
- kind = " report" ,
146
- title = NOTIFICATION_TITLE ,
147
- message = " evaluating" ,
148
- })
149
-
150
144
local status , stdout_lines , stderr_lines = run_job ({
151
145
command = " nix" ,
152
146
args = {
@@ -218,34 +212,98 @@ local find_nix_fmt = function(opts, done)
218
212
return true
219
213
end
220
214
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
+
221
264
async .run (function ()
222
- client .send_progress_notification (NOTIFICATION_TOKEN , {
265
+ client .send_progress_notification (notification_token , {
223
266
kind = " begin" ,
224
- title = NOTIFICATION_TITLE ,
267
+ title = notification_title ,
225
268
})
226
269
227
270
local _done = function (result )
228
271
done (result )
229
- client .send_progress_notification (NOTIFICATION_TOKEN , {
272
+ client .send_progress_notification (notification_token , {
230
273
kind = " end" ,
231
- title = NOTIFICATION_TITLE ,
274
+ title = notification_title ,
232
275
message = " done" ,
233
276
})
234
277
end
235
278
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 (opts .root )
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
+ cwd = opts .root ,
295
+ })
240
296
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 )
297
+ if status ~= 0 then
298
+ local stderr = table.concat (stderr_lines , " \n " )
299
+ vim .defer_fn (function ()
300
+ log :warn (string.format (" unable to build 'nix fmt' entrypoint. stderr: %s" , stderr ))
301
+ end , 0 )
302
+ return false
303
+ end
304
+
305
+ local stdout = table.concat (stdout_lines , " \n " )
306
+ nix_fmt_path = stdout
249
307
end
250
308
251
309
return _done (nix_fmt_path )
0 commit comments