feg208 updated this revision to Diff 347709.
feg208 added a comment.

Addresses review comments


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D101868/new/

https://reviews.llvm.org/D101868

Files:
  clang/docs/ClangFormatStyleOptions.rst
  clang/docs/ReleaseNotes.rst
  clang/include/clang/Format/Format.h
  clang/lib/Format/ContinuationIndenter.cpp
  clang/lib/Format/ContinuationIndenter.h
  clang/lib/Format/Format.cpp
  clang/lib/Format/FormatToken.cpp
  clang/lib/Format/FormatToken.h
  clang/lib/Format/TokenAnnotator.cpp
  clang/lib/Format/TokenAnnotator.h
  clang/unittests/Format/FormatTest.cpp

Index: clang/unittests/Format/FormatTest.cpp
===================================================================
--- clang/unittests/Format/FormatTest.cpp
+++ clang/unittests/Format/FormatTest.cpp
@@ -16367,6 +16367,162 @@
                getLLVMStyle());
 }
 
+TEST_F(FormatTest, CatchAlignArrayOfStructures) {
+  auto Style = getLLVMStyle();
+  Style.AlignArrayOfStructures = true;
+  Style.AlignConsecutiveAssignments =
+      FormatStyle::AlignConsecutiveStyle::ACS_Consecutive;
+  Style.AlignConsecutiveDeclarations =
+      FormatStyle::AlignConsecutiveStyle::ACS_Consecutive;
+  verifyFormat("struct test demo[] = {\n"
+               "    {56,    23, \"hello\" },\n"
+               "    {-1, 93463, \"world\" },\n"
+               "    { 7,     5,    \"!!\" }\n"
+               "};\n",
+               Style);
+
+  verifyFormat("struct test demo[] = {\n"
+               "    {56,    23, \"hello\" }, // first line\n"
+               "    {-1, 93463, \"world\" }, // second line\n"
+               "    { 7,     5,    \"!!\" }  // third line\n"
+               "};\n",
+               Style);
+
+  verifyFormat("struct test demo[4] = {\n"
+               "    { 56,    23, 21,       \"oh\" }, // first line\n"
+               "    { -1, 93463, 22,       \"my\" }, // second line\n"
+               "    {  7,     5,  1, \"goodness\" }  // third line\n"
+               "    {234,     5,  1, \"gracious\" }  // fourth line\n"
+               "};\n",
+               Style);
+
+  verifyFormat("struct test demo[3] = {\n"
+               "    {56,    23, \"hello\" },\n"
+               "    {-1, 93463, \"world\" },\n"
+               "    { 7,     5,    \"!!\" }\n"
+               "};\n",
+               Style);
+  verifyFormat("struct test demo[3] = {\n"
+               "    {int{56},    23, \"hello\" },\n"
+               "    {int{-1}, 93463, \"world\" },\n"
+               "    { int{7},     5,    \"!!\" }\n"
+               "};\n",
+               Style);
+  verifyFormat("struct test demo[] = {\n"
+               "    {56,    23, \"hello\" },\n"
+               "    {-1, 93463, \"world\" },\n"
+               "    { 7,     5,    \"!!\" },\n"
+               "};\n",
+               Style);
+  verifyFormat("test demo[] = {\n"
+               "    {56,    23, \"hello\" },\n"
+               "    {-1, 93463, \"world\" },\n"
+               "    { 7,     5,    \"!!\" },\n"
+               "};\n",
+               Style);
+  verifyFormat("demo = std::array<struct test, 3>{\n"
+               "    test{56,    23, \"hello\" },\n"
+               "    test{-1, 93463, \"world\" },\n"
+               "    test{ 7,     5,    \"!!\" },\n"
+               "};\n",
+               Style);
+  verifyFormat("test demo[] = {\n"
+               "    {56,    23, \"hello\" },\n"
+               "#if X\n"
+               "    {-1, 93463, \"world\" },\n"
+               "#endif\n"
+               "    { 7,     5,    \"!!\" }\n"
+               "};\n",
+               Style);
+
+  verifyFormat("test demo[] = {\n"
+               "    { 7,    23,\n"
+               "    \"hello world i am a very long line that really, in any\"\n"
+               "    \"just world, ought to be split over multiple lines\" },\n"
+               "    {-1, 93463,                                 \"world\" },\n"
+               "    {56,     5,                                    \"!!\" }\n"
+               "};\n",
+               Style);
+
+  verifyFormat("return GradForUnaryCwise(g, {\n"
+               "                                {{\"sign\"}, \"Sign\",  "
+               "{\"x\", \"dy\"} },\n"
+               "                                {  {\"dx\"},  \"Mul\", {\"dy\""
+               ", \"sign\"} },\n"
+               "                            });\n",
+               Style);
+
+  Style.ColumnLimit = 0;
+  EXPECT_EQ(
+      "test demo[] = {\n"
+      "    {56,    23, \"hello world i am a very long line that really, "
+      "in any just world, ought to be split over multiple lines\" },\n"
+      "    {-1, 93463,                                                  "
+      "                                                 \"world\" },\n"
+      "    { 7,     5,                                                  "
+      "                                                    \"!!\" },\n"
+      "};",
+      format("test demo[] = {{56, 23, \"hello world i am a very long line "
+             "that really, in any just world, ought to be split over multiple "
+             "lines\"},{-1, 93463, \"world\"},{7, 5, \"!!\"},};",
+             Style));
+  Style.ColumnLimit = 20;
+  EXPECT_EQ(
+      "demo = std::array<\n"
+      "    struct test, 3>{\n"
+      "    test{56,    23,\n"
+      "        \"hello \"\n"
+      "        \"world i \"\n"
+      "        \"am a very \"\n"
+      "        \"long line \"\n"
+      "        \"that \"\n"
+      "        \"really, \"\n"
+      "        \"in any \"\n"
+      "        \"just \"\n"
+      "        \"world, \"\n"
+      "        \"ought to \"\n"
+      "        \"be split \"\n"
+      "        \"over \"\n"
+      "        \"multiple \"\n"
+      "        \"lines\" },\n"
+      "    test{-1, 93463,\n"
+      "        \"world\" },\n"
+      "    test{ 7,     5,\n"
+      "           \"!!\" },\n"
+      "};",
+      format("demo = std::array<struct test, 3>{test{56, 23, \"hello world "
+             "i am a very long line that really, in any just world, ought "
+             "to be split over multiple lines\"},test{-1, 93463, \"world\"},"
+             "test{7, 5, \"!!\"},};",
+             Style));
+  // This caused a core dump by enabling Alignment in the LLVMStyle globally
+  Style = getLLVMStyleWithColumns(50);
+  Style.AlignArrayOfStructures = true;
+  verifyFormat("static A x = {\n"
+               "    {{init1, init2, init3, init4},\n"
+               "     {init1, init2, init3, init4} }\n"
+               "};",
+               Style);
+
+  // FIXME This is a case where the lines are obviously malformed
+  /*
+  Style.ColumnLimit = 100;
+  EXPECT_EQ(
+      "test demo[] = {\n"
+      "    {56,    23,\n"
+      "    \"hello world i am a very long line that really, in any just world"
+      ", ought to be split over \"\n"
+      "    \"multiple lines\"    },\n"
+      "    {-1, 93463, \"world\" },\n"
+      "    { 7,     5,    \"!!\" },\n"
+      "};",
+      format("test demo[] = {{56, 23, \"hello world i am a very long line "
+             "that really, in any just world, ought to be split over multiple "
+             "lines\"},{-1, 93463, \"world\"},{7, 5, \"!!\"},};",
+             Style));
+    */
+}
+
 TEST_F(FormatTest, UnderstandsPragmas) {
   verifyFormat("#pragma omp reduction(| : var)");
   verifyFormat("#pragma omp reduction(+ : var)");
Index: clang/lib/Format/TokenAnnotator.h
===================================================================
--- clang/lib/Format/TokenAnnotator.h
+++ clang/lib/Format/TokenAnnotator.h
@@ -18,6 +18,9 @@
 #include "UnwrappedLineParser.h"
 #include "clang/Format/Format.h"
 
+#include <deque>
+#include <vector>
+
 namespace clang {
 class SourceManager;
 
@@ -31,7 +34,8 @@
   LT_ObjCProperty, // An @property line.
   LT_Other,
   LT_PreprocessorDirective,
-  LT_VirtualFunctionDecl
+  LT_VirtualFunctionDecl,
+  LT_ArrayOfStructInitializer
 };
 
 class AnnotatedLine {
@@ -189,6 +193,13 @@
 
   void calculateUnbreakableTailLengths(AnnotatedLine &Line);
 
+  void calculateArrayInitializerColumnList(AnnotatedLine &Line);
+
+  FormatToken *
+  calculateInitializerColumnList(AnnotatedLine &Line,
+                                 std::deque<FormatToken *> &&ColumnEntries,
+                                 FormatToken *CurrentToken, unsigned Depth);
+
   const FormatStyle &Style;
 
   const AdditionalKeywords &Keywords;
Index: clang/lib/Format/TokenAnnotator.cpp
===================================================================
--- clang/lib/Format/TokenAnnotator.cpp
+++ clang/lib/Format/TokenAnnotator.cpp
@@ -729,11 +729,21 @@
     return false;
   }
 
+  bool couldBeInStructArrayInitializer() const {
+    const auto end = Contexts.rbegin() + 2;
+    auto last = Contexts.rbegin();
+    unsigned Depth = 0;
+    for (; last != end; ++last) {
+      if (last->ContextKind == tok::l_brace)
+        ++Depth;
+    }
+    return Depth == 2 && last->ContextKind != tok::l_brace;
+  }
+
   bool parseBrace() {
     if (CurrentToken) {
       FormatToken *Left = CurrentToken->Previous;
       Left->ParentBracket = Contexts.back().ContextKind;
-
       if (Contexts.back().CaretFound)
         Left->setType(TT_ObjCBlockLBrace);
       Contexts.back().CaretFound = false;
@@ -746,10 +756,17 @@
           Left->Previous->is(TT_JsTypeColon))
         Contexts.back().IsExpression = false;
 
+      unsigned CommaCount = 0;
       while (CurrentToken) {
         if (CurrentToken->is(tok::r_brace)) {
           Left->MatchingParen = CurrentToken;
           CurrentToken->MatchingParen = Left;
+          if (Style.AlignArrayOfStructures) {
+            if (Left->ParentBracket == tok::l_brace &&
+                couldBeInStructArrayInitializer() && CommaCount > 0) {
+              Contexts.back().InStructArrayInitializer = true;
+            }
+          }
           next();
           return true;
         }
@@ -773,9 +790,11 @@
               Style.Language == FormatStyle::LK_JavaScript)
             Left->setType(TT_DictLiteral);
         }
-        if (CurrentToken->is(tok::comma) &&
-            Style.Language == FormatStyle::LK_JavaScript)
-          Left->setType(TT_DictLiteral);
+        if (CurrentToken->is(tok::comma)) {
+          if (Style.Language == FormatStyle::LK_JavaScript)
+            Left->setType(TT_DictLiteral);
+          ++CommaCount;
+        }
         if (!consumeToken())
           return false;
       }
