@@ -318,10 +318,11 @@ bool ContinuationIndenter::canBreak(const LineState &State) {
318
318
return false ;
319
319
320
320
// Don't create a 'hanging' indent if there are multiple blocks in a single
321
- // statement.
321
+ // statement and we are aligning lambda blocks to their signatures .
322
322
if (Previous.is (tok::l_brace) && State.Stack .size () > 1 &&
323
323
State.Stack [State.Stack .size () - 2 ].NestedBlockInlined &&
324
- State.Stack [State.Stack .size () - 2 ].HasMultipleNestedBlocks ) {
324
+ State.Stack [State.Stack .size () - 2 ].HasMultipleNestedBlocks &&
325
+ Style .LambdaBodyIndentation == FormatStyle::LBI_Signature) {
325
326
return false ;
326
327
}
327
328
@@ -335,6 +336,11 @@ bool ContinuationIndenter::canBreak(const LineState &State) {
335
336
// If binary operators are moved to the next line (including commas for some
336
337
// styles of constructor initializers), that's always ok.
337
338
if (!Current.isOneOf (TT_BinaryOperator, tok::comma) &&
339
+ // Allow breaking opening brace of lambdas (when passed as function
340
+ // arguments) to a new line when BeforeLambdaBody brace wrapping is
341
+ // enabled.
342
+ (!Style .BraceWrapping .BeforeLambdaBody ||
343
+ Current.isNot (TT_LambdaLBrace)) &&
338
344
CurrentState.NoLineBreakInOperand ) {
339
345
return false ;
340
346
}
@@ -662,34 +668,37 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
662
668
const FormatToken &Previous = *State.NextToken ->Previous ;
663
669
auto &CurrentState = State.Stack .back ();
664
670
665
- bool DisallowLineBreaksOnThisLine = Style .isCpp () && [&Current] {
666
- // Deal with lambda arguments in C++. The aim here is to ensure that we
667
- // don't over-indent lambda function bodies when lambdas are passed as
668
- // arguments to function calls. We do this by ensuring that either all
669
- // arguments (including any lambdas) go on the same line as the function
670
- // call, or we break before the first argument.
671
- auto PrevNonComment = Current.getPreviousNonComment ();
672
- if (!PrevNonComment || PrevNonComment->isNot (tok::l_paren))
673
- return false ;
674
- if (Current.isOneOf (tok::comment, tok::l_paren, TT_LambdaLSquare))
675
- return false ;
676
- auto BlockParameterCount = PrevNonComment->BlockParameterCount ;
677
- if (BlockParameterCount == 0 )
678
- return false ;
671
+ bool DisallowLineBreaksOnThisLine =
672
+ Style .LambdaBodyIndentation == FormatStyle::LBI_Signature &&
673
+ Style .isCpp () && [&Current] {
674
+ // Deal with lambda arguments in C++. The aim here is to ensure that we
675
+ // don't over-indent lambda function bodies when lambdas are passed as
676
+ // arguments to function calls. We do this by ensuring that either all
677
+ // arguments (including any lambdas) go on the same line as the function
678
+ // call, or we break before the first argument.
679
+ auto PrevNonComment = Current.getPreviousNonComment ();
680
+ if (!PrevNonComment || PrevNonComment->isNot (tok::l_paren))
681
+ return false ;
682
+ if (Current.isOneOf (tok::comment, tok::l_paren, TT_LambdaLSquare))
683
+ return false ;
684
+ auto BlockParameterCount = PrevNonComment->BlockParameterCount ;
685
+ if (BlockParameterCount == 0 )
686
+ return false ;
679
687
680
- // Multiple lambdas in the same function call.
681
- if (BlockParameterCount > 1 )
682
- return true ;
688
+ // Multiple lambdas in the same function call.
689
+ if (BlockParameterCount > 1 )
690
+ return true ;
683
691
684
- // A lambda followed by another arg.
685
- if (!PrevNonComment->Role )
686
- return false ;
687
- auto Comma = PrevNonComment->Role ->lastComma ();
688
- if (!Comma)
689
- return false ;
690
- auto Next = Comma->getNextNonComment ();
691
- return Next && !Next->isOneOf (TT_LambdaLSquare, tok::l_brace, tok::caret);
692
- }();
692
+ // A lambda followed by another arg.
693
+ if (!PrevNonComment->Role )
694
+ return false ;
695
+ auto Comma = PrevNonComment->Role ->lastComma ();
696
+ if (!Comma)
697
+ return false ;
698
+ auto Next = Comma->getNextNonComment ();
699
+ return Next &&
700
+ !Next->isOneOf (TT_LambdaLSquare, tok::l_brace, tok::caret);
701
+ }();
693
702
694
703
if (DisallowLineBreaksOnThisLine)
695
704
State.NoLineBreak = true ;
@@ -1067,9 +1076,40 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State,
1067
1076
NestedBlockSpecialCase ||
1068
1077
(Current.MatchingParen &&
1069
1078
Current.MatchingParen ->is (TT_RequiresExpressionLBrace));
1070
- if (!NestedBlockSpecialCase)
1071
- for (ParenState &PState : llvm::drop_end (State.Stack ))
1072
- PState.BreakBeforeParameter = true ;
1079
+ if (!NestedBlockSpecialCase) {
1080
+ auto ParentLevelIt = std::next (State.Stack .rbegin ());
1081
+ if (Style .LambdaBodyIndentation == FormatStyle::LBI_OuterScope &&
1082
+ Current.MatchingParen && Current.MatchingParen ->is (TT_LambdaLBrace)) {
1083
+ // If the first character on the new line is a lambda's closing brace, the
1084
+ // stack still contains that lambda's parenthesis. As such, we need to
1085
+ // recurse further down the stack than usual to find the parenthesis level
1086
+ // containing the lambda, which is where we want to set
1087
+ // BreakBeforeParameter.
1088
+ //
1089
+ // We specifically special case "OuterScope"-formatted lambdas here
1090
+ // because, when using that setting, breaking before the parameter
1091
+ // directly following the lambda is particularly unsightly. However, when
1092
+ // "OuterScope" is not set, the logic to find the parent parenthesis level
1093
+ // still appears to be sometimes incorrect. It has not been fixed yet
1094
+ // because it would lead to significant changes in existing behaviour.
1095
+ //
1096
+ // TODO: fix the non-"OuterScope" case too.
1097
+ auto FindCurrentLevel = [&](const auto &It) {
1098
+ return std::find_if (It, State.Stack .rend (), [](const auto &PState) {
1099
+ return PState.Tok != nullptr ; // Ignore fake parens.
1100
+ });
1101
+ };
1102
+ auto MaybeIncrement = [&](const auto &It) {
1103
+ return It != State.Stack .rend () ? std::next (It) : It;
1104
+ };
1105
+ auto LambdaLevelIt = FindCurrentLevel (State.Stack .rbegin ());
1106
+ auto LevelContainingLambdaIt =
1107
+ FindCurrentLevel (MaybeIncrement (LambdaLevelIt));
1108
+ ParentLevelIt = MaybeIncrement (LevelContainingLambdaIt);
1109
+ }
1110
+ for (auto I = ParentLevelIt, E = State.Stack .rend (); I != E; ++I)
1111
+ I->BreakBeforeParameter = true ;
1112
+ }
1073
1113
1074
1114
if (PreviousNonComment &&
1075
1115
!PreviousNonComment->isOneOf (tok::comma, tok::colon, tok::semi) &&
@@ -1079,7 +1119,11 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State,
1079
1119
!PreviousNonComment->isOneOf (
1080
1120
TT_BinaryOperator, TT_FunctionAnnotationRParen, TT_JavaAnnotation,
1081
1121
TT_LeadingJavaAnnotation) &&
1082
- Current.isNot (TT_BinaryOperator) && !PreviousNonComment->opensScope ()) {
1122
+ Current.isNot (TT_BinaryOperator) && !PreviousNonComment->opensScope () &&
1123
+ // We don't want to enforce line breaks for subsequent arguments just
1124
+ // because we have been forced to break before a lambda body.
1125
+ (!Style .BraceWrapping .BeforeLambdaBody ||
1126
+ Current.isNot (TT_LambdaLBrace))) {
1083
1127
CurrentState.BreakBeforeParameter = true ;
1084
1128
}
1085
1129
@@ -1098,7 +1142,7 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State,
1098
1142
1099
1143
if (CurrentState.AvoidBinPacking ) {
1100
1144
// If we are breaking after '(', '{', '<', or this is the break after a ':'
1101
- // to start a member initializater list in a constructor, this should not
1145
+ // to start a member initializer list in a constructor, this should not
1102
1146
// be considered bin packing unless the relevant AllowAll option is false or
1103
1147
// this is a dict/object literal.
1104
1148
bool PreviousIsBreakingCtorInitializerColon =
0 commit comments