ksyx updated this revision to Diff 396358.
ksyx marked an inline comment as done.
ksyx added a comment.
- Change bool type of the option to enum type
- Generate empty line for enum block
Testfile:
// test.cpp
#include <algorithm>
#include <cstdio>
#include <cstring>
struct Foo {
int a,b,c;
};
namespace Ns {
class Bar {
public:
struct Foobar {
int a;
int b;
};
private:
int t;
int method1()
{
// Intentional test for different
// line breaking mode of brackets
}
template<typename T>
int method2(T x) {
// ...
}
enum Foo {
FOO,
BAR
};
int method3(int par) {}
};
class C {
};
}
namespace Ns2 {
}
Tests:
bin/clang-format test2.cpp # Test default (leave as it is)
bin/clang-format -style='{SeparateDefinitionBlocks: Between}' test2.cpp #
Test normal functionality
bin/clang-format -style='{SeparateDefinitionBlocks: Leave}' test2.cpp # Test
turning off
bin/clang-format -style='{SeparateDefinitionBlocks: Between, BasedOnStyle:
WebKit}' test2.cpp # Case: opening bracket { has its own line
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D116314/new/
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
@@ -450,6 +450,14 @@
}
};
+template <>
+struct ScalarEnumerationTraits<FormatStyle::SeparateDefinitionStyle> {
+ static void enumeration(IO &IO, FormatStyle::SeparateDefinitionStyle &Value) {
+ IO.enumCase(Value, "Leave", FormatStyle::SDS_Leave);
+ IO.enumCase(Value, "Between", FormatStyle::SDS_Between);
+ }
+};
+
template <>
struct ScalarEnumerationTraits<FormatStyle::SpaceBeforeParensStyle> {
static void enumeration(IO &IO, FormatStyle::SpaceBeforeParensStyle &Value) {
@@ -770,6 +778,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 +1202,7 @@
LLVMStyle.ObjCSpaceBeforeProtocolList = true;
LLVMStyle.PointerAlignment = FormatStyle::PAS_Right;
LLVMStyle.ReferenceAlignment = FormatStyle::RAS_Pointer;
+ LLVMStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Leave;
LLVMStyle.ShortNamespaceLines = 1;
LLVMStyle.SpacesBeforeTrailingComments = 1;
LLVMStyle.Standard = FormatStyle::LS_Latest;
@@ -1863,13 +1873,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 +2022,99 @@
}
};
+// 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, tok::kw_enum);
+ };
+ 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)) ||
+ TargetToken->is(tok::kw_enum))) {
+ 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 +3151,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,52 @@
bool ReflowComments;
// clang-format on
+ enum SeparateDefinitionStyle {
+ /// Leave definition blocks separated as they are
+ SDS_Leave,
+ /// Insert empty lines between definition blocks
+ SDS_Between
+ };
+
+ /// Specifies the use of empty lines to separate definition blocks, including
+ /// classes, structs, enum, and functions, for C++ language.
+ /// \code
+ /// SeparateDefinitions
+ /// Leave v.s. Between
+ /// #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
+ SeparateDefinitionStyle 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,53 @@
/* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of
* information */
+**SeparateDefinitionBlocks** (``SeparateDefinitionStyle``) :versionbadge:`clang-format 15`
+ Specifies the use of empty lines to separate definition blocks, including classes,
+ structs, enum, and functions, for C++ language.
+
+ Possible values:
+
+ * ``SDS_Leave`` (in configuration: ``Leave``)
+ Leave definition blocks separated as they are.
+
+ * ``SDS_Between`` (in configuration: ``Between``)
+ Insert empty lines between definition blocks.
+
+ .. code-block:: c++
+
+ SeparateDefinitions
+ Leave v.s. Between
+ #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