Index: lib/Format/ContinuationIndenter.cpp =================================================================== --- lib/Format/ContinuationIndenter.cpp +++ 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 != @@ -641,6 +643,7 @@ // before the corresponding } or ]. if (PreviousNonComment && (PreviousNonComment->isOneOf(tok::l_brace, TT_ArrayInitializerLSquare) || + (PreviousNonComment->is(tok::less) && Style.Language == FormatStyle::LK_Proto) || (PreviousNonComment->is(TT_TemplateString) && PreviousNonComment->opensScope()))) State.Stack.back().BreakBeforeClosingBrace = true; @@ -682,7 +685,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 +1040,8 @@ 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) || + (Current.is(tok::less) && Style.Language == FormatStyle::LK_Proto)) { if (Current.opensBlockOrBlockTypeList(Style)) { NewIndent = Style.IndentWidth + std::min(State.Column, State.Stack.back().NestedBlockIndent); Index: lib/Format/FormatToken.h =================================================================== --- lib/Format/FormatToken.h +++ 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: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ 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,9 +2558,10 @@ // 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->isOneOf(tok::l_brace, tok::less) && + 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; Index: lib/Format/UnwrappedLineParser.h =================================================================== --- lib/Format/UnwrappedLineParser.h +++ 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: lib/Format/UnwrappedLineParser.cpp =================================================================== --- lib/Format/UnwrappedLineParser.cpp +++ 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: unittests/Format/FormatTestProto.cpp =================================================================== --- unittests/Format/FormatTestProto.cpp +++ unittests/Format/FormatTestProto.cpp @@ -184,6 +184,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) {