Skip to content

Commit 1c997fe

Browse files
dmasloffowenca
andauthored
[clang-format] Add option WrapNamespaceBodyWithNewlines (#106145)
It wraps the body of namespace with additional newlines, turning this code: ``` namespace N { int function(); } ``` into the following: ``` namespace N { int function(); } ``` --------- Co-authored-by: Owen Pan <[email protected]>
1 parent b6c06d1 commit 1c997fe

File tree

7 files changed

+244
-1
lines changed

7 files changed

+244
-1
lines changed

clang/docs/ClangFormatStyleOptions.rst

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6843,6 +6843,45 @@ the configuration (without a prefix: ``Auto``).
68436843
68446844
For example: BOOST_PP_STRINGIZE
68456845

6846+
.. _WrapNamespaceBodyWithEmptyLines:
6847+
6848+
**WrapNamespaceBodyWithEmptyLines** (``WrapNamespaceBodyWithEmptyLinesStyle``) :versionbadge:`clang-format 20` :ref:`<WrapNamespaceBodyWithEmptyLines>`
6849+
Wrap namespace body with empty lines.
6850+
6851+
Possible values:
6852+
6853+
* ``WNBWELS_Never`` (in configuration: ``Never``)
6854+
Remove all empty lines at the beginning and the end of namespace body.
6855+
6856+
.. code-block:: c++
6857+
6858+
namespace N1 {
6859+
namespace N2
6860+
function();
6861+
}
6862+
}
6863+
6864+
* ``WNBWELS_Always`` (in configuration: ``Always``)
6865+
Always have at least one empty line at the beginning and the end of
6866+
namespace body except that the number of empty lines between consecutive
6867+
nested namespace definitions is not increased.
6868+
6869+
.. code-block:: c++
6870+
6871+
namespace N1 {
6872+
namespace N2 {
6873+
6874+
function();
6875+
6876+
}
6877+
}
6878+
6879+
* ``WNBWELS_Leave`` (in configuration: ``Leave``)
6880+
Keep existing newlines at the beginning and the end of namespace body.
6881+
``MaxEmptyLinesToKeep`` still applies.
6882+
6883+
6884+
68466885
.. END_FORMAT_STYLE_OPTIONS
68476886
68486887
Adding additional style options

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1127,6 +1127,7 @@ clang-format
11271127
- Adds ``AllowShortNamespacesOnASingleLine`` option.
11281128
- Adds ``VariableTemplates`` option.
11291129
- Adds support for bash globstar in ``.clang-format-ignore``.
1130+
- Adds ``WrapNamespaceBodyWithEmptyLines`` option.
11301131

11311132
libclang
11321133
--------