@@ -1339,6 +1358,12 @@
       return LT_ObjCMethodDecl;
     }
 
+    for (const auto &ctx : Contexts) {
+      if (ctx.InStructArrayInitializer) {
+        return LT_ArrayOfStructInitializer;
+      }
+    }
+
     return LT_Other;
   }
 
@@ -1414,6 +1439,7 @@
     bool IsForEachMacro = false;
     bool InCpp11AttributeSpecifier = false;
     bool InCSharpAttributeSpecifier = false;
+    bool InStructArrayInitializer = false;
   };
 
   /// Puts a new \c Context onto the stack \c Contexts for the lifetime
@@ -1429,7 +1455,16 @@
                                    P.Contexts.back().IsExpression));
     }
 
-    ~ScopedContextCreator() { P.Contexts.pop_back(); }
+    ~ScopedContextCreator() {
+      if (P.Style.AlignArrayOfStructures) {
+        if (P.Contexts.back().InStructArrayInitializer) {
+          P.Contexts.pop_back();
+          P.Contexts.back().InStructArrayInitializer = true;
+          return;
+        }
+      }
+      P.Contexts.pop_back();
+    }
   };
 
   void modifyContext(const FormatToken &Current) {
@@ -2473,6 +2508,11 @@
                               : Line.FirstStartColumn + Line.First->ColumnWidth;
   FormatToken *Current = Line.First->Next;
   bool InFunctionDecl = Line.MightBeFunctionDecl;
+  bool AlignArrayOfStructures = (Style.AlignArrayOfStructures &&
+                                 Line.Type == LT_ArrayOfStructInitializer);
+  if (AlignArrayOfStructures)
+    calculateArrayInitializerColumnList(Line);
+
   while (Current) {
     if (isFunctionDeclarationName(*Current, Line))
       Current->setType(TT_FunctionDeclarationName);
@@ -2592,6 +2632,86 @@
   }
 }
 
