Index: docs/ClangFormatStyleOptions.rst =================================================================== --- docs/ClangFormatStyleOptions.rst +++ docs/ClangFormatStyleOptions.rst @@ -160,6 +160,17 @@ argument2); \endcode +**AlignAssignmentOperators** (``bool``) + If ``true``, aligns variable 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 variable 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 AlignAssignmentOperators; + /// \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("AlignAssignmentOperators", Style.AlignAssignmentOperators); IO.mapOptional("AllowAllParametersOfDeclarationOnNextLine", Style.AllowAllParametersOfDeclarationOnNextLine); IO.mapOptional("AllowShortBlocksOnASingleLine", @@ -329,6 +330,7 @@ LLVMStyle.AlignAfterOpenBracket = true; LLVMStyle.AlignOperands = true; LLVMStyle.AlignTrailingComments = true; + LLVMStyle.AlignAssignmentOperators = 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 @@ -178,6 +178,13 @@ /// the specified \p Column. void alignEscapedNewlines(unsigned Start, unsigned End, unsigned Column); + /// \brief Align assignment operators over all \c Changes. + void alignAssignmentOperators(); + + /// \brief Align assignment operators from change \p Start to change \p End at + /// the specified \p Column. + void alignAssignmentOperators(unsigned Start, unsigned End, unsigned Column); + /// \brief Fill \c Replaces with the replacements for all effective changes. void generateChanges(); 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(); + alignAssignmentOperators(); alignTrailingComments(); alignEscapedNewlines(); generateChanges(); @@ -263,6 +264,72 @@ } } +void WhitespaceManager::alignAssignmentOperators() { + if (!Style.AlignAssignmentOperators) + 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) { + alignAssignmentOperators(StartOfSequence, i, MinColumn); + MinColumn = 0; + StartOfSequence = 0; + } + FoundEqualOnLine = false; + } else if (Changes[i].NewlinesBefore > 1) { + CurrentLine += Changes[i].NewlinesBefore; + if (StartOfSequence > 0) { + alignAssignmentOperators(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) + alignAssignmentOperators(StartOfSequence, Changes.size(), MinColumn); +} + +void WhitespaceManager::alignAssignmentOperators(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; + } +} + void WhitespaceManager::generateChanges() { for (unsigned i = 0, e = Changes.size(); i != e; ++i) { const Change &C = Changes[i]; Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -8347,6 +8347,65 @@ verifyFormat("a or_eq 8;", Spaces); } +TEST_F(FormatTest, AlignAssignmentOperators) { + FormatStyle Alignment = getLLVMStyle(); + Alignment.AlignAssignmentOperators = 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.AlignAssignmentOperators = 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)); +} + TEST_F(FormatTest, LinuxBraceBreaking) { FormatStyle LinuxBraceStyle = getLLVMStyle(); LinuxBraceStyle.BreakBeforeBraces = FormatStyle::BS_Linux; @@ -8886,6 +8945,7 @@ CHECK_PARSE_BOOL(AlignEscapedNewlinesLeft); CHECK_PARSE_BOOL(AlignOperands); CHECK_PARSE_BOOL(AlignTrailingComments); + CHECK_PARSE_BOOL(AlignAssignmentOperators); CHECK_PARSE_BOOL(AllowAllParametersOfDeclarationOnNextLine); CHECK_PARSE_BOOL(AllowShortBlocksOnASingleLine); CHECK_PARSE_BOOL(AllowShortCaseLabelsOnASingleLine);