Index: lib/Format/TokenAnnotator.h =================================================================== --- lib/Format/TokenAnnotator.h +++ lib/Format/TokenAnnotator.h @@ -59,7 +59,7 @@ I->Tok->Previous = Current; Current = Current->Next; Current->Children.clear(); - for (const auto& Child : Node.Children) { + for (const auto &Child : Node.Children) { Children.push_back(new AnnotatedLine(Child)); Current->Children.push_back(Children.back()); } @@ -80,6 +80,11 @@ } } + /// \c True if this line starts with the given tokens in order. + template bool StartsWith(Ts... rest) const { + return StartsWith(First, rest...); + } + FormatToken *First; FormatToken *Last; @@ -107,6 +112,16 @@ // Disallow copying. AnnotatedLine(const AnnotatedLine &) = delete; void operator=(const AnnotatedLine &) = delete; + + template + bool StartsWith(FormatToken *token, A K1) const { + return token != NULL && token->is(K1); + } + + template + bool StartsWith(FormatToken *token, A K1, Ts... rest) const { + return StartsWith(token, K1) && StartsWith(token->Next, rest...); + } }; /// \brief Determines extra information about the tokens comprising an Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -87,7 +87,7 @@ if (CurrentToken->Previous->isOneOf(tok::pipepipe, tok::ampamp) && CurrentToken->Previous->is(TT_BinaryOperator) && Contexts[Contexts.size() - 2].IsExpression && - Line.First->isNot(tok::kw_template)) + Line.StartsWith(tok::kw_template)) return false; updateParameterCount(Left, CurrentToken); if (!consumeToken()) @@ -457,7 +457,7 @@ if (Contexts.back().ColonIsDictLiteral) { Tok->Type = TT_DictLiteral; } else if (Contexts.back().ColonIsObjCMethodExpr || - Line.First->is(TT_ObjCMethodSpecifier)) { + Line.StartsWith(TT_ObjCMethodSpecifier)) { Tok->Type = TT_ObjCMethodExpr; Tok->Previous->Type = TT_SelectorName; if (Tok->Previous->ColumnWidth > @@ -503,7 +503,7 @@ if (!parseParens()) return false; if (Line.MustBeDeclaration && Contexts.size() == 1 && - !Contexts.back().IsExpression && Line.First->isNot(TT_ObjCProperty) && + !Contexts.back().IsExpression && !Line.StartsWith(TT_ObjCProperty) && (!Tok->Previous || !Tok->Previous->isOneOf(tok::kw_decltype, TT_LeadingJavaAnnotation))) Line.MightBeFunctionDecl = true; @@ -581,7 +581,7 @@ if (Contexts.back().InCtorInitializer) Tok->Type = TT_CtorInitializerComma; else if (Contexts.back().FirstStartOfName && - (Contexts.size() == 1 || Line.First->is(tok::kw_for))) { + (Contexts.size() == 1 || Line.StartsWith(tok::kw_for))) { Contexts.back().FirstStartOfName->PartOfMultiVariableDeclStmt = true; Line.IsMultiVariableDeclStmt = true; } @@ -724,7 +724,7 @@ if (ImportStatement) return LT_ImportStatement; - if (Line.First->is(TT_ObjCMethodSpecifier)) { + if (Line.StartsWith(TT_ObjCMethodSpecifier)) { if (Contexts.back().FirstObjCSelectorName) Contexts.back().FirstObjCSelectorName->LongestObjCSelectorName = Contexts.back().LongestObjCSelectorName; @@ -820,7 +820,7 @@ !Line.First->isOneOf(tok::kw_template, tok::kw_using) && (!Current.Previous || Current.Previous->isNot(tok::kw_operator))) { Contexts.back().IsExpression = true; - if (!Line.First->is(TT_UnaryOperator)) { + if (!Line.StartsWith(TT_UnaryOperator)) { for (FormatToken *Previous = Current.Previous; Previous && !Previous->isOneOf(tok::comma, tok::semi); Previous = Previous->Previous) { @@ -1441,11 +1441,11 @@ ExpressionParser ExprParser(Style, Keywords, Line); ExprParser.parse(); - if (Line.First->is(TT_ObjCMethodSpecifier)) + if (Line.StartsWith(TT_ObjCMethodSpecifier)) Line.Type = LT_ObjCMethodDecl; - else if (Line.First->is(TT_ObjCDecl)) + else if (Line.StartsWith(TT_ObjCDecl)) Line.Type = LT_ObjCDecl; - else if (Line.First->is(TT_ObjCProperty)) + else if (Line.StartsWith(TT_ObjCProperty)) Line.Type = LT_ObjCProperty; Line.First->SpacesRequiredBefore = 1; @@ -1638,7 +1638,7 @@ if (Right.isOneOf(TT_StartOfName, TT_FunctionDeclarationName) || Right.is(tok::kw_operator)) { - if (Line.First->is(tok::kw_for) && Right.PartOfMultiVariableDeclStmt) + if (Line.StartsWith(tok::kw_for) && Right.PartOfMultiVariableDeclStmt) return 3; if (Left.is(TT_StartOfName)) return 110; @@ -1674,8 +1674,7 @@ (!Right.Next || Right.Next->isNot(tok::l_paren))) { // Moving trailing annotations to the next line is fine for ObjC method // declarations. - if (Line.First->is(TT_ObjCMethodSpecifier)) - + if (Line.StartsWith(TT_ObjCMethodSpecifier)) return 10; // Generally, breaking before a trailing annotation is bad unless it is // function-like. It seems to be especially preferable to keep standard @@ -1687,7 +1686,7 @@ } // In for-loops, prefer breaking at ',' and ';'. - if (Line.First->is(tok::kw_for) && Left.is(tok::equal)) + if (Line.StartsWith(tok::kw_for) && Left.is(tok::equal)) return 4; // In Objective-C method expressions, prefer breaking before "param:" over @@ -1845,7 +1844,8 @@ tok::kw_new, tok::kw_delete) && (!Left.Previous || Left.Previous->isNot(tok::period))))) || (Style.SpaceBeforeParens == FormatStyle::SBPO_Always && - (Left.is(tok::identifier) || Left.isFunctionLikeKeyword() || Left.is(tok::r_paren)) && + (Left.is(tok::identifier) || Left.isFunctionLikeKeyword() || + Left.is(tok::r_paren)) && Line.Type != LT_PreprocessorDirective); } if (Left.is(tok::at) && Right.Tok.getObjCKeywordID() != tok::objc_not_keyword) @@ -1990,7 +1990,7 @@ Left.MatchingParen && Left.MatchingParen->is(TT_OverloadedOperatorLParen)) return false; if (Right.is(tok::less) && Left.isNot(tok::l_paren) && - Line.First->is(tok::hash)) + Line.StartsWith(tok::hash)) return true; if (Right.is(TT_TrailingUnaryOperator)) return false; @@ -2031,7 +2031,7 @@ Left.isNot(TT_CtorInitializerColon) && (Right.NewlinesBefore > 0 && Right.HasUnescapedNewline); if (Left.isTrailingComment()) - return true; + return true; if (Left.isStringLiteral() && (Right.isStringLiteral() || Right.is(TT_ObjCStringLiteral))) return true; @@ -2067,6 +2067,14 @@ return Style.AllowShortFunctionsOnASingleLine == FormatStyle::SFS_None || (Left.NestingLevel == 0 && Line.Level == 0 && Style.AllowShortFunctionsOnASingleLine == FormatStyle::SFS_Inline); + + if (Style.Language == FormatStyle::LK_JavaScript && Left.is(tok::l_brace) && + Line.Level == 0 && (Line.StartsWith(tok::kw_enum) || + Line.StartsWith(tok::kw_export, tok::kw_enum))) + // JavaScript top-level enum key/value pairs are put on separate lines + // instead of bin-packing. + return true; + if (isAllmanBrace(Left) || isAllmanBrace(Right)) return Style.BreakBeforeBraces == FormatStyle::BS_Allman || Style.BreakBeforeBraces == FormatStyle::BS_GNU; @@ -2088,13 +2096,12 @@ if (Right.is(tok::char_constant) && Left.is(tok::plus) && Left.Previous && Left.Previous->is(tok::char_constant)) return true; - if (Left.is(TT_DictLiteral) && Left.is(tok::l_brace) && - Line.Level == 0 && Left.Previous && - Left.Previous->is(tok::equal) && - Line.First->isOneOf(tok::identifier, Keywords.kw_import, - tok::kw_export, tok::kw_const) && + if (Left.is(TT_DictLiteral) && Left.is(tok::l_brace) && Line.Level == 0 && + Left.Previous && Left.Previous->is(tok::equal) && + Line.First->isOneOf(tok::identifier, Keywords.kw_import, tok::kw_export, + tok::kw_const) && // kw_var is a pseudo-token that's a tok::identifier, so matches above. - !Line.First->is(Keywords.kw_var)) + !Line.StartsWith(Keywords.kw_var)) // Object literals on the top level of a file are treated as "enum-style". // Each key/value pair is put on a separate line, instead of bin-packing. return true; Index: lib/Format/UnwrappedLineParser.cpp =================================================================== --- lib/Format/UnwrappedLineParser.cpp +++ lib/Format/UnwrappedLineParser.cpp @@ -251,7 +251,6 @@ assert(PPLevelBranchIndex.back() <= PPLevelBranchCount.back()); } } while (!PPLevelBranchIndex.empty()); - } void UnwrappedLineParser::parseFile() { @@ -774,7 +773,16 @@ parseBracedList(); break; case tok::kw_enum: + // We fall through to parsing a structural element afterwards, so that in + // enum A {} n, m; + // "} n, m;" will end up in one unwrapped line. parseEnum(); + if (Style.Language == FormatStyle::LK_JavaScript) { + // This does not apply to JavaScript. Java is handled separately in + // parseJavaEnumBody. + addUnwrappedLine(); + return; + } break; case tok::kw_typedef: nextToken(); @@ -879,8 +887,7 @@ ? FormatTok->NewlinesBefore > 0 : CommentsBeforeNextToken.front()->NewlinesBefore > 0; - if (FollowedByNewline && - (Text.size() >= 5 || FunctionLike) && + if (FollowedByNewline && (Text.size() >= 5 || FunctionLike) && tokenCanStartNewLine(FormatTok->Tok) && Text == Text.upper()) { addUnwrappedLine(); return; @@ -1040,7 +1047,7 @@ if (FormatTok->is(tok::l_brace)) tryToParseBracedList(); else - while(FormatTok->isNot(tok::l_brace) && !eof()) + while (FormatTok->isNot(tok::l_brace) && !eof()) nextToken(); } @@ -1073,7 +1080,7 @@ nextToken(); // Fat arrows can be followed by simple expressions or by child blocks // in curly braces. - if (FormatTok->is(tok::l_brace)){ + if (FormatTok->is(tok::l_brace)) { parseChildBlock(); continue; } @@ -1511,10 +1518,6 @@ nextToken(); addUnwrappedLine(); } - - // We fall through to parsing a structural element afterwards, so that in - // enum A {} n, m; - // "} n, m;" will end up in one unwrapped line. } void UnwrappedLineParser::parseJavaEnumBody() { @@ -1583,7 +1586,6 @@ const FormatToken &InitialToken = *FormatTok; nextToken(); - // The actual identifier can be a nested name specifier, and in macros // it is often token-pasted. while (FormatTok->isOneOf(tok::identifier, tok::coloncolon, tok::hashhash, Index: unittests/Format/FormatTestJS.cpp =================================================================== --- unittests/Format/FormatTestJS.cpp +++ unittests/Format/FormatTestJS.cpp @@ -256,9 +256,8 @@ } TEST_F(FormatTestJS, ArrayLiterals) { - verifyFormat( - "var aaaaa: List =\n" - " [new SomeThingAAAAAAAAAAAA(), new SomeThingBBBBBBBBB()];"); + verifyFormat("var aaaaa: List =\n" + " [new SomeThingAAAAAAAAAAAA(), new SomeThingBBBBBBBBB()];"); verifyFormat("return [\n" " aaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" " bbbbbbbbbbbbbbbbbbbbbbbbbbb,\n" @@ -688,6 +687,18 @@ "var y;"); } +TEST_F(FormatTestJS, EnumDeclarations) { + verifyFormat("enum Foo {\n" + " A = 1,\n" + " B\n" + "}"); + verifyFormat("enum Foo {\n" + " A = 1,\n" + " B\n" + "}\n" + "var x = 1;"); +} + TEST_F(FormatTestJS, MetadataAnnotations) { verifyFormat("@A\nclass C {\n}"); verifyFormat("@A({arg: 'value'})\nclass C {\n}"); @@ -840,9 +851,7 @@ "var y;")); } -TEST_F(FormatTestJS, CastSyntax) { - verifyFormat("var x = foo;"); -} +TEST_F(FormatTestJS, CastSyntax) { verifyFormat("var x = foo;"); } TEST_F(FormatTestJS, TypeArguments) { verifyFormat("class X {}");