[clang-format] Style to separate definition blocks

This commit resolves GitHub issue #45895 (Bugzilla #46550), to
add or remove empty line between definition blocks including
namespaces, classes, structs, enums and functions.

Reviewed By: MyDeveloperDay, curdeius, HazardyKnusperkeks

Differential Revision: https://reviews.llvm.org/D116314
This commit is contained in:
ksyx
2021-12-28 10:01:26 -05:00
parent cfe3180742
commit 6f6f88ffda
11 changed files with 677 additions and 7 deletions

View File

@@ -3402,6 +3402,65 @@ the configuration (without a prefix: ``Auto``).
/* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of
* information */
**SeparateDefinitionBlocks** (``SeparateDefinitionStyle``) :versionbadge:`clang-format 14`
Specifies the use of empty lines to separate definition blocks, including classes,
structs, enums, and functions.
Possible values:
* ``SDS_Leave`` (in configuration: ``Leave``)
Leave definition blocks as they are.
* ``SDS_Always`` (in configuration: ``Always``)
Insert an empty line between definition blocks.
* ``SDS_Never`` (in configuration: ``Never``)
Remove any empty line between definition blocks.
.. code-block:: c++
Never v.s. Always
#include <cstring> #include <cstring>
struct Foo {
int a, b, c; struct Foo {
}; int a, b, c;
namespace Ns { };
class Bar {
public: namespace Ns {
struct Foobar { class Bar {
int a; public:
int b; struct Foobar {
}; int a;
private: int b;
int t; };
int method1() {
// ... private:
} int t;
enum List {
ITEM1, int method1() {
ITEM2 // ...
}; }
template<typename T>
int method2(T x) { enum List {
// ... ITEM1,
} ITEM2
int i, j, k; };
int method3(int par) {
// ... template<typename T>
} int method2(T x) {
}; // ...
class C {}; }
}
int i, j, k;
int method3(int par) {
// ...
}
};
class C {};
}
**ShortNamespaceLines** (``Unsigned``) :versionbadge:`clang-format 14`
The maximal number of unwrapped lines that a short namespace spans.
Defaults to 1.

View File

@@ -297,6 +297,10 @@ clang-format
`const` `volatile` `static` `inline` `constexpr` `restrict`
to be controlled relative to the `type`.
- Option ``SeparateDefinitionBlocks`` has been added to insert or remove empty
lines between definition blocks including functions, classes, structs, enums,
and namespaces.
- Add a ``Custom`` style to ``SpaceBeforeParens``, to better configure the
space before parentheses. The custom options can be set using
``SpaceBeforeParensOptions``.

View File

