Skip to content

Commit aac6f97

Browse files
jflyMic92
authored andcommitted
Add nix formatter build command
`nix formatter build` is sort of like `nix build`: it builds, links, and prints a path to the formatter program: $ nix formatter build /nix/store/cb9w44vkhk2x4adfxwgdkkf5gjmm856j-treefmt/bin/treefmt Note that unlike `nix build`, this prints the full path to the program, not just the store path (in the example above that would be `/nix/store/cb9w44vkhk2x4adfxwgdkkf5gjmm856j-treefmt`). Motivation ---------- I maintain a vim plugin that automatically runs `nix fmt` on files on save. Since `nix fmt` can be quite slow due to nix evaluation, I choose to cache the `nix fmt `entrypoint. This was very awkward to do, see the implementation for details: https://github.com/nvimtools/none-ls.nvim/blob/786460723170bda9e9f95c55a382d21436575297/lua/null-ls/builtins/formatting/nix_flake_fmt.lua#L83-L110. I recently discovered that my implementation was buggy (it didn't handle flakes that expose a `formatter` package, such as nixpkgs), so I had to rework the implementation: nvimtools/none-ls.nvim#272. With the new `nix formatter build` command, I can delete all this akward code, and it will be easier for other folks to build performant editor integrations for `nix fmt`.
1 parent 7133303 commit aac6f97

File tree

3 files changed

+127
-4
lines changed

3 files changed

+127
-4
lines changed

src/nix/formatter-build.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
R""(
2+
3+
# Description
4+
5+
`nix formatter build` builds the formatter specified in the flake.
6+
7+
Similar to [`nix build`](@docroot@/command-ref/new-cli/nix3-build.md),
8+
unless `--no-link` is specified, after a successful
9+
build, it creates a symlink to the store path of the formatter. This symlink is
10+
named `./result` by default; this can be overridden using the
11+
`--out-link` option.
12+
13+
It always prints the command to standard output.
14+
15+
# Examples
16+
17+
* Build the formatter:
18+
19+
```console
20+
# nix formatter build
21+
/nix/store/cb9w44vkhk2x4adfxwgdkkf5gjmm856j-treefmt/bin/treefmt
22+
```
23+
)""

src/nix/formatter.cc

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#include "nix/cmd/command.hh"
22
#include "nix/cmd/installable-value.hh"
33
#include "nix/expr/eval.hh"
4+
#include "nix/store/local-fs-store.hh"
5+
#include "nix/cmd/installable-derived-path.hh"
46
#include "run.hh"
57

68
using namespace nix;
@@ -25,7 +27,7 @@ struct CmdFormatter : NixMultiCommand
2527

2628
static auto rCmdFormatter = registerCommand<CmdFormatter>("formatter");
2729

