Skip to content

Commit

Permalink
[clang-format] Add option to remove leading blank lines (llvm#91221)
Browse files Browse the repository at this point in the history
The options regarding which blank lines are kept are also aggregated.
The new option is `KeepEmptyLines`.

This patch was initially part of 9267f8f.  I neglected to check
the server builds before I added it.  It broke clangd.  Jie Fu fixed the
problem in 4c91b49.  I was unaware of it.  I thought the main
branch was still broken.  I reverted the first patch in
70cfece.  It broke his fix.  He reverted it in
c69ea04.  Now the feature is added again including the fix.
  • Loading branch information
sstwcw committed Jun 30, 2024
1 parent dd64e4f commit 2853a83
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 42 deletions.
6 changes: 5 additions & 1 deletion clang-tools-extra/clangd/Format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,11 @@ formatIncremental(llvm::StringRef OriginalCode, unsigned OriginalCursor,
// Never *remove* lines in response to pressing enter! This annoys users.
if (InsertedText == "\n") {
Style.MaxEmptyLinesToKeep = 1000;
Style.KeepEmptyLinesAtTheStartOfBlocks = true;
Style.KeepEmptyLines = {
/*AtEndOfFile=*/true,
/*AtStartOfBlock=*/true,
/*AtStartOfFile=*/true,
};
}

// Compute the code we want to format:
Expand Down
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 @@ -1112,6 +1112,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();
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();
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

0 comments on commit 2853a83

Please sign in to comment.