+void TokenAnnotator::calculateArrayInitializerColumnList(AnnotatedLine &Line) {
+  if (Line.First == Line.Last) {
+    return;
+  }
+  auto *CurrentToken = Line.First;
+  unsigned Depth = 0;
+  while (CurrentToken != nullptr && CurrentToken != Line.Last) {
+    if (CurrentToken->is(tok::l_brace))
+      CurrentToken = calculateInitializerColumnList(
+          Line, std::deque<FormatToken *>{}, CurrentToken->Next, Depth + 1);
+    else
+      CurrentToken = CurrentToken->Next;
+  }
+}
+
+// NOLINTNEXTLINE(misc-no-recursion)
+FormatToken *TokenAnnotator::calculateInitializerColumnList(
+    AnnotatedLine &Line, std::deque<FormatToken *> &&ColumnEntries,
+    FormatToken *CurrentToken, unsigned Depth) {
+  if (Depth == 1) {
+    CurrentToken->MustBreakBefore = true;
+    return calculateInitializerColumnList(Line, std::move(ColumnEntries),
+                                          CurrentToken, Depth + 1);
+  }
+
+  std::deque<FormatToken *> NextRow;
+  while (CurrentToken != nullptr && CurrentToken != Line.Last) {
+    if (CurrentToken->is(tok::l_brace))
+      ++Depth;
+    else if (CurrentToken->is(tok::r_brace))
+      --Depth;
+
+    if (CurrentToken->Next != nullptr) {
+      if (CurrentToken->is(tok::r_brace)) {
+        if (Depth == 2) {
+          if (CurrentToken->Next->is(tok::comma)) {
+            auto *NextLBrace = CurrentToken->Next->Next;
+            const auto *StopValue = CurrentToken->Next->getNextNonComment();
+            while (NextLBrace != nullptr && NextLBrace != StopValue)
+              NextLBrace = NextLBrace->Next;
+            if (NextLBrace != nullptr)
+              NextLBrace->MustBreakBefore = true;
+          }
+          CurrentToken->IsRowClosing = true;
+          return calculateInitializerColumnList(Line, std::move(NextRow),
+                                                CurrentToken->Next, Depth);
+        }
+
+        if (Depth == 1) {
+          // We are dropping out of the loop. So in this case take all of the
+          // entries in the last row and point the tails at the heads so we can
+          // loop around the columns ending where we began
+          while (!ColumnEntries.empty()) {
+            auto *LastToken = ColumnEntries.front();
+            if (LastToken->PreviousColumn != nullptr) {
+              auto *StartToken = LastToken->PreviousColumn;
+              while (StartToken->PreviousColumn != nullptr) {
+                StartToken = StartToken->PreviousColumn;
+              }
+              StartToken->PreviousColumn = LastToken;
+            }
+            ColumnEntries.pop_front();
+          }
+          CurrentToken->MustBreakBefore = true;
+        }
+      } else if (Depth == 3 &&
+                 CurrentToken->isOneOf(tok::comma, tok::l_brace)) {
+        CurrentToken->Next->ColumnIndex = NextRow.size();
+        NextRow.push_back(CurrentToken->Next);
+        if (!ColumnEntries.empty()) {
+          CurrentToken->Next->PreviousColumn = ColumnEntries.front();
+          ColumnEntries.pop_front();
+        }
+      }
+    }
+    CurrentToken = CurrentToken->Next;
+  }
+  return CurrentToken;
+}
+
 unsigned TokenAnnotator::splitPenalty(const AnnotatedLine &Line,
                                       const FormatToken &Tok,
                                       bool InFunctionDecl) {
Index: clang/lib/Format/FormatToken.h
===================================================================
--- clang/lib/Format/FormatToken.h
+++ clang/lib/Format/FormatToken.h
@@ -431,6 +431,20 @@
   /// The next token in the unwrapped line.
   FormatToken *Next = nullptr;
 
+  /// The previous vertical column element
+  FormatToken *PreviousColumn = nullptr;
+
+  /// Indicates that this is the closing bracket for a row
+  bool IsRowClosing = false;
+
+  /// We we split a token into multiples in the array initializer
+  /// case we need to track the size of the split for other columns
+  unsigned SplitMax = 0U;
+
+  /// For a token in a array initializer we need to track where in the
+  /// row it is so we can set offsets appropriately
+  unsigned ColumnIndex = 0U;
+
   /// If this token starts a block, this contains all the unwrapped lines
   /// in it.
   SmallVector<AnnotatedLine *, 1> Children;
@@ -695,6 +709,17 @@
 
   void copyFrom(const FormatToken &Tok) { *this = Tok; }
 
+  /// Returns the maximum width of all the columns above and below
+  /// this one
+  unsigned getMaximumColumnWidth(unsigned Limit = 0) const noexcept;
+
+  /// Returns the column width of multiple tokens in a array initializer
+  unsigned getThisColumnWidth(unsigned Limit = 0) const noexcept;
+
+  /// Returns a boolean indicated that a previous column element
+  /// was split
+  bool hasPreviousSplitMax() const noexcept;
+
 private:
   // Only allow copying via the explicit copyFrom method.
   FormatToken(const FormatToken &) = delete;
Index: clang/lib/Format/FormatToken.cpp
===================================================================
--- clang/lib/Format/FormatToken.cpp
+++ clang/lib/Format/FormatToken.cpp
@@ -16,6 +16,7 @@
 #include "ContinuationIndenter.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/Debug.h"
+#include <algorithm>
 #include <climits>
 
 namespace clang {
@@ -69,6 +70,49 @@
   }
 }
 
