]> granicus.if.org Git - clang/commitdiff
[clang-format] Add support of consecutive declarations alignment
authorDaniel Jasper <djasper@google.com>
Thu, 1 Oct 2015 10:06:54 +0000 (10:06 +0000)
committerDaniel Jasper <djasper@google.com>
Thu, 1 Oct 2015 10:06:54 +0000 (10:06 +0000)
This allows clang-format to align identifiers in consecutive
declarations. This is useful for increasing the readability of the code
in the same way the alignment of assignations is.

The code is a slightly modified version of the consecutive assignment
alignment code. Currently only the identifiers are aligned, and there is
no support of alignment of the pointer star or reference symbol.

The patch also solve the issue of alignments not being possible due to
the ColumnLimit for both the existing AlignConsecutiveAligments and the
new AlignConsecutiveDeclarations.

Patch by Beren Minor, thank you.

Review: http://reviews.llvm.org/D12362

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@248999 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Format/Format.h
lib/Format/Format.cpp
lib/Format/WhitespaceManager.cpp
lib/Format/WhitespaceManager.h
unittests/Format/FormatTest.cpp

index 19d32f90b81841c2f4e95e3e7ad57974a46408fa..8f28de71284003083bc93fb91ca21d5535f95975 100644 (file)
@@ -64,6 +64,17 @@ struct FormatStyle {
   /// \endcode
   bool AlignConsecutiveAssignments;
 
+  /// \brief If \c true, aligns consecutive declarations.
+  ///
+  /// This will align the declaration names of consecutive lines. This
+  /// will result in formattings like
+  /// \code
+  /// int         aaaa = 12;
+  /// float       b = 23;
+  /// std::string ccc = 23;
+  /// \endcode
+  bool AlignConsecutiveDeclarations;
+
   /// \brief If \c true, aligns escaped newlines as far left as possible.
   /// Otherwise puts them into the right-most column.
   bool AlignEscapedNewlinesLeft;
@@ -495,6 +506,7 @@ struct FormatStyle {
     return AccessModifierOffset == R.AccessModifierOffset &&
            AlignAfterOpenBracket == R.AlignAfterOpenBracket &&
            AlignConsecutiveAssignments == R.AlignConsecutiveAssignments &&
+           AlignConsecutiveDeclarations == R.AlignConsecutiveDeclarations &&
            AlignEscapedNewlinesLeft == R.AlignEscapedNewlinesLeft &&
            AlignOperands == R.AlignOperands &&
            AlignTrailingComments == R.AlignTrailingComments &&
index aa227a1f00a82ca14b075809e14a6f7ccd168fc5..d8a15e4cdfbbb1b493166284d3dd974948ce7d4b 100644 (file)
@@ -201,6 +201,8 @@ template <> struct MappingTraits<FormatStyle> {
     IO.mapOptional("AlignAfterOpenBracket", Style.AlignAfterOpenBracket);
     IO.mapOptional("AlignConsecutiveAssignments",
                    Style.AlignConsecutiveAssignments);
+    IO.mapOptional("AlignConsecutiveDeclarations",
+                   Style.AlignConsecutiveDeclarations);
     IO.mapOptional("AlignEscapedNewlinesLeft", Style.AlignEscapedNewlinesLeft);
     IO.mapOptional("AlignOperands", Style.AlignOperands);
     IO.mapOptional("AlignTrailingComments", Style.AlignTrailingComments);
@@ -416,6 +418,7 @@ FormatStyle getLLVMStyle() {
   LLVMStyle.AlignOperands = true;
   LLVMStyle.AlignTrailingComments = true;
   LLVMStyle.AlignConsecutiveAssignments = false;
+  LLVMStyle.AlignConsecutiveDeclarations = false;
   LLVMStyle.AllowAllParametersOfDeclarationOnNextLine = true;
   LLVMStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_All;
   LLVMStyle.AllowShortBlocksOnASingleLine = false;
index 3d6040e90cd5a807d3bec18c97449686a7c13fc7..18d73b553e6f06db636cab5937fbe342bf821743 100644 (file)
@@ -29,13 +29,15 @@ WhitespaceManager::Change::Change(
     bool CreateReplacement, const SourceRange &OriginalWhitespaceRange,
     unsigned IndentLevel, int Spaces, unsigned StartOfTokenColumn,
     unsigned NewlinesBefore, StringRef PreviousLinePostfix,
-    StringRef CurrentLinePrefix, tok::TokenKind Kind, bool ContinuesPPDirective)
+    StringRef CurrentLinePrefix, tok::TokenKind Kind, bool ContinuesPPDirective,
+    bool IsStartOfDeclName)
     : CreateReplacement(CreateReplacement),
       OriginalWhitespaceRange(OriginalWhitespaceRange),
       StartOfTokenColumn(StartOfTokenColumn), NewlinesBefore(NewlinesBefore),
       PreviousLinePostfix(PreviousLinePostfix),
       CurrentLinePrefix(CurrentLinePrefix), Kind(Kind),
-      ContinuesPPDirective(ContinuesPPDirective), IndentLevel(IndentLevel),
+      ContinuesPPDirective(ContinuesPPDirective),
+      IsStartOfDeclName(IsStartOfDeclName), IndentLevel(IndentLevel),
       Spaces(Spaces), IsTrailingComment(false), TokenLength(0),
       PreviousEndOfTokenColumn(0), EscapedNewlineColumn(0),
       StartOfBlockComment(nullptr), IndentationOffset(0) {}
@@ -52,19 +54,21 @@ void WhitespaceManager::replaceWhitespace(FormatToken &Tok, unsigned Newlines,
   if (Tok.Finalized)
     return;
   Tok.Decision = (Newlines > 0) ? FD_Break : FD_Continue;
-  Changes.push_back(Change(true, Tok.WhitespaceRange, IndentLevel, Spaces,
-                           StartOfTokenColumn, Newlines, "", "",
-                           Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst));
+  Changes.push_back(
+      Change(true, Tok.WhitespaceRange, IndentLevel, Spaces, StartOfTokenColumn,
+             Newlines, "", "", Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst,
+             Tok.is(TT_StartOfName) || Tok.is(TT_FunctionDeclarationName)));
 }
 
 void WhitespaceManager::addUntouchableToken(const FormatToken &Tok,
                                             bool InPPDirective) {
   if (Tok.Finalized)
     return;
-  Changes.push_back(Change(false, Tok.WhitespaceRange, /*IndentLevel=*/0,
-                           /*Spaces=*/0, Tok.OriginalColumn, Tok.NewlinesBefore,
-                           "", "", Tok.Tok.getKind(),
-                           InPPDirective && !Tok.IsFirst));
+  Changes.push_back(
+      Change(false, Tok.WhitespaceRange, /*IndentLevel=*/0,
+             /*Spaces=*/0, Tok.OriginalColumn, Tok.NewlinesBefore, "", "",
+             Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst,
+             Tok.is(TT_StartOfName) || Tok.is(TT_FunctionDeclarationName)));
 }
 
 void WhitespaceManager::replaceWhitespaceInToken(
@@ -84,7 +88,8 @@ void WhitespaceManager::replaceWhitespaceInToken(
       // calculate the new length of the comment and to calculate the changes
       // for which to do the alignment when aligning comments.
       Tok.is(TT_LineComment) && Newlines > 0 ? tok::comment : tok::unknown,
-      InPPDirective && !Tok.IsFirst));
+      InPPDirective && !Tok.IsFirst,
+      Tok.is(TT_StartOfName) || Tok.is(TT_FunctionDeclarationName)));
 }
 
 const tooling::Replacements &WhitespaceManager::generateReplacements() {
@@ -93,6 +98,7 @@ const tooling::Replacements &WhitespaceManager::generateReplacements() {
 
   std::sort(Changes.begin(), Changes.end(), Change::IsBeforeInFile(SourceMgr));
   calculateLineBreakInformation();
+  alignConsecutiveDeclarations();
   alignConsecutiveAssignments();
   alignTrailingComments();
   alignEscapedNewlines();
@@ -152,6 +158,7 @@ void WhitespaceManager::alignConsecutiveAssignments() {
     return;
 
   unsigned MinColumn = 0;
+  unsigned MaxColumn = UINT_MAX;
   unsigned StartOfSequence = 0;
   unsigned EndOfSequence = 0;
   bool FoundAssignmentOnLine = false;
@@ -168,6 +175,7 @@ void WhitespaceManager::alignConsecutiveAssignments() {
     if (StartOfSequence > 0 && StartOfSequence < EndOfSequence)
       alignConsecutiveAssignments(StartOfSequence, EndOfSequence, MinColumn);
     MinColumn = 0;
+    MaxColumn = UINT_MAX;
     StartOfSequence = 0;
     EndOfSequence = 0;
   };
@@ -207,7 +215,18 @@ void WhitespaceManager::alignConsecutiveAssignments() {
         StartOfSequence = i;
 
       unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
+      int LineLengthAfter = -Changes[i].Spaces;
+      for (unsigned j = i; j != e && Changes[j].NewlinesBefore == 0; ++j)
+        LineLengthAfter += Changes[j].Spaces + Changes[j].TokenLength;
+      unsigned ChangeMaxColumn = Style.ColumnLimit - LineLengthAfter;
+
+      if (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) {
+        AlignSequence();
+        StartOfSequence = i;
+      }
+
       MinColumn = std::max(MinColumn, ChangeMinColumn);
+      MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
     }
   }
 
@@ -242,6 +261,110 @@ void WhitespaceManager::alignConsecutiveAssignments(unsigned Start,
   }
 }
 
+// Walk through all of the changes and find sequences of declaration names to
+// align.  To do so, keep track of the lines and whether or not a name was found
+// on align. If a name is found on a line, extend the current sequence. If the
+// current line cannot be part of a sequence, e.g. because there is an empty
+// line before it or it contains non-declarations, finalize the previous
+// sequence.
+void WhitespaceManager::alignConsecutiveDeclarations() {
+  if (!Style.AlignConsecutiveDeclarations)
+    return;
+
+  unsigned MinColumn = 0;
+  unsigned MaxColumn = UINT_MAX;
+  unsigned StartOfSequence = 0;
+  unsigned EndOfSequence = 0;
+  bool FoundDeclarationOnLine = false;
+  bool FoundLeftBraceOnLine = false;
+  bool FoundLeftParenOnLine = false;
+
+  auto AlignSequence = [&] {
+    if (StartOfSequence > 0 && StartOfSequence < EndOfSequence)
+      alignConsecutiveDeclarations(StartOfSequence, EndOfSequence, MinColumn);
+    MinColumn = 0;
+    MaxColumn = UINT_MAX;
+    StartOfSequence = 0;
+    EndOfSequence = 0;
+  };
+
+  for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
+    if (Changes[i].NewlinesBefore != 0) {
+      EndOfSequence = i;
+      if (Changes[i].NewlinesBefore > 1 || !FoundDeclarationOnLine ||
+          FoundLeftBraceOnLine || FoundLeftParenOnLine)
+        AlignSequence();
+      FoundDeclarationOnLine = false;
+      FoundLeftBraceOnLine = false;
+      FoundLeftParenOnLine = false;
+    }
+
+    if (Changes[i].Kind == tok::r_brace) {
+      if (!FoundLeftBraceOnLine)
+        AlignSequence();
+      FoundLeftBraceOnLine = false;
+    } else if (Changes[i].Kind == tok::l_brace) {
+      FoundLeftBraceOnLine = true;
+      if (!FoundDeclarationOnLine)
+        AlignSequence();
+    } else if (Changes[i].Kind == tok::r_paren) {
+      if (!FoundLeftParenOnLine)
+        AlignSequence();
+      FoundLeftParenOnLine = false;
+    } else if (Changes[i].Kind == tok::l_paren) {
+      FoundLeftParenOnLine = true;
+      if (!FoundDeclarationOnLine)
+        AlignSequence();
+    } else if (!FoundDeclarationOnLine && !FoundLeftBraceOnLine &&
+               !FoundLeftParenOnLine && Changes[i].IsStartOfDeclName) {
+      FoundDeclarationOnLine = true;
+      if (StartOfSequence == 0)
+        StartOfSequence = i;
+
+      unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
+      int LineLengthAfter = -Changes[i].Spaces;
+      for (unsigned j = i; j != e && Changes[j].NewlinesBefore == 0; ++j)
+        LineLengthAfter += Changes[j].Spaces + Changes[j].TokenLength;
+      unsigned ChangeMaxColumn = Style.ColumnLimit - LineLengthAfter;
+
+      if (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) {
+        AlignSequence();
+        StartOfSequence = i;
+      }
+
+      MinColumn = std::max(MinColumn, ChangeMinColumn);
+      MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
+    }
+  }
+
+  EndOfSequence = Changes.size();
+  AlignSequence();
+}
+
+void WhitespaceManager::alignConsecutiveDeclarations(unsigned Start,
+                                                     unsigned End,
+                                                     unsigned Column) {
+  bool FoundDeclarationOnLine = false;
+  int Shift = 0;
+  for (unsigned i = Start; i != End; ++i) {
+    if (Changes[i].NewlinesBefore != 0) {
+      FoundDeclarationOnLine = false;
+      Shift = 0;
+    }
+
+    if (!FoundDeclarationOnLine && Changes[i].IsStartOfDeclName) {
+      FoundDeclarationOnLine = true;
+      Shift = Column - Changes[i].StartOfTokenColumn;
+      Changes[i].Spaces += Shift;
+    }
+
+    assert(Shift >= 0);
+    Changes[i].StartOfTokenColumn += Shift;
+    if (i + 1 != Changes.size())
+      Changes[i + 1].PreviousEndOfTokenColumn += Shift;
+  }
+}
+
 void WhitespaceManager::alignTrailingComments() {
   unsigned MinColumn = 0;
   unsigned MaxColumn = UINT_MAX;
index d973838329815f3fecbe195a98ffb1528551c9e4..8e03c6d3fc7be960bfad8786428121459f1e1e90 100644 (file)
@@ -110,7 +110,7 @@ private:
            unsigned IndentLevel, int Spaces, unsigned StartOfTokenColumn,
            unsigned NewlinesBefore, StringRef PreviousLinePostfix,
            StringRef CurrentLinePrefix, tok::TokenKind Kind,
-           bool ContinuesPPDirective);
+           bool ContinuesPPDirective, bool IsStartOfDeclName);
 
     bool CreateReplacement;
     // Changes might be in the middle of a token, so we cannot just keep the
