ksyx created this revision.
ksyx added a reviewer: MyDeveloperDay.
ksyx added projects: clang-format, clang.
ksyx requested review of this revision.
Herald added a subscriber: cfe-commits.
This commit resolves GitHub issue #45895
<https://github.com/llvm/llvm-project/issues/45895> (Bugzilla #46550
<https://llvm.org/bz46550>), to add empty line between definition blocks
including namespace, class, and struct.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D116314
Files:
clang/docs/ClangFormatStyleOptions.rst
clang/include/clang/Format/Format.h
clang/lib/Format/Format.cpp
Index: clang/lib/Format/Format.cpp
===================================================================
--- clang/lib/Format/Format.cpp
+++ clang/lib/Format/Format.cpp
@@ -770,6 +770,7 @@
IO.mapOptional("ReferenceAlignment", Style.ReferenceAlignment);
IO.mapOptional("ReflowComments", Style.ReflowComments);
IO.mapOptional("ShortNamespaceLines", Style.ShortNamespaceLines);
+ IO.mapOptional("SeparateDefinitionBlocks", Style.SeparateDefinitionBlocks);
IO.mapOptional("SortIncludes", Style.SortIncludes);
IO.mapOptional("SortJavaStaticImport", Style.SortJavaStaticImport);
IO.mapOptional("SortUsingDeclarations", Style.SortUsingDeclarations);
@@ -1193,6 +1194,7 @@
LLVMStyle.ObjCSpaceBeforeProtocolList = true;
LLVMStyle.PointerAlignment = FormatStyle::PAS_Right;
LLVMStyle.ReferenceAlignment = FormatStyle::RAS_Pointer;
+ LLVMStyle.SeparateDefinitionBlocks = true;
LLVMStyle.ShortNamespaceLines = 1;
LLVMStyle.SpacesBeforeTrailingComments = 1;
LLVMStyle.Standard = FormatStyle::LS_Latest;
@@ -1333,6 +1335,7 @@
/*BasedOnStyle=*/"google",
},
};
+ GoogleStyle.SeparateDefinitionBlocks = false;
GoogleStyle.SpacesBeforeTrailingComments = 2;
GoogleStyle.Standard = FormatStyle::LS_Auto;
@@ -1863,13 +1866,13 @@
return std::make_pair(Result, Penalty);
}
-private:
static bool inputUsesCRLF(StringRef Text, bool DefaultToCRLF) {
size_t LF = Text.count('\n');
size_t CR = Text.count('\r') * 2;
return LF == CR ? DefaultToCRLF : CR > LF;
}
+private:
bool
hasCpp03IncompatibleFormat(const SmallVectorImpl<AnnotatedLine *> &Lines) {
for (const AnnotatedLine *Line : Lines) {
@@ -2012,6 +2015,98 @@
}
};
+// This class separates definition blocks like classes, functions, and
+// namespaces by inserting a line break between them.
+class DefinitionBlockSeparator : public TokenAnalyzer {
+public:
+ DefinitionBlockSeparator(const Environment &Env, const FormatStyle &Style)
+ : TokenAnalyzer(Env, Style) {}
+
+ std::pair<tooling::Replacements, unsigned>
+ analyze(TokenAnnotator &Annotator,
+ SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
+ FormatTokenLexer &Tokens) override {
+ AffectedRangeMgr.computeAffectedLines(AnnotatedLines);
+ tooling::Replacements Result;
+ separateBlocks(AnnotatedLines, Result);
+ return {Result, 0};
+ }
+
+private:
+ void separateBlocks(SmallVectorImpl<AnnotatedLine *> &Lines,
+ tooling::Replacements &Result) {
+ auto likelyDefinition = [](AnnotatedLine *Line) {
+ return (Line->MightBeFunctionDecl && Line->mightBeFunctionDefinition()) ||
+ Line->First->isOneOf(tok::kw_class, tok::kw_struct,
+ tok::kw_namespace);
+ };
+ WhitespaceManager Whitespaces(
+ Env.getSourceManager(), Style,
+ Style.DeriveLineEnding
+ ? Formatter::inputUsesCRLF(
+ Env.getSourceManager().getBufferData(Env.getFileID()),
+ Style.UseCRLF)
+ : Style.UseCRLF);
+ for (unsigned I = 0; I < Lines.size(); I++) {
+ auto Line = Lines[I];
+ if (Line->First->closesScope()) {
+ auto OpeningLineIndex = Line->MatchingOpeningBlockLineIndex;
+ // Case: Opening bracket has its own line
+ if (OpeningLineIndex > 0 &&
+ Lines[OpeningLineIndex]->First->TokenText == "{") {
+ OpeningLineIndex--;
+ }
+ AnnotatedLine *OpeningLine = Lines[OpeningLineIndex];
+ // Closing a function definition
+ if (likelyDefinition(OpeningLine)) {
+ FormatToken *TargetToken = nullptr;
+ AnnotatedLine *TargetLine;
+ auto insertReplacement = [&]() {
+ assert(TargetToken);
+ Whitespaces.replaceWhitespace(*TargetToken, 2,
+ TargetToken->SpacesRequiredBefore,
+ TargetToken->StartsColumn);
+ };
+
+ // Not the first token
+ if (OpeningLineIndex > 0) {
+ TargetLine = Lines[OpeningLineIndex - 1];
+ // Not immediately following other scopes' opening
+ if (TargetLine->Affected && !TargetLine->Last->opensScope()) {
+ // Change target, since we need to put line break *before*
+ // a token
+ TargetLine = OpeningLine;
+ TargetToken = TargetLine->First;
+
+ // Avoid duplicated replacement
+ if (TargetToken && !TargetToken->opensScope()) {
+ insertReplacement();
+ }
+ }
+ }
+
+ // Not the last token
+ if (I + 1 < Lines.size()) {
+ TargetLine = Lines[I + 1];
+ TargetToken = TargetLine->First;
+
+ // Not continuously closing scopes (e.g. function + class +
+ // namespace); The token will be handled in another case if
+ // it is a definition line
+ if (TargetLine->Affected && !TargetToken->closesScope() &&
+ !likelyDefinition(TargetLine)) {
+ insertReplacement();
+ }
+ }
+ }
+ }
+ }
+ for (const auto &R : Whitespaces.generateReplacements())
+ if (Result.add(R))
+ return;
+ }
+};
+
// This class clean up the erroneous/redundant code around the given ranges in
// file.
class Cleaner : public TokenAnalyzer {
@@ -3048,6 +3143,11 @@
Passes.emplace_back([&](const Environment &Env) {
return UsingDeclarationsSorter(Env, Expanded).process();
});
+
+ if (Style.SeparateDefinitionBlocks)
+ Passes.emplace_back([&](const Environment &Env) {
+ return DefinitionBlockSeparator(Env, Expanded).process();
+ });
}
if (Style.isJavaScript() && Style.JavaScriptQuotes != FormatStyle::JSQS_Leave)
Index: clang/include/clang/Format/Format.h
===================================================================
--- clang/include/clang/Format/Format.h
+++ clang/include/clang/Format/Format.h
@@ -3050,6 +3050,45 @@
bool ReflowComments;
// clang-format on
+ /// If ``true``, clang-format will insert empty lines between definition blocks
+ /// for C++ language.
+ /// \code
+ /// SeparateDefinitions
+ /// true v.s. false
+ /// #include <cstring> #include <cstring>
+ /// struct Foo{
+ /// int a,b,c; struct Foo {
+ /// }; int a, b, c;
+ /// namespace Ns { };
+ /// class Bar {
+ /// public: namespace Ns {
+ /// struct Foobar { class Bar {
+ /// int a; public:
+ /// int b; struct Foobar {
+ /// }; int a;
+ /// private: int b;
+ /// int t; };
+ /// int method1() {
+ /// // ... private:
+ /// } int t;
+ /// template<typename T>
+ /// int method2(T x) { int method1() {
+ /// // ... // ...
+ /// } }
+ /// int method3(int par) {}
+ /// }; template <typename T> int method2(T x) {
+ /// class C { // ...
+ /// } }
+ /// }
+ /// int method3(int par) {}
+ /// };
+ ///
+ /// class C {}
+ /// } // namespace Ns
+ /// \endcode
+ /// \version 15
+ bool SeparateDefinitionBlocks;
+
/// The maximal number of unwrapped lines that a short namespace spans.
/// Defaults to 1.
///
Index: clang/docs/ClangFormatStyleOptions.rst
===================================================================
--- clang/docs/ClangFormatStyleOptions.rst
+++ clang/docs/ClangFormatStyleOptions.rst
@@ -3395,6 +3395,45 @@
/* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of
* information */
+**SeparateDefinitionBlocks** (``Boolean``) :versionbadge:`calng-format 15`
+ If ``true``, clang-format will insert empty lines between definition blocks
+ for C++ language.
+
+ .. code-block:: c++
+
+ SeparateDefinitions
+ true v.s. false
+ #include <cstring> #include <cstring>
+ struct Foo{
+ int a,b,c; struct Foo {
+ }; int a, b, c;
+ namespace Ns { };
+ class Bar {
+ public: namespace Ns {
+ struct Foobar { class Bar {
+ int a; public:
+ int b; struct Foobar {
+ }; int a;
+ private: int b;
+ int t; };
+ int method1() {
+ // ... private:
+ } int t;
+ template<typename T>
+ int method2(T x) { int method1() {
+ // ... // ...
+ } }
+ int method3(int par) {}
+ }; template <typename T> int method2(T x) {
+ class C { // ...
+ } }
+ }
+ int method3(int par) {}
+ };
+
+ class C {}
+ } // namespace Ns
+
**ShortNamespaceLines** (``Unsigned``) :versionbadge:`clang-format 14`
The maximal number of unwrapped lines that a short namespace spans.
Defaults to 1.
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits