Typz updated this revision to Diff 99421.
Typz added a comment.

clang-format: Add CompactNamespaces option

- Change option name to CompactNamespaces
- Clarify & test behavior when wrapping is needed
- Separate from the 'remove semicolon' patch


https://reviews.llvm.org/D32480

Files:
  include/clang/Format/Format.h
  lib/Format/Format.cpp
  lib/Format/NamespaceEndCommentsFixer.cpp
  lib/Format/UnwrappedLineFormatter.cpp
  unittests/Format/FormatTest.cpp

Index: unittests/Format/FormatTest.cpp
===================================================================
--- unittests/Format/FormatTest.cpp
+++ unittests/Format/FormatTest.cpp
@@ -1306,6 +1306,61 @@
                    "} // namespace in\n"
                    "} // namespace out",
                    Style));
+
+  FormatStyle LLVMWithCompactNamespaces = getLLVMStyle();
+  LLVMWithCompactNamespaces.CompactNamespaces = true;
+
+  EXPECT_EQ("namespace out { namespace in {\n"
+            "}} // namespace out::in",
+            format("namespace out {\n"
+                   "namespace in {\n"
+                   "} // namespace in\n"
+                   "} // namespace out",
+                   LLVMWithCompactNamespaces));
+
+  EXPECT_EQ("namespace out { namespace in1 {\n"
+            "} // namespace in1\n"
+            "namespace in2 {\n"
+            "}} // namespace out::in2",
+            format("namespace out {\n"
+                   "namespace in1 {\n"
+                   "} // namespace in1\n"
+                   "namespace in2 {\n"
+                   "} // namespace in2\n"
+                   "} // namespace out",
+                   LLVMWithCompactNamespaces));
+
+  EXPECT_EQ("namespace out {\n"
+            "int i;\n"
+            "namespace in {\n"
+            "int j;\n"
+            "} // namespace in\n"
+            "int k;\n"
+            "} // namespace out",
+            format("namespace out { int i;\n"
+                   "namespace in { int j; } // namespace in\n"
+                   "int k; } // namespace out",
+                   LLVMWithCompactNamespaces));
+
+  EXPECT_EQ("namespace aaaaaaaaaaaaaaaaaaaaaaaaaaaa {\n"
+			"namespace bbbbbbbbbbbbbbbbbbbbbbbbbbbb {\n"
+            "}} // namespace aaaaaaaaaaaaaaaaaaaaaaaaaaaa::bbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+            format("namespace aaaaaaaaaaaaaaaaaaaaaaaaaaaa {\n"
+                   "namespace bbbbbbbbbbbbbbbbbbbbbbbbbbbb {\n"
+                   "} // namespace bbbbbbbbbbbbbbbbbbbbbbbbbbbb\n"
+                   "} // namespace aaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+                   LLVMWithCompactNamespaces));
+
+  EXPECT_EQ("namespace aaaaaaaaaaaaaaaaa { namespace bbbbbbbbbbbbbbbbb {\n"
+			"namespace ccccccccccccccccc {\n"
+            "}}} // namespace aaaaaaaaaaaaaaaaa::bbbbbbbbbbbbbbbbb::ccccccccccccccccc",
+            format("namespace aaaaaaaaaaaaaaaaa {\n"
+                   "namespace bbbbbbbbbbbbbbbbb {\n"
+                   "namespace ccccccccccccccccc {\n"
+                   "} // namespace ccccccccccccccccc\n"
+                   "} // namespace bbbbbbbbbbbbbbbbb\n"
+                   "} // namespace aaaaaaaaaaaaaaaaa",
+                   LLVMWithCompactNamespaces));
 }
 
 TEST_F(FormatTest, FormatsExternC) { verifyFormat("extern \"C\" {\nint a;"); }
@@ -8683,6 +8738,7 @@
   CHECK_PARSE_BOOL(BreakConstructorInitializersBeforeComma);
   CHECK_PARSE_BOOL(BreakStringLiterals);
   CHECK_PARSE_BOOL(BreakBeforeInheritanceComma)
+  CHECK_PARSE_BOOL(CompactNamespaces);
   CHECK_PARSE_BOOL(ConstructorInitializerAllOnOneLineOrOnePerLine);
   CHECK_PARSE_BOOL(DerivePointerAlignment);
   CHECK_PARSE_BOOL_FIELD(DerivePointerAlignment, "DerivePointerBinding");
Index: lib/Format/UnwrappedLineFormatter.cpp
===================================================================
--- lib/Format/UnwrappedLineFormatter.cpp
+++ lib/Format/UnwrappedLineFormatter.cpp
@@ -127,12 +127,33 @@
   unsigned Indent = 0;
 };
 
