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

The options regarding which blank lines are kept are also aggregated.
The new option is `KeepEmptyLines`.
This commit is contained in:
sstwcw
2024-05-06 14:34:08 +00:00
parent 4e0a0eae58
commit 9267f8f19a
7 changed files with 124 additions and 41 deletions

View File

@@ -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:

View File

@@ -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
--------

View File

@@ -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 {
@@ -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 &&

View File

@@ -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);
@@ -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);
@@ -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);
@@ -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;
@@ -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;

View File

@@ -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() &&
@@ -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);

View File

@@ -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);
@@ -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,

View File

@@ -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) {
@@ -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"
@@ -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"
@@ -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.
@@ -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);