Skip to content

[clang-format] Introduce "ReflowComments: IndentOnly" to re-indent comments without breaking internal structure (think Doxygen). #96804

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Oct 12, 2024
Merged
50 changes: 37 additions & 13 deletions clang/docs/ClangFormatStyleOptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5406,22 +5406,46 @@ the configuration (without a prefix: ``Auto``).

.. _ReflowComments:

**ReflowComments** (``Boolean``) :versionbadge:`clang-format 3.8` :ref:`¶ <ReflowComments>`
If ``true``, clang-format will attempt to re-flow comments. That is it
will touch a comment and *reflow* long comments into new lines, trying to
obey the ``ColumnLimit``.
**ReflowComments** (``ReflowCommentsStyle``) :versionbadge:`clang-format 3.8` :ref:`¶ <ReflowComments>`
Comment reformatting style.

.. code-block:: c++
Possible values:

* ``RCS_Never`` (in configuration: ``Never``)
Leave comments untouched.

.. code-block:: c++

// veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information
/* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information */
/* third veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information
* and a misaligned second line */

* ``RCS_IndentOnly`` (in configuration: ``IndentOnly``)
Only apply indentation rules, moving comments left or right, without
changing formatting inside the comments.

.. code-block:: c++

// veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information
/* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information */
/* third veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information
* and a misaligned second line */

* ``RCS_Always`` (in configuration: ``Always``)
Apply indentation rules and reflow long comments into new lines, trying
to obey the ``ColumnLimit``.

.. code-block:: c++

// veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of
// information
/* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of
* information */
/* third veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of
* information and a misaligned second line */

false:
// veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information
/* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information */

true:
// veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of
// information
/* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of
* information */

.. _RemoveBracesLLVM:

Expand Down
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,8 @@ clang-format
- Adds ``BreakBinaryOperations`` option.
- Adds ``TemplateNames`` option.
- Adds ``AlignFunctionDeclarations`` option to ``AlignConsecutiveDeclarations``.
- Adds ``IndentOnly`` suboption to ``ReflowComments`` to fix the indentation of multi-line comments
without touching their contents, renames ``false`` to ``Never``, and ``true`` to ``Always``.

