Index: docs/ClangFormatStyleOptions.rst =================================================================== --- docs/ClangFormatStyleOptions.rst +++ docs/ClangFormatStyleOptions.rst @@ -185,6 +185,20 @@ +**AlignConsecutiveMacros** (``bool``) + If ``true``, aligns consecutive C/C++ preprocessor macros. + + This will align the C/C++ preprocessor macros of consecutive lines. This + will result in formattings like + + .. code-block:: c++ + + #define SHORT_NAME 42 + #define LONGER_NAME 0x007f + #define EVEN_LONGER_NAME (2) + #define foo(x) (x * x) + #define bar(y, z) (y + z) + **AlignConsecutiveAssignments** (``bool``) If ``true``, aligns consecutive assignments. Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -79,6 +79,19 @@ /// brackets. BracketAlignmentStyle AlignAfterOpenBracket; + /// \brief If ``true``, aligns consecutive C/C++ preprocessor macros. + /// + /// This will align C/C++ preprocessor macros of consecutive lines. + /// Will result in formattings like + /// \code + /// #define SHORT_NAME 42 + /// #define LONGER_NAME 0x007f + /// #define EVEN_LONGER_NAME (2) + /// #define foo(x) (x * x) + /// #define bar(y, z) (y + z) + /// \endcode + bool AlignConsecutiveMacros; + /// If ``true``, aligns consecutive assignments. /// /// This will align the assignment operators of consecutive lines. This Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -313,6 +313,7 @@ IO.mapOptional("AccessModifierOffset", Style.AccessModifierOffset); IO.mapOptional("AlignAfterOpenBracket", Style.AlignAfterOpenBracket); + IO.mapOptional("AlignConsecutiveMacros", Style.AlignConsecutiveMacros); IO.mapOptional("AlignConsecutiveAssignments", Style.AlignConsecutiveAssignments); IO.mapOptional("AlignConsecutiveDeclarations", @@ -626,6 +627,7 @@ LLVMStyle.AlignAfterOpenBracket = FormatStyle::BAS_Align; LLVMStyle.AlignOperands = true; LLVMStyle.AlignTrailingComments = true; + LLVMStyle.AlignConsecutiveMacros = false; LLVMStyle.AlignConsecutiveAssignments = false; LLVMStyle.AlignConsecutiveDeclarations = false; LLVMStyle.AllowAllParametersOfDeclarationOnNextLine = true; Index: lib/Format/WhitespaceManager.h =================================================================== --- lib/Format/WhitespaceManager.h +++ lib/Format/WhitespaceManager.h @@ -169,6 +169,9 @@ /// \c EscapedNewlineColumn for the first tokens or token parts in a line. void calculateLineBreakInformation(); + /// \brief Align consecutive C/C++ preprocessor macros over all \c Changes. + void alignConsecutiveMacros(); + /// Align consecutive assignments over all \c Changes. void alignConsecutiveAssignments(); Index: lib/Format/WhitespaceManager.cpp =================================================================== --- lib/Format/WhitespaceManager.cpp +++ lib/Format/WhitespaceManager.cpp @@ -91,6 +91,7 @@ llvm::sort(Changes, Change::IsBeforeInFile(SourceMgr)); calculateLineBreakInformation(); + alignConsecutiveMacros(); alignConsecutiveDeclarations(); alignConsecutiveAssignments(); alignTrailingComments(); @@ -428,6 +429,140 @@ return i; } +// Aligns a sequence of matching tokens, on the MinColumn column. +// +// Sequences start from the first matching token to align, and end at the +// first token of the first line that doesn't need to be aligned. +// +// We need to adjust the StartOfTokenColumn of each Change that is on a line +// containing any matching token to be aligned and located after such token. +static void AlignMacroSequence( + unsigned &StartOfSequence, unsigned &EndOfSequence, unsigned &MinColumn, + unsigned &MaxColumn, bool &FoundMatchOnLine, + std::function AlignMacrosMatches, + SmallVector &Changes) { + if (StartOfSequence > 0 && StartOfSequence < EndOfSequence) { + + FoundMatchOnLine = false; + int Shift = 0; + + for (unsigned I = StartOfSequence; I != EndOfSequence; ++I) { + if (Changes[I].NewlinesBefore > 0) { + Shift = 0; + FoundMatchOnLine = false; + } + + // If this is the first matching token to be aligned, remember by how many + // spaces it has to be shifted, so the rest of the changes on the line are + // shifted by the same amount + if (!FoundMatchOnLine && AlignMacrosMatches(Changes[I])) { + FoundMatchOnLine = true; + Shift = MinColumn - Changes[I].StartOfTokenColumn; + Changes[I].Spaces += Shift; + } + + assert(Shift >= 0); + Changes[I].StartOfTokenColumn += Shift; + if (I + 1 != Changes.size()) + Changes[I + 1].PreviousEndOfTokenColumn += Shift; + } + } + + MinColumn = 0; + MaxColumn = UINT_MAX; + StartOfSequence = 0; + EndOfSequence = 0; +} + +void WhitespaceManager::alignConsecutiveMacros() { + if (!Style.AlignConsecutiveMacros) + return; + + auto AlignMacrosMatches = [](const Change &C) { + const FormatToken *Current = C.Tok; + unsigned SpacesRequiredBefore = 1; + + if (Current->SpacesRequiredBefore == 0 || !Current->Previous) + return false; + + Current = Current->Previous; + + // If token is a ")", skip over the parameter list, to the + // token that precedes the "(" + if (Current->is(tok::r_paren) && Current->MatchingParen) { + Current = Current->MatchingParen->Previous; + SpacesRequiredBefore = 0; + } + + if (!Current || !Current->is(tok::identifier)) + return false; + + if (!Current->Previous || !Current->Previous->is(tok::pp_define)) + return false; + + // For a macro function, 0 spaces are required between the + // identifier and the lparen that opens the parameter list. + // For a simple macro, 1 space is required between the + // identifier and the first token of the defined value. + return Current->Next->SpacesRequiredBefore == SpacesRequiredBefore; + }; + + unsigned MinColumn = 0; + unsigned MaxColumn = UINT_MAX; + + // Start and end of the token sequence we're processing. + unsigned StartOfSequence = 0; + unsigned EndOfSequence = 0; + + // Whether a matching token has been found on the current line. + bool FoundMatchOnLine = false; + + AlignMacroSequence(StartOfSequence, EndOfSequence, MinColumn, MaxColumn, + FoundMatchOnLine, AlignMacrosMatches, Changes); + + unsigned I = 0; + for (unsigned E = Changes.size(); I != E; ++I) { + if (Changes[I].NewlinesBefore != 0) { + EndOfSequence = I; + // If there is a blank line, or if the last line didn't contain any + // matching token, the sequence ends here. + if (Changes[I].NewlinesBefore > 1 || !FoundMatchOnLine) + AlignMacroSequence(StartOfSequence, EndOfSequence, MinColumn, MaxColumn, + FoundMatchOnLine, AlignMacrosMatches, Changes); + + FoundMatchOnLine = false; + } + + if (!AlignMacrosMatches(Changes[I])) + continue; + + FoundMatchOnLine = 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 we are restricted by the maximum column width, end the sequence. + if (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) { + AlignMacroSequence(StartOfSequence, EndOfSequence, MinColumn, MaxColumn, + FoundMatchOnLine, AlignMacrosMatches, Changes); + StartOfSequence = I; + } + + MinColumn = std::max(MinColumn, ChangeMinColumn); + MaxColumn = std::min(MaxColumn, ChangeMaxColumn); + } + + EndOfSequence = I; + AlignMacroSequence(StartOfSequence, EndOfSequence, MinColumn, MaxColumn, + FoundMatchOnLine, AlignMacrosMatches, Changes); +} + void WhitespaceManager::alignConsecutiveAssignments() { if (!Style.AlignConsecutiveAssignments) return; Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -9544,8 +9544,104 @@ NoSpaceStyle); } +TEST_F(FormatTest, AlignConsecutiveMacros) { + FormatStyle Style = getLLVMStyle(); + Style.AlignConsecutiveAssignments = true; + Style.AlignConsecutiveDeclarations = true; + Style.AlignConsecutiveMacros = false; + + verifyFormat("#define a 3\n" + "#define bbbb 4\n" + "#define ccc (5)", + Style); + + verifyFormat("#define f(x) (x * x)\n" + "#define fff(x, y, z) (x * y + z)\n" + "#define ffff(x, y) (x - y)", + Style); + + verifyFormat("#define foo(x, y) (x + y)\n" + "#define bar (5, 6)(2 + 2)", + Style); + + verifyFormat("#define a 3\n" + "#define bbbb 4\n" + "#define ccc (5)\n" + "#define f(x) (x * x)\n" + "#define fff(x, y, z) (x * y + z)\n" + "#define ffff(x, y) (x - y)", + Style); + + Style.AlignConsecutiveMacros = true; + verifyFormat("#define a 3\n" + "#define bbbb 4\n" + "#define ccc (5)", + Style); + + verifyFormat("#define f(x) (x * x)\n" + "#define fff(x, y, z) (x * y + z)\n" + "#define ffff(x, y) (x - y)", + Style); + + verifyFormat("#define foo(x, y) (x + y)\n" + "#define bar (5, 6)(2 + 2)", + Style); + + verifyFormat("#define a 3\n" + "#define bbbb 4\n" + "#define ccc (5)\n" + "#define f(x) (x * x)\n" + "#define fff(x, y, z) (x * y + z)\n" + "#define ffff(x, y) (x - y)", + Style); + + verifyFormat("#define a 5\n" + "#define foo(x, y) (x + y)\n" + "#define CCC (6)\n" + "auto lambda = []() {\n" + " auto ii = 0;\n" + " float j = 0;\n" + " return 0;\n" + "};\n" + "int i = 0;\n" + "float i2 = 0;\n" + "auto v = type{\n" + " i = 1, //\n" + " (i = 2), //\n" + " i = 3 //\n" + "};", + Style); + + Style.AlignConsecutiveMacros = false; + Style.ColumnLimit = 20; + + verifyFormat("#define a \\\n" + " \"aabbbbbbbbbbbb\"\n" + "#define D \\\n" + " \"aabbbbbbbbbbbb\" \\\n" + " \"ccddeeeeeeeee\"\n" + "#define B \\\n" + " \"QQQQQQQQQQQQQ\" \\\n" + " \"FFFFFFFFFFFFF\" \\\n" + " \"LLLLLLLL\"\n", + Style); + + Style.AlignConsecutiveMacros = true; + verifyFormat("#define a \\\n" + " \"aabbbbbbbbbbbb\"\n" + "#define D \\\n" + " \"aabbbbbbbbbbbb\" \\\n" + " \"ccddeeeeeeeee\"\n" + "#define B \\\n" + " \"QQQQQQQQQQQQQ\" \\\n" + " \"FFFFFFFFFFFFF\" \\\n" + " \"LLLLLLLL\"\n", + Style); +} + TEST_F(FormatTest, AlignConsecutiveAssignments) { FormatStyle Alignment = getLLVMStyle(); + Alignment.AlignConsecutiveMacros = true; Alignment.AlignConsecutiveAssignments = false; verifyFormat("int a = 5;\n" "int oneTwoThree = 123;", @@ -9735,6 +9831,7 @@ TEST_F(FormatTest, AlignConsecutiveDeclarations) { FormatStyle Alignment = getLLVMStyle(); + Alignment.AlignConsecutiveMacros = true; Alignment.AlignConsecutiveDeclarations = false; verifyFormat("float const a = 5;\n" "int oneTwoThree = 123;", @@ -10856,6 +10953,7 @@ Style.Language = FormatStyle::LK_Cpp; CHECK_PARSE_BOOL(AlignOperands); CHECK_PARSE_BOOL(AlignTrailingComments); + CHECK_PARSE_BOOL(AlignConsecutiveMacros); CHECK_PARSE_BOOL(AlignConsecutiveAssignments); CHECK_PARSE_BOOL(AlignConsecutiveDeclarations); CHECK_PARSE_BOOL(AllowAllParametersOfDeclarationOnNextLine);