+unsigned FormatToken::getMaximumColumnWidth(unsigned Limit) const noexcept {
+  unsigned MaxWidth = getThisColumnWidth();
+  auto *PColumn = PreviousColumn;
+  while (PColumn != nullptr && PColumn != this) {
+    MaxWidth = std::max(MaxWidth, PColumn->getThisColumnWidth(Limit));
+    PColumn = PColumn->PreviousColumn;
+  }
+  return MaxWidth;
+}
+
+unsigned FormatToken::getThisColumnWidth(unsigned Limit) const noexcept {
+  auto ColWidth =
+      (SplitMax > 0 && SplitMax < ColumnWidth) ? SplitMax : ColumnWidth;
+  const auto *NextColEntry = Next;
+  while (NextColEntry != nullptr &&
+         !NextColEntry->isOneOf(tok::r_brace, tok::comma)) {
+    if (NextColEntry->SplitMax > 0 &&
+        NextColEntry->SplitMax < NextColEntry->ColumnWidth) {
+      ColWidth += NextColEntry->SplitMax;
+    } else if (Limit > 0 && ColWidth + NextColEntry->ColumnWidth >= Limit) {
+      ColWidth = NextColEntry->ColumnWidth;
+    } else {
+      ColWidth += NextColEntry->ColumnWidth;
+    }
+    NextColEntry = NextColEntry->Next;
+  }
+  return ColWidth;
+}
+
+bool FormatToken::hasPreviousSplitMax() const noexcept {
+  if (SplitMax > 0)
+    return true;
+  // We can't recurse here because there is no stopping
+  // condition in the case in which there is no SplitMaximum
+  const auto *PColumn = PreviousColumn;
+  while (PColumn != nullptr && PColumn != this) {
+    if (PColumn->SplitMax > 0)
+      return true;
+    PColumn = PColumn->PreviousColumn;
+  }
+  return false;
+}
+
 TokenRole::~TokenRole() {}
 
 void TokenRole::precomputeFormattingInfos(const FormatToken *Token) {}
