Skip to content

[clang-format] Add option to remove leading blank lines #91221

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 1 commit into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
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
48 changes: 38 additions & 10 deletions clang/docs/ClangFormatStyleOptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4443,23 +4443,51 @@ the configuration (without a prefix: ``Auto``).
false:
import {VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying,} from "some/module.js"

.. _KeepEmptyLines:

**KeepEmptyLines** (``KeepEmptyLinesStyle``) :versionbadge:`clang-format 19` :ref:`¶ <KeepEmptyLines>`
Which empty lines are kept. See ``MaxEmptyLinesToKeep`` for how many
consecutive empty lines are kept.

Nested configuration flags:

Options regarding which empty lines are kept.

For example, the config below will remove empty lines at start of the
file, end of the file, and start of blocks.


.. code-block:: c++

KeepEmptyLines:
AtEndOfFile: false
AtStartOfBlock: false
AtStartOfFile: false

* ``bool AtEndOfFile`` Keep empty lines at end of file.

* ``bool AtStartOfBlock`` Keep empty lines at start of a block.

.. code-block:: c++

true: false:
if (foo) { vs. if (foo) {
bar();
bar(); }
}

* ``bool AtStartOfFile`` Keep empty lines at start of file.


.. _KeepEmptyLinesAtEOF:

**KeepEmptyLinesAtEOF** (``Boolean``) :versionbadge:`clang-format 17` :ref:`¶ <KeepEmptyLinesAtEOF>`
Keep empty lines (up to ``MaxEmptyLinesToKeep``) at end of file.
This option is deprecated. See ``AtEndOfFile`` of ``KeepEmptyLines``.

.. _KeepEmptyLinesAtTheStartOfBlocks:

**KeepEmptyLinesAtTheStartOfBlocks** (``Boolean``) :versionbadge:`clang-format 3.7` :ref:`¶ <KeepEmptyLinesAtTheStartOfBlocks>`
If true, the empty line at the start of blocks is kept.

.. code-block:: c++

true: false:
if (foo) { vs. if (foo) {
bar();
bar(); }
}
This option is deprecated. See ``AtStartOfBlock`` of ``KeepEmptyLines``.

.. _LambdaBodyIndentation:

Expand Down
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1110,6 +1110,8 @@ clang-format
- Adds ``AllowShortCaseExpressionOnASingleLine`` option.
- Adds ``AlignCaseArrows`` suboption to ``AlignConsecutiveShortCaseStatements``.
- Adds ``LeftWithLastLine`` suboption to ``AlignEscapedNewlines``.
- Adds ``KeepEmptyLines`` option to deprecate ``KeepEmptyLinesAtEOF``
and ``KeepEmptyLinesAtTheStartOfBlocks``.