+bool isNamespaceToken(const FormatToken *NamespaceTok) {
+  // Detect "(inline)? namespace" in the beginning of a line.
+  if (NamespaceTok->is(tok::kw_inline))
+    NamespaceTok = NamespaceTok->getNextNonComment();
+  if (!NamespaceTok || NamespaceTok->isNot(tok::kw_namespace))
+    return false;
+  return true;
+}
+
+bool isEndOfNamespace(const AnnotatedLine *line,
+                      const SmallVectorImpl<AnnotatedLine *> &AnnotatedLines) {
+  if (!line->startsWith(tok::r_brace))
+    return false;
+  size_t StartLineIndex = line->MatchingOpeningBlockLineIndex;
+  if (StartLineIndex == UnwrappedLine::kInvalidIndex)
+    return false;
+  assert(StartLineIndex < AnnotatedLines.size());
+  const FormatToken *NamespaceTok = AnnotatedLines[StartLineIndex]->First;
+  return isNamespaceToken(NamespaceTok);
+}
+
 class LineJoiner {
 public:
   LineJoiner(const FormatStyle &Style, const AdditionalKeywords &Keywords,
              const SmallVectorImpl<AnnotatedLine *> &Lines)
       : Style(Style), Keywords(Keywords), End(Lines.end()),
-        Next(Lines.begin()) {}
+        Next(Lines.begin()), AnnotatedLines(Lines) {}
 
   /// \brief Returns the next line, merging multiple lines into one if possible.
   const AnnotatedLine *getNextMergedLine(bool DryRun,
@@ -195,6 +216,28 @@
         (Style.AllowShortFunctionsOnASingleLine == FormatStyle::SFS_Inline &&
          TheLine->Level != 0);
 
+    if (Style.CompactNamespaces) {
+      if (isNamespaceToken(TheLine->First)) {
+        int i = 0;
+        while (I + 1 + i != E && isNamespaceToken(I[i + 1]->First) &&
+            I[i + 1]->Last->TotalLength < Limit) {
+          Limit -= I[i + 1]->Last->TotalLength;
+          i++;
+        }
+        return i;
+      }
+
+      if (isEndOfNamespace(TheLine, AnnotatedLines)) {
+        int i = 0;
+        while (I + 1 + i != E && isEndOfNamespace(I[i + 1], AnnotatedLines)) {
+          // No space between consecutive braces
+          I[i + 1]->First->SpacesRequiredBefore = !I[i]->Last->is(tok::r_brace);
+          i++;
+        }
+        return i;
+      }
+    }
+
     if (TheLine->Last->is(TT_FunctionLBrace) &&
         TheLine->First != TheLine->Last) {
       return MergeShortFunctions ? tryMergeSimpleBlock(I, E, Limit) : 0;
@@ -449,6 +492,7 @@
   const SmallVectorImpl<AnnotatedLine *>::const_iterator End;
 
   SmallVectorImpl<AnnotatedLine *>::const_iterator Next;
+  const SmallVectorImpl<AnnotatedLine *> &AnnotatedLines;
 };
 
 static void markFinalized(FormatToken *Tok) {
Index: lib/Format/NamespaceEndCommentsFixer.cpp
===================================================================
--- lib/Format/NamespaceEndCommentsFixer.cpp
+++ lib/Format/NamespaceEndCommentsFixer.cpp
@@ -120,6 +120,23 @@
                  << llvm::toString(std::move(Err)) << "\n";
   }
 }
+
+const FormatToken * getNamespaceToken(const AnnotatedLine *line,
+                                      const SmallVectorImpl<AnnotatedLine *> &AnnotatedLines) {
+    if (!line->Affected || line->InPPDirective || !line->startsWith(tok::r_brace))
+      return NULL;
+    size_t StartLineIndex = line->MatchingOpeningBlockLineIndex;
+    if (StartLineIndex == UnwrappedLine::kInvalidIndex)
+      return NULL;
+    assert(StartLineIndex < AnnotatedLines.size());
+    const FormatToken *NamespaceTok = AnnotatedLines[StartLineIndex]->First;
+    // Detect "(inline)? namespace" in the beginning of a line.
+    if (NamespaceTok->is(tok::kw_inline))
+      NamespaceTok = NamespaceTok->getNextNonComment();
+    if (!NamespaceTok || NamespaceTok->isNot(tok::kw_namespace))
+      return NULL;
+    return NamespaceTok;
+}
 } // namespace
 
 NamespaceEndCommentsFixer::NamespaceEndCommentsFixer(const Environment &Env,
@@ -133,20 +150,12 @@
   AffectedRangeMgr.computeAffectedLines(AnnotatedLines.begin(),
                                         AnnotatedLines.end());
   tooling::Replacements Fixes;
+  std::string AllNamespaceNames = "";
+  size_t StartLineIndex = SIZE_MAX;
   for (size_t I = 0, E = AnnotatedLines.size(); I != E; ++I) {
-    if (!AnnotatedLines[I]->Affected || AnnotatedLines[I]->InPPDirective ||
-        !AnnotatedLines[I]->startsWith(tok::r_brace))
-      continue;
     const AnnotatedLine *EndLine = AnnotatedLines[I];
-    size_t StartLineIndex = EndLine->MatchingOpeningBlockLineIndex;
-    if (StartLineIndex == UnwrappedLine::kInvalidIndex)
-      continue;
-    assert(StartLineIndex < E);
-    const FormatToken *NamespaceTok = AnnotatedLines[StartLineIndex]->First;
-    // Detect "(inline)? namespace" in the beginning of a line.
-    if (NamespaceTok->is(tok::kw_inline))
-      NamespaceTok = NamespaceTok->getNextNonComment();
-    if (!NamespaceTok || NamespaceTok->isNot(tok::kw_namespace))
+    const FormatToken *NamespaceTok = getNamespaceToken(EndLine, AnnotatedLines);
+    if (!NamespaceTok)
       continue;
     FormatToken *RBraceTok = EndLine->First;
     if (RBraceTok->Finalized)
@@ -160,6 +169,22 @@
       if (!Style.AllowSemicolonAfterNamespace)
         removeTrailingSemicolon(RBraceTok->Next, SourceMgr, &Fixes);
     }
+    if (StartLineIndex == SIZE_MAX)
+      StartLineIndex = EndLine->MatchingOpeningBlockLineIndex;
+    std::string NamespaceName = computeName(NamespaceTok);
+    bool CombineWithNext = I + 1 < E &&
+                           Style.CompactNamespaces &&
+                           getNamespaceToken(AnnotatedLines[I + 1], AnnotatedLines) &&
+                           !AnnotatedLines[I + 1]->First->Finalized;
+    if (CombineWithNext) {
+      if (hasEndComment(EndCommentPrevTok) && validEndComment(EndCommentPrevTok, NamespaceName))
+        // remove end comment, it will be merged in next one
+        updateEndComment(EndCommentPrevTok, std::string(), SourceMgr, &Fixes);
+      AllNamespaceNames = "::" + NamespaceName + AllNamespaceNames;
+      continue;
+    }
+    NamespaceName += std::move(AllNamespaceNames);
+    AllNamespaceNames = std::string();
     // The next token in the token stream after the place where the end comment
     // token must be. This is either the next token on the current line or the
     // first token on the next line.
@@ -171,17 +196,16 @@
     bool AddNewline = EndCommentNextTok &&
                       EndCommentNextTok->NewlinesBefore == 0 &&
                       EndCommentNextTok->isNot(tok::eof);
-    const std::string NamespaceName = computeName(NamespaceTok);
     const std::string EndCommentText =
         computeEndCommentText(NamespaceName, AddNewline);
     if (!hasEndComment(EndCommentPrevTok)) {
       bool isShort = I - StartLineIndex <= kShortNamespaceMaxLines + 1;
       if (!isShort)
         addEndComment(EndCommentPrevTok, EndCommentText, SourceMgr, &Fixes);
-      continue;
-    }
-    if (!validEndComment(EndCommentPrevTok, NamespaceName))
+    } else if (!validEndComment(EndCommentPrevTok, NamespaceName)) {
       updateEndComment(EndCommentPrevTok, EndCommentText, SourceMgr, &Fixes);
+    }
+    StartLineIndex = SIZE_MAX;
   }
   return Fixes;
 }