Index: clang/lib/Format/Format.cpp
===================================================================
--- clang/lib/Format/Format.cpp
+++ clang/lib/Format/Format.cpp
@@ -506,6 +506,7 @@
 
     IO.mapOptional("AccessModifierOffset", Style.AccessModifierOffset);
     IO.mapOptional("AlignAfterOpenBracket", Style.AlignAfterOpenBracket);
+    IO.mapOptional("AlignArrayOfStructures", Style.AlignArrayOfStructures);
     IO.mapOptional("AlignConsecutiveMacros", Style.AlignConsecutiveMacros);
     IO.mapOptional("AlignConsecutiveAssignments",
                    Style.AlignConsecutiveAssignments);
@@ -941,6 +942,7 @@
   LLVMStyle.AccessModifierOffset = -2;
   LLVMStyle.AlignEscapedNewlines = FormatStyle::ENAS_Right;
   LLVMStyle.AlignAfterOpenBracket = FormatStyle::BAS_Align;
+  LLVMStyle.AlignArrayOfStructures = false;
   LLVMStyle.AlignOperands = FormatStyle::OAS_Align;
   LLVMStyle.AlignTrailingComments = true;
   LLVMStyle.AlignConsecutiveAssignments = FormatStyle::ACS_None;
Index: clang/lib/Format/ContinuationIndenter.h
===================================================================
--- clang/lib/Format/ContinuationIndenter.h
+++ clang/lib/Format/ContinuationIndenter.h
@@ -188,6 +188,9 @@
   /// by clang-format and string literals with escaped newlines.
   bool nextIsMultilineString(const LineState &State);
 
