ksyx updated this revision to Diff 396358. ksyx marked an inline comment as done. ksyx added a comment.
- Change bool type of the option to enum type - Generate empty line for enum block Testfile: // test.cpp #include <algorithm> #include <cstdio> #include <cstring> struct Foo { int a,b,c; }; namespace Ns { class Bar { public: struct Foobar { int a; int b; }; private: int t; int method1() { // Intentional test for different // line breaking mode of brackets } template<typename T> int method2(T x) { // ... } enum Foo { FOO, BAR }; int method3(int par) {} }; class C { }; } namespace Ns2 { } Tests: bin/clang-format test2.cpp # Test default (leave as it is) bin/clang-format -style='{SeparateDefinitionBlocks: Between}' test2.cpp # Test normal functionality bin/clang-format -style='{SeparateDefinitionBlocks: Leave}' test2.cpp # Test turning off bin/clang-format -style='{SeparateDefinitionBlocks: Between, BasedOnStyle: WebKit}' test2.cpp # Case: opening bracket { has its own line CHANGES SINCE LAST ACTION https://reviews.llvm.org/D116314/new/ https://reviews.llvm.org/D116314 Files: clang/docs/ClangFormatStyleOptions.rst clang/include/clang/Format/Format.h clang/lib/Format/Format.cpp
Index: clang/lib/Format/Format.cpp =================================================================== --- clang/lib/Format/Format.cpp +++ clang/lib/Format/Format.cpp @@ -450,6 +450,14 @@ } }; +template <> +struct ScalarEnumerationTraits<FormatStyle::SeparateDefinitionStyle> { + static void enumeration(IO &IO, FormatStyle::SeparateDefinitionStyle &Value) { + IO.enumCase(Value, "Leave", FormatStyle::SDS_Leave); + IO.enumCase(Value, "Between", FormatStyle::SDS_Between); + } +}; + template <> struct ScalarEnumerationTraits<FormatStyle::SpaceBeforeParensStyle> { static void enumeration(IO &IO, FormatStyle::SpaceBeforeParensStyle &Value) { @@ -770,6 +778,7 @@ IO.mapOptional("ReferenceAlignment", Style.ReferenceAlignment); IO.mapOptional("ReflowComments", Style.ReflowComments); IO.mapOptional("ShortNamespaceLines", Style.ShortNamespaceLines); + IO.mapOptional("SeparateDefinitionBlocks", Style.SeparateDefinitionBlocks); IO.mapOptional("SortIncludes", Style.SortIncludes); IO.mapOptional("SortJavaStaticImport", Style.SortJavaStaticImport); IO.mapOptional("SortUsingDeclarations", Style.SortUsingDeclarations); @@ -1193,6 +1202,7 @@ 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; @@ -1863,13 +1873,13 @@ return std::make_pair(Result, Penalty); } -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; } +private: bool hasCpp03IncompatibleFormat(const SmallVectorImpl<AnnotatedLine *> &Lines) { for (const AnnotatedLine *Line : Lines) { @@ -2012,6 +2022,99 @@ } }; +// This class separates definition blocks like classes, functions, and +// namespaces by inserting a line break between them. +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 { + AffectedRangeMgr.computeAffectedLines(AnnotatedLines); + tooling::Replacements Result; + separateBlocks(AnnotatedLines, Result); + return {Result, 0}; + } + +private: + void separateBlocks(SmallVectorImpl<AnnotatedLine *> &Lines, + tooling::Replacements &Result) { + auto likelyDefinition = [](AnnotatedLine *Line) { + return (Line->MightBeFunctionDecl && Line->mightBeFunctionDefinition()) || + Line->First->isOneOf(tok::kw_class, tok::kw_struct, + tok::kw_namespace, tok::kw_enum); + }; + WhitespaceManager Whitespaces( + Env.getSourceManager(), Style, + Style.DeriveLineEnding + ? Formatter::inputUsesCRLF( + Env.getSourceManager().getBufferData(Env.getFileID()), + Style.UseCRLF) + : Style.UseCRLF); + for (unsigned I = 0; I < Lines.size(); I++) { + auto Line = Lines[I]; + if (Line->First->closesScope()) { + auto OpeningLineIndex = Line->MatchingOpeningBlockLineIndex; + // Case: Opening bracket has its own line + if (OpeningLineIndex > 0 && + Lines[OpeningLineIndex]->First->TokenText == "{") { + OpeningLineIndex--; + } + AnnotatedLine *OpeningLine = Lines[OpeningLineIndex]; + // Closing a function definition + if (likelyDefinition(OpeningLine)) { + FormatToken *TargetToken = nullptr; + AnnotatedLine *TargetLine; + auto insertReplacement = [&]() { + assert(TargetToken); + Whitespaces.replaceWhitespace(*TargetToken, 2, + TargetToken->SpacesRequiredBefore, + TargetToken->StartsColumn); + }; + + // Not the first token + if (OpeningLineIndex > 0) { + TargetLine = Lines[OpeningLineIndex - 1]; + // Not immediately following other scopes' opening + if (TargetLine->Affected && !TargetLine->Last->opensScope()) { + // Change target, since we need to put line break *before* + // a token + TargetLine = OpeningLine; + TargetToken = TargetLine->First; + + // Avoid duplicated replacement + if (TargetToken && !TargetToken->opensScope()) { + insertReplacement(); + } + } + } + + // Not the last token + if (I + 1 < Lines.size()) { + TargetLine = Lines[I + 1]; + TargetToken = TargetLine->First; + + // Not continuously closing scopes (e.g. function + class + + // namespace); The token will be handled in another case if + // it is a definition line + if (TargetLine->Affected && ((!TargetToken->closesScope() && + !likelyDefinition(TargetLine)) || + TargetToken->is(tok::kw_enum))) { + insertReplacement(); + } + } + } + } + } + for (const auto &R : Whitespaces.generateReplacements()) + if (Result.add(R)) + return; + } +}; + // This class clean up the erroneous/redundant code around the given ranges in // file. class Cleaner : public TokenAnalyzer { @@ -3048,6 +3151,11 @@ Passes.emplace_back([&](const Environment &Env) { return UsingDeclarationsSorter(Env, Expanded).process(); }); + + if (Style.SeparateDefinitionBlocks) + Passes.emplace_back([&](const Environment &Env) { + return DefinitionBlockSeparator(Env, Expanded).process(); + }); } if (Style.isJavaScript() && Style.JavaScriptQuotes != FormatStyle::JSQS_Leave) Index: clang/include/clang/Format/Format.h =================================================================== --- clang/include/clang/Format/Format.h +++ clang/include/clang/Format/Format.h @@ -3050,6 +3050,52 @@ bool ReflowComments; // clang-format on + enum SeparateDefinitionStyle { + /// Leave definition blocks separated as they are + SDS_Leave, + /// Insert empty lines between definition blocks + SDS_Between + }; + + /// Specifies the use of empty lines to separate definition blocks, including + /// classes, structs, enum, and functions, for C++ language. + /// \code + /// SeparateDefinitions + /// Leave v.s. Between + /// #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; + /// template<typename T> + /// int method2(T x) { int method1() { + /// // ... // ... + /// } } + /// int method3(int par) {} + /// }; template <typename T> int method2(T x) { + /// class C { // ... + /// }; } + /// } + /// int method3(int par) {} + /// }; + /// + /// class C {}; + /// } // namespace Ns + /// \endcode + /// \version 15 + SeparateDefinitionStyle SeparateDefinitionBlocks; + /// The maximal number of unwrapped lines that a short namespace spans. /// Defaults to 1. /// Index: clang/docs/ClangFormatStyleOptions.rst =================================================================== --- clang/docs/ClangFormatStyleOptions.rst +++ clang/docs/ClangFormatStyleOptions.rst @@ -3395,6 +3395,53 @@ /* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of * information */ +**SeparateDefinitionBlocks** (``SeparateDefinitionStyle``) :versionbadge:`clang-format 15` + Specifies the use of empty lines to separate definition blocks, including classes, + structs, enum, and functions, for C++ language. + + Possible values: + + * ``SDS_Leave`` (in configuration: ``Leave``) + Leave definition blocks separated as they are. + + * ``SDS_Between`` (in configuration: ``Between``) + Insert empty lines between definition blocks. + + .. code-block:: c++ + + SeparateDefinitions + Leave v.s. Between + #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; + template<typename T> + int method2(T x) { int method1() { + // ... // ... + } } + int method3(int par) {} + }; template <typename T> int method2(T x) { + class C { // ... + }; } + } + int method3(int par) {} + }; + + class C {}; + } // namespace Ns + **ShortNamespaceLines** (``Unsigned``) :versionbadge:`clang-format 14` The maximal number of unwrapped lines that a short namespace spans. Defaults to 1.
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits