Skip to content

Commit 786db63

Browse files
authored
[clang-format] Add KeepFormFeed option (#113268)
Closes #113170.
1 parent 3336352 commit 786db63

File tree

10 files changed

+110
-16
lines changed

10 files changed

+110
-16
lines changed

clang/docs/ClangFormatStyleOptions.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4663,6 +4663,14 @@ the configuration (without a prefix: ``Auto``).
46634663
**KeepEmptyLinesAtTheStartOfBlocks** (``Boolean``) :versionbadge:`clang-format 3.7` :ref:`<KeepEmptyLinesAtTheStartOfBlocks>`
46644664
This option is deprecated. See ``AtStartOfBlock`` of ``KeepEmptyLines``.
46654665

4666+
.. _KeepFormFeed:
4667+
4668+
**KeepFormFeed** (``Boolean``) :versionbadge:`clang-format 20` :ref:`<KeepFormFeed>`
4669+
Keep the form feed character if it's immediately preceded and followed by
4670+
a newline. Multiple form feeds and newlines within a whitespace range are
4671+
replaced with a single newline and form feed followed by the remaining
4672+
newlines.
4673+
46664674
.. _LambdaBodyIndentation:
46674675

46684676
**LambdaBodyIndentation** (``LambdaBodyIndentationKind``) :versionbadge:`clang-format 13` :ref:`<LambdaBodyIndentation>`

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,7 @@ clang-format
710710
multi-line comments without touching their contents, renames ``false`` to
711711
``Never``, and ``true`` to ``Always``.
712712
- Adds ``RemoveEmptyLinesInUnwrappedLines`` option.
713+
- Adds ``KeepFormFeed`` option and set it to ``true`` for ``GNU`` style.
713714

714715
libclang
715716
--------

clang/include/clang/Format/Format.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3207,6 +3207,13 @@ struct FormatStyle {
32073207
/// \version 3.7
32083208
// bool KeepEmptyLinesAtTheStartOfBlocks;
32093209

3210+
/// Keep the form feed character if it's immediately preceded and followed by
3211+
/// a newline. Multiple form feeds and newlines within a whitespace range are
3212+
/// replaced with a single newline and form feed followed by the remaining
3213+
/// newlines.
3214+
/// \version 20
3215+
bool KeepFormFeed;
3216+
32103217
/// Indentation logic for lambda bodies.
32113218
enum LambdaBodyIndentationKind : int8_t {
32123219
/// Align lambda body relative to the lambda signature. This is the default.
@@ -5222,7 +5229,8 @@ struct FormatStyle {
52225229
JavaImportGroups == R.JavaImportGroups &&
52235230
JavaScriptQuotes == R.JavaScriptQuotes &&
52245231
JavaScriptWrapImports == R.JavaScriptWrapImports &&
5225-
KeepEmptyLines == R.KeepEmptyLines && Language == R.Language &&
5232+
KeepEmptyLines == R.KeepEmptyLines &&
5233+
KeepFormFeed == R.KeepFormFeed && Language == R.Language &&
52265234
LambdaBodyIndentation == R.LambdaBodyIndentation &&
52275235
LineEnding == R.LineEnding && MacroBlockBegin == R.MacroBlockBegin &&
52285236
MacroBlockEnd == R.MacroBlockEnd && Macros == R.Macros &&

clang/lib/Format/Format.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,6 +1052,7 @@ template <> struct MappingTraits<FormatStyle> {
10521052
IO.mapOptional("JavaScriptQuotes", Style.JavaScriptQuotes);
10531053
IO.mapOptional("JavaScriptWrapImports", Style.JavaScriptWrapImports);
10541054
IO.mapOptional("KeepEmptyLines", Style.KeepEmptyLines);
1055+
IO.mapOptional("KeepFormFeed", Style.KeepFormFeed);
10551056
IO.mapOptional("LambdaBodyIndentation", Style.LambdaBodyIndentation);
10561057
IO.mapOptional("LineEnding", Style.LineEnding);
10571058
IO.mapOptional("MacroBlockBegin", Style.MacroBlockBegin);
@@ -1567,6 +1568,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
15671568
/*AtStartOfBlock=*/true,
15681569
/*AtStartOfFile=*/true,
15691570
};
1571+
LLVMStyle.KeepFormFeed = false;
15701572
LLVMStyle.LambdaBodyIndentation = FormatStyle::LBI_Signature;
15711573
LLVMStyle.Language = Language;
15721574
LLVMStyle.LineEnding = FormatStyle::LE_DeriveLF;
@@ -1927,6 +1929,7 @@ FormatStyle getGNUStyle() {
19271929
Style.ColumnLimit = 79;
19281930
Style.Cpp11BracedListStyle = false;
19291931
Style.FixNamespaceComments = false;
1932+
Style.KeepFormFeed = true;
19301933
Style.SpaceBeforeParens = FormatStyle::SBPO_Always;
19311934
return Style;
19321935
}

clang/lib/Format/FormatToken.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,9 @@ struct FormatToken {
586586
/// Might be function declaration open/closing paren.
587587
bool MightBeFunctionDeclParen = false;
588588

589+
/// Has "\n\f\n" or "\n\f\r\n" before TokenText.
590+
bool HasFormFeedBefore = false;
591+
589592
/// Number of optional braces to be inserted after this token:
590593
/// -1: a single left brace
591594
/// 0: no braces

clang/lib/Format/FormatTokenLexer.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,6 +1186,14 @@ FormatToken *FormatTokenLexer::getNextToken() {
11861186
Column = 0;
11871187
break;
11881188
case '\f':
1189+
if (Style.KeepFormFeed && !FormatTok->HasFormFeedBefore &&
1190+
// The form feed is immediately preceded and followed by a newline.
1191+
i > 0 && Text[i - 1] == '\n' &&
1192+
((i + 1 < e && Text[i + 1] == '\n') ||
1193+
(i + 2 < e && Text[i + 1] == '\r' && Text[i + 2] == '\n'))) {
1194+
FormatTok->HasFormFeedBefore = true;
1195+
}
1196+
[[fallthrough]];
11891197
case '\v':
11901198
Column = 0;
11911199
break;

clang/lib/Format/WhitespaceManager.cpp

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1674,7 +1674,7 @@ void WhitespaceManager::generateChanges() {
16741674
C.PreviousEndOfTokenColumn,
16751675
C.EscapedNewlineColumn);
16761676
} else {
1677-
appendNewlineText(ReplacementText, C.NewlinesBefore);
1677+
appendNewlineText(ReplacementText, C);
16781678
}
16791679
// FIXME: This assert should hold if we computed the column correctly.
16801680
// assert((int)C.StartOfTokenColumn >= C.Spaces);
@@ -1706,15 +1706,18 @@ void WhitespaceManager::storeReplacement(SourceRange Range, StringRef Text) {
17061706
}
17071707
}
17081708

1709-
void WhitespaceManager::appendNewlineText(std::string &Text,
1710-
unsigned Newlines) {
1711-
if (UseCRLF) {
1712-
Text.reserve(Text.size() + 2 * Newlines);
1713-
for (unsigned i = 0; i < Newlines; ++i)
1714-
Text.append("\r\n");
1715-
} else {
1716-
Text.append(Newlines, '\n');
1717-
}
1709+
void WhitespaceManager::appendNewlineText(std::string &Text, const Change &C) {
1710+
if (C.NewlinesBefore <= 0)
1711+
return;
1712+
1713+
StringRef Newline = UseCRLF ? "\r\n" : "\n";
1714+
Text.append(Newline);
1715+
1716+
if (C.Tok->HasFormFeedBefore)
1717+
Text.append("\f");
1718+
1719+
for (unsigned I = 1; I < C.NewlinesBefore; ++I)
1720+
Text.append(Newline);
17181721
}
17191722

17201723
void WhitespaceManager::appendEscapedNewlineText(

clang/lib/Format/WhitespaceManager.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ class WhitespaceManager {
349349

350350
/// Stores \p Text as the replacement for the whitespace in \p Range.
351351
void storeReplacement(SourceRange Range, StringRef Text);
352-
void appendNewlineText(std::string &Text, unsigned Newlines);
352+
void appendNewlineText(std::string &Text, const Change &C);
353353
void appendEscapedNewlineText(std::string &Text, unsigned Newlines,
354354
unsigned PreviousEndOfTokenColumn,
355355
unsigned EscapedNewlineColumn);

clang/unittests/Format/ConfigParseTest.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,24 +165,25 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
165165
CHECK_PARSE_BOOL(BreakBeforeTernaryOperators);
166166
CHECK_PARSE_BOOL(BreakStringLiterals);
167167
CHECK_PARSE_BOOL(CompactNamespaces);
168+
CHECK_PARSE_BOOL(Cpp11BracedListStyle);
168169
CHECK_PARSE_BOOL(DerivePointerAlignment);
169170
CHECK_PARSE_BOOL_FIELD(DerivePointerAlignment, "DerivePointerBinding");
170171
CHECK_PARSE_BOOL(DisableFormat);
171172
CHECK_PARSE_BOOL(IndentAccessModifiers);
172-
CHECK_PARSE_BOOL(IndentCaseLabels);
173173
CHECK_PARSE_BOOL(IndentCaseBlocks);
174+
CHECK_PARSE_BOOL(IndentCaseLabels);
174175
CHECK_PARSE_BOOL(IndentGotoLabels);
175-
CHECK_PARSE_BOOL_FIELD(IndentRequiresClause, "IndentRequires");
176176
CHECK_PARSE_BOOL(IndentRequiresClause);
177+
CHECK_PARSE_BOOL_FIELD(IndentRequiresClause, "IndentRequires");
177178
CHECK_PARSE_BOOL(IndentWrappedFunctionNames);
178179
CHECK_PARSE_BOOL(InsertBraces);
179180
CHECK_PARSE_BOOL(InsertNewlineAtEOF);
180181
CHECK_PARSE_BOOL_FIELD(KeepEmptyLines.AtEndOfFile, "KeepEmptyLinesAtEOF");
181182
CHECK_PARSE_BOOL_FIELD(KeepEmptyLines.AtStartOfBlock,
182183
"KeepEmptyLinesAtTheStartOfBlocks");
184+
CHECK_PARSE_BOOL(KeepFormFeed);
183185
CHECK_PARSE_BOOL(ObjCSpaceAfterProperty);
184186
CHECK_PARSE_BOOL(ObjCSpaceBeforeProtocolList);
185-
CHECK_PARSE_BOOL(Cpp11BracedListStyle);
186187
CHECK_PARSE_BOOL(RemoveBracesLLVM);
187188
CHECK_PARSE_BOOL(RemoveEmptyLinesInUnwrappedLines);
188189
CHECK_PARSE_BOOL(RemoveSemicolon);

clang/unittests/Format/FormatTest.cpp

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28135,7 +28135,7 @@ TEST_F(FormatTest, BreakBinaryOperations) {
2813528135
Style);
2813628136
}
2813728137

28138-
TEST_F(FormatTest, RemovesEmptyLinesInUnwrappedLines) {
28138+
TEST_F(FormatTest, RemoveEmptyLinesInUnwrappedLines) {
2813928139
auto Style = getLLVMStyle();
2814028140
Style.RemoveEmptyLinesInUnwrappedLines = true;
2814128141

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

28215+
TEST_F(FormatTest, KeepFormFeed) {
28216+
auto Style = getLLVMStyle();
28217+
Style.KeepFormFeed = true;
28218+
28219+
constexpr StringRef NoFormFeed{"int i;\n"
28220+
"\n"
28221+
"void f();"};
28222+
verifyFormat(NoFormFeed,
28223+
"int i;\n"
28224+
" \f\n"
28225+
"void f();",
28226+
Style);
28227+
verifyFormat(NoFormFeed,
28228+
"int i;\n"
28229+
"\n"
28230+
"\fvoid f();",
28231+
Style);
28232+
verifyFormat(NoFormFeed,
28233+
"\fint i;\n"
28234+
"\n"
28235+
"void f();",
28236+
Style);
28237+
verifyFormat(NoFormFeed,
28238+
"int i;\n"
28239+
"\n"
28240+
"void f();\f",
28241+
Style);
28242+
28243+
constexpr StringRef FormFeed{"int i;\n"
28244+
"\f\n"
28245+
"void f();"};
28246+
verifyNoChange(FormFeed, Style);
28247+
28248+
Style.LineEnding = FormatStyle::LE_LF;
28249+
verifyFormat(FormFeed,
28250+
"int i;\r\n"
28251+
"\f\r\n"
28252+
"void f();",
28253+
Style);
28254+
28255+
constexpr StringRef FormFeedBeforeEmptyLine{"int i;\n"
28256+
"\f\n"
28257+
"\n"
28258+
"void f();"};
28259+
Style.MaxEmptyLinesToKeep = 2;
28260+
verifyFormat(FormFeedBeforeEmptyLine,
28261+
"int i;\n"
28262+
"\n"
28263+
"\f\n"
28264+
"void f();",
28265+
Style);
28266+
verifyFormat(FormFeedBeforeEmptyLine,
28267+
"int i;\n"
28268+
"\f\n"
28269+
"\f\n"
28270+
"void f();",
28271+
Style);
28272+
}
28273+
2821528274
} // namespace
2821628275
} // namespace test
2821728276
} // namespace format

0 commit comments

Comments
 (0)