libclang
--------
Expand Down
56 changes: 41 additions & 15 deletions clang/include/clang/Format/Format.h
Original file line number Diff line number Diff line change
Expand Up @@ -3095,20 +3095,49 @@ struct FormatStyle {
bool JavaScriptWrapImports;
// clang-format on

/// Keep empty lines (up to ``MaxEmptyLinesToKeep``) at end of file.
/// \version 17
bool KeepEmptyLinesAtEOF;

/// If true, the empty line at the start of blocks is kept.
/// Options regarding which empty lines are kept.
///
/// For example, the config below will remove empty lines at start of the
/// file, end of the file, and start of blocks.
///
/// \code
/// true: false:
/// if (foo) { vs. if (foo) {
/// bar();
/// bar(); }
/// }
/// KeepEmptyLines:
/// AtEndOfFile: false
/// AtStartOfBlock: false
/// AtStartOfFile: false
/// \endcode
struct KeepEmptyLinesStyle {
/// Keep empty lines at end of file.
bool AtEndOfFile;
/// Keep empty lines at start of a block.
/// \code
/// true: false:
/// if (foo) { vs. if (foo) {
/// bar();
/// bar(); }
/// }
/// \endcode
bool AtStartOfBlock;
/// Keep empty lines at start of file.
bool AtStartOfFile;
bool operator==(const KeepEmptyLinesStyle &R) const {
return AtEndOfFile == R.AtEndOfFile &&
AtStartOfBlock == R.AtStartOfBlock &&
AtStartOfFile == R.AtStartOfFile;
}
};
/// Which empty lines are kept. See ``MaxEmptyLinesToKeep`` for how many
/// consecutive empty lines are kept.
/// \version 19
KeepEmptyLinesStyle KeepEmptyLines;

/// This option is deprecated. See ``AtEndOfFile`` of ``KeepEmptyLines``.
/// \version 17
// bool KeepEmptyLinesAtEOF;

/// This option is deprecated. See ``AtStartOfBlock`` of ``KeepEmptyLines``.
/// \version 3.7
bool KeepEmptyLinesAtTheStartOfBlocks;
// bool KeepEmptyLinesAtTheStartOfBlocks;

/// Indentation logic for lambda bodies.
enum LambdaBodyIndentationKind : int8_t {
Expand Down Expand Up @@ -5033,10 +5062,7 @@ struct FormatStyle {
JavaImportGroups == R.JavaImportGroups &&
JavaScriptQuotes == R.JavaScriptQuotes &&
JavaScriptWrapImports == R.JavaScriptWrapImports &&
KeepEmptyLinesAtEOF == R.KeepEmptyLinesAtEOF &&
KeepEmptyLinesAtTheStartOfBlocks ==
R.KeepEmptyLinesAtTheStartOfBlocks &&
Language == R.Language &&
KeepEmptyLines == R.KeepEmptyLines && Language == R.Language &&
LambdaBodyIndentation == R.LambdaBodyIndentation &&
LineEnding == R.LineEnding && MacroBlockBegin == R.MacroBlockBegin &&
MacroBlockEnd == R.MacroBlockEnd && Macros == R.Macros &&
Expand Down
24 changes: 18 additions & 6 deletions clang/lib/Format/Format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,14 @@ template <> struct ScalarEnumerationTraits<FormatStyle::JavaScriptQuoteStyle> {
}
};

template <> struct MappingTraits<FormatStyle::KeepEmptyLinesStyle> {
static void mapping(IO &IO, FormatStyle::KeepEmptyLinesStyle &Value) {
IO.mapOptional("AtEndOfFile", Value.AtEndOfFile);
IO.mapOptional("AtStartOfBlock", Value.AtStartOfBlock);
IO.mapOptional("AtStartOfFile", Value.AtStartOfFile);
}
};

template <> struct ScalarEnumerationTraits<FormatStyle::LanguageKind> {
static void enumeration(IO &IO, FormatStyle::LanguageKind &Value) {
IO.enumCase(Value, "Cpp", FormatStyle::LK_Cpp);
Expand Down Expand Up @@ -869,6 +877,9 @@ template <> struct MappingTraits<FormatStyle> {
OnCurrentLine);
IO.mapOptional("DeriveLineEnding", DeriveLineEnding);
IO.mapOptional("DerivePointerBinding", Style.DerivePointerAlignment);
IO.mapOptional("KeepEmptyLinesAtEOF", Style.KeepEmptyLines.AtEndOfFile);
IO.mapOptional("KeepEmptyLinesAtTheStartOfBlocks",
Style.KeepEmptyLines.AtStartOfBlock);
IO.mapOptional("IndentFunctionDeclarationAfterType",
Style.IndentWrappedFunctionNames);
IO.mapOptional("IndentRequires", Style.IndentRequiresClause);
Expand Down Expand Up @@ -1004,9 +1015,7 @@ template <> struct MappingTraits<FormatStyle> {
IO.mapOptional("JavaImportGroups", Style.JavaImportGroups);
IO.mapOptional("JavaScriptQuotes", Style.JavaScriptQuotes);
IO.mapOptional("JavaScriptWrapImports", Style.JavaScriptWrapImports);
IO.mapOptional("KeepEmptyLinesAtTheStartOfBlocks",
Style.KeepEmptyLinesAtTheStartOfBlocks);
IO.mapOptional("KeepEmptyLinesAtEOF", Style.KeepEmptyLinesAtEOF);
IO.mapOptional("KeepEmptyLines", Style.KeepEmptyLines);
IO.mapOptional("LambdaBodyIndentation", Style.LambdaBodyIndentation);
IO.mapOptional("LineEnding", Style.LineEnding);
IO.mapOptional("MacroBlockBegin", Style.MacroBlockBegin);
Expand Down Expand Up @@ -1517,8 +1526,11 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
/*Hex=*/0, /*HexMinDigits=*/0};
LLVMStyle.JavaScriptQuotes = FormatStyle::JSQS_Leave;
LLVMStyle.JavaScriptWrapImports = true;
LLVMStyle.KeepEmptyLinesAtEOF = false;
LLVMStyle.KeepEmptyLinesAtTheStartOfBlocks = true;
LLVMStyle.KeepEmptyLines = {
/*AtEndOfFile=*/false,
/*AtStartOfBlock=*/true,
/*AtStartOfFile=*/true,
};
LLVMStyle.LambdaBodyIndentation = FormatStyle::LBI_Signature;
LLVMStyle.Language = Language;
LLVMStyle.LineEnding = FormatStyle::LE_DeriveLF;
Expand Down Expand Up @@ -1641,7 +1653,7 @@ FormatStyle getGoogleStyle(FormatStyle::LanguageKind Language) {
{".*", 3, 0, false}};
GoogleStyle.IncludeStyle.IncludeIsMainRegex = "([-_](test|unittest))?$";
GoogleStyle.IndentCaseLabels = true;
GoogleStyle.KeepEmptyLinesAtTheStartOfBlocks = false;
GoogleStyle.KeepEmptyLines.AtStartOfBlock = false;
GoogleStyle.ObjCBinPackProtocolList = FormatStyle::BPS_Never;
GoogleStyle.ObjCSpaceAfterProperty = false;
GoogleStyle.ObjCSpaceBeforeProtocolList = true;
Expand Down
12 changes: 7 additions & 5 deletions clang/lib/Format/UnwrappedLineFormatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1478,11 +1478,13 @@ static auto computeNewlines(const AnnotatedLine &Line,
Newlines = std::min(Newlines, 1u);
if (Newlines == 0 && !RootToken.IsFirst)
Newlines = 1;
if (RootToken.IsFirst && !RootToken.HasUnescapedNewline)
if (RootToken.IsFirst &&
(!Style.KeepEmptyLines.AtStartOfFile || !RootToken.HasUnescapedNewline)) {
Newlines = 0;
}

// Remove empty lines after "{".
if (!Style.KeepEmptyLinesAtTheStartOfBlocks && PreviousLine &&
if (!Style.KeepEmptyLines.AtStartOfBlock && PreviousLine &&
PreviousLine->Last->is(tok::l_brace) &&
!PreviousLine->startsWithNamespace() &&
!(PrevPrevLine && PrevPrevLine->startsWithNamespace() &&
Expand Down Expand Up @@ -1554,9 +1556,9 @@ void UnwrappedLineFormatter::formatFirstToken(
unsigned NewlineIndent) {
FormatToken &RootToken = *Line.First;
if (RootToken.is(tok::eof)) {
unsigned Newlines =
std::min(RootToken.NewlinesBefore,
Style.KeepEmptyLinesAtEOF ? Style.MaxEmptyLinesToKeep + 1 : 1);
unsigned Newlines = std::min(
RootToken.NewlinesBefore,
Style.KeepEmptyLines.AtEndOfFile ? Style.MaxEmptyLinesToKeep + 1 : 1);
unsigned TokenIndent = Newlines ? NewlineIndent : 0;
Whitespaces->replaceWhitespace(RootToken, Newlines, TokenIndent,
TokenIndent);
Expand Down
8 changes: 6 additions & 2 deletions clang/unittests/Format/ConfigParseTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,9 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
CHECK_PARSE_BOOL(IndentWrappedFunctionNames);
CHECK_PARSE_BOOL(InsertBraces);
CHECK_PARSE_BOOL(InsertNewlineAtEOF);
CHECK_PARSE_BOOL(KeepEmptyLinesAtEOF);
CHECK_PARSE_BOOL(KeepEmptyLinesAtTheStartOfBlocks);
CHECK_PARSE_BOOL_FIELD(KeepEmptyLines.AtEndOfFile, "KeepEmptyLinesAtEOF");
CHECK_PARSE_BOOL_FIELD(KeepEmptyLines.AtStartOfBlock,
"KeepEmptyLinesAtTheStartOfBlocks");
CHECK_PARSE_BOOL(ObjCSpaceAfterProperty);
CHECK_PARSE_BOOL(ObjCSpaceBeforeProtocolList);
CHECK_PARSE_BOOL(Cpp11BracedListStyle);
Expand Down Expand Up @@ -226,6 +227,9 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyFunction);
CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyRecord);
CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyNamespace);
CHECK_PARSE_NESTED_BOOL(KeepEmptyLines, AtEndOfFile);
CHECK_PARSE_NESTED_BOOL(KeepEmptyLines, AtStartOfBlock);
CHECK_PARSE_NESTED_BOOL(KeepEmptyLines, AtStartOfFile);
CHECK_PARSE_NESTED_BOOL(SpaceBeforeParensOptions, AfterControlStatements);
CHECK_PARSE_NESTED_BOOL(SpaceBeforeParensOptions, AfterForeachMacros);
CHECK_PARSE_NESTED_BOOL(SpaceBeforeParensOptions,
Expand Down
15 changes: 12 additions & 3 deletions clang/unittests/Format/FormatTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ TEST_F(FormatTest, FormatsGlobalStatementsAt0) {
verifyFormat("\nint i;", " \n\t \v \f int i;");
verifyFormat("int i;\nint j;", " int i; int j;");
verifyFormat("int i;\nint j;", " int i;\n int j;");

auto Style = getLLVMStyle();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An empty line before auto Style = ...?

Style.KeepEmptyLines.AtStartOfFile = false;
verifyFormat("int i;", " \n\t \v \f int i;", Style);
}

TEST_F(FormatTest, FormatsUnwrappedLinesAtFirstFormat) {
Expand Down Expand Up @@ -163,7 +167,7 @@ TEST_F(FormatTest, RemovesEmptyLines) {
auto CustomStyle = getLLVMStyle();
CustomStyle.BreakBeforeBraces = FormatStyle::BS_Custom;
CustomStyle.BraceWrapping.AfterNamespace = true;
CustomStyle.KeepEmptyLinesAtTheStartOfBlocks = false;
CustomStyle.KeepEmptyLines.AtStartOfBlock = false;
verifyFormat("namespace N\n"
"{\n"
"\n"
Expand Down Expand Up @@ -389,7 +393,7 @@ TEST_F(FormatTest, RemovesEmptyLines) {
Style.BreakBeforeBraces = FormatStyle::BS_Custom;
Style.BraceWrapping.AfterClass = true;
Style.BraceWrapping.AfterFunction = true;
Style.KeepEmptyLinesAtTheStartOfBlocks = false;
Style.KeepEmptyLines.AtStartOfBlock = false;

verifyFormat("class Foo\n"
"{\n"
Expand Down Expand Up @@ -21956,6 +21960,11 @@ TEST_F(FormatTest, HandlesUTF8BOM) {
verifyFormat("\xef\xbb\xbf");
verifyFormat("\xef\xbb\xbf#include <iostream>");
verifyFormat("\xef\xbb\xbf\n#include <iostream>");

auto Style = getLLVMStyle();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto.

Style.KeepEmptyLines.AtStartOfFile = false;
verifyFormat("\xef\xbb\xbf#include <iostream>",
"\xef\xbb\xbf\n#include <iostream>", Style);
}

// FIXME: Encode Cyrillic and CJK characters below to appease MS compilers.
Expand Down Expand Up @@ -27230,7 +27239,7 @@ TEST_F(FormatTest, InsertNewlineAtEOF) {

TEST_F(FormatTest, KeepEmptyLinesAtEOF) {
FormatStyle Style = getLLVMStyle();
Style.KeepEmptyLinesAtEOF = true;
Style.KeepEmptyLines.AtEndOfFile = true;

const StringRef Code{"int i;\n\n"};
verifyNoChange(Code, Style);
Expand Down
Loading