[clang] [clang-format] Add new option: WrapNamespaceBodyWithNewlines (PR #106145)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 26 14:32:57 PDT 2024
https://github.com/dmasloff created https://github.com/llvm/llvm-project/pull/106145
I would like to suggest a new clang-format option for llvm-project - WrapNamespaceBodyWithNewlines. I think it can be added to upstream since it is used by many popular public repositories, for example, [ytsaurus](https://github.com/ytsaurus/ytsaurus/). You can look through their style guide at this page: https://github.com/ytsaurus/ytsaurus/blob/main/yt/styleguide/cpp.md#namespaces
As you can see from the name of the option it wraps the body of namespace with additional newlines turning this code:
```
namespace N {
int function();
}
```
into that:
```
namespace N {
int function();
}
```
Looking forward to your advices and recommendations
>From 84ba9930e4650319265b2fabd2715268a91de614 Mon Sep 17 00:00:00 2001
From: dmasloff <dmaslov24 at gmail.com>
Date: Mon, 26 Aug 2024 22:11:05 +0300
Subject: [PATCH] [clang-format] Add new option: WrapNamespaceBodyWithNewlines
---
clang/docs/ClangFormatStyleOptions.rst | 17 +++++
clang/include/clang/Format/Format.h | 18 ++++-
clang/lib/Format/Format.cpp | 3 +
clang/lib/Format/UnwrappedLineFormatter.cpp | 30 +++++++++
clang/unittests/Format/FormatTest.cpp | 73 +++++++++++++++++++++
5 files changed, 140 insertions(+), 1 deletion(-)
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index a427d7cd40fcdd..140787ad9776d9 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -6652,6 +6652,23 @@ the configuration (without a prefix: ``Auto``).
For example: BOOST_PP_STRINGIZE
+.. _WrapNamespaceBodyWithNewlines:
+
+**WrapNamespaceBodyWithNewlines** (``Boolean``) :versionbadge:`clang-format 19` :ref:`ΒΆ <WrapNamespaceBodyWithNewlines>`
+ Insert a newline at the begging and at the end of namespace definition
+
+ .. code-block:: c++
+
+ false: vs. true:
+
+ namespace a { namespace a {
+ namespace b { namespace b {
+ function();
+ } function();
+ }
+ }
+ }
+
.. END_FORMAT_STYLE_OPTIONS
Adding additional style options
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index d8b62c7652a0f6..eb8f198cc8a919 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -5057,6 +5057,21 @@ struct FormatStyle {
/// \version 11
std::vector<std::string> WhitespaceSensitiveMacros;
+ /// Insert a newline at the begging and at the end of namespace definition
+ /// \code
+ /// false: vs. true:
+ ///
+ /// namespace a { namespace a {
+ /// namespace b { namespace b {
+ /// function();
+ /// } function();
+ /// }
+ /// }
+ /// }
+ /// \endcode
+ /// \version 19
+ bool WrapNamespaceBodyWithNewlines;
+
bool operator==(const FormatStyle &R) const {
return AccessModifierOffset == R.AccessModifierOffset &&
AlignAfterOpenBracket == R.AlignAfterOpenBracket &&
@@ -5234,7 +5249,8 @@ struct FormatStyle {
TypenameMacros == R.TypenameMacros && UseTab == R.UseTab &&
VerilogBreakBetweenInstancePorts ==
R.VerilogBreakBetweenInstancePorts &&
- WhitespaceSensitiveMacros == R.WhitespaceSensitiveMacros;
+ WhitespaceSensitiveMacros == R.WhitespaceSensitiveMacros &&
+ WrapNamespaceBodyWithNewlines == R.WrapNamespaceBodyWithNewlines;
}
std::optional<FormatStyle> GetLanguageStyle(LanguageKind Language) const;
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index d2463b892fbb96..65d7737b397212 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -1154,6 +1154,8 @@ template <> struct MappingTraits<FormatStyle> {
Style.VerilogBreakBetweenInstancePorts);
IO.mapOptional("WhitespaceSensitiveMacros",
Style.WhitespaceSensitiveMacros);
+ IO.mapOptional("WrapNamespaceBodyWithNewlines",
+ Style.WrapNamespaceBodyWithNewlines);
// If AlwaysBreakAfterDefinitionReturnType was specified but
// BreakAfterReturnType was not, initialize the latter from the former for
@@ -1623,6 +1625,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.WhitespaceSensitiveMacros.push_back("NS_SWIFT_NAME");
LLVMStyle.WhitespaceSensitiveMacros.push_back("PP_STRINGIZE");
LLVMStyle.WhitespaceSensitiveMacros.push_back("STRINGIZE");
+ LLVMStyle.WrapNamespaceBodyWithNewlines = false;
LLVMStyle.PenaltyBreakAssignment = prec::Assignment;
LLVMStyle.PenaltyBreakBeforeFirstCallParameter = 19;
diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp
index 1804c1437fd41d..f3f76ac227df50 100644
--- a/clang/lib/Format/UnwrappedLineFormatter.cpp
+++ b/clang/lib/Format/UnwrappedLineFormatter.cpp
@@ -32,6 +32,24 @@ bool isRecordLBrace(const FormatToken &Tok) {
TT_StructLBrace, TT_UnionLBrace);
}
+bool LineStartsNamespaceScope(const AnnotatedLine* Line,
+ const AnnotatedLine* PreviousLine,
+ const AnnotatedLine* PrevPrevLine) {
+ return PreviousLine && PreviousLine->Last->is(tok::l_brace) &&
+ (PreviousLine->startsWithNamespace() ||
+ (PrevPrevLine && PrevPrevLine->startsWithNamespace() &&
+ PreviousLine->startsWith(tok::l_brace))) && !Line->startsWithNamespace();
+}
+
+bool LineEndsNamespaceScope(const AnnotatedLine *Line,
+ const SmallVectorImpl<AnnotatedLine*> &Lines) {
+ const FormatToken* tok = Line->First;
+ if (!tok || tok->isNot(tok::r_brace)) {
+ return false;
+ }
+ return getNamespaceToken(Line, Lines) != nullptr;
+}
+
/// Tracks the indent level of \c AnnotatedLines across levels.
///
/// \c nextLine must be called for each \c AnnotatedLine, after which \c
@@ -1493,6 +1511,18 @@ static auto computeNewlines(const AnnotatedLine &Line,
Newlines = 1;
}
+ // Insert empty line after "{" that opens namespace scope
+ if (Style.WrapNamespaceBodyWithNewlines &&
+ LineStartsNamespaceScope(&Line, PreviousLine, PrevPrevLine)) {
+ Newlines = 2;
+ }
+
+ // Insert empty line before "}" that closes namespace scope
+ if (Style.WrapNamespaceBodyWithNewlines &&
+ LineEndsNamespaceScope(&Line, Lines) && !LineEndsNamespaceScope(PreviousLine, Lines)) {
+ Newlines = std::max(Newlines, 2u);
+ }
+
// Insert or remove empty line before access specifiers.
if (PreviousLine && RootToken.isAccessSpecifier()) {
switch (Style.EmptyLineBeforeAccessModifier) {
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index a383a624434b1f..94d8d652f80046 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -28084,6 +28084,79 @@ TEST_F(FormatTest, BreakBinaryOperations) {
Style);
}
+TEST_F(FormatTest, WrappingNamespaceBodyWithNewlines) {
+ FormatStyle Style = getLLVMStyle();
+ Style.FixNamespaceComments = false;
+ Style.ShortNamespaceLines = 0;
+ Style.WrapNamespaceBodyWithNewlines = true;
+
+ // Empty namespace
+ verifyFormat("namespace N {};", Style);
+
+ // Single namespace
+ verifyFormat("namespace N {\n\n"
+ "int f1(int a) { return 2 * a; }\n\n"
+ "};", Style);
+
+ // Nested namespace
+ verifyFormat("namespace N1 {\n"
+ "namespace N2 {\n"
+ "namespace N3 {\n\n"
+ "int f1() {\n"
+ " int a = 1;\n"
+ " return a;\n"
+ "}\n\n"
+ "}\n"
+ "}\n"
+ "}", Style);
+
+ Style.WrapNamespaceBodyWithNewlines = false;
+
+ // Empty namespace
+ verifyFormat("namespace N {};", Style);
+
+ // Single namespace
+ verifyFormat("namespace N {\n"
+ "int f1(int a) { return 2 * a; }\n"
+ "};", Style);
+
+ // Nested namespace
+ verifyFormat("namespace N1 {\n"
+ "namespace N2 {\n"
+ "namespace N3 {\n"
+ "int f1() {\n"
+ " int a = 1;\n"
+ " return a;\n"
+ "}\n"
+ "}\n"
+ "}\n"
+ "}", Style);
+}
+
+TEST_F(FormatTest, WrappingNamespaceBodyWithNewlinesWithCompactNamespaces) {
+ FormatStyle Style = getLLVMStyle();
+ Style.FixNamespaceComments = false;
+ Style.CompactNamespaces = true;
+ Style.WrapNamespaceBodyWithNewlines = true;
+
+
+ verifyFormat("namespace N1 { namespace N2 { namespace N3 {\n\n"
+ "int f1() {\n"
+ " int a = 1;\n"
+ " return a;\n"
+ "}\n\n"
+ "}}}", Style);
+
+ Style.WrapNamespaceBodyWithNewlines = false;
+
+ verifyFormat("namespace N1 { namespace N2 { namespace N3 {\n"
+ "int f1() {\n"
+ " int a = 1;\n"
+ " return a;\n"
+ "}\n"
+ "}}}", Style);
+}
+
} // namespace
} // namespace test
} // namespace format
More information about the cfe-commits
mailing list