Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -693,6 +693,17 @@ /// \endcode bool BreakBeforeTernaryOperators; + /// \brief If ``true``, clang-format will break before the colon in the constructor + /// initializers. + /// \code + /// true: false: + /// SomeClass::Constructor() vs. SomeClass::Constructor() : aaaaaaaa(aaaaaaaa) { + /// : aaaaaaaa(aaaaaaaa) { return 0; + /// return 0; } + /// } + /// \endcode + bool BreakConstructorInitializersBeforeColon; + /// \brief Always break constructor initializers before commas and align /// the commas with the colon. /// \code @@ -1370,6 +1381,8 @@ BreakBeforeBinaryOperators == R.BreakBeforeBinaryOperators && BreakBeforeBraces == R.BreakBeforeBraces && BreakBeforeTernaryOperators == R.BreakBeforeTernaryOperators && + BreakConstructorInitializersBeforeColon == + R.BreakConstructorInitializersBeforeColon && BreakConstructorInitializersBeforeComma == R.BreakConstructorInitializersBeforeComma && BreakAfterJavaFieldAnnotations == R.BreakAfterJavaFieldAnnotations && Index: lib/Format/ContinuationIndenter.cpp =================================================================== --- lib/Format/ContinuationIndenter.cpp +++ lib/Format/ContinuationIndenter.cpp @@ -179,12 +179,19 @@ getColumnLimit(State)) return true; if (Current.is(TT_CtorInitializerColon) && + Style.BreakConstructorInitializersBeforeColon && (State.Column + State.Line->Last->TotalLength - Current.TotalLength + 2 > getColumnLimit(State) || State.Stack.back().BreakBeforeParameter) && ((Style.AllowShortFunctionsOnASingleLine != FormatStyle::SFS_All) || Style.BreakConstructorInitializersBeforeComma || Style.ColumnLimit != 0)) return true; + if (Previous.is(TT_CtorInitializerColon) && + !Style.BreakConstructorInitializersBeforeColon && + (State.Column + State.Line->Last->TotalLength - Previous.TotalLength > + getColumnLimit(State) || + State.Stack.back().BreakBeforeParameter)) + return true; if (Current.is(TT_ObjCMethodExpr) && !Previous.is(TT_SelectorName) && State.Line->startsWith(TT_ObjCMethodSpecifier)) return true; @@ -455,6 +462,10 @@ !Previous.is(TT_OverloadedOperator)) || (Previous.is(tok::colon) && Previous.is(TT_ObjCMethodExpr)))) { State.Stack.back().LastSpace = State.Column; + } else if (Previous.is(TT_CtorInitializerColon) && + !Style.BreakConstructorInitializersBeforeColon) { + State.Stack.back().Indent = State.Column; + State.Stack.back().LastSpace = State.Column; } else if ((Previous.isOneOf(TT_BinaryOperator, TT_ConditionalExpr, TT_CtorInitializerColon)) && ((Previous.getPrecedence() != prec::Assignment && @@ -612,7 +623,7 @@ State.Stack[i].BreakBeforeParameter = true; if (PreviousNonComment && - !PreviousNonComment->isOneOf(tok::comma, tok::semi) && + !PreviousNonComment->isOneOf(tok::comma, tok::colon, tok::semi) && (PreviousNonComment->isNot(TT_TemplateCloser) || Current.NestingLevel != 0) && !PreviousNonComment->isOneOf( @@ -746,6 +757,10 @@ return ContinuationIndent; if (NextNonComment->is(TT_CtorInitializerComma)) return State.Stack.back().Indent; + if (PreviousNonComment && PreviousNonComment->is(TT_CtorInitializerColon) && + !Style.BreakConstructorInitializersBeforeColon) + return State.Stack.back().Indent + + (Style.BreakConstructorInitializersBeforeComma ? 2 : 0); if (NextNonComment->isOneOf(TT_CtorInitializerColon, TT_InheritanceColon, TT_InheritanceComma)) return State.FirstIndent + Style.ConstructorInitializerIndentWidth; @@ -805,7 +820,8 @@ State.FirstIndent + Style.ContinuationIndentWidth; } } - if (Current.is(TT_CtorInitializerColon)) { + if (Current.is(TT_CtorInitializerColon) && + Style.BreakConstructorInitializersBeforeColon) { // Indent 2 from the column, so: // SomeClass::SomeClass() // : First(...), ... @@ -818,6 +834,14 @@ State.Stack.back().AvoidBinPacking = true; State.Stack.back().BreakBeforeParameter = false; } + if (Current.is(TT_CtorInitializerColon) && + !Style.BreakConstructorInitializersBeforeColon) { + State.Stack.back().Indent = + State.FirstIndent + Style.ConstructorInitializerIndentWidth; + State.Stack.back().NestedBlockIndent = State.Stack.back().Indent; + if (Style.ConstructorInitializerAllOnOneLineOrOnePerLine) + State.Stack.back().AvoidBinPacking = true; + } if (Current.is(TT_InheritanceColon)) State.Stack.back().Indent = State.FirstIndent + Style.ContinuationIndentWidth; Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -291,6 +291,8 @@ IO.mapOptional("BreakBeforeBraces", Style.BreakBeforeBraces); IO.mapOptional("BreakBeforeTernaryOperators", Style.BreakBeforeTernaryOperators); + IO.mapOptional("BreakConstructorInitializersBeforeColon", + Style.BreakConstructorInitializersBeforeColon); IO.mapOptional("BreakConstructorInitializersBeforeComma", Style.BreakConstructorInitializersBeforeComma); IO.mapOptional("BreakAfterJavaFieldAnnotations", @@ -522,6 +524,7 @@ LLVMStyle.BraceWrapping = {false, false, false, false, false, false, false, false, false, false, false}; LLVMStyle.BreakAfterJavaFieldAnnotations = false; + LLVMStyle.BreakConstructorInitializersBeforeColon = true; LLVMStyle.BreakConstructorInitializersBeforeComma = false; LLVMStyle.BreakBeforeInheritanceComma = false; LLVMStyle.BreakStringLiterals = true; Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -1984,7 +1984,7 @@ if (Left.is(tok::comment)) return 1000; - if (Left.isOneOf(TT_RangeBasedForLoopColon, TT_InheritanceColon)) + if (Left.isOneOf(TT_RangeBasedForLoopColon, TT_InheritanceColon, TT_CtorInitializerColon)) return 2; if (Right.isMemberAccess()) { @@ -2490,7 +2490,12 @@ Right.Previous->MatchingParen->NestingLevel == 0 && Style.AlwaysBreakTemplateDeclarations) return true; - if ((Right.isOneOf(TT_CtorInitializerComma, TT_CtorInitializerColon)) && + if (Right.is(TT_CtorInitializerComma) && + Style.BreakConstructorInitializersBeforeComma && + !Style.ConstructorInitializerAllOnOneLineOrOnePerLine) + return true; + if (Right.is(TT_CtorInitializerColon) && + Style.BreakConstructorInitializersBeforeColon && Style.BreakConstructorInitializersBeforeComma && !Style.ConstructorInitializerAllOnOneLineOrOnePerLine) return true; @@ -2599,7 +2604,8 @@ // The first comment in a braced lists is always interpreted as belonging to // the first list element. Otherwise, it should be placed outside of the // list. - return Left.BlockKind == BK_BracedInit; + return Left.BlockKind == BK_BracedInit || + (Left.is(TT_CtorInitializerColon) && !Style.BreakConstructorInitializersBeforeColon); if (Left.is(tok::question) && Right.is(tok::colon)) return false; if (Right.is(TT_ConditionalExpr) || Right.is(tok::question)) @@ -2672,6 +2678,10 @@ if (Right.is(tok::identifier) && Right.Next && Right.Next->is(TT_DictLiteral)) return true; + if (Left.is(TT_CtorInitializerColon)) + return !Style.BreakConstructorInitializersBeforeColon; + if (Right.is(TT_CtorInitializerColon)) + return Style.BreakConstructorInitializersBeforeColon; if (Left.is(TT_CtorInitializerComma) && Style.BreakConstructorInitializersBeforeComma) return false; Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -56,16 +56,17 @@ return *Result; } - FormatStyle getLLVMStyleWithColumns(unsigned ColumnLimit) { - FormatStyle Style = getLLVMStyle(); + FormatStyle getStyleWithColumns(FormatStyle Style, unsigned ColumnLimit) { Style.ColumnLimit = ColumnLimit; return Style; } + FormatStyle getLLVMStyleWithColumns(unsigned ColumnLimit) { + return getStyleWithColumns(getLLVMStyle(), ColumnLimit); + } + FormatStyle getGoogleStyleWithColumns(unsigned ColumnLimit) { - FormatStyle Style = getGoogleStyle(); - Style.ColumnLimit = ColumnLimit; - return Style; + return getStyleWithColumns(getGoogleStyle(), ColumnLimit); } void verifyFormat(llvm::StringRef Code, @@ -2699,6 +2700,128 @@ " aaaa(aaaa) {}")); } +TEST_F(FormatTest, BreakConstructorInitializersBeforeColon) { + FormatStyle BreakBeforeColon = getLLVMStyle(); + BreakBeforeColon.BreakConstructorInitializersBeforeColon = false; + + verifyFormat("Constructor() : Initializer(FitsOnTheLine) {}"); + verifyFormat("Constructor() : Inttializer(FitsOnTheLine) {}", + getStyleWithColumns(BreakBeforeColon, 45)); + verifyFormat("Constructor() :\n" + " Inttializer(FitsOnTheLine) {}", + getStyleWithColumns(BreakBeforeColon, 44)); + verifyFormat("Constructor() :\n" + " Inttializer(FitsOnTheLine) {}", + getStyleWithColumns(BreakBeforeColon, 43)); + + verifyFormat("template \n" + "Constructor() : Initializer(FitsOnTheLine) {}", + getStyleWithColumns(BreakBeforeColon, 50)); + + verifyFormat( + "SomeClass::Constructor() :\n" + " aaaaaaaaaaaaa(aaaaaaaaaaaaaa), aaaaaaaaaaaaaaa(aaaaaaaaaaaa) {}", + BreakBeforeColon); + + verifyFormat( + "SomeClass::Constructor() :\n" + " aaaaaaaaaaaaa(aaaaaaaaaaaaaa), aaaaaaaaaaaaa(aaaaaaaaaaaaaa),\n" + " aaaaaaaaaaaaa(aaaaaaaaaaaaaa) {}", + BreakBeforeColon); + verifyFormat( + "SomeClass::Constructor() :\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa),\n" + " aaaaaaaaaaaaaaa(aaaaaaaaaaaa) {}", + BreakBeforeColon); + verifyFormat("Constructor(aaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" + " aaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) :\n" + " aaaaaaaaaa(aaaaaa) {}", + BreakBeforeColon); + + verifyFormat("Constructor() :\n" + " aaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaa),\n" + " aaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaa),\n" + " aaaaaaaaaaaaaaaaaaaaaaa() {}", + BreakBeforeColon); + + verifyFormat("Constructor() :\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) {}", + BreakBeforeColon); + + verifyFormat("Constructor(int Parameter = 0) :\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaa),\n" + " aaaaaaaaaaaa(aaaaaaaaaaaaaaaaa) {}", + BreakBeforeColon); + verifyFormat("Constructor() :\n" + " aaaaaaaaaaaaaaaaaaaaaa(a), bbbbbbbbbbbbbbbbbbbbbbbb(b) {\n" + "}", + getStyleWithColumns(BreakBeforeColon, 60)); + verifyFormat("Constructor() :\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n" + " aaaaaaaaaaaaaaaaaaaaaaaaa(aaaa, aaaa)) {}", + BreakBeforeColon); + + // Here a line could be saved by splitting the second initializer onto two + // lines, but that is not desirable. + verifyFormat("Constructor() :\n" + " aaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaa),\n" + " aaaaaaaaaaa(aaaaaaaaaaa),\n" + " aaaaaaaaaaaaaaaaaaaaat(aaaaaaaaaaaaaaaaaaaaaaaaaaaa) {}", + BreakBeforeColon); + + FormatStyle OnePerLine = BreakBeforeColon; + OnePerLine.ConstructorInitializerAllOnOneLineOrOnePerLine = true; + OnePerLine.AllowAllParametersOfDeclarationOnNextLine = false; + verifyFormat("SomeClass::Constructor() :\n" + " aaaaaaaaaaaaa(aaaaaaaaaaaaaa),\n" + " aaaaaaaaaaaaa(aaaaaaaaaaaaaa),\n" + " aaaaaaaaaaaaa(aaaaaaaaaaaaaa) {}", + OnePerLine); + verifyFormat("SomeClass::Constructor() :\n" + " aaaaaaaaaaaaa(aaaaaaaaaaaaaa), // Some comment\n" + " aaaaaaaaaaaaa(aaaaaaaaaaaaaa),\n" + " aaaaaaaaaaaaa(aaaaaaaaaaaaaa) {}", + OnePerLine); + verifyFormat("MyClass::MyClass(int var) :\n" + " some_var_(var), // 4 space indent\n" + " some_other_var_(var + 1) { // lined up\n" + "}", + OnePerLine); + verifyFormat("Constructor() :\n" + " aaaaa(aaaaaa),\n" + " aaaaa(aaaaaa),\n" + " aaaaa(aaaaaa),\n" + " aaaaa(aaaaaa),\n" + " aaaaa(aaaaaa) {}", + OnePerLine); + verifyFormat("Constructor() :\n" + " aaaaa(aaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaa,\n" + " aaaaaaaaaaaaaaaaaaaaaa) {}", + OnePerLine); + OnePerLine.BinPackParameters = false; + verifyFormat( + "Constructor() :\n" + " aaaaaaaaaaaaaaaaaaaaaaaa(\n" + " aaaaaaaaaaa().aaa(),\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) {}", + OnePerLine); + OnePerLine.ColumnLimit = 60; + verifyFormat("Constructor() :\n" + " aaaaaaaaaaaaaaaaaaaa(a),\n" + " bbbbbbbbbbbbbbbbbbbbbbbb(b) {}", + OnePerLine); + + EXPECT_EQ("Constructor() :\n" + " // Comment forcing unwanted break.\n" + " aaaa(aaaa) {}", + format("Constructor() :\n" + " // Comment forcing unwanted break.\n" + " aaaa(aaaa) {}", + BreakBeforeColon)); +} + TEST_F(FormatTest, MemoizationTests) { // This breaks if the memoization lookup does not take \c Indent and // \c LastSpace into account. @@ -8679,6 +8802,7 @@ CHECK_PARSE_BOOL(BinPackParameters); CHECK_PARSE_BOOL(BreakAfterJavaFieldAnnotations); CHECK_PARSE_BOOL(BreakBeforeTernaryOperators); + CHECK_PARSE_BOOL(BreakConstructorInitializersBeforeColon); CHECK_PARSE_BOOL(BreakConstructorInitializersBeforeComma); CHECK_PARSE_BOOL(BreakStringLiterals); CHECK_PARSE_BOOL(BreakBeforeInheritanceComma)