Index: lib/Format/Format.cpp
===================================================================
--- lib/Format/Format.cpp
+++ lib/Format/Format.cpp
@@ -287,20 +287,21 @@
     IO.mapOptional("BinPackArguments", Style.BinPackArguments);
     IO.mapOptional("BinPackParameters", Style.BinPackParameters);
     IO.mapOptional("BraceWrapping", Style.BraceWrapping);
+    IO.mapOptional("BreakAfterJavaFieldAnnotations",
+                   Style.BreakAfterJavaFieldAnnotations);
     IO.mapOptional("BreakBeforeBinaryOperators",
                    Style.BreakBeforeBinaryOperators);
     IO.mapOptional("BreakBeforeBraces", Style.BreakBeforeBraces);
+    IO.mapOptional("BreakBeforeInheritanceComma",
+                   Style.BreakBeforeInheritanceComma);
     IO.mapOptional("BreakBeforeTernaryOperators",
                    Style.BreakBeforeTernaryOperators);
     IO.mapOptional("BreakConstructorInitializersBeforeComma",
                    Style.BreakConstructorInitializersBeforeComma);
-    IO.mapOptional("BreakAfterJavaFieldAnnotations",
-                   Style.BreakAfterJavaFieldAnnotations);
     IO.mapOptional("BreakStringLiterals", Style.BreakStringLiterals);
     IO.mapOptional("ColumnLimit", Style.ColumnLimit);
     IO.mapOptional("CommentPragmas", Style.CommentPragmas);
