Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -349,14 +349,42 @@ /// \endcode bool AlwaysBreakBeforeMultilineStrings; - /// \brief If ``true``, always break after the ``template<...>`` of a template - /// declaration. - /// \code - /// true: false: - /// template vs. template class C {}; - /// class C {}; - /// \endcode - bool AlwaysBreakTemplateDeclarations; + /// \brief Different ways to break after the template declaration. + enum BreakTemplateDeclarationsStyle { + /// Break after template declaration automatically. + /// ``PenaltyBreakTemplateDeclaration`` is taken into account. + /// \code + /// template class C { + /// }; + /// + /// template T foo() { + /// } + /// \endcode + BTDS_None, + /// Break after template declaration in function declaration only. + /// \code + /// template class C { + /// }; + /// + /// template + /// T foo() { + /// } + /// \endcode + BTDS_BeforeFunction, + /// Always break after template declaration. + /// \code + /// template class C { + /// }; + /// + /// template + /// T foo() { + /// } + /// \endcode + BTDS_All + }; + + /// \brief The template declaration breaking style to use. + BreakTemplateDeclarationsStyle AlwaysBreakTemplateDeclarations; /// \brief If ``false``, a function call's arguments will either be all on the /// same line or will have one line each. @@ -1332,6 +1360,9 @@ /// \brief The penalty for each line break introduced inside a string literal. unsigned PenaltyBreakString; + /// \brief The penalty for breaking after template declaration. + unsigned PenaltyBreakTemplateDeclaration; + /// \brief The penalty for each character outside of the column limit. unsigned PenaltyExcessCharacter; @@ -1693,6 +1724,8 @@ PenaltyBreakString == R.PenaltyBreakString && PenaltyExcessCharacter == R.PenaltyExcessCharacter && PenaltyReturnTypeOnItsOwnLine == R.PenaltyReturnTypeOnItsOwnLine && + PenaltyBreakTemplateDeclaration == + R.PenaltyBreakTemplateDeclaration && PointerAlignment == R.PointerAlignment && RawStringFormats == R.RawStringFormats && SpaceAfterCStyleCast == R.SpaceAfterCStyleCast && Index: lib/Format/ContinuationIndenter.cpp =================================================================== --- lib/Format/ContinuationIndenter.cpp +++ lib/Format/ContinuationIndenter.cpp @@ -329,6 +329,12 @@ Style.Language == FormatStyle::LK_JavaScript)) return true; + // If the template declaration spans multiple lines, force wrap before the + // function/class declaration + if (Previous.ClosesTemplateDeclaration && + State.Stack.back().BreakBeforeParameter) + return true; + if (State.Column <= NewLineColumn) return false; @@ -380,7 +386,7 @@ // for cases where the entire line does not fit on a single line as a // different LineFormatter would be used otherwise. if (Previous.ClosesTemplateDeclaration) - return true; + return Style.AlwaysBreakTemplateDeclarations != FormatStyle::BTDS_None; if (Previous.is(TT_FunctionAnnotationRParen)) return true; if (Previous.is(TT_LeadingJavaAnnotation) && Current.isNot(tok::l_paren) && Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -158,6 +158,19 @@ } }; +template <> +struct ScalarEnumerationTraits { + static void enumeration(IO &IO, FormatStyle::BreakTemplateDeclarationsStyle &Value) { + IO.enumCase(Value, "None", FormatStyle::BTDS_None); + IO.enumCase(Value, "BeforeFunction", FormatStyle::BTDS_BeforeFunction); + IO.enumCase(Value, "All", FormatStyle::BTDS_All); + + // For backward compatibility. + IO.enumCase(Value, "false", FormatStyle::BTDS_BeforeFunction); + IO.enumCase(Value, "true", FormatStyle::BTDS_All); + } +}; + template <> struct ScalarEnumerationTraits { static void @@ -389,6 +402,8 @@ IO.mapOptional("PenaltyBreakFirstLessLess", Style.PenaltyBreakFirstLessLess); IO.mapOptional("PenaltyBreakString", Style.PenaltyBreakString); + IO.mapOptional("PenaltyBreakTemplateDeclaration", + Style.PenaltyBreakTemplateDeclaration); IO.mapOptional("PenaltyExcessCharacter", Style.PenaltyExcessCharacter); IO.mapOptional("PenaltyReturnTypeOnItsOwnLine", Style.PenaltyReturnTypeOnItsOwnLine); @@ -596,7 +611,7 @@ LLVMStyle.AlwaysBreakAfterReturnType = FormatStyle::RTBS_None; LLVMStyle.AlwaysBreakAfterDefinitionReturnType = FormatStyle::DRTBS_None; LLVMStyle.AlwaysBreakBeforeMultilineStrings = false; - LLVMStyle.AlwaysBreakTemplateDeclarations = false; + LLVMStyle.AlwaysBreakTemplateDeclarations = FormatStyle::BTDS_BeforeFunction; LLVMStyle.BinPackArguments = true; LLVMStyle.BinPackParameters = true; LLVMStyle.BreakBeforeBinaryOperators = FormatStyle::BOS_None; @@ -663,6 +678,7 @@ LLVMStyle.PenaltyExcessCharacter = 1000000; LLVMStyle.PenaltyReturnTypeOnItsOwnLine = 60; LLVMStyle.PenaltyBreakBeforeFirstCallParameter = 19; + LLVMStyle.PenaltyBreakTemplateDeclaration = prec::Relational; LLVMStyle.DisableFormat = false; LLVMStyle.SortIncludes = true; @@ -692,7 +708,7 @@ GoogleStyle.AllowShortIfStatementsOnASingleLine = true; GoogleStyle.AllowShortLoopsOnASingleLine = true; GoogleStyle.AlwaysBreakBeforeMultilineStrings = true; - GoogleStyle.AlwaysBreakTemplateDeclarations = true; + GoogleStyle.AlwaysBreakTemplateDeclarations = FormatStyle::BTDS_All; GoogleStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = true; GoogleStyle.DerivePointerAlignment = true; GoogleStyle.IncludeCategories = { @@ -795,7 +811,7 @@ MozillaStyle.AlwaysBreakAfterReturnType = FormatStyle::RTBS_TopLevel; MozillaStyle.AlwaysBreakAfterDefinitionReturnType = FormatStyle::DRTBS_TopLevel; - MozillaStyle.AlwaysBreakTemplateDeclarations = true; + MozillaStyle.AlwaysBreakTemplateDeclarations = FormatStyle::BTDS_All; MozillaStyle.BinPackParameters = false; MozillaStyle.BinPackArguments = false; MozillaStyle.BreakBeforeBraces = FormatStyle::BS_Mozilla; Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -2171,6 +2171,8 @@ return 2; return 1; } + if (Left.ClosesTemplateDeclaration) + return Style.PenaltyBreakTemplateDeclaration; if (Left.is(TT_ConditionalExpr)) return prec::Conditional; prec::Level Level = Left.getPrecedence(); @@ -2641,7 +2643,7 @@ if (Right.Previous->ClosesTemplateDeclaration && Right.Previous->MatchingParen && Right.Previous->MatchingParen->NestingLevel == 0 && - Style.AlwaysBreakTemplateDeclarations) + Style.AlwaysBreakTemplateDeclarations == FormatStyle::BTDS_All) return true; if (Right.is(TT_CtorInitializerComma) && Style.BreakConstructorInitializers == FormatStyle::BCIS_BeforeComma && Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -5367,7 +5367,7 @@ " const typename aaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaa);"); FormatStyle AlwaysBreak = getLLVMStyle(); - AlwaysBreak.AlwaysBreakTemplateDeclarations = true; + AlwaysBreak.AlwaysBreakTemplateDeclarations = FormatStyle::BTDS_All; verifyFormat("template \nclass C {};", AlwaysBreak); verifyFormat("template \nvoid f();", AlwaysBreak); verifyFormat("template \nvoid f() {}", AlwaysBreak); @@ -5385,6 +5385,32 @@ "public:\n" " E *f();\n" "};"); + + FormatStyle NeverBreak = getLLVMStyle(); + NeverBreak.AlwaysBreakTemplateDeclarations = FormatStyle::BTDS_None; + verifyFormat("template class C {};", NeverBreak); + verifyFormat("template void f();", NeverBreak); + verifyFormat("template void f() {}", NeverBreak); + verifyFormat("template \nvoid foo(aaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbb) {}", + NeverBreak); + verifyFormat("void aaaaaaaaaaaaaaaaaaa(\n" + " ccccccccccccccccccccccccccccccccccccccccccccccc);", + NeverBreak); + verifyFormat("template