libclang
--------
Expand Down
51 changes: 35 additions & 16 deletions clang/include/clang/Format/Format.h
Original file line number Diff line number Diff line change
Expand Up @@ -3847,24 +3847,43 @@ struct FormatStyle {
ReferenceAlignmentStyle ReferenceAlignment;

// clang-format off
/// If ``true``, clang-format will attempt to re-flow comments. That is it
/// will touch a comment and *reflow* long comments into new lines, trying to
/// obey the ``ColumnLimit``.
/// \code
/// false:
/// // veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information
/// /* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information */
///
/// true:
/// // veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of
/// // information
/// /* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of
/// * information */
/// \endcode
/// \version 3.8
bool ReflowComments;
/// \brief Types of comment reflow style.
enum ReflowCommentsStyle : int8_t {
/// Leave comments untouched.
/// \code
/// // veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information
/// /* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information */
/// /* third veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information
/// * and a misaligned second line */
/// \endcode
RCS_Never,
/// Only apply indentation rules, moving comments left or right, without
/// changing formatting inside the comments.
/// \code
/// // veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information
/// /* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information */
/// /* third veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information
/// * and a misaligned second line */
/// \endcode
RCS_IndentOnly,
/// Apply indentation rules and reflow long comments into new lines, trying
/// to obey the ``ColumnLimit``.
/// \code
/// // veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of
/// // information
/// /* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of
/// * information */
/// /* third veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of
/// * information and a misaligned second line */
/// \endcode
RCS_Always
};
// clang-format on

/// \brief Comment reformatting style.
/// \version 3.8
ReflowCommentsStyle ReflowComments;

/// Remove optional braces of control statements (``if``, ``else``, ``for``,
/// and ``while``) in C++ according to the LLVM coding style.
/// \warning
Expand Down
10 changes: 6 additions & 4 deletions clang/lib/Format/BreakableToken.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ BreakableComment::getSplit(unsigned LineIndex, unsigned TailOffset,
unsigned ColumnLimit, unsigned ContentStartColumn,
const llvm::Regex &CommentPragmasRegex) const {
// Don't break lines matching the comment pragmas regex.
if (CommentPragmasRegex.match(Content[LineIndex]))
if (!AlwaysReflow || CommentPragmasRegex.match(Content[LineIndex]))
return Split(StringRef::npos, 0);
return getCommentSplit(Content[LineIndex].substr(TailOffset),
ContentStartColumn, ColumnLimit, Style.TabWidth,
Expand Down Expand Up @@ -608,7 +608,7 @@ BreakableToken::Split BreakableBlockComment::getSplit(
unsigned LineIndex, unsigned TailOffset, unsigned ColumnLimit,
unsigned ContentStartColumn, const llvm::Regex &CommentPragmasRegex) const {
// Don't break lines matching the comment pragmas regex.
if (CommentPragmasRegex.match(Content[LineIndex]))
if (!AlwaysReflow || CommentPragmasRegex.match(Content[LineIndex]))
return Split(StringRef::npos, 0);
return getCommentSplit(Content[LineIndex].substr(TailOffset),
ContentStartColumn, ColumnLimit, Style.TabWidth,
Expand Down Expand Up @@ -855,7 +855,8 @@ bool BreakableBlockComment::mayReflow(
StringRef IndentContent = Content[LineIndex];
if (Lines[LineIndex].ltrim(Blanks).starts_with("*"))
IndentContent = Lines[LineIndex].ltrim(Blanks).substr(1);
return LineIndex > 0 && !CommentPragmasRegex.match(IndentContent) &&
return LineIndex > 0 && AlwaysReflow &&
!CommentPragmasRegex.match(IndentContent) &&
mayReflowContent(Content[LineIndex]) && !Tok.Finalized &&
!switchesFormatting(tokenAt(LineIndex));
}
Expand Down Expand Up @@ -1160,7 +1161,8 @@ bool BreakableLineCommentSection::mayReflow(
// // text that protrudes
// // into text with different indent
// We do reflow in that case in block comments.
return LineIndex > 0 && !CommentPragmasRegex.match(IndentContent) &&
return LineIndex > 0 && AlwaysReflow &&
!CommentPragmasRegex.match(IndentContent) &&
mayReflowContent(Content[LineIndex]) && !Tok.Finalized &&
!switchesFormatting(tokenAt(LineIndex)) &&
OriginalPrefix[LineIndex] == OriginalPrefix[LineIndex - 1];
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Format/BreakableToken.h
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,8 @@ class BreakableComment : public BreakableToken {
// The intended start column of the first line of text from this section.
unsigned StartColumn;

const bool AlwaysReflow = Style.ReflowComments == FormatStyle::RCS_Always;

// The prefix to use in front a line that has been reflown up.
// For example, when reflowing the second line after the first here:
// // comment 1
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Format/ContinuationIndenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2471,7 +2471,7 @@ ContinuationIndenter::createBreakableToken(const FormatToken &Current,
State.Line->InPPDirective, Encoding, Style);
}
} else if (Current.is(TT_BlockComment)) {
if (!Style.ReflowComments ||
if (Style.ReflowComments == FormatStyle::RCS_Never ||
// If a comment token switches formatting, like
// /* clang-format on */, we don't want to break it further,
// but we may still want to adjust its indentation.
Expand All @@ -2492,7 +2492,7 @@ ContinuationIndenter::createBreakableToken(const FormatToken &Current,
}
return true;
}();
if (!Style.ReflowComments ||
if (Style.ReflowComments == FormatStyle::RCS_Never ||
CommentPragmasRegex.match(Current.TokenText.substr(2)) ||
switchesFormatting(Current) || !RegularComments) {
return nullptr;
Expand Down
13 changes: 12 additions & 1 deletion clang/lib/Format/Format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,17 @@ template <> struct MappingTraits<FormatStyle::RawStringFormat> {
}
};

template <> struct ScalarEnumerationTraits<FormatStyle::ReflowCommentsStyle> {
static void enumeration(IO &IO, FormatStyle::ReflowCommentsStyle &Value) {
IO.enumCase(Value, "Never", FormatStyle::RCS_Never);
IO.enumCase(Value, "IndentOnly", FormatStyle::RCS_IndentOnly);
IO.enumCase(Value, "Always", FormatStyle::RCS_Always);
// For backward compatibility:
IO.enumCase(Value, "false", FormatStyle::RCS_Never);
IO.enumCase(Value, "true", FormatStyle::RCS_Always);
}
};

template <>
struct ScalarEnumerationTraits<FormatStyle::ReferenceAlignmentStyle> {
static void enumeration(IO &IO, FormatStyle::ReferenceAlignmentStyle &Value) {
Expand Down Expand Up @@ -1569,7 +1580,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.PPIndentWidth = -1;
LLVMStyle.QualifierAlignment = FormatStyle::QAS_Leave;
LLVMStyle.ReferenceAlignment = FormatStyle::RAS_Pointer;
LLVMStyle.ReflowComments = true;
LLVMStyle.ReflowComments = FormatStyle::RCS_Always;
LLVMStyle.RemoveBracesLLVM = false;
LLVMStyle.RemoveParentheses = FormatStyle::RPS_Leave;
LLVMStyle.RemoveSemicolon = false;
Expand Down
10 changes: 5 additions & 5 deletions clang/lib/Format/UnwrappedLineParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4623,9 +4623,9 @@ bool UnwrappedLineParser::isOnNewLine(const FormatToken &FormatTok) {
// section on \p Line.
static bool
continuesLineCommentSection(const FormatToken &FormatTok,
const UnwrappedLine &Line,
const UnwrappedLine &Line, const FormatStyle &Style,
const llvm::Regex &CommentPragmasRegex) {
if (Line.Tokens.empty())
if (Line.Tokens.empty() || Style.ReflowComments != FormatStyle::RCS_Always)
return false;

StringRef IndentContent = FormatTok.TokenText;
Expand Down Expand Up @@ -4738,7 +4738,7 @@ void UnwrappedLineParser::flushComments(bool NewlineBeforeNext) {
// FIXME: Consider putting separate line comment sections as children to the
// unwrapped line instead.
Tok->ContinuesLineCommentSection =
continuesLineCommentSection(*Tok, *Line, CommentPragmasRegex);
continuesLineCommentSection(*Tok, *Line, Style, CommentPragmasRegex);
if (isOnNewLine(*Tok) && JustComments && !Tok->ContinuesLineCommentSection)
addUnwrappedLine();
pushToken(Tok);
Expand Down Expand Up @@ -4811,8 +4811,8 @@ void UnwrappedLineParser::distributeComments(
if (HasTrailAlignedWithNextToken && i == StartOfTrailAlignedWithNextToken) {
FormatTok->ContinuesLineCommentSection = false;
} else {
FormatTok->ContinuesLineCommentSection =
continuesLineCommentSection(*FormatTok, *Line, CommentPragmasRegex);
FormatTok->ContinuesLineCommentSection = continuesLineCommentSection(
*FormatTok, *Line, Style, CommentPragmasRegex);
}
if (!FormatTok->ContinuesLineCommentSection &&
(isOnNewLine(*FormatTok) || FormatTok->IsFirst)) {
Expand Down
11 changes: 10 additions & 1 deletion clang/unittests/Format/ConfigParseTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,6 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
CHECK_PARSE_BOOL(ObjCSpaceAfterProperty);
CHECK_PARSE_BOOL(ObjCSpaceBeforeProtocolList);
CHECK_PARSE_BOOL(Cpp11BracedListStyle);
CHECK_PARSE_BOOL(ReflowComments);
CHECK_PARSE_BOOL(RemoveBracesLLVM);
CHECK_PARSE_BOOL(RemoveSemicolon);
CHECK_PARSE_BOOL(SkipMacroDefinitionBody);
Expand Down Expand Up @@ -381,6 +380,16 @@ TEST(ConfigParseTest, ParsesConfiguration) {
CHECK_PARSE("PointerBindsToType: Middle", PointerAlignment,
FormatStyle::PAS_Middle);

Style.ReflowComments = FormatStyle::RCS_Always;
CHECK_PARSE("ReflowComments: Never", ReflowComments, FormatStyle::RCS_Never);
CHECK_PARSE("ReflowComments: IndentOnly", ReflowComments,
FormatStyle::RCS_IndentOnly);
CHECK_PARSE("ReflowComments: Always", ReflowComments,
FormatStyle::RCS_Always);
// For backward compatibility:
CHECK_PARSE("ReflowComments: false", ReflowComments, FormatStyle::RCS_Never);
CHECK_PARSE("ReflowComments: true", ReflowComments, FormatStyle::RCS_Always);

Style.Standard = FormatStyle::LS_Auto;
CHECK_PARSE("Standard: c++03", Standard, FormatStyle::LS_Cpp03);
CHECK_PARSE("Standard: c++11", Standard, FormatStyle::LS_Cpp11);
Expand Down
6 changes: 3 additions & 3 deletions clang/unittests/Format/FormatTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18108,7 +18108,7 @@ TEST_F(FormatTest, AlignConsecutiveMacros) {

// Test across comments
Style.MaxEmptyLinesToKeep = 10;
Style.ReflowComments = false;
Style.ReflowComments = FormatStyle::RCS_Never;
Style.AlignConsecutiveMacros.AcrossComments = true;
verifyFormat("#define a 3\n"
"// line comment\n"
Expand Down Expand Up @@ -18855,7 +18855,7 @@ TEST_F(FormatTest, AlignConsecutiveAssignmentsAcrossEmptyLinesAndComments) {
"y = 1;",
Alignment);

Alignment.ReflowComments = true;
Alignment.ReflowComments = FormatStyle::RCS_Always;
Alignment.ColumnLimit = 50;
verifyFormat("int x = 0;\n"
"int yy = 1; /// specificlennospace\n"
Expand Down Expand Up @@ -19253,7 +19253,7 @@ TEST_F(FormatTest, AlignConsecutiveAssignments) {
"y = 1;",
Alignment);

EXPECT_EQ(Alignment.ReflowComments, true);
EXPECT_EQ(Alignment.ReflowComments, FormatStyle::RCS_Always);
Alignment.ColumnLimit = 50;
verifyFormat("int x = 0;\n"
"int yy = 1; /// specificlennospace\n"
Expand Down
29 changes: 28 additions & 1 deletion clang/unittests/Format/FormatTestComments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -520,9 +520,36 @@ TEST_F(FormatTestComments, AlignsBlockComments) {

TEST_F(FormatTestComments, CommentReflowingCanBeTurnedOff) {
FormatStyle Style = getLLVMStyleWithColumns(20);
Style.ReflowComments = false;
Style.ReflowComments = FormatStyle::RCS_Never;
verifyFormat("// aaaaaaaaa aaaaaaaaaa aaaaaaaaaa", Style);
verifyFormat("/* aaaaaaaaa aaaaaaaaaa aaaaaaaaaa */", Style);
verifyNoChange("/* aaaaaaaaa aaaaaaaaaa aaaaaaaaaa\n"
"aaaaaaaaa*/",
Style);
verifyNoChange("/* aaaaaaaaa aaaaaaaaaa aaaaaaaaaa\n"
" aaaaaaaaa*/",
Style);
verifyNoChange("/* aaaaaaaaa aaaaaaaaaa aaaaaaaaaa\n"
" * aaaaaaaaa*/",
Style);
}

TEST_F(FormatTestComments, CommentReflowingCanApplyOnlyToIndents) {
FormatStyle Style = getLLVMStyleWithColumns(20);
Style.ReflowComments = FormatStyle::RCS_IndentOnly;
verifyFormat("// aaaaaaaaa aaaaaaaaaa aaaaaaaaaa", Style);
verifyFormat("/* aaaaaaaaa aaaaaaaaaa aaaaaaaaaa */", Style);
verifyNoChange("/* aaaaaaaaa aaaaaaaaaa aaaaaaaaaa\n"
"aaaaaaaaa*/",
Style);
verifyNoChange("/* aaaaaaaaa aaaaaaaaaa aaaaaaaaaa\n"
" aaaaaaaaa*/",
Style);
verifyFormat("/* aaaaaaaaa aaaaaaaaaa aaaaaaaaaa\n"
" * aaaaaaaaa*/",
"/* aaaaaaaaa aaaaaaaaaa aaaaaaaaaa\n"
" * aaaaaaaaa*/",
Style);
}

TEST_F(FormatTestComments, CorrectlyHandlesLengthOfBlockComments) {
Expand Down