+  /// make sure row positions is properly initialized and return the max width
+  unsigned setRowPositions(const FormatToken &Current, LineState &State);
+
   FormatStyle Style;
   const AdditionalKeywords &Keywords;
   const SourceManager &SourceMgr;
@@ -453,6 +456,13 @@
   /// Does not need to be considered for memoization because it doesn't change.
   const AnnotatedLine *Line;
 
+  /// The column position of the end of each of the row elements
+  /// for array initializers
+  std::vector<std::pair<unsigned, unsigned>> RowPositions;
+
+  /// Keep track of the current column index in array initializers
+  unsigned CurrentColumnIndex = 0U;
+
   /// Comparison operator to be able to used \c LineState in \c map.
   bool operator<(const LineState &Other) const {
     if (NextToken != Other.NextToken)
@@ -474,6 +484,8 @@
       return false;
     return Stack < Other.Stack;
   }
+
+  unsigned getLBraceDepth() const;
 };
 
 } // end namespace format
Index: clang/lib/Format/ContinuationIndenter.cpp
===================================================================
--- clang/lib/Format/ContinuationIndenter.cpp
+++ clang/lib/Format/ContinuationIndenter.cpp
@@ -597,10 +597,37 @@
       PPColumnCorrection = -1;
   }
 
-  if (!DryRun)
-    Whitespaces.replaceWhitespace(Current, /*Newlines=*/0, Spaces,
-                                  State.Column + Spaces + PPColumnCorrection);
-
+  if (!DryRun) {
+    if (Style.AlignArrayOfStructures &&
+        State.Line->Type == LT_ArrayOfStructInitializer) {
+      auto ColumnPos = State.Column;
+      if (Current.PreviousColumn != nullptr) {
+        auto MaxWidth = setRowPositions(Current, State);
+        Spaces = MaxWidth - Current.getThisColumnWidth();
+        Spaces += ((Current.ColumnIndex != 0) ? 1 : 0U);
+        if ((Current.hasPreviousSplitMax() ||
+             MaxWidth < Current.getMaximumColumnWidth()) &&
+            Current.ColumnIndex > 0) {
+          // We had a line get broken so net the previous
+          // column
+          auto LeftPosition =
+              State.RowPositions[Current.ColumnIndex - 1].first +
+              State.RowPositions[Current.ColumnIndex - 1].second - 1;
+          if (Spaces > LeftPosition)
+            Spaces -= LeftPosition;
+          else if (Spaces < LeftPosition)
+            Spaces = 1;
+        }
+        ColumnPos = State.RowPositions[Current.ColumnIndex].first;
+      } else if (Current.IsRowClosing) {
+        Spaces += 1;
+      }
+      Whitespaces.replaceWhitespace(Current, /*Newlines=*/0, Spaces, ColumnPos);
+    } else {
+      Whitespaces.replaceWhitespace(Current, /*Newlines=*/0, Spaces,
+                                    State.Column + Spaces + PPColumnCorrection);
+    }
+  }
   // If "BreakBeforeInheritanceComma" mode, don't break within the inheritance
   // declaration unless there is multiple inheritance.
   if (Style.BreakInheritanceList == FormatStyle::BILS_BeforeComma &&
@@ -885,9 +912,45 @@
         std::max(1u, std::min(Current.NewlinesBefore, MaxEmptyLinesToKeep));
     bool ContinuePPDirective =
         State.Line->InPPDirective && State.Line->Type != LT_ImportStatement;
-    Whitespaces.replaceWhitespace(Current, Newlines, State.Column, State.Column,
-                                  State.Stack.back().IsAligned,
-                                  ContinuePPDirective);
+    bool AlignArrayOfStructures =
+        (Style.AlignArrayOfStructures &&
+         State.Line->Type == LT_ArrayOfStructInitializer);
+    if (AlignArrayOfStructures) {
+      unsigned Depth = State.getLBraceDepth();
+      if (Current.PreviousColumn != nullptr) {
+        auto MaxWidth = setRowPositions(Current, State);
+        auto ColumnPos = State.RowPositions[Current.ColumnIndex].first;
+        auto Spaces = MaxWidth - Current.getThisColumnWidth();
+        // FIXME really we shouldn't have this test here
+        if (Current.ColumnIndex == 0 &&
+            (ColumnPos + Current.getThisColumnWidth()) < Style.ColumnLimit) {
+          Newlines = 0;
+          State.RowPositions[Current.ColumnIndex] =
+              std::make_pair(State.Column + 1, MaxWidth);
+        } else if (Current.ColumnIndex > 0 && Newlines > 0) {
+          // We are linebreaking at a column so set the position to the first
+          State.RowPositions[Current.ColumnIndex] =
+              std::make_pair(State.RowPositions[0].first,
+                             State.RowPositions[Current.ColumnIndex].second);
+          ColumnPos = State.RowPositions[Current.ColumnIndex].first;
+          Spaces += ColumnPos - 1;
+        }
+        Whitespaces.replaceWhitespace(Current, Newlines, Spaces, ColumnPos);
+      } else if (!Current.isOneOf(tok::r_brace, tok::comma, tok::l_brace) &&
+                 State.RowPositions.size() > State.CurrentColumnIndex &&
+                 Depth == 2) {
+        auto ColumnPos = State.RowPositions[State.CurrentColumnIndex].first;
+        Whitespaces.replaceWhitespace(Current, Newlines, ColumnPos - 1,
+                                      ColumnPos);
+      } else {
+        Whitespaces.replaceWhitespace(Current, Newlines, State.Column,
+                                      State.Column);
+      }
+    } else {
+      Whitespaces.replaceWhitespace(Current, Newlines, State.Column,
+                                    State.Column, State.Stack.back().IsAligned,
+                                    ContinuePPDirective);
+    }
   }
 
   if (!Current.isTrailingComment())
@@ -1951,6 +2014,12 @@
       unsigned UnbreakableTailLength = (State.NextToken && canBreak(State))
                                            ? 0
                                            : Current.UnbreakableTailLength;
+      if (Style.AlignArrayOfStructures &&
+          State.Line->Type == LT_ArrayOfStructInitializer) {
+        if (!State.RowPositions.empty()) {
+          StartColumn = State.RowPositions[0].first - 1;
+        }
+      }
       return std::make_unique<BreakableStringLiteral>(
           Current, StartColumn, Prefix, Postfix, UnbreakableTailLength,
           State.Line->InPPDirective, Encoding, Style);
@@ -2188,10 +2257,24 @@
 
       LLVM_DEBUG(llvm::dbgs() << "    Breaking at: " << TailOffset + Split.first
                               << ", " << Split.second << "\n");
-      if (!DryRun)
+      if (!DryRun) {
+        if (Style.AlignArrayOfStructures &&
+            State.Line->Type == LT_ArrayOfStructInitializer) {
+          // So at this point we have a initializer. We need to make sure the
+          // related column tokens are updated as well as any changes already
+          // inflight linked to those tokens
+          auto *CurrentWritableToken = State.NextToken->Previous;
+          for (; CurrentWritableToken != &Current;
+               CurrentWritableToken = CurrentWritableToken->Previous) {
+          }
+          assert(CurrentWritableToken != nullptr);
+          CurrentWritableToken->SplitMax =
+              CurrentWritableToken->TokenText.substr(TailOffset + Split.first)
+                  .size();
+        }
         Token->insertBreak(LineIndex, TailOffset, Split, ContentIndent,
                            Whitespaces);
-
+      }
       Penalty += NewBreakPenalty;
       TailOffset += Split.first + Split.second;
       RemainingTokenColumns = NewRemainingTokenColumns;
