diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -846,15 +846,57 @@ -**AlignTrailingComments** (``Boolean``) :versionbadge:`clang-format 3.7` +**AlignTrailingComments** (``TrailingCommentsAlignmentStyle``) :versionbadge:`clang-format 3.7` If ``true``, aligns trailing comments. + NOTE: As of clang-format 16 this option is not a bool but can be set + to the following enum values. Still ``true`` is parsed to ``Align`` and + ``false`` is parsed to ``DontAlign``. + + .. code-block:: c++ true: false: int a; // My comment a vs. int a; // My comment a int b = 2; // comment b int b = 2; // comment about b + Possible values: + + * ``TCAS_DontAlign`` (in configuration: ``DontAlign``) + Don't align trailing comments. + + .. code-block:: c++ + + int a; // comment + int ab; // comment + + int abc; // comment + int abcd; // comment + + * ``TCAS_Align`` (in configuration: ``Align``) + Align trailing comments. + + .. code-block:: c++ + + int a; // comment + int ab; // comment + + int abc; // comment + int abcd; // comment + + * ``TCAS_AlignAcrossEmptyLines`` (in configuration: ``AlignAcrossEmptyLines``) + Align trailing comments across empty lines. + + .. code-block:: c++ + + int a; // comment + int ab; // comment + + int abc; // comment + int abcd; // comment + + + **AllowAllArgumentsOnNextLine** (``Boolean``) :versionbadge:`clang-format 9` If a function call or braced initializer list doesn't fit on a line, allow putting all arguments onto the next line, even if @@ -3567,7 +3609,7 @@ .. code-block:: yaml - QualifierOrder: ['inline', 'static', 'type', 'const'] + QualifierOrder: ['inline', 'static' , 'type', 'const'] .. code-block:: c++ diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -369,14 +369,50 @@ /// \version 3.5 OperandAlignmentStyle AlignOperands; + /// Different styles for aligning trailing comments. + enum TrailingCommentsAlignmentStyle : int8_t { + /// Don't align trailing comments. + /// \code + /// int a; // comment + /// int ab; // comment + /// + /// int abc; // comment + /// int abcd; // comment + /// \endcode + TCAS_DontAlign, + /// Align trailing comments. + /// \code + /// int a; // comment + /// int ab; // comment + /// + /// int abc; // comment + /// int abcd; // comment + /// \endcode + TCAS_Align, + /// Align trailing comments across empty lines. + /// \code + /// int a; // comment + /// int ab; // comment + /// + /// int abc; // comment + /// int abcd; // comment + /// \endcode + TCAS_AlignAcrossEmptyLines, + }; + /// If ``true``, aligns trailing comments. + /// + /// NOTE: As of clang-format 16 this option is not a bool but can be set + /// to the following enum values. Still ``true`` is parsed to ``Align`` and + /// ``false`` is parsed to ``DontAlign``. + /// /// \code /// true: false: /// int a; // My comment a vs. int a; // My comment a /// int b = 2; // comment b int b = 2; // comment about b /// \endcode /// \version 3.7 - bool AlignTrailingComments; + TrailingCommentsAlignmentStyle AlignTrailingComments; /// \brief If a function call or braced initializer list doesn't fit on a /// line, allow putting all arguments onto the next line, even if diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -473,6 +473,21 @@ } }; +template <> +struct ScalarEnumerationTraits { + static void enumeration(IO &IO, + FormatStyle::TrailingCommentsAlignmentStyle &Value) { + IO.enumCase(Value, "DontAlign", FormatStyle::TCAS_DontAlign); + IO.enumCase(Value, "Align", FormatStyle::TCAS_Align); + IO.enumCase(Value, "AlignAcrossEmptyLines", + FormatStyle::TCAS_AlignAcrossEmptyLines); + + // For backward compatibility. + IO.enumCase(Value, "true", FormatStyle::TCAS_Align); + IO.enumCase(Value, "false", FormatStyle::TCAS_DontAlign); + } +}; + template <> struct ScalarEnumerationTraits { static void enumeration(IO &IO, FormatStyle::PointerAlignmentStyle &Value) { IO.enumCase(Value, "Middle", FormatStyle::PAS_Middle); @@ -1181,7 +1196,6 @@ LLVMStyle.AlignAfterOpenBracket = FormatStyle::BAS_Align; LLVMStyle.AlignArrayOfStructures = FormatStyle::AIAS_None; LLVMStyle.AlignOperands = FormatStyle::OAS_Align; - LLVMStyle.AlignTrailingComments = true; LLVMStyle.AlignConsecutiveAssignments = {}; LLVMStyle.AlignConsecutiveAssignments.Enabled = false; LLVMStyle.AlignConsecutiveAssignments.AcrossEmptyLines = false; @@ -1191,6 +1205,7 @@ LLVMStyle.AlignConsecutiveBitFields = {}; LLVMStyle.AlignConsecutiveDeclarations = {}; LLVMStyle.AlignConsecutiveMacros = {}; + LLVMStyle.AlignTrailingComments = FormatStyle::TCAS_Align; LLVMStyle.AllowAllArgumentsOnNextLine = true; LLVMStyle.AllowAllParametersOfDeclarationOnNextLine = true; LLVMStyle.AllowShortEnumsOnASingleLine = true; @@ -1447,7 +1462,7 @@ if (Language == FormatStyle::LK_Java) { GoogleStyle.AlignAfterOpenBracket = FormatStyle::BAS_DontAlign; GoogleStyle.AlignOperands = FormatStyle::OAS_DontAlign; - GoogleStyle.AlignTrailingComments = false; + GoogleStyle.AlignTrailingComments = FormatStyle::TCAS_DontAlign; GoogleStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Empty; GoogleStyle.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Never; GoogleStyle.AlwaysBreakBeforeMultilineStrings = false; @@ -1595,7 +1610,7 @@ Style.AccessModifierOffset = -4; Style.AlignAfterOpenBracket = FormatStyle::BAS_DontAlign; Style.AlignOperands = FormatStyle::OAS_DontAlign; - Style.AlignTrailingComments = false; + Style.AlignTrailingComments = FormatStyle::TCAS_DontAlign; Style.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Empty; Style.BreakBeforeBinaryOperators = FormatStyle::BOS_All; Style.BreakBeforeBraces = FormatStyle::BS_WebKit; diff --git a/clang/lib/Format/WhitespaceManager.cpp b/clang/lib/Format/WhitespaceManager.cpp --- a/clang/lib/Format/WhitespaceManager.cpp +++ b/clang/lib/Format/WhitespaceManager.cpp @@ -927,6 +927,11 @@ unsigned StartOfSequence = 0; bool BreakBeforeNext = false; unsigned Newlines = 0; + unsigned int NewLineThreshold = 1; + if (Style.AlignTrailingComments == Style.TCAS_AlignAcrossEmptyLines) { + NewLineThreshold = Style.MaxEmptyLinesToKeep + 1; + } + for (unsigned i = 0, e = Changes.size(); i != e; ++i) { if (Changes[i].StartOfBlockComment) continue; @@ -957,7 +962,7 @@ Changes[i - 1].Tok->is(tok::r_brace) && Changes[i - 1].StartOfTokenColumn == 0; bool WasAlignedWithStartOfNextLine = false; - if (Changes[i].NewlinesBefore == 1) { // A comment on its own line. + if (Changes[i].NewlinesBefore >= 1) { // A comment on its own line. unsigned CommentColumn = SourceMgr.getSpellingColumnNumber( Changes[i].OriginalWhitespaceRange.getEnd()); for (unsigned j = i + 1; j != e; ++j) { @@ -974,12 +979,13 @@ break; } } - if (!Style.AlignTrailingComments || FollowsRBraceInColumn0) { + if (Style.AlignTrailingComments == FormatStyle::TCAS_DontAlign || + FollowsRBraceInColumn0) { alignTrailingComments(StartOfSequence, i, MinColumn); MinColumn = ChangeMinColumn; MaxColumn = ChangeMinColumn; StartOfSequence = i; - } else if (BreakBeforeNext || Newlines > 1 || + } else if (BreakBeforeNext || Newlines > NewLineThreshold || (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) || // Break the comment sequence if the previous line did not end // in a trailing comment. diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -20041,7 +20041,6 @@ TEST_F(FormatTest, ParsesConfigurationBools) { FormatStyle Style = {}; Style.Language = FormatStyle::LK_Cpp; - CHECK_PARSE_BOOL(AlignTrailingComments); CHECK_PARSE_BOOL(AllowAllArgumentsOnNextLine); CHECK_PARSE_BOOL(AllowAllParametersOfDeclarationOnNextLine); CHECK_PARSE_BOOL(AllowShortCaseLabelsOnASingleLine); @@ -20365,6 +20364,19 @@ FormatStyle::OAS_DontAlign); CHECK_PARSE("AlignOperands: true", AlignOperands, FormatStyle::OAS_Align); + Style.AlignTrailingComments = FormatStyle::TCAS_Align; + CHECK_PARSE("AlignTrailingComments: DontAlign", AlignTrailingComments, + FormatStyle::TCAS_DontAlign); + CHECK_PARSE("AlignTrailingComments: Align", AlignTrailingComments, + FormatStyle::TCAS_Align); + CHECK_PARSE("AlignTrailingComments: AlignAcrossEmptyLines", + AlignTrailingComments, FormatStyle::TCAS_AlignAcrossEmptyLines); + // For backwards compatibility + CHECK_PARSE("AlignTrailingComments: true", AlignTrailingComments, + FormatStyle::TCAS_Align); + CHECK_PARSE("AlignTrailingComments: false", AlignTrailingComments, + FormatStyle::TCAS_DontAlign); + Style.UseTab = FormatStyle::UT_ForIndentation; CHECK_PARSE("UseTab: Never", UseTab, FormatStyle::UT_Never); CHECK_PARSE("UseTab: ForIndentation", UseTab, FormatStyle::UT_ForIndentation); diff --git a/clang/unittests/Format/FormatTestComments.cpp b/clang/unittests/Format/FormatTestComments.cpp --- a/clang/unittests/Format/FormatTestComments.cpp +++ b/clang/unittests/Format/FormatTestComments.cpp @@ -2858,6 +2858,182 @@ "int a; //\n"); } +TEST_F(FormatTestComments, AlignTrailingCommentsAcrossEmptyLines) { + FormatStyle Style = getLLVMStyle(); + Style.AlignTrailingComments = FormatStyle::TCAS_AlignAcrossEmptyLines; + verifyFormat("#include \"a.h\" // simple\n" + "\n" + "#include \"aa.h\" // example case\n", + Style); + + verifyFormat("#include \"a.h\" // align across\n" + "\n" + "#include \"aa.h\" // two empty lines\n" + "\n" + "#include \"aaa.h\" // in a row\n", + Style); + + verifyFormat("#include \"a.h\" // align\n" + "#include \"aa.h\" // comment\n" + "#include \"aaa.h\" // blocks\n" + "\n" + "#include \"aaaa.h\" // across\n" + "#include \"aaaaa.h\" // one\n" + "#include \"aaaaaa.h\" // empty line\n", + Style); + + verifyFormat("#include \"a.h\" // align trailing comments\n" + "#include \"a.h\"\n" + "#include \"aa.h\" // across a line without comment\n", + Style); + + verifyFormat("#include \"a.h\" // align across\n" + "#include \"a.h\"\n" + "#include \"aa.h\" // two lines without comment\n" + "#include \"a.h\"\n" + "#include \"aaa.h\" // in a row\n", + Style); + + verifyFormat("#include \"a.h\" // align\n" + "#include \"aa.h\" // comment\n" + "#include \"aaa.h\" // blocks\n" + "#include \"a.h\"\n" + "#include \"aaaa.h\" // across\n" + "#include \"aaaaa.h\" // a line without\n" + "#include \"aaaaaa.h\" // comment\n", + Style); + + // Start of testing the combination with MaxEmptyLinesToKeep + Style.MaxEmptyLinesToKeep = 0; + verifyFormat("#include \"a.h\" // comment\n" + "#include \"aa.h\" // comment\n", + Style); + + Style.MaxEmptyLinesToKeep = 1; + verifyFormat("#include \"a.h\" // comment\n" + "\n" + "#include \"aa.h\" // comment\n", + Style); + + Style.MaxEmptyLinesToKeep = 2; + verifyFormat("#include \"a.h\" // comment\n" + "\n" + "\n" + "#include \"aa.h\" // comment\n", + Style); + + // Reset the setting + Style.MaxEmptyLinesToKeep = 1; + // End of testing the combination with MaxEmptyLinesToKeep + + Style.ColumnLimit = 15; + EXPECT_EQ("int ab; // line\n" + "int a; // long\n" + " // long\n" + "\n" + " // long", + format("int ab; // line\n" + "int a; // long long\n" + "\n" + "// long", + Style)); + + Style.ColumnLimit = 15; + EXPECT_EQ("int ab; // line\n" + "\n" + "int a; // long\n" + " // long\n", + format("int ab; // line\n" + "\n" + "int a; // long long\n", + Style)); + + Style.ColumnLimit = 30; + EXPECT_EQ("int foo = 12345; // comment\n" + "int bar =\n" + " 1234; // This is a very\n" + " // long comment\n" + " // which is wrapped\n" + " // arround.\n" + "\n" + "int x = 2; // Is this still\n" + " // aligned?\n", + format("int foo = 12345; // comment\n" + "int bar = 1234; // This is a very long comment\n" + " // which is wrapped arround.\n" + "\n" + "int x = 2; // Is this still aligned?\n", + Style)); + + Style.ColumnLimit = 35; + EXPECT_EQ("int foo = 12345; // comment\n" + "int bar =\n" + " 1234; // This is a very long\n" + " // comment which is\n" + " // wrapped arround.\n" + "\n" + "int x =\n" + " 2; // Is this still aligned?\n", + format("int foo = 12345; // comment\n" + "int bar = 1234; // This is a very long comment\n" + " // which is wrapped arround.\n" + "\n" + "int x = 2; // Is this still aligned?\n", + Style)); + + Style.ColumnLimit = 40; + EXPECT_EQ("int foo = 12345; // comment\n" + "int bar =\n" + " 1234; // This is a very long comment\n" + " // which is wrapped arround.\n" + "\n" + "int x = 2; // Is this still aligned?\n", + format("int foo = 12345; // comment\n" + "int bar = 1234; // This is a very long comment\n" + " // which is wrapped arround.\n" + "\n" + "int x = 2; // Is this still aligned?\n", + Style)); + + Style.ColumnLimit = 45; + EXPECT_EQ("int foo = 12345; // comment\n" + "int bar =\n" + " 1234; // This is a very long comment\n" + " // which is wrapped arround.\n" + "\n" + "int x = 2; // Is this still aligned?\n", + format("int foo = 12345; // comment\n" + "int bar = 1234; // This is a very long comment\n" + " // which is wrapped arround.\n" + "\n" + "int x = 2; // Is this still aligned?\n", + Style)); + + Style.ColumnLimit = 80; + EXPECT_EQ("int a; // line about a\n" + "\n" + "// line about b\n" + "long b;", + format("int a; // line about a\n" + "\n" + " // line about b\n" + " long b;", + Style)); + + Style.ColumnLimit = 80; + EXPECT_EQ("int a; // line about a\n" + "\n" + "// line 1 about b\n" + "// line 2 about b\n" + "long b;", + format("int a; // line about a\n" + "\n" + " // line 1 about b\n" + " // line 2 about b\n" + " long b;", + Style)); +} + TEST_F(FormatTestComments, AlignsBlockCommentDecorations) { EXPECT_EQ("/*\n" " */",