Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -751,6 +751,28 @@ /// \endcode bool BreakBeforeInheritanceComma; + /// \brief If ``true``, consecutive namespace declarations will be on the same + /// line. If ``false``, each namespace is declared on a new line. + /// \code + /// true: + /// namespace Foo { namespace Bar { + /// }} + /// + /// false: + /// namespace Foo { + /// namespace Bar { + /// } + /// } + /// \endcode + /// + /// If it does not fit on a single line, the overflowing namespaces get wrapped: + /// /// \code + /// namespace Foo { namespace Bar { + /// namespace Extra { + /// }}} + /// \endcode + bool CompactNamespaces; + /// \brief If the constructor initializers don't fit on a line, put each /// initializer on its own line. /// \code @@ -1379,6 +1401,7 @@ BreakBeforeBinaryOperators == R.BreakBeforeBinaryOperators && BreakBeforeBraces == R.BreakBeforeBraces && BreakBeforeTernaryOperators == R.BreakBeforeTernaryOperators && + CompactNamespaces == R.CompactNamespaces && BreakConstructorInitializersBeforeComma == R.BreakConstructorInitializersBeforeComma && BreakAfterJavaFieldAnnotations == R.BreakAfterJavaFieldAnnotations && Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -287,20 +287,21 @@ IO.mapOptional("BinPackArguments", Style.BinPackArguments); IO.mapOptional("BinPackParameters", Style.BinPackParameters); IO.mapOptional("BraceWrapping", Style.BraceWrapping); + IO.mapOptional("BreakAfterJavaFieldAnnotations", + Style.BreakAfterJavaFieldAnnotations); IO.mapOptional("BreakBeforeBinaryOperators", Style.BreakBeforeBinaryOperators); IO.mapOptional("BreakBeforeBraces", Style.BreakBeforeBraces); + IO.mapOptional("BreakBeforeInheritanceComma", + Style.BreakBeforeInheritanceComma); IO.mapOptional("BreakBeforeTernaryOperators", Style.BreakBeforeTernaryOperators); IO.mapOptional("BreakConstructorInitializersBeforeComma", Style.BreakConstructorInitializersBeforeComma); - IO.mapOptional("BreakAfterJavaFieldAnnotations", - Style.BreakAfterJavaFieldAnnotations); IO.mapOptional("BreakStringLiterals", Style.BreakStringLiterals); IO.mapOptional("ColumnLimit", Style.ColumnLimit); IO.mapOptional("CommentPragmas", Style.CommentPragmas); - IO.mapOptional("BreakBeforeInheritanceComma", - Style.BreakBeforeInheritanceComma); + IO.mapOptional("CompactNamespaces", Style.CompactNamespaces); IO.mapOptional("ConstructorInitializerAllOnOneLineOrOnePerLine", Style.ConstructorInitializerAllOnOneLineOrOnePerLine); IO.mapOptional("ConstructorInitializerIndentWidth", @@ -516,8 +517,8 @@ LLVMStyle.AlwaysBreakAfterDefinitionReturnType = FormatStyle::DRTBS_None; LLVMStyle.AlwaysBreakBeforeMultilineStrings = false; LLVMStyle.AlwaysBreakTemplateDeclarations = false; - LLVMStyle.BinPackParameters = true; LLVMStyle.BinPackArguments = true; + LLVMStyle.BinPackParameters = true; LLVMStyle.BreakBeforeBinaryOperators = FormatStyle::BOS_None; LLVMStyle.BreakBeforeTernaryOperators = true; LLVMStyle.BreakBeforeBraces = FormatStyle::BS_Attach; @@ -529,6 +530,7 @@ LLVMStyle.BreakStringLiterals = true; LLVMStyle.ColumnLimit = 80; LLVMStyle.CommentPragmas = "^ IWYU pragma:"; + LLVMStyle.CompactNamespaces = false; LLVMStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = false; LLVMStyle.ConstructorInitializerIndentWidth = 4; LLVMStyle.ContinuationIndentWidth = 4; Index: lib/Format/NamespaceEndCommentsFixer.cpp =================================================================== --- lib/Format/NamespaceEndCommentsFixer.cpp +++ lib/Format/NamespaceEndCommentsFixer.cpp @@ -120,6 +120,23 @@ << llvm::toString(std::move(Err)) << "\n"; } } + +const FormatToken * getNamespaceToken(const AnnotatedLine *line, + const SmallVectorImpl &AnnotatedLines) { + if (!line->Affected || line->InPPDirective || !line->startsWith(tok::r_brace)) + return NULL; + size_t StartLineIndex = line->MatchingOpeningBlockLineIndex; + if (StartLineIndex == UnwrappedLine::kInvalidIndex) + return NULL; + assert(StartLineIndex < AnnotatedLines.size()); + const FormatToken *NamespaceTok = AnnotatedLines[StartLineIndex]->First; + // Detect "(inline)? namespace" in the beginning of a line. + if (NamespaceTok->is(tok::kw_inline)) + NamespaceTok = NamespaceTok->getNextNonComment(); + if (!NamespaceTok || NamespaceTok->isNot(tok::kw_namespace)) + return NULL; + return NamespaceTok; +} } // namespace NamespaceEndCommentsFixer::NamespaceEndCommentsFixer(const Environment &Env, @@ -133,20 +150,12 @@ AffectedRangeMgr.computeAffectedLines(AnnotatedLines.begin(), AnnotatedLines.end()); tooling::Replacements Fixes; + std::string AllNamespaceNames = ""; + size_t StartLineIndex = SIZE_MAX; for (size_t I = 0, E = AnnotatedLines.size(); I != E; ++I) { - if (!AnnotatedLines[I]->Affected || AnnotatedLines[I]->InPPDirective || - !AnnotatedLines[I]->startsWith(tok::r_brace)) - continue; const AnnotatedLine *EndLine = AnnotatedLines[I]; - size_t StartLineIndex = EndLine->MatchingOpeningBlockLineIndex; - if (StartLineIndex == UnwrappedLine::kInvalidIndex) - continue; - assert(StartLineIndex < E); - const FormatToken *NamespaceTok = AnnotatedLines[StartLineIndex]->First; - // Detect "(inline)? namespace" in the beginning of a line. - if (NamespaceTok->is(tok::kw_inline)) - NamespaceTok = NamespaceTok->getNextNonComment(); - if (!NamespaceTok || NamespaceTok->isNot(tok::kw_namespace)) + const FormatToken *NamespaceTok = getNamespaceToken(EndLine, AnnotatedLines); + if (!NamespaceTok) continue; FormatToken *RBraceTok = EndLine->First; if (RBraceTok->Finalized) @@ -160,6 +169,22 @@ if (!Style.AllowSemicolonAfterNamespace) removeTrailingSemicolon(RBraceTok->Next, SourceMgr, &Fixes); } + if (StartLineIndex == SIZE_MAX) + StartLineIndex = EndLine->MatchingOpeningBlockLineIndex; + std::string NamespaceName = computeName(NamespaceTok); + bool CombineWithNext = I + 1 < E && + Style.CompactNamespaces && + getNamespaceToken(AnnotatedLines[I + 1], AnnotatedLines) && + !AnnotatedLines[I + 1]->First->Finalized; + if (CombineWithNext) { + if (hasEndComment(EndCommentPrevTok) && validEndComment(EndCommentPrevTok, NamespaceName)) + // remove end comment, it will be merged in next one + updateEndComment(EndCommentPrevTok, std::string(), SourceMgr, &Fixes); + AllNamespaceNames = "::" + NamespaceName + AllNamespaceNames; + continue; + } + NamespaceName += std::move(AllNamespaceNames); + AllNamespaceNames = std::string(); // The next token in the token stream after the place where the end comment // token must be. This is either the next token on the current line or the // first token on the next line. @@ -171,17 +196,16 @@ bool AddNewline = EndCommentNextTok && EndCommentNextTok->NewlinesBefore == 0 && EndCommentNextTok->isNot(tok::eof); - const std::string NamespaceName = computeName(NamespaceTok); const std::string EndCommentText = computeEndCommentText(NamespaceName, AddNewline); if (!hasEndComment(EndCommentPrevTok)) { bool isShort = I - StartLineIndex <= kShortNamespaceMaxLines + 1; if (!isShort) addEndComment(EndCommentPrevTok, EndCommentText, SourceMgr, &Fixes); - continue; - } - if (!validEndComment(EndCommentPrevTok, NamespaceName)) + } else if (!validEndComment(EndCommentPrevTok, NamespaceName)) { updateEndComment(EndCommentPrevTok, EndCommentText, SourceMgr, &Fixes); + } + StartLineIndex = SIZE_MAX; } return Fixes; } Index: lib/Format/UnwrappedLineFormatter.cpp =================================================================== --- lib/Format/UnwrappedLineFormatter.cpp +++ lib/Format/UnwrappedLineFormatter.cpp @@ -127,12 +127,33 @@ unsigned Indent = 0; }; +bool isNamespaceToken(const FormatToken *NamespaceTok) { + // Detect "(inline)? namespace" in the beginning of a line. + if (NamespaceTok->is(tok::kw_inline)) + NamespaceTok = NamespaceTok->getNextNonComment(); + if (!NamespaceTok || NamespaceTok->isNot(tok::kw_namespace)) + return false; + return true; +} + +bool isEndOfNamespace(const AnnotatedLine *line, + const SmallVectorImpl &AnnotatedLines) { + if (!line->startsWith(tok::r_brace)) + return false; + size_t StartLineIndex = line->MatchingOpeningBlockLineIndex; + if (StartLineIndex == UnwrappedLine::kInvalidIndex) + return false; + assert(StartLineIndex < AnnotatedLines.size()); + const FormatToken *NamespaceTok = AnnotatedLines[StartLineIndex]->First; + return isNamespaceToken(NamespaceTok); +} + class LineJoiner { public: LineJoiner(const FormatStyle &Style, const AdditionalKeywords &Keywords, const SmallVectorImpl &Lines) : Style(Style), Keywords(Keywords), End(Lines.end()), - Next(Lines.begin()) {} + Next(Lines.begin()), AnnotatedLines(Lines) {} /// \brief Returns the next line, merging multiple lines into one if possible. const AnnotatedLine *getNextMergedLine(bool DryRun, @@ -195,6 +216,28 @@ (Style.AllowShortFunctionsOnASingleLine == FormatStyle::SFS_Inline && TheLine->Level != 0); + if (Style.CompactNamespaces) { + if (isNamespaceToken(TheLine->First)) { + int i = 0; + while (I + 1 + i != E && isNamespaceToken(I[i + 1]->First) && + I[i + 1]->Last->TotalLength < Limit) { + Limit -= I[i + 1]->Last->TotalLength; + i++; + } + return i; + } + + if (isEndOfNamespace(TheLine, AnnotatedLines)) { + int i = 0; + while (I + 1 + i != E && isEndOfNamespace(I[i + 1], AnnotatedLines)) { + // No space between consecutive braces + I[i + 1]->First->SpacesRequiredBefore = !I[i]->Last->is(tok::r_brace); + i++; + } + return i; + } + } + if (TheLine->Last->is(TT_FunctionLBrace) && TheLine->First != TheLine->Last) { return MergeShortFunctions ? tryMergeSimpleBlock(I, E, Limit) : 0; @@ -449,6 +492,7 @@ const SmallVectorImpl::const_iterator End; SmallVectorImpl::const_iterator Next; + const SmallVectorImpl &AnnotatedLines; }; static void markFinalized(FormatToken *Tok) { Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -1306,6 +1306,61 @@ "} // namespace in\n" "} // namespace out", Style)); + + FormatStyle LLVMWithCompactNamespaces = getLLVMStyle(); + LLVMWithCompactNamespaces.CompactNamespaces = true; + + EXPECT_EQ("namespace out { namespace in {\n" + "}} // namespace out::in", + format("namespace out {\n" + "namespace in {\n" + "} // namespace in\n" + "} // namespace out", + LLVMWithCompactNamespaces)); + + EXPECT_EQ("namespace out { namespace in1 {\n" + "} // namespace in1\n" + "namespace in2 {\n" + "}} // namespace out::in2", + format("namespace out {\n" + "namespace in1 {\n" + "} // namespace in1\n" + "namespace in2 {\n" + "} // namespace in2\n" + "} // namespace out", + LLVMWithCompactNamespaces)); + + EXPECT_EQ("namespace out {\n" + "int i;\n" + "namespace in {\n" + "int j;\n" + "} // namespace in\n" + "int k;\n" + "} // namespace out", + format("namespace out { int i;\n" + "namespace in { int j; } // namespace in\n" + "int k; } // namespace out", + LLVMWithCompactNamespaces)); + + EXPECT_EQ("namespace aaaaaaaaaaaaaaaaaaaaaaaaaaaa {\n" + "namespace bbbbbbbbbbbbbbbbbbbbbbbbbbbb {\n" + "}} // namespace aaaaaaaaaaaaaaaaaaaaaaaaaaaa::bbbbbbbbbbbbbbbbbbbbbbbbbbbb", + format("namespace aaaaaaaaaaaaaaaaaaaaaaaaaaaa {\n" + "namespace bbbbbbbbbbbbbbbbbbbbbbbbbbbb {\n" + "} // namespace bbbbbbbbbbbbbbbbbbbbbbbbbbbb\n" + "} // namespace aaaaaaaaaaaaaaaaaaaaaaaaaaaa", + LLVMWithCompactNamespaces)); + + EXPECT_EQ("namespace aaaaaaaaaaaaaaaaa { namespace bbbbbbbbbbbbbbbbb {\n" + "namespace ccccccccccccccccc {\n" + "}}} // namespace aaaaaaaaaaaaaaaaa::bbbbbbbbbbbbbbbbb::ccccccccccccccccc", + format("namespace aaaaaaaaaaaaaaaaa {\n" + "namespace bbbbbbbbbbbbbbbbb {\n" + "namespace ccccccccccccccccc {\n" + "} // namespace ccccccccccccccccc\n" + "} // namespace bbbbbbbbbbbbbbbbb\n" + "} // namespace aaaaaaaaaaaaaaaaa", + LLVMWithCompactNamespaces)); } TEST_F(FormatTest, FormatsExternC) { verifyFormat("extern \"C\" {\nint a;"); } @@ -8683,6 +8738,7 @@ CHECK_PARSE_BOOL(BreakConstructorInitializersBeforeComma); CHECK_PARSE_BOOL(BreakStringLiterals); CHECK_PARSE_BOOL(BreakBeforeInheritanceComma) + CHECK_PARSE_BOOL(CompactNamespaces); CHECK_PARSE_BOOL(ConstructorInitializerAllOnOneLineOrOnePerLine); CHECK_PARSE_BOOL(DerivePointerAlignment); CHECK_PARSE_BOOL_FIELD(DerivePointerAlignment, "DerivePointerBinding");