Skip to content

Commit e56a829

Browse files
committedJun 14, 2017
clang-format: Add CompactNamespaces option
Summary: Add CompactNamespaces option, to pack namespace declarations on the same line (somewhat similar to C++17 nested namespace definition). With this option, consecutive namespace declarations are kept on the same line: namespace foo { namespace bar { ... }} // namespace foo::bar Reviewers: krasimir, djasper, klimek Reviewed By: djasper Subscribers: kimgr, cfe-commits, klimek Tags: #clang-tools-extra Differential Revision: https://reviews.llvm.org/D32480 llvm-svn: 305384
1 parent 1680ea4 commit e56a829

File tree

7 files changed

+339
-23
lines changed

7 files changed

+339
-23
lines changed
 

Diff for: ‎clang/include/clang/Format/Format.h

+24
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,29 @@ struct FormatStyle {
791791
/// \endcode
792792
bool BreakBeforeInheritanceComma;
793793

794+
/// \brief If ``true``, consecutive namespace declarations will be on the same
795+
/// line. If ``false``, each namespace is declared on a new line.
796+
/// \code
797+
/// true:
798+
/// namespace Foo { namespace Bar {
799+
/// }}
800+
///
801+
/// false:
802+
/// namespace Foo {
803+
/// namespace Bar {
804+
/// }
805+
/// }
806+
/// \endcode
807+
///
808+
/// If it does not fit on a single line, the overflowing namespaces get
809+
/// wrapped:
810+
/// \code
811+
/// namespace Foo { namespace Bar {
812+
/// namespace Extra {
813+
/// }}}
814+
/// \endcode
815+
bool CompactNamespaces;
816+
794817
/// \brief If the constructor initializers don't fit on a line, put each
795818
/// initializer on its own line.
796819
/// \code
@@ -1422,6 +1445,7 @@ struct FormatStyle {
14221445
BreakBeforeBraces == R.BreakBeforeBraces &&
14231446
BreakBeforeTernaryOperators == R.BreakBeforeTernaryOperators &&
14241447
BreakConstructorInitializers == R.BreakConstructorInitializers &&
1448+
CompactNamespaces == R.CompactNamespaces &&
14251449
BreakAfterJavaFieldAnnotations == R.BreakAfterJavaFieldAnnotations &&
14261450
BreakStringLiterals == R.BreakStringLiterals &&
14271451
ColumnLimit == R.ColumnLimit && CommentPragmas == R.CommentPragmas &&

Diff for: ‎clang/lib/Format/Format.cpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,8 @@ template <> struct MappingTraits<FormatStyle> {
310310
IO.mapOptional("BreakBeforeBinaryOperators",
311311
Style.BreakBeforeBinaryOperators);
312312
IO.mapOptional("BreakBeforeBraces", Style.BreakBeforeBraces);
313+
IO.mapOptional("BreakBeforeInheritanceComma",
314+
Style.BreakBeforeInheritanceComma);
313315
IO.mapOptional("BreakBeforeTernaryOperators",
314316
Style.BreakBeforeTernaryOperators);
315317

@@ -330,8 +332,7 @@ template <> struct MappingTraits<FormatStyle> {
330332
IO.mapOptional("BreakStringLiterals", Style.BreakStringLiterals);
331333
IO.mapOptional("ColumnLimit", Style.ColumnLimit);
332334
IO.mapOptional("CommentPragmas", Style.CommentPragmas);
333-
IO.mapOptional("BreakBeforeInheritanceComma",
334-
Style.BreakBeforeInheritanceComma);
335+
IO.mapOptional("CompactNamespaces", Style.CompactNamespaces);
335336
IO.mapOptional("ConstructorInitializerAllOnOneLineOrOnePerLine",
336337
Style.ConstructorInitializerAllOnOneLineOrOnePerLine);
337338
IO.mapOptional("ConstructorInitializerIndentWidth",
@@ -550,8 +551,8 @@ FormatStyle getLLVMStyle() {
550551
LLVMStyle.AlwaysBreakAfterDefinitionReturnType = FormatStyle::DRTBS_None;
551552
LLVMStyle.AlwaysBreakBeforeMultilineStrings = false;
552553
LLVMStyle.AlwaysBreakTemplateDeclarations = false;
553-
LLVMStyle.BinPackParameters = true;
554554
LLVMStyle.BinPackArguments = true;
555+
LLVMStyle.BinPackParameters = true;
555556
LLVMStyle.BreakBeforeBinaryOperators = FormatStyle::BOS_None;
556557
LLVMStyle.BreakBeforeTernaryOperators = true;
557558
LLVMStyle.BreakBeforeBraces = FormatStyle::BS_Attach;
@@ -563,6 +564,7 @@ FormatStyle getLLVMStyle() {
563564
LLVMStyle.BreakStringLiterals = true;
564565
LLVMStyle.ColumnLimit = 80;
565566
LLVMStyle.CommentPragmas = "^ IWYU pragma:";
567+
LLVMStyle.CompactNamespaces = false;
566568
LLVMStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = false;
567569
LLVMStyle.ConstructorInitializerIndentWidth = 4;
568570
LLVMStyle.ContinuationIndentWidth = 4;

Diff for: ‎clang/lib/Format/NamespaceEndCommentsFixer.cpp

+48-16
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,24 @@ void updateEndComment(const FormatToken *RBraceTok, StringRef EndCommentText,
107107
<< llvm::toString(std::move(Err)) << "\n";
108108
}
109109
}
110+
111+
const FormatToken *
112+
getNamespaceToken(const AnnotatedLine *line,
113+
const SmallVectorImpl<AnnotatedLine *> &AnnotatedLines) {
114+
if (!line->Affected || line->InPPDirective || !line->startsWith(tok::r_brace))
115+
return nullptr;
116+
size_t StartLineIndex = line->MatchingOpeningBlockLineIndex;
117+
if (StartLineIndex == UnwrappedLine::kInvalidIndex)
118+
return nullptr;
119+
assert(StartLineIndex < AnnotatedLines.size());
120+
const FormatToken *NamespaceTok = AnnotatedLines[StartLineIndex]->First;
121+
// Detect "(inline)? namespace" in the beginning of a line.
122+
if (NamespaceTok->is(tok::kw_inline))
123+
NamespaceTok = NamespaceTok->getNextNonComment();
124+
if (!NamespaceTok || NamespaceTok->isNot(tok::kw_namespace))
125+
return nullptr;
126+
return NamespaceTok;
127+
}
110128
} // namespace
111129

112130
NamespaceEndCommentsFixer::NamespaceEndCommentsFixer(const Environment &Env,
@@ -120,20 +138,14 @@ tooling::Replacements NamespaceEndCommentsFixer::analyze(
120138
AffectedRangeMgr.computeAffectedLines(AnnotatedLines.begin(),
121139
AnnotatedLines.end());
122140
tooling::Replacements Fixes;
141+
std::string AllNamespaceNames = "";
142+
size_t StartLineIndex = SIZE_MAX;
143+
unsigned int CompactedNamespacesCount = 0;
123144
for (size_t I = 0, E = AnnotatedLines.size(); I != E; ++I) {
124-
if (!AnnotatedLines[I]->Affected || AnnotatedLines[I]->InPPDirective ||
125-
!AnnotatedLines[I]->startsWith(tok::r_brace))
126-
continue;
127145
const AnnotatedLine *EndLine = AnnotatedLines[I];
128-
size_t StartLineIndex = EndLine->MatchingOpeningBlockLineIndex;
129-
if (StartLineIndex == UnwrappedLine::kInvalidIndex)
130-
continue;
131-
assert(StartLineIndex < E);
132-
const FormatToken *NamespaceTok = AnnotatedLines[StartLineIndex]->First;
133-
// Detect "(inline)? namespace" in the beginning of a line.
134-
if (NamespaceTok->is(tok::kw_inline))
135-
NamespaceTok = NamespaceTok->getNextNonComment();
136-
if (!NamespaceTok || NamespaceTok->isNot(tok::kw_namespace))
146+
const FormatToken *NamespaceTok =
147+
getNamespaceToken(EndLine, AnnotatedLines);
148+
if (!NamespaceTok)
137149
continue;
138150
FormatToken *RBraceTok = EndLine->First;
139151
if (RBraceTok->Finalized)
@@ -145,6 +157,27 @@ tooling::Replacements NamespaceEndCommentsFixer::analyze(
145157
if (RBraceTok->Next && RBraceTok->Next->is(tok::semi)) {
146158
EndCommentPrevTok = RBraceTok->Next;
147159
}
160+
if (StartLineIndex == SIZE_MAX)
161+
StartLineIndex = EndLine->MatchingOpeningBlockLineIndex;
162+
std::string NamespaceName = computeName(NamespaceTok);
163+
if (Style.CompactNamespaces) {
164+
if ((I + 1 < E) &&
165+
getNamespaceToken(AnnotatedLines[I + 1], AnnotatedLines) &&
166+
StartLineIndex - CompactedNamespacesCount - 1 ==
167+
AnnotatedLines[I + 1]->MatchingOpeningBlockLineIndex &&
168+
!AnnotatedLines[I + 1]->First->Finalized) {
169+
if (hasEndComment(EndCommentPrevTok)) {
170+
// remove end comment, it will be merged in next one
171+
updateEndComment(EndCommentPrevTok, std::string(), SourceMgr, &Fixes);
172+
}
173+
CompactedNamespacesCount++;
174+
AllNamespaceNames = "::" + NamespaceName + AllNamespaceNames;
175+
continue;
176+
}
177+
NamespaceName += std::move(AllNamespaceNames);
178+
CompactedNamespacesCount = 0;
179+
AllNamespaceNames = std::string();
180+
}
148181
// The next token in the token stream after the place where the end comment
149182
// token must be. This is either the next token on the current line or the
150183
// first token on the next line.
@@ -156,17 +189,16 @@ tooling::Replacements NamespaceEndCommentsFixer::analyze(
156189
bool AddNewline = EndCommentNextTok &&
157190
EndCommentNextTok->NewlinesBefore == 0 &&
158191
EndCommentNextTok->isNot(tok::eof);
159-
const std::string NamespaceName = computeName(NamespaceTok);
160192
const std::string EndCommentText =
161193
computeEndCommentText(NamespaceName, AddNewline);
162194
if (!hasEndComment(EndCommentPrevTok)) {
163195
bool isShort = I - StartLineIndex <= kShortNamespaceMaxLines + 1;
164196
if (!isShort)
165197
addEndComment(EndCommentPrevTok, EndCommentText, SourceMgr, &Fixes);
166-
continue;
167-
}
168-
if (!validEndComment(EndCommentPrevTok, NamespaceName))
198+
} else if (!validEndComment(EndCommentPrevTok, NamespaceName)) {
169199
updateEndComment(EndCommentPrevTok, EndCommentText, SourceMgr, &Fixes);
200+
}
201+
StartLineIndex = SIZE_MAX;
170202
}
171203
return Fixes;
172204
}

Diff for: ‎clang/lib/Format/UnwrappedLineFormatter.cpp

+65-4
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,13 @@ class LevelIndentTracker {
6666
Indent += Offset;
6767
}
6868

69+
/// \brief Update the indent state given that \p Line indent should be
70+
/// skipped.
71+
void skipLine(const AnnotatedLine &Line) {
72+
while (IndentForLevel.size() <= Line.Level)
73+
IndentForLevel.push_back(Indent);
74+
}
75+
6976
/// \brief Update the level indent to adapt to the given \p Line.
7077
///
7178
/// When a line is not formatted, we move the subsequent lines on the same
@@ -127,12 +134,31 @@ class LevelIndentTracker {
127134
unsigned Indent = 0;
128135
};
129136

137+
bool isNamespaceDeclaration(const AnnotatedLine *Line) {
138+
const FormatToken *NamespaceTok = Line->First;
139+
// Detect "(inline)? namespace" in the beginning of a line.
140+
if (NamespaceTok->is(tok::kw_inline))
141+
NamespaceTok = NamespaceTok->getNextNonComment();
142+
return NamespaceTok && NamespaceTok->is(tok::kw_namespace);
143+
}
144+
145+
bool isEndOfNamespace(const AnnotatedLine *Line,
146+
const SmallVectorImpl<AnnotatedLine *> &AnnotatedLines) {
147+
if (!Line->startsWith(tok::r_brace))
148+
return false;
149+
size_t StartLineIndex = Line->MatchingOpeningBlockLineIndex;
150+
if (StartLineIndex == UnwrappedLine::kInvalidIndex)
151+
return false;
152+
assert(StartLineIndex < AnnotatedLines.size());
153+
return isNamespaceDeclaration(AnnotatedLines[StartLineIndex]);
154+
}
155+
130156
class LineJoiner {
131157
public:
132158
LineJoiner(const FormatStyle &Style, const AdditionalKeywords &Keywords,
133159
const SmallVectorImpl<AnnotatedLine *> &Lines)
134-
: Style(Style), Keywords(Keywords), End(Lines.end()),
135-
Next(Lines.begin()) {}
160+
: Style(Style), Keywords(Keywords), End(Lines.end()), Next(Lines.begin()),
161+
AnnotatedLines(Lines) {}
136162

137163
/// \brief Returns the next line, merging multiple lines into one if possible.
138164
const AnnotatedLine *getNextMergedLine(bool DryRun,
@@ -142,7 +168,7 @@ class LineJoiner {
142168
const AnnotatedLine *Current = *Next;
143169
IndentTracker.nextLine(*Current);
144170
unsigned MergedLines =
145-
tryFitMultipleLinesInOne(IndentTracker.getIndent(), Next, End);
171+
tryFitMultipleLinesInOne(IndentTracker, Next, End);
146172
if (MergedLines > 0 && Style.ColumnLimit == 0)
147173
// Disallow line merging if there is a break at the start of one of the
148174
// input lines.
@@ -159,9 +185,11 @@ class LineJoiner {
159185
private:
160186
/// \brief Calculates how many lines can be merged into 1 starting at \p I.
161187
unsigned
162-
tryFitMultipleLinesInOne(unsigned Indent,
188+
tryFitMultipleLinesInOne(LevelIndentTracker &IndentTracker,
163189
SmallVectorImpl<AnnotatedLine *>::const_iterator I,
164190
SmallVectorImpl<AnnotatedLine *>::const_iterator E) {
191+
const unsigned Indent = IndentTracker.getIndent();
192+
165193
// Can't join the last line with anything.
166194
if (I + 1 == E)
167195
return 0;
@@ -201,6 +229,38 @@ class LineJoiner {
201229
(Style.AllowShortFunctionsOnASingleLine == FormatStyle::SFS_Inline &&
202230
TheLine->Level != 0);
203231

232+
if (Style.CompactNamespaces) {
233+
if (isNamespaceDeclaration(TheLine)) {
234+
int i = 0;
235+
unsigned closingLine = TheLine->MatchingOpeningBlockLineIndex - 1;
236+
for (; I + 1 + i != E && isNamespaceDeclaration(I[i + 1]) &&
237+
closingLine == I[i + 1]->MatchingOpeningBlockLineIndex &&
238+
I[i + 1]->Last->TotalLength < Limit;
239+
i++, closingLine--) {
240+
// No extra indent for compacted namespaces
241+
IndentTracker.skipLine(*I[i + 1]);
242+
243+
Limit -= I[i + 1]->Last->TotalLength;
244+
}
245+
return i;
246+
}
247+
248+
if (isEndOfNamespace(TheLine, AnnotatedLines)) {
249+
int i = 0;
250+
unsigned openingLine = TheLine->MatchingOpeningBlockLineIndex - 1;
251+
for (; I + 1 + i != E && isEndOfNamespace(I[i + 1], AnnotatedLines) &&
252+
openingLine == I[i + 1]->MatchingOpeningBlockLineIndex;
253+
i++, openingLine--) {
254+
// No space between consecutive braces
255+
I[i + 1]->First->SpacesRequiredBefore = !I[i]->Last->is(tok::r_brace);
256+
257+
// Indent like the outer-most namespace
258+
IndentTracker.nextLine(*I[i + 1]);
259+
}
260+
return i;
261+
}
262+
}
263+
204264
if (TheLine->Last->is(TT_FunctionLBrace) &&
205265
TheLine->First != TheLine->Last) {
206266
return MergeShortFunctions ? tryMergeSimpleBlock(I, E, Limit) : 0;
@@ -458,6 +518,7 @@ class LineJoiner {
458518
const SmallVectorImpl<AnnotatedLine *>::const_iterator End;
459519

460520
SmallVectorImpl<AnnotatedLine *>::const_iterator Next;
521+
const SmallVectorImpl<AnnotatedLine *> &AnnotatedLines;
461522
};
462523

463524
static void markFinalized(FormatToken *Tok) {

Diff for: ‎clang/lib/Format/UnwrappedLineParser.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,11 @@ void UnwrappedLineParser::parseBlock(bool MustBeDeclaration, bool AddLevel,
492492
nextToken();
493493
Line->Level = InitialLevel;
494494
Line->MatchingOpeningBlockLineIndex = OpeningLineIndex;
495+
if (OpeningLineIndex != UnwrappedLine::kInvalidIndex) {
496+
// Update the opening line to add the forward reference as well
497+
(*CurrentLines)[OpeningLineIndex].MatchingOpeningBlockLineIndex =
498+
CurrentLines->size() - 1;
499+
}
495500
}
496501

497502
static bool isGoogScope(const UnwrappedLine &Line) {

0 commit comments

Comments
 (0)
Please sign in to comment.