Skip to content

Commit 217f807

Browse files
committed
BreakBeforeTemplateClose
1 parent b9ac390 commit 217f807

File tree

7 files changed

+144
-0
lines changed

7 files changed

+144
-0
lines changed

clang/docs/ClangFormatStyleOptions.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3416,6 +3416,29 @@ the configuration (without a prefix: ``Auto``).
34163416

34173417

34183418

3419+
3420+
.. _BreakBeforeTemplateClose:
3421+
3422+
**BreakBeforeTemplateClose** (``Boolean``) :versionbadge:`clang-format 20` :ref:`<BreakBeforeTemplateClose>`
3423+
If ``true``, a line break will be placed before the ``>`` in a multiline template declaration.
3424+
3425+
.. code-block:: c++
3426+
3427+
true:
3428+
template <
3429+
typename Foo,
3430+
typename Bar,
3431+
typename Baz
3432+
>
3433+
3434+
false:
3435+
template <
3436+
typename Foo,
3437+
typename Bar,
3438+
typename Baz>
3439+
3440+
3441+
34193442
.. _BreakBeforeTernaryOperators:
34203443

34213444
**BreakBeforeTernaryOperators** (``Boolean``) :versionbadge:`clang-format 3.7` :ref:`<BreakBeforeTernaryOperators>`

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,7 @@ clang-format
976976
``Never``, and ``true`` to ``Always``.
977977
- Adds ``RemoveEmptyLinesInUnwrappedLines`` option.
978978
- Adds ``KeepFormFeed`` option and set it to ``true`` for ``GNU`` style.
979+
- Adds ``BreakBeforeTemplateClose`` option.
979980

980981
libclang
981982
--------