clang/include/clang/Format/Format.h

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5143,6 +5143,39 @@ struct FormatStyle {
51435143
/// \version 11
51445144
std::vector<std::string> WhitespaceSensitiveMacros;
51455145

5146+
/// Different styles for wrapping namespace body with empty lines.
5147+
enum WrapNamespaceBodyWithEmptyLinesStyle : int8_t {
5148+
/// Remove all empty lines at the beginning and the end of namespace body.
5149+
/// \code
5150+
/// namespace N1 {
5151+
/// namespace N2
5152+
/// function();
5153+
/// }
5154+
/// }
5155+
/// \endcode
5156+
WNBWELS_Never,
5157+
/// Always have at least one empty line at the beginning and the end of
5158+
/// namespace body except that the number of empty lines between consecutive
5159+
/// nested namespace definitions is not increased.
5160+
/// \code
5161+
/// namespace N1 {
5162+
/// namespace N2 {
5163+
///
5164+
/// function();
5165+
///
5166+
/// }
5167+
/// }
5168+
/// \endcode
5169+
WNBWELS_Always,
5170+
/// Keep existing newlines at the beginning and the end of namespace body.
5171+
/// ``MaxEmptyLinesToKeep`` still applies.
5172+
WNBWELS_Leave
5173+
};
5174+
5175+
/// Wrap namespace body with empty lines.
5176+
/// \version 20
5177+
WrapNamespaceBodyWithEmptyLinesStyle WrapNamespaceBodyWithEmptyLines;
5178+
51465179
bool operator==(const FormatStyle &R) const {
51475180
return AccessModifierOffset == R.AccessModifierOffset &&
51485181
AlignAfterOpenBracket == R.AlignAfterOpenBracket &&
@@ -5326,7 +5359,8 @@ struct FormatStyle {
53265359
UseTab == R.UseTab && VariableTemplates == R.VariableTemplates &&
53275360
VerilogBreakBetweenInstancePorts ==
53285361
R.VerilogBreakBetweenInstancePorts &&
5329-
WhitespaceSensitiveMacros == R.WhitespaceSensitiveMacros;
5362+
WhitespaceSensitiveMacros == R.WhitespaceSensitiveMacros &&
5363+
WrapNamespaceBodyWithEmptyLines == R.WrapNamespaceBodyWithEmptyLines;
53305364
}
53315365

53325366
std::optional<FormatStyle> GetLanguageStyle(LanguageKind Language) const;

clang/lib/Format/Format.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,18 @@ template <> struct ScalarEnumerationTraits<FormatStyle::UseTabStyle> {
839839
}
840840
};
841841

842+
template <>
843+
struct ScalarEnumerationTraits<
844+
FormatStyle::WrapNamespaceBodyWithEmptyLinesStyle> {
845+
static void
846+
enumeration(IO &IO,
847+
FormatStyle::WrapNamespaceBodyWithEmptyLinesStyle &Value) {
848+
IO.enumCase(Value, "Never", FormatStyle::WNBWELS_Never);
849+
IO.enumCase(Value, "Always", FormatStyle::WNBWELS_Always);
850+
IO.enumCase(Value, "Leave", FormatStyle::WNBWELS_Leave);
851+
}
852+
};
853+
842854
template <> struct MappingTraits<FormatStyle> {
843855
static void mapping(IO &IO, FormatStyle &Style) {
844856
// When reading, read the language first, we need it for getPredefinedStyle.
@@ -1171,6 +1183,8 @@ template <> struct MappingTraits<FormatStyle> {
11711183
Style.VerilogBreakBetweenInstancePorts);
11721184
IO.mapOptional("WhitespaceSensitiveMacros",
11731185
Style.WhitespaceSensitiveMacros);
1186+
IO.mapOptional("WrapNamespaceBodyWithEmptyLines",
1187+
Style.WrapNamespaceBodyWithEmptyLines);
11741188

11751189
// If AlwaysBreakAfterDefinitionReturnType was specified but
11761190
// BreakAfterReturnType was not, initialize the latter from the former for
@@ -1639,6 +1653,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
16391653
LLVMStyle.WhitespaceSensitiveMacros.push_back("NS_SWIFT_NAME");
16401654
LLVMStyle.WhitespaceSensitiveMacros.push_back("PP_STRINGIZE");
16411655
LLVMStyle.WhitespaceSensitiveMacros.push_back("STRINGIZE");
1656+
LLVMStyle.WrapNamespaceBodyWithEmptyLines = FormatStyle::WNBWELS_Leave;
16421657

16431658
LLVMStyle.PenaltyBreakAssignment = prec::Assignment;
16441659
LLVMStyle.PenaltyBreakBeforeFirstCallParameter = 19;

clang/lib/Format/UnwrappedLineFormatter.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1584,6 +1584,23 @@ static auto computeNewlines(const AnnotatedLine &Line,
15841584
Newlines = 1;
15851585
}
15861586

1587+
if (Style.WrapNamespaceBodyWithEmptyLines != FormatStyle::WNBWELS_Leave) {
1588+
// Modify empty lines after TT_NamespaceLBrace.
1589+
if (PreviousLine && PreviousLine->endsWith(TT_NamespaceLBrace)) {
1590+
if (Style.WrapNamespaceBodyWithEmptyLines == FormatStyle::WNBWELS_Never)
1591+
Newlines = 1;
1592+
else if (!Line.startsWithNamespace())
1593+
Newlines = std::max(Newlines, 2u);
1594+
}
1595+
// Modify empty lines before TT_NamespaceRBrace.
1596+
if (Line.startsWith(TT_NamespaceRBrace)) {
1597+
if (Style.WrapNamespaceBodyWithEmptyLines == FormatStyle::WNBWELS_Never)
1598+
Newlines = 1;
1599+
else if (!PreviousLine->startsWith(TT_NamespaceRBrace))
1600+
Newlines = std::max(Newlines, 2u);
1601+
}
1602+
}
1603+
15871604
// Insert or remove empty line before access specifiers.
15881605
if (PreviousLine && RootToken.isAccessSpecifier()) {
15891606
switch (Style.EmptyLineBeforeAccessModifier) {

clang/unittests/Format/ConfigParseTest.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,13 @@ TEST(ConfigParseTest, ParsesConfiguration) {
865865
CHECK_PARSE("SortUsingDeclarations: true", SortUsingDeclarations,
866866
FormatStyle::SUD_LexicographicNumeric);
867867

868+
CHECK_PARSE("WrapNamespaceBodyWithEmptyLines: Never",
869+
WrapNamespaceBodyWithEmptyLines, FormatStyle::WNBWELS_Never);
870+
CHECK_PARSE("WrapNamespaceBodyWithEmptyLines: Always",
871+
WrapNamespaceBodyWithEmptyLines, FormatStyle::WNBWELS_Always);
872+
CHECK_PARSE("WrapNamespaceBodyWithEmptyLines: Leave",
873+
WrapNamespaceBodyWithEmptyLines, FormatStyle::WNBWELS_Leave);
874+
868875
// FIXME: This is required because parsing a configuration simply overwrites
869876
// the first N elements of the list instead of resetting it.
870877
Style.ForEachMacros.clear();

clang/unittests/Format/FormatTest.cpp

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28427,6 +28427,136 @@ TEST_F(FormatTest, ShortNamespacesOption) {
2842728427
Style);
2842828428
}
2842928429

28430+
TEST_F(FormatTest, WrapNamespaceBodyWithEmptyLinesNever) {
28431+
auto Style = getLLVMStyle();
28432+
Style.FixNamespaceComments = false;
28433+
Style.MaxEmptyLinesToKeep = 2;
28434+
Style.WrapNamespaceBodyWithEmptyLines = FormatStyle::WNBWELS_Never;
28435+
28436+
// Empty namespace.
28437+
verifyFormat("namespace N {}", Style);
28438+
28439+
// Single namespace.
28440+
verifyFormat("namespace N {\n"
28441+
"int f1(int a) { return 2 * a; }\n"
28442+
"}",
28443+
"namespace N {\n"
28444+
"\n"
28445+
"\n"
28446+
"int f1(int a) { return 2 * a; }\n"
28447+
"\n"
28448+
"\n"
28449+
"}",
28450+
Style);
28451+
28452+
// Nested namespace.
28453+
verifyFormat("namespace N1 {\n"
28454+
"namespace N2 {\n"
28455+
"int a = 1;\n"
28456+
"}\n"
28457+
"}",
28458+
"namespace N1 {\n"
28459+
"\n"
28460+
"\n"
28461+
"namespace N2 {\n"
28462+
"\n"
28463+
"int a = 1;\n"
28464+
"\n"
28465+
"}\n"
28466+
"\n"
28467+
"\n"
28468+
"}",
28469+
Style);
28470+
28471+
Style.CompactNamespaces = true;
28472+
28473+
verifyFormat("namespace N1 { namespace N2 {\n"
28474+
"int a = 1;\n"
28475+
"}}",
28476+
"namespace N1 { namespace N2 {\n"
28477+
"\n"
28478+
"\n"
28479+
"int a = 1;\n"
28480+
"\n"
28481+
"\n"
28482+
"}}",
28483+
Style);
28484+
}
28485+
28486+
TEST_F(FormatTest, WrapNamespaceBodyWithEmptyLinesAlways) {
28487+
auto Style = getLLVMStyle();
28488+
Style.FixNamespaceComments = false;
28489+
Style.MaxEmptyLinesToKeep = 2;
28490+
Style.WrapNamespaceBodyWithEmptyLines = FormatStyle::WNBWELS_Always;
28491+
28492+
// Empty namespace.
28493+
verifyFormat("namespace N {}", Style);
28494+
28495+
// Single namespace.
28496+
verifyFormat("namespace N {\n"
28497+
"\n"
28498+
"int f1(int a) { return 2 * a; }\n"
28499+
"\n"
28500+
"}",
28501+
"namespace N {\n"
28502+
"int f1(int a) { return 2 * a; }\n"
28503+
"}",
28504+
Style);
28505+
28506+
// Nested namespace.
28507+
verifyFormat("namespace N1 {\n"
28508+
"namespace N2 {\n"
28509+
"\n"
28510+
"int a = 1;\n"
28511+
"\n"
28512+
"}\n"
28513+
"}",
28514+
"namespace N1 {\n"
28515+
"namespace N2 {\n"
28516+
"int a = 1;\n"
28517+
"}\n"
28518+
"}",
28519+
Style);
28520+
28521+
verifyFormat("namespace N1 {\n"
28522+
"\n"
28523+
"namespace N2 {\n"
28524+
"\n"
28525+
"\n"
28526+
"int a = 1;\n"
28527+
"\n"
28528+
"\n"
28529+
"}\n"
28530+
"\n"
28531+
"}",
28532+
"namespace N1 {\n"
28533+
"\n"
28534+
"namespace N2 {\n"
28535+
"\n"
28536+
"\n"
28537+
"\n"
28538+
"int a = 1;\n"
28539+
"\n"
28540+
"\n"
28541+
"\n"
28542+
"}\n"
28543+
"\n"
28544+
"}",
28545+
Style);
28546+
28547+
Style.CompactNamespaces = true;
28548+
28549+
verifyFormat("namespace N1 { namespace N2 {\n"
28550+
"\n"
28551+
"int a = 1;\n"
28552+
"\n"
28553+
"}}",
28554+
"namespace N1 { namespace N2 {\n"
28555+
"int a = 1;\n"
28556+
"}}",
28557+
Style);
28558+
}
28559+
2843028560
} // namespace
2843128561
} // namespace test
2843228562
} // namespace format

0 commit comments

Comments
 (0)