Skip to content

Absorb single-line destructured params into parenthesized functions#377

Open
dyegoaurelio wants to merge 1 commit intomasterfrom
fn-identation
Open

Absorb single-line destructured params into parenthesized functions#377
dyegoaurelio wants to merge 1 commit intomasterfrom
fn-identation

Conversation

@dyegoaurelio
Copy link
Copy Markdown
Contributor

closes #375

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 20, 2026

Nixpkgs diff

@dyegoaurelio
Copy link
Copy Markdown
Contributor Author

I didn't expect that the nixpkgs diff would be that big

Copy link
Copy Markdown
Contributor

@MattSturgeon MattSturgeon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking the time to implement this!

I still think it's generally the right direction. However, I'm also a little cautious of the implications for diff noise resulting from indentation changing back and forth, which I hadn't considered in the last meeting.

Comment thread src/Nixfmt/Pretty.hs
Comment on lines +583 to +584
-- braces must be on the same source line, and up to 2 attrs without defaults,
-- with an optional ellipsis.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it also worth considering whether the set pattern has an @-name?

E.g. { foo, bar, ... } is fairly trivial, but { foo, bar, ... }@args is less so.

And/or consider the total char count of the formatted set pattern (including @-name and braces), similar to how we count the total chars of a formatted line? That'd account for long arg names and long @-names.

Comment thread src/Nixfmt/Pretty.hs
-- Absorb function declarations but only those with simple parameter(s)
(Abstraction (IDParameter _) _ (Term t)) | isAbsorbable t -> True
(Abstraction (IDParameter _) _ body@(Abstraction{})) -> isAbsorbableExpr body
-- Absorb function declarations with attribute set parameters if the attribute set is simple enough, and the body is absorbable.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to "soft absorb", allowing us to respect line breaks in user input?

E.g. if the user explicitly wants

{ foo, ... }:
{ }

to have the function body on a separate line, we could respect this.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this may be useful for modules, where it is common to add/remove attrs from the destructured set pattern.

Frequently switching between 2-3 attrs would cause frequent indentation change diffs.

Especially for modules defined within a wider expression, e.g.:

{
  perSystem =
    { pkgs, system, ... }:
    { };
}

If we start adding/removing a third argument from that example, the entire function body gets indented/dedented.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would also avoid a treewide nixpkgs diff, since we already have the line breaks in nixpkgs.

Instead, we'd have the option of looking for functions that benefit from absorbed formatting and manually removing their line breaks.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this would be tricky to implement. We don't have the concept of soft absorption in the formatter.

Another alternative would be to don't mark the expression as absorbable if the source line is the same as it's parent.

However, given this input

    perSystem =
      { pkgs, system, ... }:
      {
        out = pkgs.lib.optionalString (system == "x86_64-linux") "foo";
      };
    
    perSystem = { pkgs, system, ... }:
      {
        out = pkgs.lib.optionalString (system == "x86_64-linux") "foo";
      };

the goal would be to not absorb the first one, but absorb the second one, right?

The problem is that for both cases we'd be resolving whether the expression

{ pkgs, system, ... }:
      {
        out = pkgs.lib.optionalString (system == "x86_64-linux") "foo";
      };

is absorbable. (it has no context about where it's positioned)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So in short, it's possible but would require much more changes to the code

# Stuff
}
)
(a: { b, ... }: c: {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really an improvement? IMO, mixed plain and attrs function args should not be on the same line

# comment on attr parameter
(
# comment
{ a }: { x = 1; })
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Closing parenthesis is wrong here

@nixos-discourse
Copy link
Copy Markdown

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/formatting-team-meeting-2026-03-31/76739/1

When a function with a destructured attribute set parameter fits on a
single line, absorb the body onto the same line as the colon, reducing
unnecessary indentation in parenthesized expressions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Reduce indentation in parentheses-wrapped functions

4 participants