diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 8f23a4aa27a92..fa552d65e208a 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -3555,6 +3555,11 @@ the configuration (without a prefix: ``Auto``). false: import {VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying,} from "some/module.js" +.. _KeepEmptyLinesAtEOF: + +**KeepEmptyLinesAtEOF** (``Boolean``) :versionbadge:`clang-format 17` :ref:`¶ ` + Keep empty lines (up to ``MaxEmptyLinesToKeep``) at end of file. + .. _KeepEmptyLinesAtTheStartOfBlocks: **KeepEmptyLinesAtTheStartOfBlocks** (``Boolean``) :versionbadge:`clang-format 3.7` :ref:`¶ ` diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index e24b1cda53711..a30d9f8a38d7e 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -700,6 +700,7 @@ clang-format - Fix all known issues associated with ``LambdaBodyIndentation: OuterScope``. - Add ``BracedInitializerIndentWidth`` which can be used to configure the indentation level of the contents of braced init lists. +- Add ``KeepEmptyLinesAtEOF`` to keep empty lines at end of file. libclang -------- diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 6a9d435174cdd..74b3e15918184 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -2675,6 +2675,10 @@ 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. /// \code /// true: false: @@ -4364,6 +4368,7 @@ struct FormatStyle { JavaImportGroups == R.JavaImportGroups && JavaScriptQuotes == R.JavaScriptQuotes && JavaScriptWrapImports == R.JavaScriptWrapImports && + KeepEmptyLinesAtEOF == R.KeepEmptyLinesAtEOF && KeepEmptyLinesAtTheStartOfBlocks == R.KeepEmptyLinesAtTheStartOfBlocks && Language == R.Language && diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 6e2b6a662e7e1..5fee5e6c261a9 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -942,6 +942,7 @@ template <> struct MappingTraits { IO.mapOptional("JavaScriptWrapImports", Style.JavaScriptWrapImports); IO.mapOptional("KeepEmptyLinesAtTheStartOfBlocks", Style.KeepEmptyLinesAtTheStartOfBlocks); + IO.mapOptional("KeepEmptyLinesAtEOF", Style.KeepEmptyLinesAtEOF); IO.mapOptional("LambdaBodyIndentation", Style.LambdaBodyIndentation); IO.mapOptional("LineEnding", Style.LineEnding); IO.mapOptional("MacroBlockBegin", Style.MacroBlockBegin); @@ -1410,6 +1411,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { /*Hex=*/0, /*HexMinDigits=*/0}; LLVMStyle.JavaScriptQuotes = FormatStyle::JSQS_Leave; LLVMStyle.JavaScriptWrapImports = true; + LLVMStyle.KeepEmptyLinesAtEOF = false; LLVMStyle.KeepEmptyLinesAtTheStartOfBlocks = true; LLVMStyle.LambdaBodyIndentation = FormatStyle::LBI_Signature; LLVMStyle.LineEnding = FormatStyle::LE_DeriveLF; diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index fc5d4150ed777..f229742b19d97 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -1513,7 +1513,9 @@ void UnwrappedLineFormatter::formatFirstToken( unsigned NewlineIndent) { FormatToken &RootToken = *Line.First; if (RootToken.is(tok::eof)) { - unsigned Newlines = std::min(RootToken.NewlinesBefore, 1u); + unsigned Newlines = + std::min(RootToken.NewlinesBefore, + Style.KeepEmptyLinesAtEOF ? Style.MaxEmptyLinesToKeep + 1 : 1); unsigned TokenIndent = Newlines ? NewlineIndent : 0; Whitespaces->replaceWhitespace(RootToken, Newlines, TokenIndent, TokenIndent); diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp index 169c93d1143eb..6c720ec22054c 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -167,6 +167,7 @@ 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(ObjCSpaceAfterProperty); CHECK_PARSE_BOOL(ObjCSpaceBeforeProtocolList); diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index f188ab6f581cf..adc1eda41a91a 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -25744,6 +25744,15 @@ TEST_F(FormatTest, InsertNewlineAtEOF) { verifyFormat("int i;\n", "int i;", Style); } +TEST_F(FormatTest, KeepEmptyLinesAtEOF) { + FormatStyle Style = getLLVMStyle(); + Style.KeepEmptyLinesAtEOF = true; + + const StringRef Code{"int i;\n\n"}; + verifyFormat(Code, Code, Style); + verifyFormat(Code, "int i;\n\n\n", Style); +} + TEST_F(FormatTest, SpaceAfterUDL) { verifyFormat("auto c = (4s).count();"); verifyFormat("auto x = 5s .count() == 5;");