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

oops left some debugging messages in


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/Format.cpp
  clang/lib/Format/FormatToken.h
  clang/lib/Format/TokenAnnotator.cpp
  clang/lib/Format/TokenAnnotator.h
  clang/lib/Format/WhitespaceManager.cpp
  clang/lib/Format/WhitespaceManager.h
  clang/unittests/Format/FormatTest.cpp

Index: clang/unittests/Format/FormatTest.cpp
===================================================================
--- clang/unittests/Format/FormatTest.cpp
+++ clang/unittests/Format/FormatTest.cpp
@@ -16401,6 +16401,229 @@
                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 = 80;
+  verifyFormat("test demo[] = {\n"
+               "    {56,    23, /* a comment */ \"hello\"},\n"
+               "    {-1, 93463,                 \"world\"},\n"
+               "    { 7,     5,                    \"!!\"}\n"
+               "};\n",
+               Style);
+
+  verifyFormat("test demo[] = {\n"
+               "    {56,    23,                    \"hello\"},\n"
+               "    {-1, 93463, \"world\" /* comment here */},\n"
+               "    { 7,     5,                       \"!!\"}\n"
+               "};\n",
+               Style);
+
+  verifyFormat("test demo[] = {\n"
+               "    {56, /* a comment */ 23, \"hello\"},\n"
+               "    {-1,              93463, \"world\"},\n"
+               "    { 7,                  5,    \"!!\"}\n"
+               "};\n",
+               Style);
+
+  Style.ColumnLimit = 20;
+  EXPECT_EQ(
+      "demo = std::array<\n"
+      "    struct test, 3>{\n"
+      "    test{\n"
+      "         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);
+  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));
+
+  Style = getLLVMStyleWithColumns(50);
+  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"
+               "static A x = {\n"
+               "    {{init1, init2, init3, init4},\n"
+               "     {init1, init2, init3, init4}}\n"
+               "};",
+               Style);
+  Style.ColumnLimit = 100;
+  Style.AlignConsecutiveAssignments =
+      FormatStyle::AlignConsecutiveStyle::ACS_AcrossComments;
+  Style.AlignConsecutiveDeclarations =
+      FormatStyle::AlignConsecutiveStyle::ACS_AcrossComments;
+  verifyFormat("struct test demo[] = {\n"
+               "    {56,    23, \"hello\"},\n"
+               "    {-1, 93463, \"world\"},\n"
+               "    { 7,     5,    \"!!\"}\n"
+               "};\n"
+               "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);
+  EXPECT_EQ(
+      "test demo[] = {\n"
+      "    {56,\n"
+      "     \"hello world i am a very long line that really, in any just world"
+      ", ought to be split over \"\n"
+      "     \"multiple lines\",    23},\n"
+      "    {-1,      \"world\", 93463},\n"
+      "    { 7,         \"!!\",     5},\n"
+      "};",
+      format("test demo[] = {{56, \"hello world i am a very long line "
+             "that really, in any just world, ought to be split over multiple "
+             "lines\", 23},{-1, \"world\", 93463},{7, \"!!\", 5},};",
+             Style));
+}
+
 TEST_F(FormatTest, UnderstandsPragmas) {
   verifyFormat("#pragma omp reduction(| : var)");
   verifyFormat("#pragma omp reduction(+ : var)");
Index: clang/lib/Format/WhitespaceManager.h
===================================================================
--- clang/lib/Format/WhitespaceManager.h
+++ clang/lib/Format/WhitespaceManager.h
@@ -20,6 +20,7 @@
 #include "clang/Format/Format.h"
 #include <string>
 #include <tuple>
+#include <vector>
 
 namespace clang {
 namespace format {
@@ -173,6 +174,27 @@
   };
 
 private:
+  struct CellDescription {
+    unsigned Index = 0;
+    unsigned Cell = 0;
+    unsigned EndIndex = 0;
+    bool HasSplit = false;
+    CellDescription *NextColumnElement = nullptr;
+
+    constexpr bool operator==(const CellDescription &oth) const noexcept {
+      return Index == oth.Index && Cell == oth.Cell && EndIndex == oth.EndIndex;
+    }
+    constexpr bool operator!=(const CellDescription &oth) const noexcept {
+      return !(*this == oth);
+    }
+  };
+
+  struct CellDescriptions {
+    std::vector<CellDescription> Cells;
+    unsigned CellCount = 0;
+    unsigned InitialSpaces = 0;
+  };
+
   /// Calculate \c IsTrailingComment, \c TokenLength for the last tokens
   /// or token parts in a line and \c PreviousEndOfTokenColumn and
   /// \c EscapedNewlineColumn for the first tokens or token parts in a line.
@@ -207,6 +229,42 @@
   /// the specified \p Column.
   void alignEscapedNewlines(unsigned Start, unsigned End, unsigned Column);
 
+  /// Align Array Initializers over all \c Changes
+  void alignArrayInitializers();
+
+  /// Align Array Initializers from change \p Start to change \p End at
+  /// the specified \p Column
+  void alignArrayInitializers(unsigned Start, unsigned End);
+
+  /// Calculate the cell width between two indexes
+  unsigned calculateCellWidth(unsigned Start, unsigned End,
+                              bool WithSpaces = false);
+
+  /// Get a set of fully specified CellDescriptions between \p Start and
+  /// \p End of the change list
+  CellDescriptions getCells(unsigned Start, unsigned End);
+
+  /// Does this \p Cell contain a split element?
+  static bool isSplitCell(const CellDescription &Cell);
+
+  /// Get the width of the preceeding cells from \p Start to \p End
+  template <typename I>
+  auto getNetWidth(const I &Start, const I &End, unsigned InitialSpaces) {
+    auto NetWidth = InitialSpaces;
+    for (auto PrevIter = Start; PrevIter != End; PrevIter++) {
+      // If we broke the line the initial spaces are already
+      // accounted for
+      if (Changes[PrevIter->Index].NewlinesBefore > 0)
+        NetWidth = 0;
+      NetWidth +=
+          calculateCellWidth(PrevIter->Index, PrevIter->EndIndex, true) + 1;
+    }
+    return NetWidth;
+  }
+
+  /// align a split cell with a newline to the first element in the cell
+  void alignToStartOfCell(unsigned Start, unsigned End);
+
   /// Fill \c Replaces with the replacements for all effective changes.
   void generateChanges();
 
Index: clang/lib/Format/WhitespaceManager.cpp
===================================================================
--- clang/lib/Format/WhitespaceManager.cpp
+++ clang/lib/Format/WhitespaceManager.cpp
@@ -13,6 +13,7 @@
 
 #include "WhitespaceManager.h"
 #include "llvm/ADT/STLExtras.h"
+#include <algorithm>
 
 namespace clang {
 namespace format {
@@ -100,6 +101,7 @@
   alignChainedConditionals();
   alignTrailingComments();
   alignEscapedNewlines();
+  alignArrayInitializers();
   generateChanges();
 
   return Replaces;
@@ -944,6 +946,260 @@
   }
 }
 
+void WhitespaceManager::alignArrayInitializers() {
+  if (!Style.AlignArrayOfStructures)
+    return;
+
+  for (unsigned ChangeIndex = 1U, ChangeEnd = Changes.size();
+       ChangeIndex < ChangeEnd; ++ChangeIndex) {
+    auto &C = Changes[ChangeIndex];
+    if (C.Tok->IsArrayInitializer) {
+      bool FoundComplete = false;
+      for (unsigned InsideIndex = ChangeIndex + 1; InsideIndex < ChangeEnd;
+           ++InsideIndex) {
+        if (Changes[InsideIndex].Tok == C.Tok->MatchingParen) {
+          alignArrayInitializers(ChangeIndex, InsideIndex + 1);
+          ChangeIndex = InsideIndex + 1;
+          FoundComplete = true;
+          break;
+        }
+      }
+      if (!FoundComplete)
+        ChangeIndex = ChangeEnd;
+    }
+  }
+}
+
+void WhitespaceManager::alignArrayInitializers(unsigned Start, unsigned End) {
+
+  auto CellDescs = getCells(Start, End);
+  auto &Cells = CellDescs.Cells;
+  auto CellCount = CellDescs.CellCount;
+  auto InitialSpaces = CellDescs.InitialSpaces;
+
+  // Now go through and fixup the spaces
+  auto CellIter = Cells.begin();
+  for (auto i = 0U; i < CellCount; i++, ++CellIter) {
+    auto HasSplit = isSplitCell(*CellIter);
+    unsigned NetWidth = 0U;
+    if (HasSplit)
+      NetWidth = getNetWidth(Cells.begin(), CellIter, InitialSpaces);
+    auto CellWidth =
+        calculateCellWidth(CellIter->Index, CellIter->EndIndex, true);
+    if (Changes[CellIter->Index].NewlinesBefore == 0)
+      CellWidth += NetWidth;
+    for (const auto *Next = CellIter->NextColumnElement; Next != nullptr;
+         Next = Next->NextColumnElement) {
+      auto ThisWidth = calculateCellWidth(Next->Index, Next->EndIndex, true);
+      if (Changes[Next->Index].NewlinesBefore == 0)
+        ThisWidth += NetWidth;
+      CellWidth = std::max(CellWidth, ThisWidth);
+    }
+    if (Changes[CellIter->Index].Tok->is(tok::r_brace)) {
+      // So in here we want to see if there is a brace that falls
+      // on a line that was split. If so on that line we make sure that
+      // the spaces in front of the brace are enough
+      Changes[CellIter->Index].NewlinesBefore = 0;
+      Changes[CellIter->Index].Spaces = 0;
+      for (const auto *Next = CellIter->NextColumnElement; Next != nullptr;
+           Next = Next->NextColumnElement) {
+        Changes[Next->Index].Spaces = 0;
+        Changes[Next->Index].NewlinesBefore = 0;
+      }
+      // Except if the array is empty we need the position of all the
+      // cells immediantly adjacent to this
+      if (CellIter != Cells.begin()) {
+        auto ThisNetWidth = getNetWidth(Cells.begin(), CellIter, InitialSpaces);
+        auto MaxNetWidth = ThisNetWidth;
+        auto RowCount = 1U;
+        auto Offset = std::distance(Cells.begin(), CellIter);
+        for (const auto *Next = CellIter->NextColumnElement; Next != nullptr;
+             Next = Next->NextColumnElement) {
+          auto Start = (Cells.begin() + RowCount * CellCount);
+          auto End = Start + Offset;
+          MaxNetWidth =
+              std::max(MaxNetWidth, getNetWidth(Start, End, InitialSpaces));
+          ++RowCount;
+        }
+        if (ThisNetWidth < MaxNetWidth)
+          Changes[CellIter->Index].Spaces = (MaxNetWidth - ThisNetWidth);
+        RowCount = 1U;
+        for (const auto *Next = CellIter->NextColumnElement; Next != nullptr;
+             Next = Next->NextColumnElement) {
+          auto Start = (Cells.begin() + RowCount * CellCount);
+          auto End = Start + Offset;
+          ThisNetWidth = getNetWidth(Start, End, InitialSpaces);
+          if (ThisNetWidth < MaxNetWidth)
+            Changes[Next->Index].Spaces = (MaxNetWidth - ThisNetWidth);
+          ++RowCount;
+        }
+      }
+    } else {
+      auto ThisWidth =
+          calculateCellWidth(CellIter->Index, CellIter->EndIndex, true) +
+          NetWidth;
+      if (Changes[CellIter->Index].NewlinesBefore == 0) {
+        Changes[CellIter->Index].Spaces = (CellWidth - (ThisWidth + NetWidth));
+        Changes[CellIter->Index].Spaces += (i > 0) ? 1 : 0;
+      }
+      alignToStartOfCell(CellIter->Index, CellIter->EndIndex);
+      for (const auto *Next = CellIter->NextColumnElement; Next != nullptr;
+           Next = Next->NextColumnElement) {
+        ThisWidth =
+            calculateCellWidth(Next->Index, Next->EndIndex, true) + NetWidth;
+        if (Changes[Next->Index].NewlinesBefore == 0) {
+          Changes[Next->Index].Spaces = (CellWidth - ThisWidth);
+          Changes[Next->Index].Spaces += (i > 0) ? 1 : 0;
+        }
+        alignToStartOfCell(Next->Index, Next->EndIndex);
+      }
+    }
+  }
+}
+
+bool WhitespaceManager::isSplitCell(const CellDescription &Cell) {
+  if (Cell.HasSplit)
+    return true;
+  for (const auto *Next = Cell.NextColumnElement; Next != nullptr;
+       Next = Next->NextColumnElement) {
+    if (Next->HasSplit)
+      return true;
+  }
+  return false;
+}
+
+WhitespaceManager::CellDescriptions WhitespaceManager::getCells(unsigned Start,
+                                                                unsigned End) {
+
+  unsigned Depth = 0;
+  unsigned Cell = 0;
+  unsigned CellCount = 0;
+  unsigned InitialSpaces = 0;
+  unsigned InitialTokenLength = 0;
+  std::vector<CellDescription> Cells;
+  const FormatToken *MatchingParen = nullptr;
+  for (unsigned i = Start; i < End; ++i) {
+    auto &C = Changes[i];
+    if (C.Tok->is(tok::l_brace))
+      ++Depth;
+    else if (C.Tok->is(tok::r_brace))
+      --Depth;
+    if (Depth == 2) {
+      if (C.Tok->is(tok::l_brace)) {
+        Cell = 0;
+        MatchingParen = C.Tok->MatchingParen;
+        if (InitialSpaces == 0) {
+          InitialSpaces = C.Spaces + C.TokenLength;
+          InitialTokenLength = C.TokenLength;
+          auto j = i - 1;
+          for (; Changes[j].NewlinesBefore == 0 && j > Start; --j) {
+            InitialSpaces += Changes[j].Spaces + Changes[j].TokenLength;
+            InitialTokenLength += Changes[j].TokenLength;
+          }
+          if (C.NewlinesBefore == 0) {
+            InitialSpaces += Changes[j].Spaces + Changes[j].TokenLength;
+            InitialTokenLength += Changes[j].TokenLength;
+          }
+        }
+      } else if (C.Tok->is(tok::comma)) {
+        if (!Cells.empty())
+          Cells.back().EndIndex = i;
+        Cell++;
+      }
+    } else if (Depth == 1) {
+      if (C.Tok == MatchingParen) {
+        if (!Cells.empty())
+          Cells.back().EndIndex = i;
+        Cells.push_back(CellDescription{i, ++Cell, i + 1, false, nullptr});
+        CellCount = Cell + 1;
+        // Go to the next non-comment and ensure there is a break in front
+        const auto *NextNonComment = C.Tok->getNextNonComment();
+        while (NextNonComment->is(tok::comma))
+          NextNonComment = NextNonComment->getNextNonComment();
+        auto j = i;
+        while (Changes[j].Tok != NextNonComment && j < End)
+          j++;
+        if (j < End && Changes[j].NewlinesBefore == 0 &&
+            Changes[j].Tok->isNot(tok::r_brace)) {
+          Changes[j].NewlinesBefore = 1;
+          // Account for the added token lengths
+          Changes[j].Spaces = InitialSpaces - InitialTokenLength;
+        }
+      } else if (C.Tok->is(tok::comment)) {
+        // Trailing comments stay at a space past the last token
+        C.Spaces = Changes[i - 1].Tok->is(tok::comma) ? 1 : 2;
+      }
+    } else if (Depth == 0 && C.Tok->is(tok::r_brace)) {
+      C.NewlinesBefore = 1;
+    }
+    if (C.Tok->StartsColumn) {
+      // This gets us past tokens that have been split over multiple
+      // lines
+      bool HasSplit = false;
+      if (Changes[i].NewlinesBefore > 0) {
+        // So if we split a line previously and the tail line + this token is
+        // less then the column limit we remove the split here and just put
+        // the column start at a space past the comma
+        auto j = i - 1;
+        if ((j - 1) > Start && Changes[j].Tok->is(tok::comma) &&
+            Changes[j-1].NewlinesBefore > 0) {
+          --j;
+          auto LineLimit = Changes[j].Spaces + Changes[j].TokenLength;
+          if (LineLimit < Style.ColumnLimit) {
+            Changes[i].NewlinesBefore = 0;
+            Changes[i].Spaces = 1;
+          }
+        }
+      }
+      while (Changes[i].NewlinesBefore > 0 && Changes[i].Tok == C.Tok) {
+        Changes[i].Spaces = InitialSpaces;
+        ++i;
+        HasSplit = true;
+      }
+      if (Changes[i].Tok != C.Tok)
+        --i;
+      Cells.push_back(CellDescription{i, Cell, i, HasSplit, nullptr});
+    }
+  }
+
+  // Now link the pointers
+  for (auto CellIter = Cells.begin(); CellIter != Cells.end(); ++CellIter) {
+    if (CellIter->NextColumnElement == nullptr &&
+        ((CellIter + 1) != Cells.end())) {
+      for (auto NextIter = CellIter + 1; NextIter != Cells.end(); ++NextIter) {
+        if (NextIter->Cell == CellIter->Cell) {
+          CellIter->NextColumnElement = &(*NextIter);
+          break;
+        }
+      }
+    }
+  }
+  return {Cells, CellCount, InitialSpaces};
+}
+
+unsigned WhitespaceManager::calculateCellWidth(unsigned Start, unsigned End,
+                                               bool WithSpaces) {
+  unsigned CellWidth = 0;
+  for (auto i = Start; i < End; i++) {
+    if (Changes[i].NewlinesBefore > 0)
+      CellWidth = 0;
+    CellWidth += Changes[i].TokenLength;
+    CellWidth += (WithSpaces ? Changes[i].Spaces : 0);
+  }
+  return CellWidth;
+}
+
+void WhitespaceManager::alignToStartOfCell(unsigned Start, unsigned End) {
+  if ((End - Start) <= 1)
+    return;
+  // If the line is broken anywhere in there make sure everything
+  // is aligned to the parent
+  for (auto i = Start + 1; i < End; i++) {
+    if (Changes[i].NewlinesBefore > 0)
+      Changes[i].Spaces = Changes[Start].Spaces;
+  }
+}
+
 void WhitespaceManager::generateChanges() {
   for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
     const Change &C = Changes[i];
Index: clang/lib/Format/TokenAnnotator.h
===================================================================
--- clang/lib/Format/TokenAnnotator.h
+++ clang/lib/Format/TokenAnnotator.h
@@ -31,7 +31,8 @@
   LT_ObjCProperty, // An @property line.
   LT_Other,
   LT_PreprocessorDirective,
-  LT_VirtualFunctionDecl
+  LT_VirtualFunctionDecl,
+  LT_ArrayOfStructInitializer
 };
 
 class AnnotatedLine {
@@ -189,6 +190,12 @@
 
   void calculateUnbreakableTailLengths(AnnotatedLine &Line);
 
+  void calculateArrayInitializerColumnList(AnnotatedLine &Line);
+
+  FormatToken *calculateInitializerColumnList(AnnotatedLine &Line,
+                                              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,45 @@
   }
 }
 
+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->IsArrayInitializer = true;
+      if (CurrentToken->Next != nullptr)
+        CurrentToken->Next->MustBreakBefore = true;
+      CurrentToken =
+          calculateInitializerColumnList(Line, CurrentToken->Next, Depth + 1);
+    } else {
+      CurrentToken = CurrentToken->Next;
+    }
+  }
+}
+
+FormatToken *TokenAnnotator::calculateInitializerColumnList(
+    AnnotatedLine &Line, FormatToken *CurrentToken, unsigned Depth) {
+  while (CurrentToken != nullptr && CurrentToken != Line.Last) {
+    if (CurrentToken->is(tok::l_brace)) {
+      ++Depth;
+    } else if (CurrentToken->is(tok::r_brace))
+      --Depth;
+    if (Depth == 2 && CurrentToken->isOneOf(tok::l_brace, tok::comma)) {
+      CurrentToken = CurrentToken->Next;
+      if (CurrentToken != nullptr) {
+        CurrentToken->StartsColumn = true;
+        CurrentToken = CurrentToken->Previous;
+      } else
+        break;
+    }
+    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,12 @@
   /// The next token in the unwrapped line.
   FormatToken *Next = nullptr;
 
+  /// The first token in set of column elements
+  bool StartsColumn = false;
+
+  /// This starts an array initializer
+  bool IsArrayInitializer = false;
+
   /// If this token starts a block, this contains all the unwrapped lines
   /// in it.
   SmallVector<AnnotatedLine *, 1> Children;
Index: clang/lib/Format/Format.cpp
===================================================================
--- clang/lib/Format/Format.cpp
+++ clang/lib/Format/Format.cpp
@@ -507,6 +507,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);
@@ -942,6 +943,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.cpp
===================================================================
--- clang/lib/Format/ContinuationIndenter.cpp
+++ clang/lib/Format/ContinuationIndenter.cpp
@@ -600,7 +600,6 @@
   if (!DryRun)
     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 &&
@@ -2191,7 +2190,6 @@
       if (!DryRun)
         Token->insertBreak(LineIndex, TailOffset, Split, ContentIndent,
                            Whitespaces);
-
       Penalty += NewBreakPenalty;
       TailOffset += Split.first + Split.second;
       RemainingTokenColumns = NewRemainingTokenColumns;
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
@@ -3258,6 +3270,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
@@ -259,6 +259,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