clang/include/clang/Format/Format.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2248,6 +2248,25 @@ struct FormatStyle {
22482248
/// \version 16
22492249
BreakBeforeInlineASMColonStyle BreakBeforeInlineASMColon;
22502250

2251+
/// If ``true``, a line break will be placed before the ``>`` in a multiline
2252+
/// template declaration.
2253+
/// \code
2254+
/// true:
2255+
/// template <
2256+
/// typename Foo,
2257+
/// typename Bar,
2258+
/// typename Baz
2259+
/// >
2260+
///
2261+
/// false:
2262+
/// template <
2263+
/// typename Foo,
2264+
/// typename Bar,
2265+
/// typename Baz>
2266+
/// \endcode
2267+
/// \version 20
2268+
bool BreakBeforeTemplateClose;
2269+
22512270
/// If ``true``, ternary operators will be placed after line breaks.
22522271
/// \code
22532272
/// true:
@@ -5184,6 +5203,7 @@ struct FormatStyle {
51845203
BreakBeforeBraces == R.BreakBeforeBraces &&
51855204
BreakBeforeConceptDeclarations == R.BreakBeforeConceptDeclarations &&
51865205
BreakBeforeInlineASMColon == R.BreakBeforeInlineASMColon &&
5206+
BreakBeforeTemplateClose == R.BreakBeforeTemplateClose &&
51875207
BreakBeforeTernaryOperators == R.BreakBeforeTernaryOperators &&
51885208
BreakBinaryOperations == R.BreakBinaryOperations &&
51895209
BreakConstructorInitializers == R.BreakConstructorInitializers &&

clang/lib/Format/ContinuationIndenter.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,10 +382,25 @@ bool ContinuationIndenter::canBreak(const LineState &State) {
382382
return !State.NoLineBreak && !CurrentState.NoLineBreak;
383383
}
384384

385+
bool isMatchingBraceOnSameLine(const FormatToken *Token) {
386+
if (!Token->MatchingParen)
387+
return false;
388+
const FormatToken *Matching = Token->MatchingParen;
389+
const FormatToken *Current = Token;
390+
while (Current && Current != Matching) {
391+
if (Current->NewlinesBefore > 0)
392+
return false;
393+
Current = Current->Previous;
394+
}
395+
return true;
396+
}
397+
385398
bool ContinuationIndenter::mustBreak(const LineState &State) {
386399
const FormatToken &Current = *State.NextToken;
387400
const FormatToken &Previous = *Current.Previous;
388401
const auto &CurrentState = State.Stack.back();
402+
if (Current.ClosesTemplateDeclaration && Style.BreakBeforeTemplateClose)
403+
return !isMatchingBraceOnSameLine(State.NextToken);
389404
if (Style.BraceWrapping.BeforeLambdaBody && Current.CanBreakBefore &&
390405
Current.is(TT_LambdaLBrace) && Previous.isNot(TT_LineComment)) {
391406
auto LambdaBodyLength = getLengthToMatchingParen(Current, State.Stack);
@@ -1279,6 +1294,9 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) {
12791294
FormatToken &Current = *State.NextToken;
12801295
const auto &CurrentState = State.Stack.back();
12811296

1297+
if (Current.ClosesTemplateDeclaration && Style.BreakBeforeTemplateClose)
1298+
return CurrentState.Indent - Style.ContinuationIndentWidth;
1299+
12821300
if (CurrentState.IsCSharpGenericTypeConstraint &&
12831301
Current.isNot(TT_CSharpGenericTypeConstraint)) {
12841302
return CurrentState.ColonPos + 2;

clang/lib/Format/Format.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,7 @@ template <> struct MappingTraits<FormatStyle> {
10001000
IO.mapOptional("BreakBeforeBraces", Style.BreakBeforeBraces);
10011001
IO.mapOptional("BreakBeforeInlineASMColon",
10021002
Style.BreakBeforeInlineASMColon);
1003+
IO.mapOptional("BreakBeforeTemplateClose", Style.BreakBeforeTemplateClose);
10031004
IO.mapOptional("BreakBeforeTernaryOperators",
10041005
Style.BreakBeforeTernaryOperators);
10051006
IO.mapOptional("BreakBinaryOperations", Style.BreakBinaryOperations);
@@ -1514,6 +1515,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
15141515
LLVMStyle.BreakBeforeBraces = FormatStyle::BS_Attach;
15151516
LLVMStyle.BreakBeforeConceptDeclarations = FormatStyle::BBCDS_Always;
15161517
LLVMStyle.BreakBeforeInlineASMColon = FormatStyle::BBIAS_OnlyMultiline;
1518+
LLVMStyle.BreakBeforeTemplateClose = false;
15171519
LLVMStyle.BreakBeforeTernaryOperators = true;
15181520
LLVMStyle.BreakBinaryOperations = FormatStyle::BBO_Never;
15191521
LLVMStyle.BreakConstructorInitializers = FormatStyle::BCIS_BeforeColon;

clang/unittests/Format/ConfigParseTest.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
162162
CHECK_PARSE_BOOL(BinPackArguments);
163163
CHECK_PARSE_BOOL(BreakAdjacentStringLiterals);
164164
CHECK_PARSE_BOOL(BreakAfterJavaFieldAnnotations);
165+
CHECK_PARSE_BOOL(BreakBeforeTemplateClose);
165166
CHECK_PARSE_BOOL(BreakBeforeTernaryOperators);
166167
CHECK_PARSE_BOOL(BreakStringLiterals);
167168
CHECK_PARSE_BOOL(CompactNamespaces);

clang/unittests/Format/FormatTest.cpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11077,6 +11077,85 @@ TEST_F(FormatTest, WrapsTemplateDeclarationsWithComments) {
1107711077
Style);
1107811078
}
1107911079

11080+
TEST_F(FormatTest, BreakBeforeTemplateClose) {
11081+
FormatStyle Style = getGoogleStyle(FormatStyle::LK_Cpp);
11082+
Style.BreakTemplateDeclarations = FormatStyle::BTDS_Yes;
11083+
Style.ColumnLimit = 0;
11084+
verifyNoChange("template <typename Foo>\n"
11085+
"void foo() {}",
11086+
Style);
11087+
verifyNoChange("template <\n"
11088+
" typename Foo,\n"
11089+
" typename Bar>\n"
11090+
"void foo() {}",
11091+
Style);
11092+
// when BreakBeforeTemplateClose is off, this line break is removed:
11093+
verifyFormat("template <\n"
11094+
" typename Foo,\n"
11095+
" typename Bar>\n"
11096+
"void foo() {}",
11097+
"template <\n"
11098+
" typename Foo,\n"
11099+
" typename Bar\n"
11100+
">\n"
11101+
"void foo() {}",
11102+
Style);
11103+
Style.BreakBeforeTemplateClose = true;
11104+
// BreakBeforeTemplateClose should NOT force multiline templates
11105+
verifyNoChange("template <typename Foo>\n"
11106+
"void foo() {}",
11107+
Style);
11108+
verifyNoChange("template <typename Foo, typename Bar>\n"
11109+
"void foo() {}",
11110+
Style);
11111+
// it should allow a line break:
11112+
verifyNoChange("template <\n"
11113+
" typename Foo\n"
11114+
">\n"
11115+
"void foo() {}",
11116+
Style);
11117+
verifyNoChange("template <\n"
11118+
" typename Foo,\n"
11119+
" typename Bar\n"
11120+
">\n"
11121+
"void foo() {}",
11122+
Style);
11123+
// it should add a line break if not already present:
11124+
verifyFormat("template <\n"
11125+
" typename Foo\n"
11126+
">\n"
11127+
"void foo() {}",
11128+
"template <\n"
11129+
" typename Foo>\n"
11130+
"void foo() {}",
11131+
Style);
11132+
verifyFormat("template <\n"
11133+
" typename Foo,\n"
11134+
" typename Bar\n"
11135+
">\n"
11136+
"void foo() {}",
11137+
"template <\n"
11138+
" typename Foo,\n"
11139+
" typename Bar>\n"
11140+
"void foo() {}",
11141+
Style);
11142+
// when within an indent scope, the > should be placed appropriately:
11143+
verifyFormat("struct Baz {\n"
11144+
" template <\n"
11145+
" typename Foo,\n"
11146+
" typename Bar\n"
11147+
" >\n"
11148+
" void foo() {}\n"
11149+
"};",
11150+
"struct Baz {\n"
11151+
" template <\n"
11152+
" typename Foo,\n"
11153+
" typename Bar>\n"
11154+
" void foo() {}\n"
11155+
"};",
11156+
Style);
11157+
}
11158+
1108011159
TEST_F(FormatTest, WrapsTemplateParameters) {
1108111160
FormatStyle Style = getLLVMStyle();
1108211161
Style.AlignAfterOpenBracket = FormatStyle::BAS_DontAlign;

0 commit comments

Comments
 (0)