1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-06-25 10:41:16 +02:00

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:
7864607231/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:
https://github.com/nvimtools/none-ls.nvim/pull/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`.
This commit is contained in:
Jeremy Fleischman 2025-04-23 16:54:01 -07:00
parent 5ea7b97147
commit ba6b617e75
No known key found for this signature in database
3 changed files with 127 additions and 4 deletions

View file

@ -0,0 +1,23 @@
R""(
# Description
`nix formatter build` builds the formatter specified in the flake.
Similar to [`nix build`](@docroot@/command-ref/new-cli/nix3-build.md),
unless `--no-link` is specified, after a successful
build, it creates a symlink to the store path of the formatter. This symlink is
named `./result` by default; this can be overridden using the
`--out-link` option.
It always prints the command to standard output.
# Examples
* Build the formatter:
```console
# nix formatter build
/nix/store/cb9w44vkhk2x4adfxwgdkkf5gjmm856j-treefmt/bin/treefmt
```
)""

View file

@ -1,6 +1,8 @@
#include "nix/cmd/command.hh"
#include "nix/cmd/installable-value.hh"
#include "nix/expr/eval.hh"
#include "nix/store/local-fs-store.hh"
#include "nix/cmd/installable-derived-path.hh"
#include "run.hh"
using namespace nix;
@ -25,7 +27,7 @@ struct CmdFormatter : NixMultiCommand
static auto rCmdFormatter = registerCommand<CmdFormatter>("formatter");
struct CmdFormatterRun : SourceExprCommand
struct CmdFormatterRun : SourceExprCommand, MixJSON
{
std::vector<std::string> args;
@ -87,6 +89,80 @@ struct CmdFormatterRun : SourceExprCommand
static auto rFormatterRun = registerCommand2<CmdFormatterRun>({"formatter", "run"});
struct CmdFormatterBuild : SourceExprCommand
{
Path outLink = "result";
CmdFormatterBuild()
{
addFlag({
.longName = "out-link",
.shortName = 'o',
.description = "Use *path* as prefix for the symlink to the build result. It defaults to `result`.",
.labels = {"path"},
.handler = {&outLink},
.completer = completePath,
});
addFlag({
.longName = "no-link",
.description = "Do not create symlink to the build results.",
.handler = {&outLink, Path("")},
});
}
std::string description() override
{
return "build the current flake's formatter";
}
std::string doc() override
{
return
#include "formatter-build.md"
;
}
Category category() override
{
return catSecondary;
}
Strings getDefaultFlakeAttrPaths() override
{
return Strings{"formatter." + settings.thisSystem.get()};
}
Strings getDefaultFlakeAttrPathPrefixes() override
{
return Strings{};
}
void run(ref<Store> store) override
{
auto evalState = getEvalState();
auto evalStore = getEvalStore();
auto installable_ = parseInstallable(store, ".");
auto & installable = InstallableValue::require(*installable_);
auto unresolvedApp = installable.toApp(*evalState);
auto app = unresolvedApp.resolve(evalStore, store);
Installables installableContext;
for (auto & ctxElt : unresolvedApp.unresolved.context)
installableContext.push_back(make_ref<InstallableDerivedPath>(store, DerivedPath{ctxElt}));
auto buildables = Installable::build(evalStore, store, Realise::Outputs, installableContext);
if (outLink != "")
if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>())
createOutLinks(outLink, toBuiltPaths(buildables), *store2);
logger->cout("%s", app.program);
};
};
static auto rFormatterBuild = registerCommand2<CmdFormatterBuild>({"formatter", "build"});
struct CmdFmt : CmdFormatterRun
{
void run(ref<Store> store) override