Skip to content

[clang-format] Add KeepFormFeed option #113268

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 3 commits into from
Oct 24, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions clang/docs/ClangFormatStyleOptions.rst
Original file line number Diff line number Diff line change
@@ -4663,6 +4663,14 @@ the configuration (without a prefix: ``Auto``).
**KeepEmptyLinesAtTheStartOfBlocks** (``Boolean``) :versionbadge:`clang-format 3.7` :ref:`¶ <KeepEmptyLinesAtTheStartOfBlocks>`
This option is deprecated. See ``AtStartOfBlock`` of ``KeepEmptyLines``.

.. _KeepFormFeed:

**KeepFormFeed** (``Boolean``) :versionbadge:`clang-format 20` :ref:`¶ <KeepFormFeed>`
Keep the form feed character if it's immediately preceded and followed by
a newline. Multiple form feeds and newlines within a whitespace range are
replaced with a single newline and form feed followed by the remaining
newlines.

.. _LambdaBodyIndentation:

**LambdaBodyIndentation** (``LambdaBodyIndentationKind``) :versionbadge:`clang-format 13` :ref:`¶ <LambdaBodyIndentation>`
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
@@ -705,6 +705,7 @@ clang-format
multi-line comments without touching their contents, renames ``false`` to
``Never``, and ``true`` to ``Always``.
- Adds ``RemoveEmptyLinesInUnwrappedLines`` option.
- Adds ``KeepFormFeed`` option and set it to ``true`` for ``GNU`` style.

libclang
--------
10 changes: 9 additions & 1 deletion clang/include/clang/Format/Format.h
Original file line number Diff line number Diff line change
@@ -3207,6 +3207,13 @@ struct FormatStyle {
/// \version 3.7
// bool KeepEmptyLinesAtTheStartOfBlocks;

/// Keep the form feed character if it's immediately preceded and followed by
/// a newline. Multiple form feeds and newlines within a whitespace range are
/// replaced with a single newline and form feed followed by the remaining
/// newlines.
/// \version 20
bool KeepFormFeed;

/// Indentation logic for lambda bodies.
enum LambdaBodyIndentationKind : int8_t {
/// Align lambda body relative to the lambda signature. This is the default.
@@ -5222,7 +5229,8 @@ struct FormatStyle {
JavaImportGroups == R.JavaImportGroups &&
JavaScriptQuotes == R.JavaScriptQuotes &&
JavaScriptWrapImports == R.JavaScriptWrapImports &&
KeepEmptyLines == R.KeepEmptyLines && Language == R.Language &&
KeepEmptyLines == R.KeepEmptyLines &&
KeepFormFeed == R.KeepFormFeed && Language == R.Language &&
LambdaBodyIndentation == R.LambdaBodyIndentation &&
LineEnding == R.LineEnding && MacroBlockBegin == R.MacroBlockBegin &&
MacroBlockEnd == R.MacroBlockEnd && Macros == R.Macros &&
3 changes: 3 additions & 0 deletions clang/lib/Format/Format.cpp
Original file line number Diff line number Diff line change
@@ -1052,6 +1052,7 @@ template <> struct MappingTraits<FormatStyle> {
IO.mapOptional("JavaScriptQuotes", Style.JavaScriptQuotes);
IO.mapOptional("JavaScriptWrapImports", Style.JavaScriptWrapImports);
IO.mapOptional("KeepEmptyLines", Style.KeepEmptyLines);
IO.mapOptional("KeepFormFeed", Style.KeepFormFeed);
IO.mapOptional("LambdaBodyIndentation", Style.LambdaBodyIndentation);
IO.mapOptional("LineEnding", Style.LineEnding);
IO.mapOptional("MacroBlockBegin", Style.MacroBlockBegin);
@@ -1567,6 +1568,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
/*AtStartOfBlock=*/true,
/*AtStartOfFile=*/true,
};
LLVMStyle.KeepFormFeed = false;
LLVMStyle.LambdaBodyIndentation = FormatStyle::LBI_Signature;
LLVMStyle.Language = Language;
LLVMStyle.LineEnding = FormatStyle::LE_DeriveLF;
@@ -1927,6 +1929,7 @@ FormatStyle getGNUStyle() {
Style.ColumnLimit = 79;
Style.Cpp11BracedListStyle = false;
Style.FixNamespaceComments = false;
Style.KeepFormFeed = true;
Style.SpaceBeforeParens = FormatStyle::SBPO_Always;
return Style;
}
3 changes: 3 additions & 0 deletions clang/lib/Format/FormatToken.h
Original file line number Diff line number Diff line change
@@ -586,6 +586,9 @@ struct FormatToken {
/// Might be function declaration open/closing paren.
bool MightBeFunctionDeclParen = false;

/// Has "\n\f\n" or "\n\f\r\n" before TokenText.
bool HasFormFeedBefore = false;

/// Number of optional braces to be inserted after this token:
/// -1: a single left brace
/// 0: no braces
8 changes: 8 additions & 0 deletions clang/lib/Format/FormatTokenLexer.cpp
Original file line number Diff line number Diff line change
@@ -1186,6 +1186,14 @@ FormatToken *FormatTokenLexer::getNextToken() {
Column = 0;
break;
case '\f':
if (Style.KeepFormFeed && !FormatTok->HasFormFeedBefore &&
// The form feed is immediately preceded and followed by a newline.
i > 0 && Text[i - 1] == '\n' &&
((i + 1 < e && Text[i + 1] == '\n') ||
(i + 2 < e && Text[i + 1] == '\r' && Text[i + 2] == '\n'))) {
FormatTok->HasFormFeedBefore = true;
}
[[fallthrough]];
case '\v':
Column = 0;
break;
23 changes: 13 additions & 10 deletions clang/lib/Format/WhitespaceManager.cpp
Original file line number Diff line number Diff line change
@@ -1674,7 +1674,7 @@ void WhitespaceManager::generateChanges() {
C.PreviousEndOfTokenColumn,
C.EscapedNewlineColumn);
} else {
appendNewlineText(ReplacementText, C.NewlinesBefore);
appendNewlineText(ReplacementText, C);
}
// FIXME: This assert should hold if we computed the column correctly.
// assert((int)C.StartOfTokenColumn >= C.Spaces);
@@ -1706,15 +1706,18 @@ void WhitespaceManager::storeReplacement(SourceRange Range, StringRef Text) {
}
}

void WhitespaceManager::appendNewlineText(std::string &Text,
unsigned Newlines) {
if (UseCRLF) {
Text.reserve(Text.size() + 2 * Newlines);
for (unsigned i = 0; i < Newlines; ++i)
Text.append("\r\n");
} else {
Text.append(Newlines, '\n');
}
void WhitespaceManager::appendNewlineText(std::string &Text, const Change &C) {
if (C.NewlinesBefore <= 0)
return;

StringRef Newline = UseCRLF ? "\r\n" : "\n";
Text.append(Newline);

if (C.Tok->HasFormFeedBefore)
Text.append("\f");

for (unsigned I = 1; I < C.NewlinesBefore; ++I)
Text.append(Newline);
}

void WhitespaceManager::appendEscapedNewlineText(
2 changes: 1 addition & 1 deletion clang/lib/Format/WhitespaceManager.h
Original file line number Diff line number Diff line change
@@ -349,7 +349,7 @@ class WhitespaceManager {

/// Stores \p Text as the replacement for the whitespace in \p Range.
void storeReplacement(SourceRange Range, StringRef Text);
void appendNewlineText(std::string &Text, unsigned Newlines);
void appendNewlineText(std::string &Text, const Change &C);
void appendEscapedNewlineText(std::string &Text, unsigned Newlines,
unsigned PreviousEndOfTokenColumn,
unsigned EscapedNewlineColumn);
7 changes: 4 additions & 3 deletions clang/unittests/Format/ConfigParseTest.cpp
Original file line number Diff line number Diff line change
@@ -165,24 +165,25 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
CHECK_PARSE_BOOL(BreakBeforeTernaryOperators);
CHECK_PARSE_BOOL(BreakStringLiterals);
CHECK_PARSE_BOOL(CompactNamespaces);
CHECK_PARSE_BOOL(Cpp11BracedListStyle);
CHECK_PARSE_BOOL(DerivePointerAlignment);
CHECK_PARSE_BOOL_FIELD(DerivePointerAlignment, "DerivePointerBinding");
CHECK_PARSE_BOOL(DisableFormat);
CHECK_PARSE_BOOL(IndentAccessModifiers);
CHECK_PARSE_BOOL(IndentCaseLabels);
CHECK_PARSE_BOOL(IndentCaseBlocks);
CHECK_PARSE_BOOL(IndentCaseLabels);
CHECK_PARSE_BOOL(IndentGotoLabels);
CHECK_PARSE_BOOL_FIELD(IndentRequiresClause, "IndentRequires");
CHECK_PARSE_BOOL(IndentRequiresClause);
CHECK_PARSE_BOOL_FIELD(IndentRequiresClause, "IndentRequires");
CHECK_PARSE_BOOL(IndentWrappedFunctionNames);
CHECK_PARSE_BOOL(InsertBraces);
CHECK_PARSE_BOOL(InsertNewlineAtEOF);
CHECK_PARSE_BOOL_FIELD(KeepEmptyLines.AtEndOfFile, "KeepEmptyLinesAtEOF");
CHECK_PARSE_BOOL_FIELD(KeepEmptyLines.AtStartOfBlock,
"KeepEmptyLinesAtTheStartOfBlocks");
CHECK_PARSE_BOOL(KeepFormFeed);
CHECK_PARSE_BOOL(ObjCSpaceAfterProperty);
CHECK_PARSE_BOOL(ObjCSpaceBeforeProtocolList);
CHECK_PARSE_BOOL(Cpp11BracedListStyle);
CHECK_PARSE_BOOL(RemoveBracesLLVM);
CHECK_PARSE_BOOL(RemoveEmptyLinesInUnwrappedLines);
CHECK_PARSE_BOOL(RemoveSemicolon);
61 changes: 60 additions & 1 deletion clang/unittests/Format/FormatTest.cpp
Original file line number Diff line number Diff line change
@@ -28135,7 +28135,7 @@ TEST_F(FormatTest, BreakBinaryOperations) {
Style);
}

TEST_F(FormatTest, RemovesEmptyLinesInUnwrappedLines) {
TEST_F(FormatTest, RemoveEmptyLinesInUnwrappedLines) {
auto Style = getLLVMStyle();
Style.RemoveEmptyLinesInUnwrappedLines = true;

@@ -28212,6 +28212,65 @@ TEST_F(FormatTest, RemovesEmptyLinesInUnwrappedLines) {
Style);
}

TEST_F(FormatTest, KeepFormFeed) {
auto Style = getLLVMStyle();
Style.KeepFormFeed = true;

constexpr StringRef NoFormFeed{"int i;\n"
"\n"
"void f();"};
verifyFormat(NoFormFeed,
"int i;\n"
" \f\n"
"void f();",
Style);
verifyFormat(NoFormFeed,
"int i;\n"
"\n"
"\fvoid f();",
Style);
verifyFormat(NoFormFeed,
"\fint i;\n"
"\n"
"void f();",
Style);
verifyFormat(NoFormFeed,
"int i;\n"
"\n"
"void f();\f",
Style);

constexpr StringRef FormFeed{"int i;\n"
"\f\n"
"void f();"};
verifyNoChange(FormFeed, Style);

Style.LineEnding = FormatStyle::LE_LF;
verifyFormat(FormFeed,
"int i;\r\n"
"\f\r\n"
"void f();",
Style);

constexpr StringRef FormFeedBeforeEmptyLine{"int i;\n"
"\f\n"
"\n"
"void f();"};
Style.MaxEmptyLinesToKeep = 2;
verifyFormat(FormFeedBeforeEmptyLine,
"int i;\n"
"\n"
"\f\n"
"void f();",
Style);
verifyFormat(FormFeedBeforeEmptyLine,
"int i;\n"
"\f\n"
"\f\n"
"void f();",
Style);
}

} // namespace
} // namespace test
} // namespace format