@@ -126,6 +126,7 @@ private:
     // the \c BreakableToken is still doing its own alignment.
     tok::TokenKind Kind;
     bool ContinuesPPDirective;
+    bool IsStartOfDeclName;
 
     // The number of nested blocks the token is in. This is used to add tabs
     // only for the indentation, and not for alignment, when
@@ -173,6 +174,14 @@ private:
   void alignConsecutiveAssignments(unsigned Start, unsigned End,
                                    unsigned Column);
 
+  /// \brief Align consecutive declarations over all \c Changes.
+  void alignConsecutiveDeclarations();
+
+  /// \brief Align consecutive declarations from change \p Start to change \p
+  /// End at the specified \p Column.
+  void alignConsecutiveDeclarations(unsigned Start, unsigned End,
+                                    unsigned Column);
+
   /// \brief Align trailing comments over all \c Changes.
   void alignTrailingComments();
 
index b0c9d46b1ac1bf4c30a43dbf6eafe81db0a5adf4..0921b38093fe0b424059eb9dc78705e6329dcd89 100644 (file)
@@ -8645,6 +8645,189 @@ TEST_F(FormatTest, AlignConsecutiveAssignments) {
       Alignment);
 }
 
+TEST_F(FormatTest, AlignConsecutiveDeclarations) {
+  FormatStyle Alignment = getLLVMStyle();
+  Alignment.AlignConsecutiveDeclarations = false;
+  verifyFormat("float const a = 5;\n"
+               "int oneTwoThree = 123;",
+               Alignment);
+  verifyFormat("int a = 5;\n"
+               "float const oneTwoThree = 123;",
+               Alignment);
+
+  Alignment.AlignConsecutiveDeclarations = true;
+  verifyFormat("float const a = 5;\n"
+               "int         oneTwoThree = 123;",
+               Alignment);
+  verifyFormat("int         a = method();\n"
+               "float const oneTwoThree = 133;",
+               Alignment);
+  verifyFormat("int i = 1, j = 10;\n"
+               "something = 2000;",
+               Alignment);
+  verifyFormat("something = 2000;\n"
+               "int i = 1, j = 10;\n",
+               Alignment);
+  verifyFormat("float      something = 2000;\n"
+               "double     another = 911;\n"
+               "int        i = 1, j = 10;\n"
+               "const int *oneMore = 1;\n"
+               "unsigned   i = 2;",
+               Alignment);
+  verifyFormat("float a = 5;\n"
+               "int   one = 1;\n"
+               "method();\n"
+               "const double       oneTwoThree = 123;\n"
+               "const unsigned int oneTwo = 12;",
+               Alignment);
+  verifyFormat("int      oneTwoThree{0}; // comment\n"
+               "unsigned oneTwo;         // comment",
+               Alignment);
+  EXPECT_EQ("float const a = 5;\n"
+            "\n"
+            "int oneTwoThree = 123;",
+            format("float const   a = 5;\n"
+                   "\n"
+                   "int           oneTwoThree= 123;",
+                   Alignment));
+  EXPECT_EQ("float a = 5;\n"
+            "int   one = 1;\n"
+            "\n"
+            "unsigned oneTwoThree = 123;",
+            format("float    a = 5;\n"
+                   "int      one = 1;\n"
+                   "\n"
+                   "unsigned oneTwoThree = 123;",
+                   Alignment));
+  EXPECT_EQ("float a = 5;\n"
+            "int   one = 1;\n"
+            "\n"
+            "unsigned oneTwoThree = 123;\n"
+            "int      oneTwo = 12;",
+            format("float    a = 5;\n"
+                   "int one = 1;\n"
+                   "\n"
+                   "unsigned oneTwoThree = 123;\n"
+                   "int oneTwo = 12;",
+                   Alignment));
+  Alignment.AlignConsecutiveAssignments = true;
+  verifyFormat("float      something = 2000;\n"
+               "double     another   = 911;\n"
+               "int        i = 1, j = 10;\n"
+               "const int *oneMore = 1;\n"
+               "unsigned   i       = 2;",
+               Alignment);
+  verifyFormat("int      oneTwoThree = {0}; // comment\n"
+               "unsigned oneTwo      = 0;   // comment",
+               Alignment);
+  EXPECT_EQ("void SomeFunction(int parameter = 0) {\n"
+            "  int const i   = 1;\n"
+            "  int *     j   = 2;\n"
+            "  int       big = 10000;\n"
+            "\n"
+            "  unsigned oneTwoThree = 123;\n"
+            "  int      oneTwo      = 12;\n"
+            "  method();\n"
+            "  float k  = 2;\n"
+            "  int   ll = 10000;\n"
+            "}",
+            format("void SomeFunction(int parameter= 0) {\n"
+                   " int const  i= 1;\n"
+                   "  int *j=2;\n"
+                   " int big  =  10000;\n"
+                   "\n"
+                   "unsigned oneTwoThree  =123;\n"
+                   "int oneTwo = 12;\n"
+                   "  method();\n"
+                   "float k= 2;\n"
+                   "int ll=10000;\n"
+                   "}",
+                   Alignment));
+  Alignment.AlignConsecutiveAssignments = false;
+  Alignment.AlignEscapedNewlinesLeft = true;
+  verifyFormat("#define A              \\\n"
+               "  int       aaaa = 12; \\\n"
+               "  float     b = 23;    \\\n"
+               "  const int ccc = 234; \\\n"
+               "  unsigned  dddddddddd = 2345;",
+               Alignment);
+  Alignment.AlignEscapedNewlinesLeft = false;
+  Alignment.ColumnLimit = 30;
+  verifyFormat("#define A                    \\\n"
+               "  int       aaaa = 12;       \\\n"
+               "  float     b = 23;          \\\n"
+               "  const int ccc = 234;       \\\n"
+               "  int       dddddddddd = 2345;",
+               Alignment);
+  Alignment.ColumnLimit = 80;
+  verifyFormat("void SomeFunction(int parameter = 1, int i = 2, int j = 3, int "
+               "k = 4, int l = 5,\n"
+               "                  int m = 6) {\n"
+               "  const int j = 10;\n"
+               "  otherThing = 1;\n"
+               "}",
+               Alignment);
+  verifyFormat("void SomeFunction(int parameter = 0) {\n"
+               "  int const i = 1;\n"
+               "  int *     j = 2;\n"
+               "  int       big = 10000;\n"
+               "}",
+               Alignment);
+  verifyFormat("class C {\n"
+               "public:\n"
+               "  int          i = 1;\n"
+               "  virtual void f() = 0;\n"
+               "};",
+               Alignment);
+  verifyFormat("float i = 1;\n"
+               "if (SomeType t = getSomething()) {\n"
+               "}\n"
+               "const unsigned j = 2;\n"
+               "int            big = 10000;",
+               Alignment);
+  verifyFormat("float j = 7;\n"
+               "for (int k = 0; k < N; ++k) {\n"
+               "}\n"
+               "unsigned j = 2;\n"
+               "int      big = 10000;\n"
+               "}",
+               Alignment);
+  Alignment.BreakBeforeBinaryOperators = FormatStyle::BOS_All;
+  verifyFormat("float              i = 1;\n"
+               "LooooooooooongType loooooooooooooooooooooongVariable\n"
+               "    = someLooooooooooooooooongFunction();\n"
+               "int j = 2;",
+               Alignment);
+  Alignment.BreakBeforeBinaryOperators = FormatStyle::BOS_None;
+  verifyFormat("int                i = 1;\n"
+               "LooooooooooongType loooooooooooooooooooooongVariable =\n"
+               "    someLooooooooooooooooongFunction();\n"
+               "int j = 2;",
+               Alignment);
+  // FIXME: Should align all three declarations
+  verifyFormat(
+      "int      i = 1;\n"
+      "SomeType a = SomeFunction(looooooooooooooooooooooongParameterA,\n"
+      "                          loooooooooooooooooooooongParameterB);\n"
+      "int j = 2;",
+      Alignment);
+
+  // Test interactions with ColumnLimit and AlignConsecutiveAssignments:
+  // We expect declarations and assignments to align, as long as it doesn't
+  // exceed the column limit, starting a new alignemnt sequence whenever it
+  // happens.
+  Alignment.AlignConsecutiveAssignments = true;
+  Alignment.ColumnLimit = 30;
+  verifyFormat("float    ii              = 1;\n"
+               "unsigned j               = 2;\n"
+               "int someVerylongVariable = 1;\n"
+               "AnotherLongType  ll = 123456;\n"
+               "VeryVeryLongType k  = 2;\n"
+               "int              myvar = 1;",
+               Alignment);
+  Alignment.ColumnLimit = 80;
+}
+
 TEST_F(FormatTest, LinuxBraceBreaking) {
   FormatStyle LinuxBraceStyle = getLLVMStyle();
   LinuxBraceStyle.BreakBeforeBraces = FormatStyle::BS_Linux;
@@ -9290,6 +9473,7 @@ TEST_F(FormatTest, ParsesConfigurationBools) {
   CHECK_PARSE_BOOL(AlignOperands);
   CHECK_PARSE_BOOL(AlignTrailingComments);
   CHECK_PARSE_BOOL(AlignConsecutiveAssignments);
+  CHECK_PARSE_BOOL(AlignConsecutiveDeclarations);
   CHECK_PARSE_BOOL(AllowAllParametersOfDeclarationOnNextLine);
   CHECK_PARSE_BOOL(AllowShortBlocksOnASingleLine);
   CHECK_PARSE_BOOL(AllowShortCaseLabelsOnASingleLine);