nixfmt
repository·master·Indexed 23 days ago
https://github.com/nixos/nixfmtA formatter for Nix code that implements the standard Nix formatting rules established by RFC 166. It provides a CLI for manual formatting, integration with editors like Neovim and VS Code, and support for git-hooks, pre-commit, and git mergetool. The tool enforces specific indentation, line length limits, and layout rules to ensure consistency across the Nix ecosystem.
What's inside nixfmt
- The standard Nix format is defined by RFC 166. It establishes rules for how Nix code should be laid out to ensure consistency across the ecosystem.
What is Absorption in Nix formatting?
masterAbsorption is a layout technique where a multiline expression starts on the same line as its preceding context instead of on a new line. This is used to potentially save a level of indentation for the content of that expression.
An expression is considered an Absorbable Term if it is an:
- Attribute set (
{}) - List (
[]) - Multiline string (
'' ... '') - Parenthesized version of any of the above
{ # Absorbed: The list starts on the same line as the binding RHS absorbed = with bar; [ 1 2 3 ]; # Not Absorbed: A comment on the same line as the expression forces a new line layout notAbsorbed = with bar; # Placing a comment here will force the non-absorbed, multiline layout. [ 1 2 3 ]; }- Attribute set (
Format if-then-else Expressions
masterRules for
if-then-else:ifandelsekeywords must always start on a new line.- The
ifandelsebodies must be indented. else ifchains are treated as a single sequence with no indentation creep.else ifchains must not be on a single line.
if cond1 then foo else if cond2 then bar else bazFormat inherit Statements
masterRules for
inherit:- Items must either be all on the same line, or all on a new line each (with indentation).
- If items are on new lines, the semicolon must be on its own line with indentation.
inherit (<source>):
- If the entire fragment fits on one line, it stays on one line.
- If only
inherit (<source>)fits, it stays on one line, and attributes follow the standardinheritrules. - Otherwise, the
(<source>)must also be on its own line.
Format Bindings
masterBindings (let bindings, attribute sets, and default function arguments) use several styles based on size:
- Single-line: Fits entirely on one line.
- Absorbed: The body fits on one line, but the binding is too long (e.g.,
foo = function { args };). - Newline and Indent: If neither of the above applies, the body starts on a new line with indentation.
Special Cases:
- Attribute sets: Must always be expanded (multiline).
- Operator chains (
++,//,+): If the first element is absorbable, it is formatted to avoid diff churn (e.g.,foo = [ bar ] ++ baz;). - Semicolons: Must always be placed on the same line as the expression they conclude.
# Single line foo = "bar"; # Absorbed add = x: y: { result = x + y; }; # Newline and indent bar = if baz == null then 10 else 20;Understand the Single-line Common Ancestor Rule
masterTo prevent hard-to-read code, Nixfmt enforces that for any two (sub-)expressions fully on a common single line, their smallest common ancestor expression must also be on that same line.
- Bad:
if cond then foo else bar(The ancestorif-then-elsespans multiple lines). - Good:
if cond then foo else bar(The ancestor is on the same line). - Bad:
foo || bar baz(The ancestorfoo || bar bazspans two lines due to precedence).
- Bad:
Formatting Strings and Interpolations
masterNixfmt preserves the quote type (
"vs'') used in the input.Indented Strings (
''...''):- If an indented string contains no newlines, double quotes, or backslashes, it is automatically converted to a simple string (
"..."). - Escape sequences are rewritten during conversion (e.g.,
''$becomes\$). - Long indented strings that exceed line limits are not automatically shortened.
Interpolations:
- Simple interpolations (short, low complexity) are rendered on a single line regardless of length.
- If an interpolation is the first thing on a string line, its contents may be absorbed into the line. Otherwise, it must start on a new line.
- If an indented string contains no newlines, double quotes, or backslashes, it is automatically converted to a simple string (
Format Attribute Sets and Lists
masterRules for collections:
- Brackets and braces must have a space (or line break) on the inside:
[,],{,}. - Empty collections are
[ ]and{ }. - They can only be on a single line if they fit and contain few items.
- Nested attribute sets are always expanded (multiline).
[ { } { foo = "bar"; } { foo = { bar = "baz"; }; } ]- Brackets and braces must have a space (or line break) on the inside:
Format Operators
masterNixfmt distinguishes between non-chainable and chainable operators.
Non-chainable operators (no associativity):
- The right-hand side must be on the same line as the operator.
- The operator must either be attached to the left-hand side or start on a new line.
Chainable operators (associative, same/decreasing precedence):
- Must be treated as a single chain.
- If the chain exceeds the line length, every operator must start on a new line.
- Operator chains in bindings can be compacted if all lines between the first and last are indented.
Format Function Declarations
masterRules for defining functions:
- The function body must not be indented relative to its first arguments.
- Identifier arguments: A small number of simple identifiers can stay on the same line as the function name. Otherwise, each gets its own line.
- Attribute set arguments: Must always start on a new line and cannot be mixed with identifier arguments. If they have few attributes, they can be on one line; otherwise, each attribute gets its own line with a trailing comma.
# Simple identifiers name: value: name ++ value # Attribute set arguments args@{ some, argument, default ? value, ... }: { # body }Format Function Application
masterIn a function application chain, the first element is the function and the rest are arguments.
Rules:
- Fit as many arguments as possible on the first line.
- If the line length limit is reached, the first argument not fitting on the first line starts a new line, and all subsequent arguments must also start on their own lines.
- Parenthesized last arguments: If the last argument is parenthesized, the parentheses are usually absorbed, and the body is put on a new line with indentation.
Example:
# All arguments fit on the first line function arg1 arg2 # Line length reached, remaining arguments on new lines function arg1 arg2 arg3 arg4 arg5Formatting Comments
masterNixfmt applies several rules to comments:
- Doc comments:
/**comments are handled according to RFC 0145. - Single-line comments:
/* ... */comments are converted to#comments (except for language annotations). - Language annotations: Comments like
/* bash */followed immediately by a string literal are preserved as block comments. If not followed by a string, they are converted to#line comments. - Empty comments: May be deleted.
- Multiline comments:
/*and*/start on new lines and are vertically aligned with the content inside having one extra level of indentation.
- Doc comments: