Index: cfe/trunk/lib/Format/ContinuationIndenter.cpp =================================================================== --- cfe/trunk/lib/Format/ContinuationIndenter.cpp +++ cfe/trunk/lib/Format/ContinuationIndenter.cpp @@ -56,6 +56,8 @@ if (Current.is(TT_CtorInitializerComma) && Style.BreakConstructorInitializers == FormatStyle::BCIS_BeforeComma) return true; + if (Style.Language == FormatStyle::LK_Proto && Current.is(TT_SelectorName)) + return true; return Previous.is(tok::comma) && !Current.isTrailingComment() && ((Previous.isNot(TT_CtorInitializerComma) || Style.BreakConstructorInitializers != @@ -499,6 +501,13 @@ } } +static bool lessOpensProtoMessageField(const FormatToken &LessTok, + const LineState &State) { + assert(LessTok.is(tok::less)); + return LessTok.NestingLevel > 0 || + (LessTok.Previous && LessTok.Previous->is(tok::equal)); +} + unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State, bool DryRun) { FormatToken &Current = *State.NextToken; @@ -641,6 +650,9 @@ // before the corresponding } or ]. if (PreviousNonComment && (PreviousNonComment->isOneOf(tok::l_brace, TT_ArrayInitializerLSquare) || + (Style.Language == FormatStyle::LK_Proto && + PreviousNonComment->is(tok::less) && + lessOpensProtoMessageField(*PreviousNonComment, State)) || (PreviousNonComment->is(TT_TemplateString) && PreviousNonComment->opensScope()))) State.Stack.back().BreakBeforeClosingBrace = true; @@ -682,7 +694,9 @@ if (NextNonComment->is(tok::l_brace) && NextNonComment->BlockKind == BK_Block) return Current.NestingLevel == 0 ? State.FirstIndent : State.Stack.back().Indent; - if (Current.isOneOf(tok::r_brace, tok::r_square) && State.Stack.size() > 1) { + if ((Current.isOneOf(tok::r_brace, tok::r_square) || + (Current.is(tok::greater) && Style.Language == FormatStyle::LK_Proto)) && + State.Stack.size() > 1) { if (Current.closesBlockOrBlockTypeList(Style)) return State.Stack[State.Stack.size() - 2].NestedBlockIndent; if (Current.MatchingParen && @@ -1035,7 +1049,9 @@ bool BreakBeforeParameter = false; unsigned NestedBlockIndent = std::max(State.Stack.back().StartOfFunctionCall, State.Stack.back().NestedBlockIndent); - if (Current.isOneOf(tok::l_brace, TT_ArrayInitializerLSquare)) { + if (Current.isOneOf(tok::l_brace, TT_ArrayInitializerLSquare) || + (Style.Language == FormatStyle::LK_Proto && Current.is(tok::less) && + lessOpensProtoMessageField(Current, State))) { if (Current.opensBlockOrBlockTypeList(Style)) { NewIndent = Style.IndentWidth + std::min(State.Column, State.Stack.back().NestedBlockIndent); Index: cfe/trunk/lib/Format/FormatToken.h =================================================================== --- cfe/trunk/lib/Format/FormatToken.h +++ cfe/trunk/lib/Format/FormatToken.h @@ -465,7 +465,8 @@ return is(TT_ArrayInitializerLSquare) || (is(tok::l_brace) && (BlockKind == BK_Block || is(TT_DictLiteral) || - (!Style.Cpp11BracedListStyle && NestingLevel == 0))); + (!Style.Cpp11BracedListStyle && NestingLevel == 0))) || + (is(tok::less) && Style.Language == FormatStyle::LK_Proto); } /// \brief Same as opensBlockOrBlockTypeList, but for the closing token. Index: cfe/trunk/lib/Format/TokenAnnotator.cpp =================================================================== --- cfe/trunk/lib/Format/TokenAnnotator.cpp +++ cfe/trunk/lib/Format/TokenAnnotator.cpp @@ -89,7 +89,8 @@ continue; } if (CurrentToken->isOneOf(tok::r_paren, tok::r_square, tok::r_brace) || - (CurrentToken->isOneOf(tok::colon, tok::question) && InExprContext)) + (CurrentToken->isOneOf(tok::colon, tok::question) && InExprContext && + Style.Language != FormatStyle::LK_Proto)) return false; // If a && or || is found and interpreted as a binary operator, this set // of angles is likely part of something like "a < b && c > d". If the @@ -103,6 +104,14 @@ !Line.startsWith(tok::kw_template)) return false; updateParameterCount(Left, CurrentToken); + if (Style.Language == FormatStyle::LK_Proto) { + if (FormatToken *Previous = CurrentToken->getPreviousNonComment()) { + if (CurrentToken->is(tok::colon) || + (CurrentToken->isOneOf(tok::l_brace, tok::less) && + Previous->isNot(tok::colon))) + Previous->Type = TT_SelectorName; + } + } if (!consumeToken()) return false; } @@ -440,7 +449,7 @@ if (CurrentToken->isOneOf(tok::r_paren, tok::r_square)) return false; updateParameterCount(Left, CurrentToken); - if (CurrentToken->isOneOf(tok::colon, tok::l_brace)) { + if (CurrentToken->isOneOf(tok::colon, tok::l_brace, tok::less)) { FormatToken *Previous = CurrentToken->getPreviousNonComment(); if (((CurrentToken->is(tok::colon) && (!Contexts.back().ColonIsDictLiteral || !Style.isCpp())) || @@ -2549,10 +2558,16 @@ // deliberate choice and might have aligned the contents of the string // literal accordingly. Thus, we try keep existing line breaks. return Right.NewlinesBefore > 0; - if (Right.Previous->is(tok::l_brace) && Right.NestingLevel == 1 && - Style.Language == FormatStyle::LK_Proto) - // Don't put enums onto single lines in protocol buffers. + if ((Right.Previous->is(tok::l_brace) || + (Right.Previous->is(tok::less) && + Right.Previous->Previous && + Right.Previous->Previous->is(tok::equal)) + ) && + Right.NestingLevel == 1 && Style.Language == FormatStyle::LK_Proto) { + // Don't put enums or option definitions onto single lines in protocol + // buffers. return true; + } if (Right.is(TT_InlineASMBrace)) return Right.HasUnescapedNewline; if (isAllmanBrace(Left) || isAllmanBrace(Right)) Index: cfe/trunk/lib/Format/UnwrappedLineParser.h =================================================================== --- cfe/trunk/lib/Format/UnwrappedLineParser.h +++ cfe/trunk/lib/Format/UnwrappedLineParser.h @@ -93,7 +93,8 @@ void readTokenWithJavaScriptASI(); void parseStructuralElement(); bool tryToParseBracedList(); - bool parseBracedList(bool ContinueOnSemicolons = false); + bool parseBracedList(bool ContinueOnSemicolons = false, + tok::TokenKind ClosingBraceKind = tok::r_brace); void parseParens(); void parseSquare(); void parseIfThenElse(); Index: cfe/trunk/lib/Format/UnwrappedLineParser.cpp =================================================================== --- cfe/trunk/lib/Format/UnwrappedLineParser.cpp +++ cfe/trunk/lib/Format/UnwrappedLineParser.cpp @@ -1176,9 +1176,11 @@ } nextToken(); - if (FormatTok->Tok.is(tok::l_brace)) { + if (FormatTok->Tok.is(tok::l_brace)) parseBracedList(); - } + else if (Style.Language == FormatStyle::LK_Proto && + FormatTok->Tok.is(tok::less)) + parseBracedList(/*ClosingBraceKind=*/tok::greater); break; case tok::l_square: parseSquare(); @@ -1346,7 +1348,8 @@ return true; } -bool UnwrappedLineParser::parseBracedList(bool ContinueOnSemicolons) { +bool UnwrappedLineParser::parseBracedList(bool ContinueOnSemicolons, + tok::TokenKind ClosingBraceKind) { bool HasError = false; nextToken(); @@ -1375,6 +1378,10 @@ parseChildBlock(); } } + if (FormatTok->Tok.getKind() == ClosingBraceKind) { + nextToken(); + return !HasError; + } switch (FormatTok->Tok.getKind()) { case tok::caret: nextToken(); @@ -1401,9 +1408,6 @@ FormatTok->BlockKind = BK_BracedInit; parseBracedList(); break; - case tok::r_brace: - nextToken(); - return !HasError; case tok::semi: // JavaScript (or more precisely TypeScript) can have semicolons in braced // lists (in so-called TypeMemberLists). Thus, the semicolon cannot be Index: cfe/trunk/unittests/Format/FormatTestProto.cpp =================================================================== --- cfe/trunk/unittests/Format/FormatTestProto.cpp +++ cfe/trunk/unittests/Format/FormatTestProto.cpp @@ -207,6 +207,142 @@ " field_c: \"OK\",\n" " msg_field: \n" "};"); + + verifyFormat("option (MyProto.options) = {\n" + " msg_field: <>\n" + " field_c: \"OK\",\n" + " msg_field: \n" + " field_e: OK\n" + " msg_field: \n" + "};"); + + verifyFormat("option (MyProto.options) = <\n" + " field_a: OK\n" + " field_b: \"OK\"\n" + " field_c: 1\n" + " field_d: 12.5\n" + " field_e: OK\n" + ">;"); + + verifyFormat("option (MyProto.options) = <\n" + " field_a: OK,\n" + " field_b: \"OK\",\n" + " field_c: 1,\n" + " field_d: 12.5,\n" + " field_e: OK,\n" + ">;"); + + verifyFormat("option (MyProto.options) = <\n" + " field_a: \"OK\"\n" + " msg_field: {field_b: OK}\n" + " field_g: OK\n" + " field_g: OK\n" + " field_g: OK\n" + ">;"); + + verifyFormat("option (MyProto.options) = <\n" + " field_a: \"OK\"\n" + " msg_field<\n" + " field_b: OK\n" + " field_c: OK\n" + " field_d: OK\n" + " field_e: OK\n" + " field_f: OK\n" + " >\n" + " field_g: OK\n" + ">;"); + + verifyFormat("option (MyProto.options) = <\n" + " field_a: \"OK\"\n" + " msg_field<\n" + " field_b: OK,\n" + " field_c: OK,\n" + " field_d: OK,\n" + " field_e: OK,\n" + " field_f: OK\n" + " >\n" + " field_g: OK\n" + ">;"); + + verifyFormat("option (MyProto.options) = <\n" + " field_a: \"OK\"\n" + " msg_field: <\n" + " field_b: OK\n" + " field_c: OK\n" + " field_d: OK\n" + " field_e: OK\n" + " field_f: OK\n" + " >\n" + " field_g: OK\n" + ">;"); + + verifyFormat("option (MyProto.options) = <\n" + " field_a: \"OK\"\n" + " msg_field: {\n" + " field_b: OK\n" + " field_c: OK\n" + " field_d: OK\n" + " field_e: OK\n" + " field_f: OK\n" + " }\n" + " field_g: OK\n" + ">;"); + + verifyFormat("option (MyProto.options) = <\n" + " field_a: \"OK\"\n" + " msg_field{\n" + " field_b: OK\n" + " field_c: OK\n" + " field_d: OK\n" + " field_e: OK\n" + " field_f: OK\n" + " }\n" + " field_g: OK\n" + ">;"); + + verifyFormat("option (MyProto.options) = {\n" + " field_a: \"OK\"\n" + " msg_field<\n" + " field_b: OK\n" + " field_c: OK\n" + " field_d: OK\n" + " field_e: OK\n" + " field_f: OK\n" + " >\n" + " field_g: OK\n" + "};"); + + verifyFormat("option (MyProto.options) = {\n" + " field_a: \"OK\"\n" + " msg_field: <\n" + " field_b: OK\n" + " field_c: OK\n" + " field_d: OK\n" + " field_e: OK\n" + " field_f: OK\n" + " >\n" + " field_g: OK\n" + "};"); + + verifyFormat("option (MyProto.options) = <\n" + " field_a: \"OK\"\n" + " msg_field{\n" + " field_b: OK\n" + " field_c: OK\n" + " field_d: OK\n" + " msg_field<\n" + " field_A: 1\n" + " field_B: 2\n" + " field_C: 3\n" + " field_D: 4\n" + " field_E: 5\n" + " >\n" + " msg_field\n" + " field_e: OK\n" + " field_f: OK\n" + " }\n" + " field_g: OK\n" + ">;"); } TEST_F(FormatTestProto, FormatsService) {