@@ -56,10 +56,12 @@ import Nixfmt.Types (
56
56
Trivium (.. ),
57
57
Whole (.. ),
58
58
ann ,
59
+ hasPreTrivia ,
59
60
hasTrivia ,
60
61
mapFirstToken ,
61
62
mapFirstToken' ,
62
63
mapLastToken' ,
64
+ matchFirstToken ,
63
65
tokenText ,
64
66
)
65
67
import Nixfmt.Util (isSpaces )
@@ -151,7 +153,7 @@ instance Pretty Binder where
151
153
pretty (Assignment selectors assign expr semicolon) =
152
154
group $
153
155
hcat selectors
154
- <> nest (hardspace <> pretty assign <> nest rhs)
156
+ <> nest (hardspace <> pretty assign <> rhs)
155
157
<> pretty semicolon
156
158
where
157
159
rhs =
@@ -262,7 +264,7 @@ instance Pretty ParamAttr where
262
264
group $
263
265
pretty name
264
266
<> hardspace
265
- <> nest (pretty qmark <> nest ( absorbRHS def) )
267
+ <> nest (pretty qmark <> absorbRHS def)
266
268
<> pretty maybeComma
267
269
-- `...`
268
270
pretty (ParamEllipsis ellipsis) =
@@ -500,6 +502,34 @@ prettyApp indentFunction pre hasPost f a =
500
502
(\ fRendered -> group' RegularG $ fRendered <> line <> absorbLast a <> post)
501
503
<> (if hasPost && not (null comment') then hardline else mempty )
502
504
505
+ prettyOp :: Bool -> Expression -> Leaf -> Doc
506
+ prettyOp forceFirstTermWide operation op =
507
+ let -- Walk the operation tree and put a list of things on the same level.
508
+ -- We still need to keep the operators around because they might have comments attached to them.
509
+ -- An operator is put together with its succeeding expression. Only the first operand has none.
510
+ flatten :: Maybe Leaf -> Expression -> [(Maybe Leaf , Expression )]
511
+ flatten opL (Operation a opR b) | opR == op = flatten opL a ++ flatten (Just opR) b
512
+ flatten opL x = [(opL, x)]
513
+
514
+ -- Called on every operand except the first one (a.k.a. RHS)
515
+ absorbOperation :: Expression -> Doc
516
+ absorbOperation (Term t) | isAbsorbable t = hardspace <> pretty t
517
+ -- Force nested operations to start on a new line
518
+ absorbOperation x@ (Operation {}) = group' RegularG $ line <> pretty x
519
+ -- Force applications to start on a new line if more than the last argument is multiline
520
+ absorbOperation (Application f a) = group $ prettyApp False line False f a
521
+ absorbOperation x = hardspace <> pretty x
522
+
523
+ prettyOperation :: (Maybe Leaf , Expression ) -> Doc
524
+ -- First element
525
+ prettyOperation (Nothing , Term t) | isAbsorbableTerm t && forceFirstTermWide = prettyTermWide t
526
+ prettyOperation (Nothing , expr) = pretty expr
527
+ -- The others
528
+ prettyOperation (Just op', expr) =
529
+ line <> pretty (moveTrailingCommentUp op') <> nest (absorbOperation expr)
530
+ in group' RegularG $
531
+ (concatMap prettyOperation . flatten Nothing ) operation
532
+
503
533
prettyWith :: Bool -> Expression -> Doc
504
534
-- absorb the body
505
535
prettyWith True (With with expr0 semicolon (Term expr1)) =
@@ -588,50 +618,48 @@ absorbRHS expr = case expr of
588
618
-- Exception to the case below: Don't force-expand attrsets if they only contain a single inherit statement
589
619
(Term (Set _ _ binders _))
590
620
| case unItems binders of [Item (Inherit {})] -> True ; _ -> False ->
591
- hardspace <> group (absorbExpr False expr)
621
+ nest $ hardspace <> group (absorbExpr False expr)
592
622
-- Absorbable expression. Always start on the same line, and force-expand attrsets
593
- _ | isAbsorbableExpr expr -> hardspace <> group (absorbExpr True expr)
623
+ _ | isAbsorbableExpr expr -> nest $ hardspace <> group (absorbExpr True expr)
594
624
-- Parenthesized expression. Same thing as the special case for parenthesized last argument in function calls.
595
- (Term (Parenthesized open expr' close)) -> hardspace <> absorbParen open expr' close
625
+ (Term (Parenthesized open expr' close)) -> nest $ hardspace <> absorbParen open expr' close
596
626
-- Not all strings are absorbable, but in this case we always want to keep them attached.
597
627
-- Because there's nothing to gain from having them start on a new line.
598
- (Term (SimpleString _)) -> hardspace <> group expr
599
- (Term (IndentedString _)) -> hardspace <> group expr
628
+ (Term (SimpleString _)) -> nest $ hardspace <> group expr
629
+ (Term (IndentedString _)) -> nest $ hardspace <> group expr
600
630
-- Same for path
601
- (Term (Path _)) -> hardspace <> group expr
631
+ (Term (Path _)) -> nest $ hardspace <> group expr
602
632
-- Non-absorbable term
603
633
-- If it is multi-line, force it to start on a new line with indentation
604
- (Term _) -> group' RegularG (line <> pretty expr)
634
+ (Term _) -> nest $ group' RegularG (line <> pretty expr)
605
635
-- Function call
606
636
-- Absorb if all arguments except the last fit into the line, start on new line otherwise
607
- (Application f a) -> prettyApp False line False f a
608
- (With {}) -> group' RegularG $ line <> pretty expr
609
- -- Special case `//` and `++` operations to be more compact in some cases
610
- -- Case 1: two arguments, LHS is absorbable term, RHS fits onto the last line
611
- (Operation (Term t) (LoneAnn op) b)
612
- | isAbsorbable t
613
- && isUpdateOrConcat op
614
- -- Exclude further operations on the RHS
615
- -- Hotfix for https://github.com/NixOS/nixfmt/issues/198
616
- && case b of (Operation {}) -> False ; _ -> True ->
617
- group' RegularG $ line <> group' Priority (prettyTermWide t) <> line <> pretty op <> hardspace <> pretty b
618
- -- Case 2a: LHS fits onto first line, RHS is an absorbable term
637
+ (Application f a) -> nest $ prettyApp False line False f a
638
+ (With {}) -> nest $ group' RegularG $ line <> pretty expr
639
+ -- Special case `//` and `++` and `+` operations to be more compact in some cases
640
+ -- The following code assumes all of these operators are parsed with right-handed associativity
641
+ -- (even though in Nix, addition technically is considered left-associative)
642
+ -- Case 1: LHS is absorbable term, unindent concatenations
643
+ -- https://github.com/NixOS/nixfmt/issues/228
644
+ (Operation (Term t) op@ (Ann {value}) _)
645
+ | isAbsorbableTerm t
646
+ && matchFirstToken (not . hasPreTrivia) t
647
+ && isUpdateConcatPlus value ->
648
+ hardspace <> prettyOp True expr op
649
+ -- Case 2: LHS fits onto first line, RHS is an absorbable term
619
650
(Operation l (LoneAnn op) (Term t))
620
- | isAbsorbable t && isUpdateOrConcat op ->
621
- group' RegularG $ line <> pretty l <> line <> group' Transparent (pretty op <> hardspace <> group' Priority (prettyTermWide t))
622
- -- Case 2b: LHS fits onto first line, RHS is a function application
623
- (Operation l (LoneAnn op) (Application f a))
624
- | isUpdateOrConcat op ->
625
- line <> group l <> line <> prettyApp False (pretty op <> hardspace) False f a
651
+ | isAbsorbable t && isUpdateConcatPlus op ->
652
+ nest $ group' RegularG $ line <> pretty l <> line <> group' Transparent (pretty op <> hardspace <> group' Priority (prettyTermWide t))
626
653
-- Everything else:
627
654
-- If it fits on one line, it fits
628
655
-- If it fits on one line but with a newline after the `=`, it fits (including semicolon)
629
656
-- Otherwise, start on new line, expand fully (including the semicolon)
630
- _ -> line <> group expr
657
+ _ -> nest $ line <> group expr
631
658
where
632
- isUpdateOrConcat TUpdate = True
633
- isUpdateOrConcat TConcat = True
634
- isUpdateOrConcat _ = False
659
+ isUpdateConcatPlus TUpdate = True
660
+ isUpdateConcatPlus TConcat = True
661
+ isUpdateConcatPlus TPlus = True
662
+ isUpdateConcatPlus _ = False
635
663
636
664
instance Pretty Expression where
637
665
pretty (Term t) = pretty t
@@ -712,31 +740,7 @@ instance Pretty Expression where
712
740
| op' == TLess || op' == TGreater || op' == TLessEqual || op' == TGreaterEqual || op' == TEqual || op' == TUnequal =
713
741
pretty a <> softline <> pretty op <> hardspace <> pretty b
714
742
-- all other operators
715
- pretty operation@ (Operation _ op _) =
716
- let -- Walk the operation tree and put a list of things on the same level.
717
- -- We still need to keep the operators around because they might have comments attached to them.
718
- -- An operator is put together with its succeeding expression. Only the first operand has none.
719
- flatten :: Maybe Leaf -> Expression -> [(Maybe Leaf , Expression )]
720
- flatten opL (Operation a opR b) | opR == op = flatten opL a ++ flatten (Just opR) b
721
- flatten opL x = [(opL, x)]
722
-
723
- -- Called on every operand except the first one (a.k.a. RHS)
724
- absorbOperation :: Expression -> Doc
725
- absorbOperation (Term t) | isAbsorbable t = hardspace <> pretty t
726
- -- Force nested operations to start on a new line
727
- absorbOperation x@ (Operation {}) = group' RegularG $ line <> pretty x
728
- -- Force applications to start on a new line if more than the last argument is multiline
729
- absorbOperation (Application f a) = group $ prettyApp False line False f a
730
- absorbOperation x = hardspace <> pretty x
731
-
732
- prettyOperation :: (Maybe Leaf , Expression ) -> Doc
733
- -- First element
734
- prettyOperation (Nothing , expr) = pretty expr
735
- -- The others
736
- prettyOperation (Just op', expr) =
737
- line <> pretty (moveTrailingCommentUp op') <> nest (absorbOperation expr)
738
- in group' RegularG $
739
- (concatMap prettyOperation . flatten Nothing ) operation
743
+ pretty operation@ (Operation _ op _) = prettyOp False operation op
740
744
pretty (MemberCheck expr qmark sel) =
741
745
pretty expr
742
746
<> softline
0 commit comments