-    IO.mapOptional("BreakBeforeInheritanceComma",
-                   Style.BreakBeforeInheritanceComma);
+    IO.mapOptional("CompactNamespaces", Style.CompactNamespaces);
     IO.mapOptional("ConstructorInitializerAllOnOneLineOrOnePerLine",
                    Style.ConstructorInitializerAllOnOneLineOrOnePerLine);
     IO.mapOptional("ConstructorInitializerIndentWidth",
@@ -516,8 +517,8 @@
   LLVMStyle.AlwaysBreakAfterDefinitionReturnType = FormatStyle::DRTBS_None;
   LLVMStyle.AlwaysBreakBeforeMultilineStrings = false;
   LLVMStyle.AlwaysBreakTemplateDeclarations = false;
-  LLVMStyle.BinPackParameters = true;
   LLVMStyle.BinPackArguments = true;
+  LLVMStyle.BinPackParameters = true;
   LLVMStyle.BreakBeforeBinaryOperators = FormatStyle::BOS_None;
   LLVMStyle.BreakBeforeTernaryOperators = true;
   LLVMStyle.BreakBeforeBraces = FormatStyle::BS_Attach;
@@ -529,6 +530,7 @@
   LLVMStyle.BreakStringLiterals = true;
   LLVMStyle.ColumnLimit = 80;
   LLVMStyle.CommentPragmas = "^ IWYU pragma:";
+  LLVMStyle.CompactNamespaces = false;
   LLVMStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = false;
   LLVMStyle.ConstructorInitializerIndentWidth = 4;
   LLVMStyle.ContinuationIndentWidth = 4;
Index: include/clang/Format/Format.h
===================================================================
--- include/clang/Format/Format.h
+++ include/clang/Format/Format.h
@@ -751,6 +751,28 @@
   /// \endcode
   bool BreakBeforeInheritanceComma;
 
+  /// \brief If ``true``, consecutive namespace declarations will be on the same
+  /// line. If ``false``, each namespace is declared on a new line.
+  /// \code
+  ///   true:
+  ///   namespace Foo { namespace Bar {
+  ///   }}
+  ///
+  ///   false:
+  ///   namespace Foo {
+  ///   namespace Bar {
+  ///   }
+  ///   }
+  /// \endcode
+  ///
+  /// If it does not fit on a single line, the overflowing namespaces get wrapped:
+  /// /// \code
+  ///   namespace Foo { namespace Bar {
+  ///   namespace Extra {
+  ///   }}}
+  /// \endcode
+  bool CompactNamespaces;
+
   /// \brief If the constructor initializers don't fit on a line, put each
   /// initializer on its own line.
   /// \code
@@ -1379,6 +1401,7 @@
            BreakBeforeBinaryOperators == R.BreakBeforeBinaryOperators &&
            BreakBeforeBraces == R.BreakBeforeBraces &&
            BreakBeforeTernaryOperators == R.BreakBeforeTernaryOperators &&
+           CompactNamespaces == R.CompactNamespaces &&
            BreakConstructorInitializersBeforeComma ==
                R.BreakConstructorInitializersBeforeComma &&
            BreakAfterJavaFieldAnnotations == R.BreakAfterJavaFieldAnnotations &&
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to