BTW to satisfy my initial complaint, another option would be to allow this:
{
outputs =
[
"out"
#]
#++ lib.optionals stdenv.isDarwin [
# "dev"
# "man"
];
}
Then when that comment will be uncommented, it would not cause a big diff. What do you think @Infinisil ? This way maintainers would be able to start out with code formatted in such a way that future diffs will be clean.
Just to add to the discussion with an example of something that I think wasn’t formatted very well
{
yazi_floating_window_winblend = defaultNullOpts.mkNullableWithRaw (types.ints.between 0
100
) 0 "`0` for fully opaque and `100` for fully transparent. See :h winblend";
}
I like that an attempt was made to attach to the ( )
parenthesis (as per the RFC), however I think this specific example would be more readable formatted with one line per argument:
{
yazi_floating_window_winblend =
defaultNullOpts.mkNullableWithRaw
(types.ints.between 0 100)
0
"`0` for fully opaque and `100` for fully transparent. See :h winblend";
}
That said, this is a specific example, I’m not sure I can come up with a general rule-based suggestion here.
Perhaps some heuristics when deciding whether to split up an expression within ( )
to prefer keeping the inner expression intact?
3 Likes
I think we could debate whether it makes sense to use comments to disable code in the first place; in a project tracked by version control it should be trivial to find removed code via file history, right?
Was there a ruling in the RFC to say that we cannot attach the opening [
when assigning a list concatenation expression rather than a single list literal ?
If reducing diff sizes is the goal, then perhaps we should be formatting it as:
{
outputs = [
"out"
]
++ lib.optionals stdenv.isDarwin [
"dev"
"man"
];
}
Or even:
{
outputs = [
"out"
] ++ lib.optionals stdenv.isDarwin [
"dev"
"man"
];
}
Although, I’m sure this has been debated before, likely during the RFC.
If so, perhaps it’s worth re-visiting the debate now that the formatting has reached a wider audience?
This is a place where the RFC is under-specified, I think (implication is not that the RFC authors did a bad job, but rather that the formatting team has freedom to do what they think best in this case):
As many arguments as possible must be fit onto the first line.
If there is at most one multi-line argument that can be absorbed and all other arguments before/after fit onto a single line respectively, then that multi-line argument is absorbed.
Otherwise, the first argument not fitting onto the first line will start a new line with indentation, and all subsequent arguments will start on their own line as well.
All arguments that are not on the same line as the function must be indented by one level.
If the last argument is parenthesized, the parentheses should get absorbed while its body is put on a new line with indentation.
Exception: If the last argument is parenthesized and its body contains an absorbable term, an alternative and more compact layout may be used instead: The body gets compacted and its term absorbed.
In this case, the inner term may be force-expanded.
This results in less indentation for many common Nix idioms.
The ‘first argument not fitting onto the first line’ is (types.ints.between 0 100)
, and it must ‘start a new line with indentation’, and ‘all subsequent arguments will start on their own line as well’. So this example is incorrect unless ‘there is at most one multi-line argument that can be absorbed’. Does (types.ints.between 0 100)
qualify as a multi-line argument? Does it depend on whether it was initially written on multiple lines? I don’t think the RFC ever said.
2 Likes
It’s not in the RFC proper, but a principle arrived at during internal discussions was
AFAIR that principle went unchallenged in public.
1 Like
I can see the logic, that the ] ++ foo [
bit in the middle being un-indented could be confusing at a glance.
I don’t feel strongly either way, but I think it’s valuable to get feedback from users who missed that discussion and/or the RFC process entirely, so I’m glad this post was opened as an attempt to gather such feedback!
1 Like
Atemu
August 10, 2024, 4:13am
27
After working with formatted code a bit and then reading to a non-formatted file, I got pretty confused at an expression that violated this rule for a good few seconds. This proves to me that it’s a pretty good rule.
2 Likes
absolutely agree with the need for short lists to remain on a single line. with a simple line like:
(someFunc [ a b c ] "potato" "bar")
it reformats it to:
(someFunc [
a
b
c
] "potato" "bar")
which is just absolutely miserable. IMO, list items should not be broken out to a new line unless the entire line exceeds, say, 100 characters or something.
there are also things like this:
(exec [ mod ctrl ] "b" (builtins.toString [
qutebrowser
''-B "${config.xdg.configHome}/qutebrowser/profiles/foo"''
''-C "${config.xdg.configHome}/qutebrowser/profiles/foo/config.py"''
]))
getting exploded into:
(exec
[
mod
ctrl
]
"b"
(
builtins.toString [
qutebrowser
''-B "${config.xdg.configHome}/qutebrowser/profiles/foo"''
''-C "${config.xdg.configHome}/qutebrowser/profiles/foo/config.py"''
]
)
)
which is far, far less readable and is just a giant waste of space. there’s absolutely no reason that each bracket and paren needs to be on its own line.
3 Likes
This has been bothering me for a while too, so I hacked together something small to address it: Don't expand lists within functions by piegamesde · Pull Request #233 · NixOS/nixfmt · GitHub
2 Likes
nh2
August 13, 2024, 4:08am
30
Is it expected that nixfmt
makes package imports un-diffable / un-cherry-pickable, or am I using it wrong?
NixOS:staging-next
← nh2:ceph-18.2.4-staging-next-fix
I'm not planning to implemented this `nixfmt` change suggested by the CI:
htt… ps://github.com/NixOS/nixpkgs/actions/runs/10363164012/job/28686308371?pr=334286
```
pkgs/development/python-modules/cryptography/40.nix: not formatted
pkgs/development/python-modules/cryptography/vectors-40.nix: not formatted
Ignoring file pkgs/tools/filesystems/ceph/default.nix because it's not formatted in the base commit
Ignoring file pkgs/top-level/python-packages.nix because it's not formatted in the base commit
Some new/changed Nix files are not properly formatted
Please run the following in `nix-shell`:
nixfmt 'pkgs/development/python-modules/cryptography/40.nix' 'pkgs/development/python-modules/cryptography/vectors-40.nix'
Error: Process completed with exit code 1.
```
It's formatting it like this, which looks like the most diff-unfriendly way of doing things, and I'm not planning to take on that maintenance burden that will make backports and other cherry-picks left and right:
```diff
-{ lib
-, stdenv
-, callPackage
-, buildPythonPackage
-, fetchPypi
-, rustPlatform
-, cargo
-, rustc
-, setuptoolsRustBuildHook
-, openssl
-, Security
-, isPyPy
-, cffi
-, pkg-config
-, pytestCheckHook
-, pytest-subtests
-, pythonOlder
-, pretend
-, libiconv
-, libxcrypt
-, iso8601
-, py
-, pytz
-, hypothesis
-}:
+{ lib, stdenv, callPackage, buildPythonPackage, fetchPypi, rustPlatform, cargo
+, rustc, setuptoolsRustBuildHook, openssl, Security, isPyPy, cffi, pkg-config
+, pytestCheckHook, pytest-subtests, pythonOlder, pretend, libiconv, libxcrypt
+, iso8601, py, pytz, hypothesis }:
```
This seems more wrong than makes sense to me -- does `staging-next` have an incorrect version of `nixfmt` somehow?
nh2
August 13, 2024, 12:34pm
32
Thanks, indeed that fixes the issue!
I propose to slightly update the message to avoid such confusion:
NixOS:staging-next
← nh2:ceph-18.2.4-staging-next-fix
> It looks like you're using nixfmt classic, not the RFC styled one. If you ente… r the `nix-shell` (which the error does mention btw :P), you'll have the right pinned version available.
@infinisil Ah indeed, that fixed it. The new formatting is good!
I had run
```sh
NIX_PATH=nixpkgs=. nix-shell -p nixfmt
```
which brought me `nixfmt-0.6.0` while to get `nixfmt-unstable-2024-07-12` one needs to run just
```
NIX_PATH=nixpkgs=. nix-shell
```
in the top-level of nixpkgs.
I didn't get that
```
Please run the following in `nix-shell`:
```
means to literally run `nix-shell` -- I had thought it meant to use `nix-shell` to obtain `nixfmt` (that is, via `-p`).
It might make sense to make that message even clearer to avoid the confusion.
I believe vm tests also gets unnecessary indentation, what use to be 2 simple lines
import ../make-test-python.nix ({ lib, ...}: {
# ...
})
now becomes the following snippet, adding a extra unnecessary indent to
import ../make-test-python.nix (
{ lib, ... }:
{
# ...
}
)
picnoir
Split this topic
August 23, 2024, 8:39am
34
Malix
June 1, 2025, 8:06pm
35
I think arbitrary spaces are objectively conceptually inferior to 1 tab for each indentation level
Nix doesn’t support tabs at the start of indented strings, so that’s a nonstarter.
2 Likes
Malix
June 1, 2025, 8:47pm
37
True.
Those would need to be merged first
opened 11:41AM - 28 Jun 20 UTC
closed 08:31PM - 29 Jun 20 UTC
**Is your feature request related to a problem? Please describe.**
The manual… says about indented strings:
> This kind of string literal intelligently strips indentation from the start of each line. To be precise, it strips from each line a number of spaces equal to the minimal indentation of the string as a whole (disregarding the indentation of empty lines).
This is exclusive to spaces and won't work on files indented using tabs.
**Describe the solution you'd like**
I'd like to have it work with tabs as well.
**Describe alternatives you've considered**
- Create some helper functionality to convert between tabs and spaces
- Create some helper functionality to strip tabbed indented strings
- Resign and use soft tabs
**Additional context**
I use tabs for indentation. Programming languages should not force me to use spaces for some features to work.
opened 02:22PM - 14 Feb 23 UTC
bug
language
breaking
**Describe the bug**
Tabs are not supported in the indentation stripping pars… er. This is the right behavior in principle, but when a user does insert a tab, the behavior does not match their indentation. The right solution is to warn and let the user fix the problem. Supporting tabs is wrong, because tab width is undefined and will never be defined consistently.
Adding a warning may make `'' ''` strings annoying for users who have to write strings with tabs at the start of lines, but their solution is already fragile, so they should move to explicit tabs (`"\t"`) or put the string in a separate file.
**Steps To Reproduce**
1. Write a file with a `''` string indented with tabs or a mix of spaces and tabs.
2. Parsed string contains tab characters.
**Expected behavior**
Warn when the first tab is encountered in the indentation stripping logic. Suggest alternatives, for both cases: user wants tabs stripped, or (less likely, current behavior) user wants to keep tabs in their string.
**`nix-env --version` output**
**Additional context**
Removing tabs from indented strings is a hash-changing breaking change. (edit: 2024-10)
- see also https://github.com/NixOS/nix/pull/9971#issuecomment-1986903150
**Priorities**
Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc).
opened 08:24AM - 07 Oct 23 UTC
closed 07:10AM - 10 Oct 23 UTC
bug
**Describe the bug**
Indented strings do not work with tabs: The "smart inden… tation" as described in the [documentation](https://nixos.org/manual/nix/stable/language/values.html#type-string) does not work:
> This kind of string literal intelligently strips indentation from the start of each line. To be precise, it strips from each line a number of spaces equal to the minimal indentation of the string as a whole (disregarding the indentation of empty lines)
**Steps To Reproduce**
In a nix file, include something like this and indent _with tabs_:
```nix
home.file."myfile".text = ''
# This should not be indented in the generated file.
'';
```
This results in the generation of myfile as follows, _with_ unwanted indentation, _with_ an unwanted empty line, and _with_ trailing space on the empty line:
```
> > # This should not be indented in the generated file.
>
```
(where `>` represents a tab character)
**Expected behavior**
"myfile" contains the text (without indentation and without additional empty line):
```
# This should not be indented in the generated file.
```
**`nix-env --version` output**
nix-env (Nix) 2.13.5
**Additional context**
I did come across https://github.com/NixOS/nix/issues/3759, but it was closed without an understandable reason, other than:
> We can't change this because it would be incompatible (it could cause the result of evaluating a derivation to change).
_(The author of that comment is [against the use of tabs in general](https://github.com/NixOS/nix/pull/2911#issuecomment-498141657).)_
Supporting tabs (and different newline formats) is an absolutely basic aspect of programming languages. Nix should not require any special handling/attention when working with tabs.
**Priorities**
Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc).
master
← cslamber:master
opened 03:06AM - 03 Jun 19 UTC
There are a fair number of derivations in nixpkgs that seem to unintentionally u… se tabs in their expressions instead of spaces (evilwm, gitit, animbar, ocaml-expat), and right now spaces are the only indentation recognized. This change allows tabs to be used by having whatever whitespace character appears first at the start of lines as the character used for indentation. No assumptions need to be made about tab configuration because this does not support mixed tabs and spaces. This would also allow people who prefer tabs in their local derivations to use that instead.
The only place where mixing is acceptable is with the final ignored line, as some of the packages specified above have spaces indenting their final lines, which seem to be intended to be ignored.
This is my first PR, so feel free to modify or critique it however you want.
master
← magistau:master
opened 05:33PM - 08 Feb 24 UTC
# Motivation
Many users prefer using hard tabs for indentation, but the exist… ing indentation stripping logic only considers spaces as indentation characters and therefore doesn't strip any tab characters.
# Context
Closes #7834.
Previously, `stripIndentation` would look for the longest common space-consisting line prefix; now it finds either the longest common space-consisting or tab-consisting prefix. A tab after a space is not considered as indentation, neither is a space after a tab.
Changing the semantics of the indentation stripping unfortunately is a blatant breaking change, so actually implementing this would require either a new syntax for tab-indented strings or (preferably) some language versioning infrastructure
2 Likes
cmm
June 2, 2025, 9:10am
39
Tabs are, in wide retrospect, an almost-perfect example of an attractive nuisance (I am personally a fan of the “smart tabs” code indentation style, but it is what it is)