@@ -2384,5 +2467,27 @@
   return false;
 }
 
+unsigned ContinuationIndenter::setRowPositions(const FormatToken &Current,
+                                               LineState &State) {
+  auto MaxWidth = Current.getMaximumColumnWidth(Style.ColumnLimit);
+  State.CurrentColumnIndex = Current.ColumnIndex;
+  if (State.RowPositions.size() <= Current.ColumnIndex)
+    State.RowPositions.push_back(std::make_pair(State.Column, MaxWidth));
+  else if (State.RowPositions[Current.ColumnIndex].second != MaxWidth) {
+    State.RowPositions[Current.ColumnIndex] =
+        std::make_pair(State.Column, MaxWidth);
+  }
+  return MaxWidth;
+}
+
+unsigned LineState::getLBraceDepth() const {
+  unsigned Depth = 0U;
+  for (const auto &PState : Stack) {
+    if (PState.Tok != nullptr)
+      Depth += (PState.Tok->is(tok::l_brace)) ? 1 : 0;
+  }
+  return Depth;
+}
+
 } // namespace format
 } // namespace clang
Index: clang/include/clang/Format/Format.h
===================================================================
--- clang/include/clang/Format/Format.h
+++ clang/include/clang/Format/Format.h
@@ -90,6 +90,18 @@
   /// brackets.
   BracketAlignmentStyle AlignAfterOpenBracket;
 
+  /// if ``true``, when using initialization for an array of structs
+  /// aligns the fields into columns
+  /// \code
+  ///   struct test demo[] =
+  ///   {
+  ///       {56, 23,    "hello" },
+  ///       {-1, 93463, "world" },
+  ///       {7,  5,     "!!"    }
+  ///   }
+  /// \endcode
+  bool AlignArrayOfStructures;
+
   /// Styles for alignment of consecutive tokens. Tokens can be assignment signs
   /// (see
   /// ``AlignConsecutiveAssignments``), bitfield member separators (see
@@ -3249,6 +3261,7 @@
   bool operator==(const FormatStyle &R) const {
     return AccessModifierOffset == R.AccessModifierOffset &&
            AlignAfterOpenBracket == R.AlignAfterOpenBracket &&
+           AlignArrayOfStructures == R.AlignArrayOfStructures &&
            AlignConsecutiveAssignments == R.AlignConsecutiveAssignments &&
            AlignConsecutiveBitFields == R.AlignConsecutiveBitFields &&
            AlignConsecutiveDeclarations == R.AlignConsecutiveDeclarations &&
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -251,6 +251,9 @@
 - ``git-clang-format`` no longer formats changes to symbolic links. (Fixes
   https://llvm.org/PR46992.)
 
+- Option ``AlignArrayOfStructure`` has been added to allow for ordering array-like
+  initializers.
+
 libclang
 --------
 
Index: clang/docs/ClangFormatStyleOptions.rst
===================================================================
--- clang/docs/ClangFormatStyleOptions.rst
+++ clang/docs/ClangFormatStyleOptions.rst
@@ -203,6 +203,17 @@
           argument1, argument2);
 
 
+**AlignArrayOfStructures** (``bool``)
+  If ``true``, when using initialization for an array
+  of structs aligns the fields into right justified columns
+
+  .. code-block:: c
+  struct test demo[] =
+  {
+        {56,    23, "hello" },
+        {-1, 93463, "world" },
+        { 7,     5,    "!!" }
+  }
 
 **AlignConsecutiveAssignments** (``AlignConsecutiveStyle``)
   Style of aligning consecutive assignments.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to