Hi djasper,
This supports checking for an arbitrary line
prefix, which is useful for e.g. JavaScript
`export class` vs `class`.
clang-format: [JS] Consistently format top level enums.
Object literal style enums are forced onto multiple lines for JS enums.
This change produces the same formatting for enums using the actual
TypeScript enum syntax:
enum Color {
Blue,
Red,
Tuna
}
clang-format: use clang-format on clang-format.
clang-format: [JS] put enums on separate lines.
This change identifies enums in JS as complete declarations so that
trailing statements are formatted on a separate line.
enum Color { Blue, Green }
var x;
http://reviews.llvm.org/D10485
Files:
lib/Format/TokenAnnotator.cpp
lib/Format/TokenAnnotator.h
lib/Format/UnwrappedLineParser.cpp
unittests/Format/FormatTestJS.cpp
EMAIL PREFERENCES
http://reviews.llvm.org/settings/panel/emailpreferences/
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/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 <typename... Ts> 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 <typename A, typename... Ts>
+ bool StartsWith(FormatToken *token, A K1) const {
+ return token != NULL && token->is(K1);
+ }
+
+ template <typename A, typename... Ts>
+ 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/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<SomeThing> =\n"
- " [new SomeThingAAAAAAAAAAAA(), new SomeThingBBBBBBBBB()];");
+ verifyFormat("var aaaaa: List<SomeThing> =\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 = <type>foo;");
-}
+TEST_F(FormatTestJS, CastSyntax) { verifyFormat("var x = <type>foo;"); }
TEST_F(FormatTestJS, TypeArguments) {
verifyFormat("class X<Y> {}");
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits