Skip to content

Commit

Permalink
[clang-format] Add KeepFormFeed option (llvm#113268)
Browse files Browse the repository at this point in the history
Closes llvm#113170.
  • Loading branch information
owenca authored and NoumanAmir657 committed Nov 4, 2024
1 parent 75480ee commit 39ec103
Show file tree
Hide file tree
Showing 10 changed files with 110 additions and 16 deletions.
8 changes: 8 additions & 0 deletions clang/docs/ClangFormatStyleOptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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>`
Expand Down
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,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
--------
Expand Down
10 changes: 9 additions & 1 deletion clang/include/clang/Format/Format.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 &&
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Format/Format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Format/FormatToken.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/Format/FormatTokenLexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
23 changes: 13 additions & 10 deletions clang/lib/Format/WhitespaceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Format/WhitespaceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
7 changes: 4 additions & 3 deletions clang/unittests/Format/ConfigParseTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
61 changes: 60 additions & 1 deletion clang/unittests/Format/FormatTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28135,7 +28135,7 @@ TEST_F(FormatTest, BreakBinaryOperations) {
Style);
}

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

Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 39ec103

Please sign in to comment.