From d2627cf88d2553a4c2e850430bdb908a4b7d2e52 Mon Sep 17 00:00:00 2001 From: Owen Pan Date: Tue, 6 Jun 2023 12:58:21 -0700 Subject: [PATCH] [clang-format] Add the KeepEmptyLinesAtEOF option Adds an option KeepEmptyLinesAtEOF to keep empty lines (up to MaxEmptyLinesToKeep) before EOF. This remedies the probably unintentional change in behavior introduced in 3d3ea84a4f8f, which started to always remove empty lines before EOF. Fixes #56054. Fixes #63150. Differential Revision: https://reviews.llvm.org/D152305 --- clang/docs/ClangFormatStyleOptions.rst | 5 +++++ clang/docs/ReleaseNotes.rst | 1 + clang/include/clang/Format/Format.h | 5 +++++ clang/lib/Format/Format.cpp | 2 ++ clang/lib/Format/UnwrappedLineFormatter.cpp | 4 +++- clang/unittests/Format/ConfigParseTest.cpp | 1 + clang/unittests/Format/FormatTest.cpp | 9 +++++++++ 7 files changed, 26 insertions(+), 1 deletion(-) diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 8f23a4aa27a9..fa552d65e208 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 e24b1cda5371..a30d9f8a38d7 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 6a9d435174cd..74b3e1591818 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 6e2b6a662e7e..5fee5e6c261a 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 fc5d4150ed77..f229742b19d9 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 169c93d1143e..6c720ec22054 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 f188ab6f581c..adc1eda41a91 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;"); -- 2.34.1