28-
struct CmdFormatterRun : SourceExprCommand
30+
struct CmdFormatterRun : SourceExprCommand, MixJSON
2931
{
3032
std::vector<std::string> args;
3133

@@ -87,6 +89,80 @@ struct CmdFormatterRun : SourceExprCommand
8789

8890
static auto rFormatterRun = registerCommand2<CmdFormatterRun>({"formatter", "run"});
8991

92+
struct CmdFormatterBuild : SourceExprCommand
93+
{
94+
Path outLink = "result";
95+
96+
CmdFormatterBuild()
97+
{
98+
addFlag({
99+
.longName = "out-link",
100+
.shortName = 'o',
101+
.description = "Use *path* as prefix for the symlink to the build result. It defaults to `result`.",
102+
.labels = {"path"},
103+
.handler = {&outLink},
104+
.completer = completePath,
105+
});
106+
107+
addFlag({
108+
.longName = "no-link",
109+
.description = "Do not create symlink to the build results.",
110+
.handler = {&outLink, Path("")},
111+
});
112+
}
113+
114+
std::string description() override
115+
{
116+
return "build the current flake's formatter";
117+
}
118+
119+
std::string doc() override
120+
{
121+
return
122+
#include "formatter-build.md"
123+
;
124+
}
125+
126+
Category category() override
127+
{
128+
return catSecondary;
129+
}
130+
131+
Strings getDefaultFlakeAttrPaths() override
132+
{
133+
return Strings{"formatter." + settings.thisSystem.get()};
134+
}
135+
136+
Strings getDefaultFlakeAttrPathPrefixes() override
137+
{
138+
return Strings{};
139+
}
140+
141+
void run(ref<Store> store) override
142+
{
143+
auto evalState = getEvalState();
144+
auto evalStore = getEvalStore();
145+
146+
auto installable_ = parseInstallable(store, ".");
147+
auto & installable = InstallableValue::require(*installable_);
148+
auto unresolvedApp = installable.toApp(*evalState);
149+
auto app = unresolvedApp.resolve(evalStore, store);
150+
151+
Installables installableContext;
152+
for (auto & ctxElt : unresolvedApp.unresolved.context)
153+
installableContext.push_back(make_ref<InstallableDerivedPath>(store, DerivedPath{ctxElt}));
154+
auto buildables = Installable::build(evalStore, store, Realise::Outputs, installableContext);
155+
156+
if (outLink != "")
157+
if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>())
158+
createOutLinks(outLink, toBuiltPaths(buildables), *store2);
159+
160+
logger->cout("%s", app.program);
161+
};
162+
};
163+
164+
static auto rFormatterBuild = registerCommand2<CmdFormatterBuild>({"formatter", "build"});
165+
90166
struct CmdFmt : CmdFormatterRun
91167
{
92168
void run(ref<Store> store) override

tests/functional/formatter.sh

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ TODO_NixOS # Provide a `shell` variable. Try not to `export` it, perhaps.
77
clearStoreIfPossible
88
rm -rf "$TEST_HOME"/.cache "$TEST_HOME"/.config "$TEST_HOME"/.local
99

10-
cp ./simple.nix ./simple.builder.sh ./fmt.simple.sh "${config_nix}" "$TEST_HOME"
10+
cp ./simple.nix ./simple.builder.sh ./formatter.simple.sh "${config_nix}" "$TEST_HOME"
1111

1212
cd "$TEST_HOME"
1313

14-
nix fmt --help | grep "forward"
14+
nix formatter --help | grep "build or run the formatter"
15+
nix fmt --help | grep "reformat your code"
16+
nix fmt run --help | grep "reformat your code"
17+
nix fmt build --help | grep "build"
1518

1619
cat << EOF > flake.nix
1720
{
@@ -23,16 +26,37 @@ cat << EOF > flake.nix
2326
buildCommand = ''
2427
mkdir -p \$out/bin
2528
echo "#! ${shell}" > \$out/bin/formatter
26-
cat \${./fmt.simple.sh} >> \$out/bin/formatter
29+
cat \${./formatter.simple.sh} >> \$out/bin/formatter
2730
chmod +x \$out/bin/formatter
2831
'';
2932
};
3033
};
3134
}
3235
EOF
36+
3337
# No arguments check
3438
[[ "$(nix fmt)" = "Formatting(0):" ]]
39+
[[ "$(nix formatter run)" = "Formatting(0):" ]]
40+
3541
# Argument forwarding check
3642
nix fmt ./file ./folder | grep 'Formatting(2): ./file ./folder'
43+
nix formatter run ./file ./folder | grep 'Formatting(2): ./file ./folder'
44+
45+
# Build checks
46+
## Defaults to a ./result.
47+
nix formatter build | grep ".\+/bin/formatter"
48+
[[ -L ./result ]]
49+
rm result
50+
51+
## Can prevent the symlink.
52+
nix formatter build --no-link
53+
[[ ! -e ./result ]]
54+
55+
## Can change the symlink name.
56+
nix formatter build --out-link my-result | grep ".\+/bin/formatter"
57+
[[ -L ./my-result ]]
58+
rm ./my-result
59+
60+
# Flake outputs check.
3761
nix flake check
3862
nix flake show | grep -P "package 'formatter'"

0 commit comments

Comments
 (0)