kamadorueda / alejandra Goto Github PK
View Code? Open in Web Editor NEWThe Uncompromising Nix Code Formatter
Home Page: https://kamadorueda.github.io/alejandra/
License: The Unlicense
The Uncompromising Nix Code Formatter
Home Page: https://kamadorueda.github.io/alejandra/
License: The Unlicense
{ lib }:
expr
Instead of
{ lib
}:
expr
(foo: bar)
instead of ( foo: bar )
Create plugins/integrations, easy ways for people to use their tool in their natural habitat (vscode, vim, emacs, even cat?, etc)
Using the statically linked versions of the tool helps making deployment straightforward
Let's version the code for that in this repository as well, so we don't confuse people where to report issues to
Currently it is formatted like this in some circumstances:
rec
{
Ideally it should be:
rec {
Hi! We're trying a bunch of different formatters for the TVL depot (nixfmt, nixpkgs-fmt). Currently none of them really do what we want, but I digress ...
We tried alejandra and it seems to have introduced errors that would break our build graph evaluation.
In fact the file surfaced in the error seems to have several pathological cases in its diff. There's a syntax error introduced somewhere (I didn't track it down exactly, but I think it's related to dynamic attribute set keys), but there's also some behaviour such as detaching comments from the correct list items.
Cheers
let
pattern = if builtins.isString elem then { system = elem; } else { parsed = elem; };
in
lib.matchAttrs pattern platform;
instead of
let
pattern = if builtins.isString elem then { system = elem; } else { parsed = elem; };
in
lib.matchAttrs pattern platform;
Using Nix v2.5.1 on Monterey, aarch64-darwin, I tried both install commands given in the README (nix-env -ivA aarch64-darwin -f https://github.com/kamadorueda/alejandra/tarball/main
at revision ea6e3bf, and nix profile install github:kamadorueda/alejandra
). The complete log ends with:
last 10 log lines:
> libredirect.c:147:27: warning: second argument to 'va_arg' is of promotable type 'mode_t' (aka 'unsigned short'); this va_arg has undefined behavior because arguments will be promoted to 'int' [-Wvarargs]
> mode = va_arg(ap, mode_t);
> ^~~~~~
> /nix/store/n9rwf7h1sids29i0zl077jq8sd9nssp3-clang-13.0.0-lib/lib/clang/13.0.0/include/stdarg.h:19:50: note: expanded from macro 'va_arg'
> #define va_arg(ap, type) __builtin_va_arg(ap, type)
> ^~~~
> 4 warnings generated.
> ld: warning: ignoring file /private/tmp/nix-build-libredirect-0.drv-0/libredirect-e9331c.o, building for macOS-arm64 but attempting to link with file built for macOS-arm64
> fatal error: /nix/store/diz9chv9r9m3pv9rzi7g3p2iq8vgsmr3-cctools-binutils-darwin-949.0.1/bin/lipo: /private/tmp/nix-build-libredirect-0.drv-0/-771885.out and /private/tmp/nix-build-libredirect-0.drv-0/-cc083d.out have the same architectures (arm64) and can't be in the same fat output file
> clang-13: error: lipo command failed with exit code 1 (use -v to see invocation)
For full logs, run 'nix log /nix/store/w4h3cdgb32bl9znyx5nf1rf83mbp8z8m-libredirect-0.drv'.
I bisected the repo down to 446a7b0, which it turns out is where support for aarch64-darwin was first added, and even that revision fails to build for the same reason.
How were you verifying that aarch64-darwin builds? (That is, do you have evidence that it does?)
Following up from: #103
It would be nice to offer binaries for MacOS as well, the logic for building them is in place, we just don't have the hardware
I want to make it super-accessible to use Alejandra, let's show in the README how to curl and get Alejandra running right away
It would be great if alejandra had the ability to provide ignore patterns to exclude some files / drectories from formatting.
In my projects I sometimes copy nix files from other projects. Those files should not be formatted, as this makes it harder to diff against future upstream changes.
Another case where formatting might not be wanted is for automatically generated nix code.
Another feature that I've seen with other formatters is to exclude code snippets from being formatted via a comment inside the code.
(Just mentioning this here, but I currently I do not have a use case for it)
As a reference:
Prettier's docs on excluding code
more than 1 binding becomes unreadable:
diff --git a/lib/fixed-points.nix b/lib/fixed-points.nix
index 6898b24c820..1c9664f6801 100644
--- a/lib/fixed-points.nix
+++ b/lib/fixed-points.nix
@@ -71,7 +71,12 @@ rec {
# into one where changes made in the first are available in the
# 'super' of the second
composeExtensions =
- f: g: final: prev: let fApplied = f final prev; prev' = prev // fApplied; in fApplied // g final prev';
+ f: g: final: prev:
+ let
+ fApplied = f final prev;
+ prev' = prev // fApplied;
+ in
+ fApplied // g final prev';
# Compose several extending functions of the type expected by 'extends' into
# one where changes made in preceding functions are made available to
# subsequent ones.
postPatch = ''
ln -s ${ doc-support } ./doc-support/result
'';
instead of
postPatch =
''
ln -s ${ doc-support } ./doc-support/result
'';
for an example please see: https://github.com/nix-community/dream2nix/compare/3d66429..cdff299#diff-206b9ce276ab5971a2489d75eb1b12999d4bf3843b7988cbe8d687cfde61dea0L23-L31
outputs = {
self,
gomod2nix,
mach-nix,
}@inp:
gets formatted to:
outputs =
{
self
,
gomod2nix
,
mach-nix
,
}
@ inp:
I think it's a respectful pattern to try to minimize impact on downstream flake.lock
-files.
I can self-assign to this.
{
"" = { pkgs
, ...
}: { };
}
I see that you once tried changing the behavior of nixpkgs-fmt to no space
(nix-community/nixpkgs-fmt#280).
Any reason why you changed your mind?
In the equivalent discussion on nixpkgs-fmt, it seemed like everyone kind of agreed that it doesn't impact readability and just results in more horizontal space taken. Also it was shown that most code in nixpkgs uses no space
(Not that I think this is important, but at least we know that not many people there are going to complain about spaces being removed).
Self explanatory, let's define the formatting rules as the public API, and version that with semver
This gives people the guarantee that we'll take care of stability, and that we have reached a maturity state of production ready
lib.mkOption {
type = types.nullOr types.str;
default = null;
}
instead of
lib.mkOption
{
type = types.nullOr types.str;
default = null;
}
if n == len
then value
else { ${ elemAt attrPath n } = atDepth (n + 1); }
Instead of
if n == len then value else { ${ elemAt attrPath n } = atDepth (n + 1); }
This would make it compatible with treefmt.
See https://numtide.github.io/treefmt/formatters-spec.html
If, and only if, a file format has changed, the formatter MUST write the new content in place of the original file.
I just tried alejandra on the main branch of dream2nix.
It seems to format many files, but gets stuck in the end and doesn't return
using src = ./.;
for this causes anything using buildFHSUserEnv to have a diff if the nix expression is altered.
This issue is to track progress of the upstream fix Nixpkgs: 157760
foo: {
bar = 1;
}
instead of
foo:
{
bar = 1;
}
Consider the following starting example:
{
a = 1;
b = 2;
#...
z = 26;
}
Now let's say the code is evolving and I need to add a let .. in
block on top:
let
double = x: x+x;
in
In the current version, this pushes the whole attrset to the right, causing a lot of whitespace formatting diff.
I think the question is; do you prefer a balanced let <statement> in <body>
, or to minimize the diff when the code changes? Both are valid choices IMO, it's just a matter of what you value most.
Here is a minimal repo of the issue:
{
fetch_builtin-url =
''[name] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`.
$ niv modify name -a type=file -a builtin=true'';
}
After formatting:
--- a/repro.nix
+++ b/repro.nix
@@ -1,5 +1,5 @@
{
fetch_builtin-url =
- ''[name] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`.
- $ niv modify name -a type=file -a builtin=true'';
+ '' [name] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`.
+ $ niv modify name -a type=file -a builtin=true'';
}
As you can see, the multi-line string has gained a few whitespaces in front, changing the content of the Nix value.
This is probably related to nix-community/rnix-parser#71
--- a/lib/lists.nix
+++ b/lib/lists.nix
@@ -48,9 +48,7 @@ in
=> "2345a"
*/
foldr =
- op:
- nul:
- list:
+ op: nul: list:
let
len = length list;
fold' =
267 │ docOption =
268 │ rec {
269 │ loc = opt.loc;
270 │ name = showOption opt.loc;
271 │ description = opt.description or null;
272 │ declarations = filter (x: x != unknownModule) opt.declarations;
273 │ internal = opt.internal or false;
274 │ visible =
275 │ if (opt ? visible && opt.visible == "shallow")
276 │ then true
277 │ else opt.visible or true;
278 │ readOnly = opt.readOnly or false;
279 │ type = opt.type.description or null;
280 │ }
281 │ // optionalAttrs (opt ? example) { example = scrubOptionValue opt.example; }
282 │ // optionalAttrs (opt ? default) { default = scrubOptionValue opt.default; }
283 │ // optionalAttrs (opt ? defaultText) { default = opt.defaultText; }
284 │ // optionalAttrs
285 │ (opt ? relatedPackages && opt.relatedPackages != null)
286 │ { inherit (opt) relatedPackages; };
Instead of:
267 │ docOption =
268 │ rec {
269 │ loc = opt.loc;
270 │ name = showOption opt.loc;
271 │ description = opt.description or null;
272 │ declarations = filter (x: x != unknownModule) opt.declarations;
273 │ internal = opt.internal or false;
274 │ visible =
275 │ if (opt ? visible && opt.visible == "shallow")
276 │ then true
277 │ else opt.visible or true;
278 │ readOnly = opt.readOnly or false;
279 │ type = opt.type.description or null;
280 │ }
281 │ // optionalAttrs (opt ? example) { example = scrubOptionValue opt.example; }
282 │ // optionalAttrs (opt ? default) { default = scrubOptionValue opt.default; }
283 │ // optionalAttrs (opt ? defaultText) { default = opt.defaultText; }
284 │ // optionalAttrs
285 │ (opt ? relatedPackages && opt.relatedPackages != null)
286 │ { inherit (opt) relatedPackages; };
same for select nodes (a.b.c
)
In the following case, the parens are not necessary:
+ {
+ binPath =
+ with pkgs;
+ makeBinPath (
+ [
+ rsync
+ util-linux
+ ]
+ );
+ }
after a key-value they can also be deleted, etc
Hi, I found a bug related to nix-community/rnix-parser#23.
Input
let b = a: a; "or" = 5; in [b or]
Nix repl
[ 5 ]
Alejandra Output
Error: unexpected TOKEN_SQUARE_B_CLOSE at 32..33, wanted any of [TOKEN_PAREN_OPEN, TOKEN_REC, TOKEN_CURLY_B_OPEN, TOKEN_SQUARE_B_OPEN, TOKEN_DYNAMIC_START, TOKEN_STRING_START, TOKEN_IDENT], at: stdin
The stdout contains the input code, which is not formatted.
Expected output
let
b = a: a;
"or" = 5;
in [b or]
{
packEntry = deps: {
inherit deps;
text = "";
};
}
instead of
{
packEntry =
deps:
{
inherit deps;
text = "";
};
}
Compact containers (like lists and attr-sets) are nice,
but they can be unreadable when they contain more than one element
Below the proposed change:
--- a/nixos-modules/editor/default.nix
+++ b/nixos-modules/editor/default.nix
@@ -44,9 +44,18 @@ let
"[python]"."editor.tabSize" = 4;
"[rust]"."editor.tabSize" = 4;
"customLocalFormatters.formatters" = [
- { command = "clang-format --sort-includes --style=microsoft"; languages = [ "cpp" ]; }
- { command = "${ nixpkgs.jq }/bin/jq -S"; languages = [ "json" "jsonc" ]; }
- { command = alejandra.outputs.defaultApp.${ nixpkgs.system }.program; languages = [ "nix" ]; }
+ {
+ command = "clang-format --sort-includes --style=microsoft";
+ languages = [ "cpp" ];
+ }
+ {
+ command = "${ nixpkgs.jq }/bin/jq -S";
+ languages = [ "json" "jsonc" ];
+ }
+ {
+ command = alejandra.outputs.defaultApp.${ nixpkgs.system }.program;
+ languages = [ "nix" ];
+ }
We don't have case expressions on Nix, and so developers use nested if/then/else statements
Currently it looks like this:
if
0
#
then
0
#
else
if
0
#
then
0
else
0
alejandra formats all my inline comments to be on the next line,
accepted_roles = [
+ # only these roles are ok
"devtools"
"workstation"
"remote"
@@ -90,8 +96,10 @@
"mqtt-server"
"mqtt-client"
- "zpool-watcher" # where our watcher runs
- "zpool-systems" # the systems being watched
+ "zpool-watcher"
+ # where our watcher runs
+ "zpool-systems"
+ # the systems being watched
"virtualbox"
that obscures the meaning :(.
Hi, apologies if there is some pattern to this that I'm not quite seeing, but looking at some reformatting patterns in my system flake:
- imports =
- [ ./hardware-configuration.nix ../bluetooth.nix ../power.nix ../wifi.nix ];
+ imports = [ ./hardware-configuration.nix ../bluetooth.nix ../power.nix ../wifi.nix ];
79 -> 87 characters. While I find this nicer to look at, this breaks the max width.
Another, more complex example is:
- nixpkgs.config.allowUnfreePredicate = pkg:
- builtins.elem (pkgs.lib.getName pkg) [ "steam-original" ];
+ nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (pkgs.lib.getName pkg) [ "steam-original" ];
On the other hand, I find this elsewhere:
- fonts = with pkgs; [ hack-font noto-fonts noto-fonts-cjk noto-fonts-emoji ];
+ fonts =
+ with pkgs; [ hack-font noto-fonts noto-fonts-cjk noto-fonts-emoji ];
80 -> 74 characters. The fact that this means 79 is the de-facto max line width aside, it seems strange that this would be formatted to strictly adhere to that limit, whereas the former examples are not.
I saw #51, but b918b9b produces the same output, and the test case seems unrelated to my untrained eyes. Sometimes abandoning strict max-line-width certainly makes sense, but it would be great if the behavior was a bit more obviously predictable :)
This one requires a big change in the architecture
I think the sane place to apply this rule would be to attribute sets, lists and let-in expressions only
diff --git a/tests/cases/dynamic/out b/tests/cases/dynamic/out
index 6f74b7d..5eb8cec 100644
--- a/tests/cases/dynamic/out
+++ b/tests/cases/dynamic/out
@@ -8,7 +8,7 @@ a
/*
d
*/
- e.${ f }
+ e.${f}
}
/*
g
current master renders this:
https://github.com/divnix/std/blob/7ac01da66a1cbc5b0a682840121bd04d545e94bb/flake.nix#L83-L84
if ./a
then b
else c
instead of:
if
./a
then
b
else
c
let
foo
in {
bar
}
instead of
let
foo
in
{
bar
}
Enter the development environment and run:
$ cargo tarpaulin -o html
Now open ./tarpaulin-report.html
in your web browser, for instance:
$ google-chrome-stable tarpaulin-report.html
And you'll see a detailed coverage of each file, line by line of the portions of the code that are exercised during the tests (green) or not (red), there may also be cases of dead code:
Help us writing test cases that make everything look green
The idea is to get to 100% coverage, which help a lot avoiding regressions, and improving the reliability of the tool
For the projects I'm maintaining, I'm looking for a code formatter.
The reason why formatters like nixpkgs-fmt aren't optimal for me is because they try to implement the predominant style in nixpkgs. The problem with this is, that some things in nixpkgs are just bad and therefore following nixpkgs style comes with compromises.
When I first encountered alejandra - The Uncompromising Nix Code Formatter
my hopes were high. Sadly I see that the readme has been updated a few days ago and now contains the statement:
We optimize for the wisdom of the crowd, which comes in big part from the 2.3 million lines of code of [Nixpkgs](https://github.com/NixOS/nixpkgs).
This stands in opposition to the title of the project itself. If alejandra actually was uncompromising
, it shouldn't be important what people have been doing before.
I understand that alejandra tries to be a viable candidate for RFC101, but that comes with compromises.
If I go through the issues here, I see comments indicating that minimizing the initial diff on nixpkgs
could be a goal of alejandra (#60 (comment)).
I'm not intending to tell anyone here what to do. RFC101 is definitely a goal worth achieving, but it is not uncompromising
and therefore would make the formatter most likely less interesting for my projects where I don't care about minimizing the diff and prefer the most practical style instead.
❯ cargo test
error: failed to parse manifest at `/home/blaggacao/src/github.com/kamadorueda/alejandra/Cargo.toml`
Caused by:
feature `edition2021` is required
this Cargo does not support nightly features, but if you
switch to nightly channel you can add
`cargo-features = ["edition2021"]` to enable this feature
{
text = ''
#!${
if libraries == [ ]
then "${python}/bin/python"
else "${python.withPackages (ps libraries)}/bin/python"
}
'';
}
the entire ${ ... }
block is missing an indent.
Here is an example that makes things less readable:
- nixos-config = (pkgs.nixos [
- ({ pkgs, ... }: { nix.package = pkgs.nixUnstable; })
- ]);
+ nixos-config = (
+ pkgs.nixos [
+ (
+ { pkgs
+ , ...
+ }:
+ { nix.package = pkgs.nixUnstable; }
+ )
+ ]
+ );
Alejandra could actually remove the first set of parenthesis as they are not needed.
Why does it break the second line multi-line? In general, it feels like function arguments should be on the same line as the parenthesis but I'm not sure.
Projects that want to track WIP-alerjandra would benefit from a cachix action to provide a readily built cache hit on new commits.
Otherwise, it can get a bit (time-)expensive to suffer the frequent rebuilds.
Luxury problem.
Only if the let-in
is in the top level
let
a = 1;
in
a
Instead of:
let
a = 1;
in
a
Original:
/*@
@
@*/
Expected:
/*
@
@
@
*/
Current:
/*
@
@
@
*/
I know that leading commas seem to be a standard in haskell and somehow everyone does it, but actually it's unhandy when editing code.
Explanation of why leading commas negatively impact editing experience is already given in the equivalent issue on nixpkgs-fmt: nix-community/nixpkgs-fmt#248 (comment)
To summarize what has already been said:
{
and subsiding ones with ,
.In the other thread, there were a lot of opposing opinions to what I'm saying, but if we are having this discussion on a scientific basis, any opposing argument to this should either contain an explanation of why my statements are wrong or irrelevant, or explain disadvantages of trailing commas.
I am still waiting for such a response.
I think if we want to achieve the best possible result, we need to leave personal preferences out of this as well as statements like but this is what everyone does
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.