@@ -95,6 +95,7 @@ const tooling::Replacements &WhitespaceManager::generateReplacements() {
95
95
alignConsecutiveMacros ();
96
96
alignConsecutiveDeclarations ();
97
97
alignConsecutiveAssignments ();
98
+ alignChainedConditionals ();
98
99
alignTrailingComments ();
99
100
alignEscapedNewlines ();
100
101
generateChanges ();
@@ -227,6 +228,32 @@ void WhitespaceManager::calculateLineBreakInformation() {
227
228
LastBlockComment = nullptr ;
228
229
}
229
230
}
231
+
232
+ // Compute conditional nesting level
233
+ // Level is increased for each conditional, unless this conditional continues
234
+ // a chain of conditional, i.e. starts immediately after the colon of another
235
+ // conditional.
236
+ SmallVector<bool , 16 > ScopeStack;
237
+ int ConditionalsLevel = 0 ;
238
+ for (auto &Change : Changes) {
239
+ for (unsigned i = 0 , e = Change.Tok ->FakeLParens .size (); i != e; ++i) {
240
+ bool isNestedConditional =
241
+ Change.Tok ->FakeLParens [e - 1 - i] == prec::Conditional &&
242
+ !(i == 0 && Change.Tok ->Previous &&
243
+ Change.Tok ->Previous ->is (TT_ConditionalExpr) &&
244
+ Change.Tok ->Previous ->is (tok::colon));
245
+ if (isNestedConditional)
246
+ ++ConditionalsLevel;
247
+ ScopeStack.push_back (isNestedConditional);
248
+ }
249
+
250
+ Change.ConditionalsLevel = ConditionalsLevel;
251
+
252
+ for (unsigned i = Change.Tok ->FakeRParens ; i > 0 && ScopeStack.size (); --i) {
253
+ if (ScopeStack.pop_back_val ())
254
+ --ConditionalsLevel;
255
+ }
256
+ }
230
257
}
231
258
232
259
// Align a single sequence of tokens, see AlignTokens below.
@@ -248,6 +275,7 @@ AlignTokenSequence(unsigned Start, unsigned End, unsigned Column, F &&Matches,
248
275
// double z);
249
276
// In the above example, we need to take special care to ensure that
250
277
// 'double z' is indented along with it's owning function 'b'.
278
+ // Special handling is required for 'nested' ternary operators.
251
279
SmallVector<unsigned , 16 > ScopeStack;
252
280
253
281
for (unsigned i = Start; i != End; ++i) {
@@ -288,7 +316,10 @@ AlignTokenSequence(unsigned Start, unsigned End, unsigned Column, F &&Matches,
288
316
unsigned ScopeStart = ScopeStack.back ();
289
317
if (Changes[ScopeStart - 1 ].Tok ->is (TT_FunctionDeclarationName) ||
290
318
(ScopeStart > Start + 1 &&
291
- Changes[ScopeStart - 2 ].Tok ->is (TT_FunctionDeclarationName)))
319
+ Changes[ScopeStart - 2 ].Tok ->is (TT_FunctionDeclarationName)) ||
320
+ Changes[i].Tok ->is (TT_ConditionalExpr) ||
321
+ (Changes[i].Tok ->Previous &&
322
+ Changes[i].Tok ->Previous ->is (TT_ConditionalExpr)))
292
323
Changes[i].Spaces += Shift;
293
324
}
294
325
@@ -341,7 +372,7 @@ static unsigned AlignTokens(const FormatStyle &Style, F &&Matches,
341
372
// abort when we hit any token in a higher scope than the starting one.
342
373
auto IndentAndNestingLevel = StartAt < Changes.size ()
343
374
? Changes[StartAt].indentAndNestingLevel ()
344
- : std::pair <unsigned , unsigned >( 0 , 0 );
375
+ : std::tuple <unsigned , unsigned , unsigned >( );
345
376
346
377
// Keep track of the number of commas before the matching tokens, we will only
347
378
// align a sequence of matching tokens if they are preceded by the same number
@@ -409,8 +440,8 @@ static unsigned AlignTokens(const FormatStyle &Style, F &&Matches,
409
440
StartOfSequence = i;
410
441
411
442
unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn ;
412
- int LineLengthAfter = - Changes[i].Spaces ;
413
- for (unsigned j = i; j != e && Changes[j].NewlinesBefore == 0 ; ++j)
443
+ int LineLengthAfter = Changes[i].TokenLength ;
444
+ for (unsigned j = i + 1 ; j != e && Changes[j].NewlinesBefore == 0 ; ++j)
414
445
LineLengthAfter += Changes[j].Spaces + Changes[j].TokenLength ;
415
446
unsigned ChangeMaxColumn = Style.ColumnLimit - LineLengthAfter;
416
447
@@ -608,6 +639,52 @@ void WhitespaceManager::alignConsecutiveDeclarations() {
608
639
Changes, /* StartAt=*/ 0 );
609
640
}
610
641
642
+ void WhitespaceManager::alignChainedConditionals ()
643
+ {
644
+ if (Style.BreakBeforeTernaryOperators ) {
645
+ AlignTokens (Style,
646
+ [](Change const &C) {
647
+ // Align question operators and last colon
648
+ return C.Tok ->is (TT_ConditionalExpr) &&
649
+ ((C.Tok ->is (tok::question) && !C.NewlinesBefore ) ||
650
+ (C.Tok ->is (tok::colon) && C.Tok ->Next &&
651
+ (C.Tok ->Next ->FakeLParens .size () == 0 ||
652
+ C.Tok ->Next ->FakeLParens .back () !=
653
+ prec::Conditional)));
654
+ },
655
+ Changes, /* StartAt=*/ 0 );
656
+ } else {
657
+ static auto AlignWrappedOperand = [](Change const &C) {
658
+ auto Previous = C.Tok ->getPreviousNonComment ();// Previous;
659
+ return C.NewlinesBefore && Previous && Previous->is (TT_ConditionalExpr) &&
660
+ (Previous->is (tok::question) ||
661
+ (Previous->is (tok::colon) &&
662
+ (C.Tok ->FakeLParens .size () == 0 ||
663
+ C.Tok ->FakeLParens .back () != prec::Conditional)));
664
+ };
665
+ // Ensure we keep alignment of wrapped operands with non-wrapped operands
666
+ // Since we actually align the operators, the wrapped operands need the
667
+ // extra offset to be properly aligned.
668
+ for (Change & C: Changes) {
669
+ if (AlignWrappedOperand (C))
670
+ C.StartOfTokenColumn -= 2 ;
671
+ }
672
+ AlignTokens (Style,
673
+ [this ](Change const &C) {
674
+ // Align question operators if next operand is not wrapped, as
675
+ // well as wrapped operands after question operator or last
676
+ // colon in conditional sequence
677
+ return (C.Tok ->is (TT_ConditionalExpr) &&
678
+ C.Tok ->is (tok::question) &&
679
+ &C != &Changes.back () &&
680
+ (&C + 1 )->NewlinesBefore == 0 &&
681
+ !(&C + 1 )->IsTrailingComment ) ||
682
+ AlignWrappedOperand (C);
683
+ },
684
+ Changes, /* StartAt=*/ 0 );
685
+ }
686
+ }
687
+
611
688
void WhitespaceManager::alignTrailingComments () {
612
689
unsigned MinColumn = 0 ;
613
690
unsigned MaxColumn = UINT_MAX;
0 commit comments