Index: lib/Format/ContinuationIndenter.h =================================================================== --- lib/Format/ContinuationIndenter.h +++ lib/Format/ContinuationIndenter.h @@ -200,16 +200,20 @@ }; struct ParenState { - ParenState(unsigned Indent, unsigned LastSpace, bool AvoidBinPacking, - bool NoLineBreak) - : Indent(Indent), LastSpace(LastSpace), NestedBlockIndent(Indent), - BreakBeforeClosingBrace(false), AvoidBinPacking(AvoidBinPacking), - BreakBeforeParameter(false), NoLineBreak(NoLineBreak), - NoLineBreakInOperand(false), LastOperatorWrapped(true), - ContainsLineBreak(false), ContainsUnwrappedBuilder(false), - AlignColons(true), ObjCSelectorNameFound(false), - HasMultipleNestedBlocks(false), NestedBlockInlined(false), - IsInsideObjCArrayLiteral(false) {} + ParenState(const FormatToken *Tok, unsigned Indent, unsigned LastSpace, + bool AvoidBinPacking, bool NoLineBreak) + : Tok(Tok), Indent(Indent), LastSpace(LastSpace), + NestedBlockIndent(Indent), BreakBeforeClosingBrace(false), + AvoidBinPacking(AvoidBinPacking), BreakBeforeParameter(false), + NoLineBreak(NoLineBreak), NoLineBreakInOperand(false), + LastOperatorWrapped(true), ContainsLineBreak(false), + ContainsUnwrappedBuilder(false), AlignColons(true), + ObjCSelectorNameFound(false), HasMultipleNestedBlocks(false), + NestedBlockInlined(false), IsInsideObjCArrayLiteral(false) {} + + /// \brief The token opening this parenthesis level, or nullptr if this level + /// is opened by fake parenthesis. + const FormatToken *Tok; /// \brief The position to which a specific parenthesis level needs to be /// indented. Index: lib/Format/ContinuationIndenter.cpp =================================================================== --- lib/Format/ContinuationIndenter.cpp +++ lib/Format/ContinuationIndenter.cpp @@ -35,11 +35,24 @@ // Returns the length of everything up to the first possible line break after // the ), ], } or > matching \c Tok. -static unsigned getLengthToMatchingParen(const FormatToken &Tok) { +static unsigned getLengthToMatchingParen(const FormatToken &Tok, + const std::vector &Stack) { if (!Tok.MatchingParen) return 0; FormatToken *End = Tok.MatchingParen; - while (End->Next && !End->Next->CanBreakBefore) { + int MatchingStackIndex = Stack.size() - 1; + while (MatchingStackIndex >= 0 && Stack[MatchingStackIndex].Tok != &Tok) + --MatchingStackIndex; + while (End->Next) { + if (End->Next->CanBreakBefore || !End->Next->closesScope()) + break; + while (MatchingStackIndex >= 0 && + Stack[MatchingStackIndex].Tok != End->Next->MatchingParen) + --MatchingStackIndex; + if (MatchingStackIndex >= 0 && + Stack[MatchingStackIndex].Tok == End->Next->MatchingParen && + Stack[MatchingStackIndex].BreakBeforeClosingBrace) + break; End = End->Next; } return End->TotalLength - Tok.TotalLength + 1; @@ -192,7 +205,7 @@ State.Column = 0; State.Line = Line; State.NextToken = Line->First; - State.Stack.push_back(ParenState(FirstIndent, FirstIndent, + State.Stack.push_back(ParenState(/*Tok=*/nullptr, FirstIndent, FirstIndent, /*AvoidBinPacking=*/false, /*NoLineBreak=*/false)); State.LineContainsContinuedForLoopSection = false; @@ -299,7 +312,7 @@ Previous.ParameterCount > 1) || opensProtoMessageField(Previous, Style)) && Style.ColumnLimit > 0 && - getLengthToMatchingParen(Previous) + State.Column - 1 > + getLengthToMatchingParen(Previous, State.Stack) + State.Column - 1 > getColumnLimit(State)) return true; @@ -1122,6 +1135,7 @@ E = Current.FakeLParens.rend(); I != E; ++I) { ParenState NewParenState = State.Stack.back(); + NewParenState.Tok = nullptr; NewParenState.ContainsLineBreak = false; NewParenState.LastOperatorWrapped = true; NewParenState.NoLineBreak = @@ -1265,7 +1279,7 @@ if (Style.ColumnLimit) { // If this '[' opens an ObjC call, determine whether all parameters fit // into one line and put one per line if they don't. - if (getLengthToMatchingParen(Current) + State.Column > + if (getLengthToMatchingParen(Current, State.Stack) + State.Column > getColumnLimit(State)) BreakBeforeParameter = true; } else { @@ -1296,7 +1310,7 @@ (Current.is(TT_TemplateOpener) && State.Stack.back().ContainsUnwrappedBuilder)); State.Stack.push_back( - ParenState(NewIndent, LastSpace, AvoidBinPacking, NoLineBreak)); + ParenState(&Current, NewIndent, LastSpace, AvoidBinPacking, NoLineBreak)); State.Stack.back().NestedBlockIndent = NestedBlockIndent; State.Stack.back().BreakBeforeParameter = BreakBeforeParameter; State.Stack.back().HasMultipleNestedBlocks = Current.BlockParameterCount > 1; @@ -1334,7 +1348,8 @@ NestedBlockIndent + (State.NextToken->is(TT_ObjCBlockLBrace) ? Style.ObjCBlockIndentWidth : Style.IndentWidth); - State.Stack.push_back(ParenState(NewIndent, State.Stack.back().LastSpace, + State.Stack.push_back(ParenState(State.NextToken, NewIndent, + State.Stack.back().LastSpace, /*AvoidBinPacking=*/true, /*NoLineBreak=*/false)); State.Stack.back().NestedBlockIndent = NestedBlockIndent; Index: lib/Format/UnwrappedLineFormatter.cpp =================================================================== --- lib/Format/UnwrappedLineFormatter.cpp +++ lib/Format/UnwrappedLineFormatter.cpp @@ -659,8 +659,8 @@ static void printLineState(const LineState &State) { llvm::dbgs() << "State: "; for (const ParenState &P : State.Stack) { - llvm::dbgs() << P.Indent << "|" << P.LastSpace << "|" << P.NestedBlockIndent - << " "; + llvm::dbgs() << (P.Tok ? P.Tok->TokenText : "F") << "|" << P.Indent << "|" + << P.LastSpace << "|" << P.NestedBlockIndent << " "; } llvm::dbgs() << State.NextToken->TokenText << "\n"; } Index: unittests/Format/FormatTestProto.cpp =================================================================== --- unittests/Format/FormatTestProto.cpp +++ unittests/Format/FormatTestProto.cpp @@ -486,6 +486,7 @@ " ccccccccccccccccccccccc: <\n" " operator: 1\n" " operator: 2\n" + " operator: 3\n" " operator { key: value }\n" " >\n" " >\n" Index: unittests/Format/FormatTestRawStrings.cpp =================================================================== --- unittests/Format/FormatTestRawStrings.cpp +++ unittests/Format/FormatTestRawStrings.cpp @@ -822,6 +822,41 @@ )test", Style)); } +TEST_F(FormatTestRawStrings, KeepsRBraceFolloedByMoreLBracesOnSameLine) { + FormatStyle Style = getRawStringPbStyleWithColumns(80); + + expect_eq( + R"test( +int f() { + if (1) { + TTTTTTTTTTTTTTTTTTTTT s = PARSE_TEXT_PROTO(R"pb( + ttttttttt { + ppppppppppppp { + [cccccccccc.pppppppppppppp.TTTTTTTTTTTTTTTTTTTT] { field_1: "123_1" } + [cccccccccc.pppppppppppppp.TTTTTTTTTTTTTTTTTTTT] { field_2: "123_2" } + } + } + )pb"); + } +} +)test", + format( + R"test( +int f() { + if (1) { + TTTTTTTTTTTTTTTTTTTTT s = PARSE_TEXT_PROTO(R"pb( + ttttttttt { + ppppppppppppp { + [cccccccccc.pppppppppppppp.TTTTTTTTTTTTTTTTTTTT] { field_1: "123_1" } + [cccccccccc.pppppppppppppp.TTTTTTTTTTTTTTTTTTTT] { field_2: "123_2" }}} + )pb"); + } +} +)test", + Style)); +} + + } // end namespace } // end namespace format } // end namespace clang Index: unittests/Format/FormatTestTextProto.cpp =================================================================== --- unittests/Format/FormatTestTextProto.cpp +++ unittests/Format/FormatTestTextProto.cpp @@ -469,6 +469,7 @@ " ccccccccccccccccccccccc: <\n" " operator: 1\n" " operator: 2\n" + " operator: 3\n" " operator { key: value }\n" " >\n" " >\n"