@@ -3054,6 +3054,63 @@ struct FormatStyle {
bool ReflowComments;
// clang-format on
enum SeparateDefinitionStyle {
/// Leave definition blocks as they are.
SDS_Leave,
/// Insert an empty line between definition blocks.
SDS_Always,
/// Remove any empty line between definition blocks.
SDS_Never
};
/// Specifies the use of empty lines to separate definition blocks, including
/// classes, structs, enums, and functions.
/// \code
/// Never v.s. Always
/// #include <cstring> #include <cstring>
/// struct Foo {
/// int a, b, c; struct Foo {
/// }; int a, b, c;
/// namespace Ns { };
/// class Bar {
/// public: namespace Ns {
/// struct Foobar { class Bar {
/// int a; public:
/// int b; struct Foobar {
/// }; int a;
/// private: int b;
/// int t; };
/// int method1() {
/// // ... private:
/// } int t;
/// enum List {
/// ITEM1, int method1() {
/// ITEM2 // ...
/// }; }
/// template<typename T>
/// int method2(T x) { enum List {
/// // ... ITEM1,
/// } ITEM2
/// int i, j, k; };
/// int method3(int par) {
/// // ... template<typename T>
/// } int method2(T x) {
/// }; // ...
/// class C {}; }
/// }
/// int i, j, k;
///
/// int method3(int par) {
/// // ...
/// }
/// };
///
/// class C {};
/// }
/// \endcode
/// \version 14
SeparateDefinitionStyle SeparateDefinitionBlocks;
/// The maximal number of unwrapped lines that a short namespace spans.
/// Defaults to 1.
///
@@ -4033,6 +4090,17 @@ tooling::Replacements fixNamespaceEndComments(const FormatStyle &Style,
ArrayRef<tooling::Range> Ranges,
StringRef FileName = "<stdin>");
/// Inserts or removes empty lines separating definition blocks including
/// classes, structs, functions, namespaces, and enums in the given \p Ranges in
/// \p Code.
///
/// Returns the ``Replacements`` that inserts or removes empty lines separating
/// definition blocks in all \p Ranges in \p Code.
tooling::Replacements separateDefinitionBlocks(const FormatStyle &Style,
StringRef Code,
ArrayRef<tooling::Range> Ranges,
StringRef FileName = "<stdin>");
/// Sort consecutive using declarations in the given \p Ranges in
/// \p Code.
///

View File

@@ -4,6 +4,7 @@ add_clang_library(clangFormat
AffectedRangeManager.cpp
BreakableToken.cpp
ContinuationIndenter.cpp
DefinitionBlockSeparator.cpp
Format.cpp
FormatToken.cpp
FormatTokenLexer.cpp

View File

@@ -0,0 +1,157 @@
//===--- DefinitionBlockSeparator.cpp ---------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file implements DefinitionBlockSeparator, a TokenAnalyzer that inserts
/// or removes empty lines separating definition blocks like classes, structs,
/// functions, enums, and namespaces in between.
///
//===----------------------------------------------------------------------===//
#include "DefinitionBlockSeparator.h"
#include "llvm/Support/Debug.h"
#define DEBUG_TYPE "definition-block-separator"
namespace clang {
namespace format {
std::pair<tooling::Replacements, unsigned> DefinitionBlockSeparator::analyze(
TokenAnnotator &Annotator, SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
FormatTokenLexer &Tokens) {
assert(Style.SeparateDefinitionBlocks != FormatStyle::SDS_Leave);
AffectedRangeMgr.computeAffectedLines(AnnotatedLines);
tooling::Replacements Result;
separateBlocks(AnnotatedLines, Result);
return {Result, 0};
}
void DefinitionBlockSeparator::separateBlocks(
SmallVectorImpl<AnnotatedLine *> &Lines, tooling::Replacements &Result) {
auto LikelyDefinition = [this](const AnnotatedLine *Line) {
if (Line->MightBeFunctionDecl && Line->mightBeFunctionDefinition())
return true;
FormatToken *CurrentToken = Line->First;
while (CurrentToken) {
if (CurrentToken->isOneOf(tok::kw_class, tok::kw_struct,
tok::kw_namespace, tok::kw_enum) ||
(Style.Language == FormatStyle::LK_JavaScript &&
CurrentToken->TokenText == "function"))
return true;
CurrentToken = CurrentToken->Next;
}
return false;
};
unsigned NewlineCount =
(Style.SeparateDefinitionBlocks == FormatStyle::SDS_Always ? 1 : 0) + 1;
WhitespaceManager Whitespaces(
Env.getSourceManager(), Style,
Style.DeriveLineEnding
? WhitespaceManager::inputUsesCRLF(
Env.getSourceManager().getBufferData(Env.getFileID()),
Style.UseCRLF)
: Style.UseCRLF);
for (unsigned I = 0; I < Lines.size(); I++) {
const auto &CurrentLine = Lines[I];
FormatToken *TargetToken = nullptr;
AnnotatedLine *TargetLine;
auto OpeningLineIndex = CurrentLine->MatchingOpeningBlockLineIndex;
const auto InsertReplacement = [&](const int NewlineToInsert) {
assert(TargetLine);
assert(TargetToken);
// Do not handle EOF newlines.
if (TargetToken->is(tok::eof) && NewlineToInsert > 0)
return;
if (!TargetLine->Affected)
return;
Whitespaces.replaceWhitespace(*TargetToken, NewlineToInsert,
TargetToken->SpacesRequiredBefore - 1,
TargetToken->StartsColumn);
};
const auto FollowingOtherOpening = [&]() {
return OpeningLineIndex == 0 ||
Lines[OpeningLineIndex - 1]->Last->opensScope();
};
const auto HasEnumOnLine = [CurrentLine]() {
FormatToken *CurrentToken = CurrentLine->First;
while (CurrentToken) {
if (CurrentToken->is(tok::kw_enum))
return true;
CurrentToken = CurrentToken->Next;
}
return false;
};
bool IsDefBlock = 0;
if (HasEnumOnLine()) {
// We have no scope opening/closing information for enum.
IsDefBlock = 1;
OpeningLineIndex = I;
TargetLine = CurrentLine;
TargetToken = CurrentLine->First;
if (!FollowingOtherOpening())
InsertReplacement(NewlineCount);
else
InsertReplacement(OpeningLineIndex != 0);
while (TargetToken && !TargetToken->is(tok::r_brace))
TargetToken = TargetToken->Next;
if (!TargetToken) {
while (I < Lines.size() && !Lines[I]->First->is(tok::r_brace))
I++;
}
} else if (CurrentLine->First->closesScope()) {
if (OpeningLineIndex > Lines.size())
continue;
// Handling the case that opening bracket has its own line.
OpeningLineIndex -= Lines[OpeningLineIndex]->First->TokenText == "{";
AnnotatedLine *OpeningLine = Lines[OpeningLineIndex];
// Closing a function definition.
if (LikelyDefinition(OpeningLine)) {
IsDefBlock = 1;
if (OpeningLineIndex > 0) {
OpeningLineIndex -=
Style.Language == FormatStyle::LK_CSharp &&
Lines[OpeningLineIndex - 1]->First->is(tok::l_square);
OpeningLine = Lines[OpeningLineIndex];
}
TargetLine = OpeningLine;
TargetToken = TargetLine->First;
if (!FollowingOtherOpening()) {
// Avoid duplicated replacement.
if (!TargetToken->opensScope())
InsertReplacement(NewlineCount);
} else
InsertReplacement(OpeningLineIndex != 0);
}
}
// Not the last token.
if (IsDefBlock && I + 1 < Lines.size()) {
TargetLine = Lines[I + 1];
TargetToken = TargetLine->First;
// No empty line for continuously closing scopes. The token will be
// handled in another case if the line following is opening a
// definition.
if (!TargetToken->closesScope()) {
if (!LikelyDefinition(TargetLine))
InsertReplacement(NewlineCount);
} else {
InsertReplacement(OpeningLineIndex != 0);
}
}
}
for (const auto &R : Whitespaces.generateReplacements())
// The add method returns an Error instance which simulates program exit
// code through overloading boolean operator, thus false here indicates
// success.
if (Result.add(R))
return;
}
} // namespace format
} // namespace clang

View File

@@ -0,0 +1,41 @@
//===--- DefinitionBlockSeparator.h -----------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file declares DefinitionBlockSeparator, a TokenAnalyzer that inserts or
/// removes empty lines separating definition blocks like classes, structs,
/// functions, enums, and namespaces in between.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_LIB_FORMAT_DEFINITIONBLOCKSEPARATOR_H
#define LLVM_CLANG_LIB_FORMAT_DEFINITIONBLOCKSEPARATOR_H
#include "TokenAnalyzer.h"
#include "WhitespaceManager.h"
namespace clang {
namespace format {
class DefinitionBlockSeparator : public TokenAnalyzer {
public:
DefinitionBlockSeparator(const Environment &Env, const FormatStyle &Style)
: TokenAnalyzer(Env, Style) {}
std::pair<tooling::Replacements, unsigned>
analyze(TokenAnnotator &Annotator,
SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
FormatTokenLexer &Tokens) override;
private:
void separateBlocks(SmallVectorImpl<AnnotatedLine *> &Lines,
tooling::Replacements &Result);
};
} // namespace format
} // namespace clang
#endif

View File

@@ -16,6 +16,7 @@
#include "AffectedRangeManager.h"
#include "BreakableToken.h"
#include "ContinuationIndenter.h"
#include "DefinitionBlockSeparator.h"
#include "FormatInternal.h"
#include "FormatTokenLexer.h"
#include "NamespaceEndCommentsFixer.h"
@@ -429,6 +430,15 @@ template <> struct ScalarEnumerationTraits<FormatStyle::PointerAlignmentStyle> {
}
};
template <>
struct ScalarEnumerationTraits<FormatStyle::SeparateDefinitionStyle> {
static void enumeration(IO &IO, FormatStyle::SeparateDefinitionStyle &Value) {
IO.enumCase(Value, "Leave", FormatStyle::SDS_Leave);
IO.enumCase(Value, "Always", FormatStyle::SDS_Always);
IO.enumCase(Value, "Never", FormatStyle::SDS_Never);
}
};
template <>
struct ScalarEnumerationTraits<FormatStyle::SpaceAroundPointerQualifiersStyle> {
static void
@@ -771,6 +781,7 @@ template <> struct MappingTraits<FormatStyle> {
IO.mapOptional("RawStringFormats", Style.RawStringFormats);
IO.mapOptional("ReferenceAlignment", Style.ReferenceAlignment);
IO.mapOptional("ReflowComments", Style.ReflowComments);
IO.mapOptional("SeparateDefinitionBlocks", Style.SeparateDefinitionBlocks);
IO.mapOptional("ShortNamespaceLines", Style.ShortNamespaceLines);
IO.mapOptional("SortIncludes", Style.SortIncludes);
IO.mapOptional("SortJavaStaticImport", Style.SortJavaStaticImport);
@@ -1195,6 +1206,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.ObjCSpaceBeforeProtocolList = true;
LLVMStyle.PointerAlignment = FormatStyle::PAS_Right;
LLVMStyle.ReferenceAlignment = FormatStyle::RAS_Pointer;
LLVMStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Leave;
LLVMStyle.ShortNamespaceLines = 1;
LLVMStyle.SpacesBeforeTrailingComments = 1;
LLVMStyle.Standard = FormatStyle::LS_Latest;
@@ -1843,7 +1855,7 @@ public:
WhitespaceManager Whitespaces(
Env.getSourceManager(), Style,
Style.DeriveLineEnding
? inputUsesCRLF(
? WhitespaceManager::inputUsesCRLF(
Env.getSourceManager().getBufferData(Env.getFileID()),
Style.UseCRLF)
: Style.UseCRLF);
@@ -1867,12 +1879,6 @@ public:
}
private:
static bool inputUsesCRLF(StringRef Text, bool DefaultToCRLF) {
size_t LF = Text.count('\n');
size_t CR = Text.count('\r') * 2;
return LF == CR ? DefaultToCRLF : CR > LF;
}
bool
hasCpp03IncompatibleFormat(const SmallVectorImpl<AnnotatedLine *> &Lines) {
for (const AnnotatedLine *Line : Lines) {
@@ -3053,6 +3059,11 @@ reformat(const FormatStyle &Style, StringRef Code,
});
}
if (Style.SeparateDefinitionBlocks != FormatStyle::SDS_Leave)
Passes.emplace_back([&](const Environment &Env) {
return DefinitionBlockSeparator(Env, Expanded).process();
});
if (Style.isJavaScript() && Style.JavaScriptQuotes != FormatStyle::JSQS_Leave)
Passes.emplace_back([&](const Environment &Env) {
return JavaScriptRequoter(Env, Expanded).process();
@@ -3141,6 +3152,16 @@ tooling::Replacements fixNamespaceEndComments(const FormatStyle &Style,
return NamespaceEndCommentsFixer(*Env, Style).process().first;
}
tooling::Replacements separateDefinitionBlocks(const FormatStyle &Style,
StringRef Code,
ArrayRef<tooling::Range> Ranges,
StringRef FileName) {
auto Env = Environment::make(Code, FileName, Ranges);
if (!Env)
return {};
return DefinitionBlockSeparator(*Env, Style).process().first;
}
tooling::Replacements sortUsingDeclarations(const FormatStyle &Style,
StringRef Code,
ArrayRef<tooling::Range> Ranges,

View File

@@ -74,6 +74,12 @@ WhitespaceManager::addReplacement(const tooling::Replacement &Replacement) {
return Replaces.add(Replacement);
}
bool WhitespaceManager::inputUsesCRLF(StringRef Text, bool DefaultToCRLF) {
size_t LF = Text.count('\n');
size_t CR = Text.count('\r') * 2;
return LF == CR ? DefaultToCRLF : CR > LF;
}
void WhitespaceManager::replaceWhitespaceInToken(
const FormatToken &Tok, unsigned Offset, unsigned ReplaceChars,
StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective,

View File

@@ -45,6 +45,9 @@ public:
bool useCRLF() const { return UseCRLF; }
/// Infers whether the input is using CRLF.
static bool inputUsesCRLF(StringRef Text, bool DefaultToCRLF);
/// Replaces the whitespace in front of \p Tok. Only call once for
/// each \c AnnotatedToken.
///

View File

@@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
add_clang_unittest(FormatTests
CleanupTest.cpp
DefinitionBlockSeparatorTest.cpp
FormatTest.cpp
FormatTestComments.cpp
FormatTestCSharp.cpp

View File

@@ -0,0 +1,309 @@
//===- DefinitionBlockSeparatorTest.cpp - Formatting unit tests -----------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "FormatTestUtils.h"
#include "clang/Format/Format.h"
#include "llvm/Support/Debug.h"
#include "gtest/gtest.h"
#define DEBUG_TYPE "definition-block-separator-test"
namespace clang {
namespace format {
namespace {
class DefinitionBlockSeparatorTest : public ::testing::Test {
protected:
static std::string
separateDefinitionBlocks(llvm::StringRef Code,
const std::vector<tooling::Range> &Ranges,
const FormatStyle &Style = getLLVMStyle()) {
LLVM_DEBUG(llvm::errs() << "---\n");
LLVM_DEBUG(llvm::errs() << Code << "\n\n");
tooling::Replacements Replaces = reformat(Style, Code, Ranges, "<stdin>");
auto Result = applyAllReplacements(Code, Replaces);
EXPECT_TRUE(static_cast<bool>(Result));
LLVM_DEBUG(llvm::errs() << "\n" << *Result << "\n\n");
return *Result;
}
static std::string
separateDefinitionBlocks(llvm::StringRef Code,
const FormatStyle &Style = getLLVMStyle()) {
return separateDefinitionBlocks(
Code,
/*Ranges=*/{1, tooling::Range(0, Code.size())}, Style);
}
static void verifyFormat(llvm::StringRef Code,
const FormatStyle &Style = getLLVMStyle(),
llvm::StringRef ExpectedCode = "") {
bool HasOriginalCode = true;
if (ExpectedCode == "") {
ExpectedCode = Code;
HasOriginalCode = false;
}
FormatStyle InverseStyle = Style;
if (Style.SeparateDefinitionBlocks == FormatStyle::SDS_Always)
InverseStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Never;
else
InverseStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
EXPECT_EQ(ExpectedCode.str(), separateDefinitionBlocks(ExpectedCode, Style))
<< "Expected code is not stable";
std::string InverseResult = separateDefinitionBlocks(Code, InverseStyle);
EXPECT_NE(Code.str(), InverseResult)
<< "Inverse formatting makes no difference";
std::string CodeToFormat =
HasOriginalCode ? Code.str() : removeEmptyLines(Code);
std::string Result = separateDefinitionBlocks(CodeToFormat, Style);
EXPECT_EQ(ExpectedCode.str(), Result) << "Test failed. Formatted:\n"
<< Result;
}
static std::string removeEmptyLines(llvm::StringRef Code) {
std::string Result = "";
for (auto Char : Code.str()) {
if (Result.size()) {
auto LastChar = Result.back();
if ((Char == '\n' && LastChar == '\n') ||
(Char == '\r' && (LastChar == '\r' || LastChar == '\n')))
continue;
}
Result.push_back(Char);
}
return Result;
}
};
TEST_F(DefinitionBlockSeparatorTest, Basic) {
FormatStyle Style = getLLVMStyle();
Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
verifyFormat("int foo(int i, int j) {\n"
" int r = i + j;\n"
" return r;\n"
"}\n"
"\n"
"int bar(int j, int k) {\n"
" int r = j + k;\n"
" return r;\n"
"}",
Style);
verifyFormat("struct foo {\n"
" int i, j;\n"
"};\n"
"\n"
"struct bar {\n"
" int j, k;\n"
"};",
Style);
verifyFormat("class foo {\n"
" int i, j;\n"
"};\n"
"\n"
"class bar {\n"
" int j, k;\n"
"};",
Style);
verifyFormat("namespace foo {\n"
"int i, j;\n"
"}\n"
"\n"
"namespace bar {\n"
"int j, k;\n"
"}",
Style);
verifyFormat("enum Foo { FOO, BAR };\n"
"\n"
"enum Bar { FOOBAR, BARFOO };\n",
Style);
}
TEST_F(DefinitionBlockSeparatorTest, Always) {
FormatStyle Style = getLLVMStyle();
Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
std::string Prefix = "namespace {\n";
std::string Postfix = "enum Foo { FOO, BAR };\n"
"\n"
"int foo(int i, int j) {\n"
" int r = i + j;\n"
" return r;\n"
"}\n"
"\n"
"int i, j, k;\n"
"\n"
"int bar(int j, int k) {\n"
" int r = j * k;\n"
" return r;\n"
"}\n"
"\n"
"enum Bar { FOOBAR, BARFOO };\n"
"} // namespace";
verifyFormat(Prefix + "\n\n\n" + removeEmptyLines(Postfix), Style,
Prefix + Postfix);
}
TEST_F(DefinitionBlockSeparatorTest, Never) {
FormatStyle Style = getLLVMStyle();
Style.SeparateDefinitionBlocks = FormatStyle::SDS_Never;
std::string Prefix = "namespace {\n";
std::string Postfix = "enum Foo { FOO, BAR };\n"
"\n"
"int foo(int i, int j) {\n"
" int r = i + j;\n"
" return r;\n"
"}\n"
"\n"
"int i, j, k;\n"
"\n"
"int bar(int j, int k) {\n"
" int r = j * k;\n"
" return r;\n"
"}\n"
"\n"
"enum Bar { FOOBAR, BARFOO };\n"
"} // namespace";
verifyFormat(Prefix + "\n\n\n" + Postfix, Style,
Prefix + removeEmptyLines(Postfix));
}
TEST_F(DefinitionBlockSeparatorTest, OpeningBracketOwnsLine) {
FormatStyle Style = getLLVMStyle();
Style.BreakBeforeBraces = FormatStyle::BS_Allman;
Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
verifyFormat("enum Foo\n"
"{\n"
" FOO,\n"
" BAR\n"
"};\n"
"\n"
"int foo(int i, int j)\n"
"{\n"
" int r = i + j;\n"
" return r;\n"
"}\n"
"\n"
"int i, j, k;\n"
"\n"
"int bar(int j, int k)\n"
"{\n"
" int r = j * k;\n"
" return r;\n"
"}\n"
"\n"
"enum Bar\n"
"{\n"
" FOOBAR,\n"
" BARFOO\n"
"};",
Style);
}
TEST_F(DefinitionBlockSeparatorTest, Leave) {
FormatStyle Style = getLLVMStyle();
Style.SeparateDefinitionBlocks = FormatStyle::SDS_Leave;
Style.MaxEmptyLinesToKeep = 3;
std::string LeaveAs = "namespace {\n"
"\n"
"enum Foo { FOO, BAR };\n"
"\n\n\n"
"int foo(int i, int j) {\n"
" int r = i + j;\n"
" return r;\n"
"}\n"
"\n"
"int i, j, k;\n"
"\n"
"int bar(int j, int k) {\n"
" int r = j * k;\n"
" return r;\n"
"}\n"
"\n"
"enum Bar { FOOBAR, BARFOO };\n"
"} // namespace";
verifyFormat(LeaveAs, Style, LeaveAs);
}
TEST_F(DefinitionBlockSeparatorTest, CSharp) {
FormatStyle Style = getLLVMStyle(FormatStyle::LK_CSharp);
Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None;
Style.AllowShortEnumsOnASingleLine = false;
verifyFormat("namespace {\r\n"
"public class SomeTinyClass {\r\n"
" int X;\r\n"
"}\r\n"
"\r\n"
"public class AnotherTinyClass {\r\n"
" int Y;\r\n"
"}\r\n"
"\r\n"
"internal static String toString() {\r\n"
"}\r\n"
"\r\n"
"public enum var {\r\n"
" none,\r\n"
" @string,\r\n"
" bool,\r\n"
" @enum\r\n"
"}\r\n"
"\r\n"
"[STAThread]\r\n"
"static void Main(string[] args) {\r\n"
" Console.WriteLine(\"HelloWorld\");\r\n"
"}\r\n"
"\r\n"
"static decimal Test() {\r\n"
"}\r\n"
"}\r\n"
"\r\n"
"public class FoobarClass {\r\n"
" int foobar;\r\n"
"}",
Style);
}
TEST_F(DefinitionBlockSeparatorTest, JavaScript) {
FormatStyle Style = getLLVMStyle(FormatStyle::LK_JavaScript);
Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None;
Style.AllowShortEnumsOnASingleLine = false;
verifyFormat("export const enum Foo {\n"
" A = 1,\n"
" B\n"
"}\n"
"\n"
"export function A() {\n"
"}\n"
"\n"
"export default function B() {\n"
"}\n"
"\n"
"export function C() {\n"
"}\n"
"\n"
"var t, p, q;\n"
"\n"
"export abstract class X {\n"
" y: number;\n"
"}\n"
"\n"
"export const enum Bar {\n"
" D = 1,\n"
" E\n"
"}",
Style);
}
} // namespace
} // namespace format
} // namespace clang