Index: docs/ClangFormatStyleOptions.rst =================================================================== --- docs/ClangFormatStyleOptions.rst +++ docs/ClangFormatStyleOptions.rst @@ -160,6 +160,17 @@ argument2); \endcode +**AlignConsecutiveAssignments** (``bool``) + If ``true``, aligns consecutive assignments. + + This will align the assignment operators of consecutive lines. This + will result in formattings like + \code + int aaaa = 12; + int b = 23; + int ccc = 23; + \endcode + **AlignEscapedNewlinesLeft** (``bool``) If ``true``, aligns escaped newlines as far left as possible. Otherwise puts them into the right-most column. Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -247,6 +247,17 @@ /// \brief If \c true, aligns trailing comments. bool AlignTrailingComments; + /// \brief If \c true, aligns consecutive assignments. + /// + /// This will align the assignment operators of consecutive lines. This + /// will result in formattings like + /// \code + /// int aaaa = 12; + /// int b = 23; + /// int ccc = 23; + /// \endcode + bool AlignConsecutiveAssignments; + /// \brief If \c true, aligns escaped newlines as far left as possible. /// Otherwise puts them into the right-most column. bool AlignEscapedNewlinesLeft; Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -174,6 +174,7 @@ IO.mapOptional("AlignEscapedNewlinesLeft", Style.AlignEscapedNewlinesLeft); IO.mapOptional("AlignOperands", Style.AlignOperands); IO.mapOptional("AlignTrailingComments", Style.AlignTrailingComments); + IO.mapOptional("AlignConsecutiveAssignments", Style.AlignConsecutiveAssignments); IO.mapOptional("AllowAllParametersOfDeclarationOnNextLine", Style.AllowAllParametersOfDeclarationOnNextLine); IO.mapOptional("AllowShortBlocksOnASingleLine", @@ -329,6 +330,7 @@ LLVMStyle.AlignAfterOpenBracket = true; LLVMStyle.AlignOperands = true; LLVMStyle.AlignTrailingComments = true; + LLVMStyle.AlignConsecutiveAssignments = false; LLVMStyle.AllowAllParametersOfDeclarationOnNextLine = true; LLVMStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_All; LLVMStyle.AllowShortBlocksOnASingleLine = false; Index: lib/Format/WhitespaceManager.h =================================================================== --- lib/Format/WhitespaceManager.h +++ lib/Format/WhitespaceManager.h @@ -164,6 +164,13 @@ /// \c EscapedNewlineColumn for the first tokens or token parts in a line. void calculateLineBreakInformation(); + /// \brief Align consecutive assignments over all \c Changes. + void alignConsecutiveAssignments(); + + /// \brief Align consecutive assignments from change \p Start to change \p End at + /// the specified \p Column. + void alignConsecutiveAssignments(unsigned Start, unsigned End, unsigned Column); + /// \brief Align trailing comments over all \c Changes. void alignTrailingComments(); Index: lib/Format/WhitespaceManager.cpp =================================================================== --- lib/Format/WhitespaceManager.cpp +++ lib/Format/WhitespaceManager.cpp @@ -93,6 +93,7 @@ std::sort(Changes.begin(), Changes.end(), Change::IsBeforeInFile(SourceMgr)); calculateLineBreakInformation(); + alignConsecutiveAssignments(); alignTrailingComments(); alignEscapedNewlines(); generateChanges(); @@ -141,6 +142,74 @@ } } +void WhitespaceManager::alignConsecutiveAssignments() { + if (!Style.AlignConsecutiveAssignments) + return; + + unsigned MinColumn = 0; + unsigned StartOfSequence = 0; + bool FoundEqualOnLine = false; + unsigned CurrentLine = 0; + for (unsigned i = 0, e = Changes.size(); i != e; ++i) { + if (Changes[i].NewlinesBefore == 1) { + CurrentLine += Changes[i].NewlinesBefore; + if (!FoundEqualOnLine && StartOfSequence > 0) { + alignConsecutiveAssignments(StartOfSequence, i, MinColumn); + MinColumn = 0; + StartOfSequence = 0; + } + FoundEqualOnLine = false; + } else if (Changes[i].NewlinesBefore > 1) { + CurrentLine += Changes[i].NewlinesBefore; + if (StartOfSequence > 0) { + alignConsecutiveAssignments(StartOfSequence, i, MinColumn); + MinColumn = 0; + StartOfSequence = 0; + } + FoundEqualOnLine = false; + } + + if (Changes[i].Kind != tok::equal) + continue; + + FoundEqualOnLine = true; + if (StartOfSequence == 0) + StartOfSequence = i; + + unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn; + MinColumn = std::max(MinColumn, ChangeMinColumn); + } + + if (StartOfSequence > 0) + alignConsecutiveAssignments(StartOfSequence, Changes.size(), MinColumn); +} + +void WhitespaceManager::alignConsecutiveAssignments(unsigned Start, unsigned End, + unsigned Column) { + bool AlignedEquals = false; + int PreviousEqualsShift = 0; + for (unsigned i = Start; i != End; ++i) { + int Shift = 0; + if (Changes[i].NewlinesBefore > 0) + AlignedEquals = false; + if (Changes[i].Kind == tok::equal) { + Shift = Column - Changes[i].StartOfTokenColumn; + AlignedEquals = true; + PreviousEqualsShift = Shift; + } + assert(Shift >= 0); + Changes[i].Spaces += Shift; + if (i + 1 != Changes.size()) + Changes[i + 1].PreviousEndOfTokenColumn += Shift; + Changes[i].StartOfTokenColumn += Shift; + if (AlignedEquals) { + Changes[i].StartOfTokenColumn += PreviousEqualsShift; + if (i + 1 != Changes.size()) + Changes[i + 1].PreviousEndOfTokenColumn += PreviousEqualsShift; + } + } +} + void WhitespaceManager::alignTrailingComments() { unsigned MinColumn = 0; unsigned MaxColumn = UINT_MAX; Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -8347,6 +8347,91 @@ verifyFormat("a or_eq 8;", Spaces); } +TEST_F(FormatTest, AlignConsecutiveAssignments) { + FormatStyle Alignment = getLLVMStyle(); + Alignment.AlignConsecutiveAssignments = false; + verifyFormat("int a = 5;\n" + "int oneTwoThree = 123;", Alignment); + EXPECT_EQ("int a = 5;\n" + "int oneTwoThree = 123;", + format("int a =5;\n" + "int oneTwoThree= 123;", Alignment)); + + Alignment.AlignConsecutiveAssignments = true; + verifyFormat("int a = 5;\n" + "int oneTwoThree = 123;", Alignment); + EXPECT_EQ("int a = 5;\n" + "int oneTwoThree = 123;", + format("int a= 5;\n" + "int oneTwoThree = 123;", Alignment)); + EXPECT_EQ("int a = 5;\n" + "\n" + "int oneTwoThree = 123;", + format("int a = 5;\n" + "\n" + "int oneTwoThree= 123;", Alignment)); + EXPECT_EQ("int a = 5;\n" + "int one = 1;\n" + "\n" + "int oneTwoThree = 123;", + format("int a = 5;\n" + "int one = 1;\n" + "\n" + "int oneTwoThree = 123;", Alignment)); + EXPECT_EQ("int a = 5;\n" + "int one = 1;\n" + "\n" + "int oneTwoThree = 123;\n" + "int oneTwo = 12;", + format("int a = 5;\n" + "int one = 1;\n" + "\n" + "int oneTwoThree = 123;\n" + "int oneTwo = 12;", Alignment)); + EXPECT_EQ("int a = 5;\n" + "int one = 1;\n" + "method();\n" + "int oneTwoThree = 123;\n" + "int oneTwo = 12;", + format("int a = 5;\n" + "int one = 1;\n" + "method();\n" + "int oneTwoThree = 123;\n" + "int oneTwo = 12;", Alignment)); + EXPECT_EQ( + "int oneTwoThree = 123; // comment\n" + "int oneTwo = 12; // comment", + format( + "int oneTwoThree = 123;// comment\n" + "int oneTwo = 12;// comment", Alignment)); + Alignment.AlignEscapedNewlinesLeft = true; + EXPECT_EQ( + "#define A \\\n" + " int aaaa = 12; \\\n" + " int b = 23; \\\n" + " int ccc = 23; \\\n" + " int dddddddddd = 2345;", + format( + "#define A \\\n" + " int aaaa = 12; \\\n" + " int b = 23; \\\n" + " int ccc = 23; \\\n" + " int dddddddddd = 2345;", Alignment)); + Alignment.AlignEscapedNewlinesLeft = false; + EXPECT_EQ( + "#define A \\\n" + " int aaaa = 12; \\\n" + " int b = 23; \\\n" + " int ccc = 23; \\\n" + " int dddddddddd = 2345;", + format( + "#define A \\\n" + " int aaaa = 12; \\\n" + " int b = 23; \\\n" + " int ccc = 23; \\\n" + " int dddddddddd = 2345;", Alignment)); +} + TEST_F(FormatTest, LinuxBraceBreaking) { FormatStyle LinuxBraceStyle = getLLVMStyle(); LinuxBraceStyle.BreakBeforeBraces = FormatStyle::BS_Linux; @@ -8886,6 +8971,7 @@ CHECK_PARSE_BOOL(AlignEscapedNewlinesLeft); CHECK_PARSE_BOOL(AlignOperands); CHECK_PARSE_BOOL(AlignTrailingComments); + CHECK_PARSE_BOOL(AlignConsecutiveAssignments); CHECK_PARSE_BOOL(AllowAllParametersOfDeclarationOnNextLine); CHECK_PARSE_BOOL(AllowShortBlocksOnASingleLine); CHECK_PARSE_BOOL(AllowShortCaseLabelsOnASingleLine);