diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -2195,7 +2195,17 @@ protected: }; +**EmptyLinesAfterAccessModifier** (``unsigned``) + Defines how many lines are put after access modifiers. + .. code-block:: c++ + + 0: 1: + struct Foo { vs. struct Foo { + private: private: + int x; + }; int x; + }; **ExperimentalAutoDetectBinPacking** (``bool``) If ``true``, clang-format detects whether function calls and diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -1953,6 +1953,9 @@ /// Defines in which cases to put empty line before access modifiers. EmptyLineBeforeAccessModifierStyle EmptyLineBeforeAccessModifier; + /// Defines how many lines are put after access modifiers. + unsigned EmptyLinesAfterAccessModifier; + /// If ``true``, clang-format detects whether function calls and /// definitions are formatted with one parameter per line. /// @@ -3201,6 +3204,7 @@ DerivePointerAlignment == R.DerivePointerAlignment && DisableFormat == R.DisableFormat && EmptyLineBeforeAccessModifier == R.EmptyLineBeforeAccessModifier && + EmptyLinesAfterAccessModifier == R.EmptyLinesAfterAccessModifier && ExperimentalAutoDetectBinPacking == R.ExperimentalAutoDetectBinPacking && FixNamespaceComments == R.FixNamespaceComments && diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -586,6 +586,8 @@ IO.mapOptional("DisableFormat", Style.DisableFormat); IO.mapOptional("EmptyLineBeforeAccessModifier", Style.EmptyLineBeforeAccessModifier); + IO.mapOptional("EmptyLinesAfterAccessModifier", + Style.EmptyLinesAfterAccessModifier); IO.mapOptional("ExperimentalAutoDetectBinPacking", Style.ExperimentalAutoDetectBinPacking); IO.mapOptional("FixNamespaceComments", Style.FixNamespaceComments); @@ -975,6 +977,7 @@ LLVMStyle.DeriveLineEnding = true; LLVMStyle.DerivePointerAlignment = false; LLVMStyle.EmptyLineBeforeAccessModifier = FormatStyle::ELBAMS_LogicalBlock; + LLVMStyle.EmptyLinesAfterAccessModifier = 0u; LLVMStyle.ExperimentalAutoDetectBinPacking = false; LLVMStyle.FixNamespaceComments = true; LLVMStyle.ForEachMacros.push_back("foreach"); diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -1278,10 +1278,10 @@ } } - // Remove empty lines after access specifiers. + // Force the configured amount of new lines after an access specifier if (PreviousLine && PreviousLine->First->isAccessSpecifier() && (!PreviousLine->InPPDirective || !RootToken.HasUnescapedNewline)) - Newlines = std::min(1u, Newlines); + Newlines = Style.EmptyLinesAfterAccessModifier + 1u; if (Newlines) Indent = NewlineIndent; diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -9179,6 +9179,48 @@ Style); } +TEST_F(FormatTest, FormatsAfterAccessModifiers) { + char const* const test0NL = + "class Foo {\n" + "private: int i;\n" + "};"; + char const* const test1NL = + "class Foo {\n" + "private:\n" + " int i;\n" + "};"; + char const* const test2NL = + "class Foo {\n" + "private:\n" + "\n" + " int i;\n" + "};"; + char const* const test3NL = + "class Foo {\n" + "private:\n" + "\n" + "\n" + " int i;\n" + "};"; + EXPECT_EQ(test1NL, format(test0NL)); + verifyFormat(test1NL); + EXPECT_EQ(test1NL, format(test2NL)); + EXPECT_EQ(test1NL, format(test3NL)); + + FormatStyle StyleWithLine = getLLVMStyle(); + StyleWithLine.EmptyLinesAfterAccessModifier = 1u; + EXPECT_EQ(test2NL, format(test0NL, StyleWithLine)); + EXPECT_EQ(test2NL, format(test1NL, StyleWithLine)); + verifyFormat(test2NL, StyleWithLine); + EXPECT_EQ(test2NL, format(test3NL, StyleWithLine)); + + StyleWithLine.EmptyLinesAfterAccessModifier = 2u; + EXPECT_EQ(test3NL, format(test0NL, StyleWithLine)); + EXPECT_EQ(test3NL, format(test1NL, StyleWithLine)); + EXPECT_EQ(test3NL, format(test2NL, StyleWithLine)); + verifyFormat(test3NL, StyleWithLine); +} + TEST_F(FormatTest, FormatsArrays) { verifyFormat("aaaaaaaaaaaaaaaaaaaaaaaaa[aaaaaaaaaaaaaaaaaaaaaaaaa]\n" " [bbbbbbbbbbbbbbbbbbbbbbbbb] = c;");