nixfmt

repository·master·Indexed 23 days ago

https://github.com/nixos/nixfmt

A 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.

Tokens
4.3K
Snippets
18
Records
31
Agent score
32%

What's inside nixfmt

  1. What is Absorption in Nix formatting?

    master

    Absorption 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
        ];
    }
  2. Format if-then-else Expressions

    master

    Rules for if-then-else:

    • if and else keywords must always start on a new line.
    • The if and else bodies must be indented.
    • else if chains are treated as a single sequence with no indentation creep.
    • else if chains must not be on a single line.
    if cond1 then
      foo
    else if cond2 then
      bar
    else
      baz
  3. Format inherit Statements

    master

    Rules 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 standard inherit rules.
    • Otherwise, the (<source>) must also be on its own line.
  4. Format Bindings

    master

    Bindings (let bindings, attribute sets, and default function arguments) use several styles based on size:

    1. Single-line: Fits entirely on one line.
    2. Absorbed: The body fits on one line, but the binding is too long (e.g., foo = function { args };).
    3. 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;
  5. Understand the Single-line Common Ancestor Rule

    master

    To 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 ancestor if-then-else spans multiple lines).
    • Good: if cond then foo else bar (The ancestor is on the same line).
    • Bad: foo || bar baz (The ancestor foo || bar baz spans two lines due to precedence).
  6. Formatting Strings and Interpolations

    master

    Nixfmt 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.
  7. Format Attribute Sets and Lists

    master

    Rules 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";
        };
      }
    ]
  8. Format Operators

    master

    Nixfmt 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.
  9. Format Function Declarations

    master

    Rules 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
    }
  10. Format Function Application

    master

    In 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
      arg5
  11. Formatting Comments

    master

    Nixfmt 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.