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
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to