1 //===--- WhitespaceManager.cpp - Format C++ code --------------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
11 /// \brief This file implements WhitespaceManager class.
13 //===----------------------------------------------------------------------===//
15 #include "WhitespaceManager.h"
16 #include "llvm/ADT/STLExtras.h"
21 bool WhitespaceManager::Change::IsBeforeInFile::
22 operator()(const Change &C1, const Change &C2) const {
23 return SourceMgr.isBeforeInTranslationUnit(
24 C1.OriginalWhitespaceRange.getBegin(),
25 C2.OriginalWhitespaceRange.getBegin());
28 WhitespaceManager::Change::Change(const FormatToken &Tok,
29 bool CreateReplacement,
30 SourceRange OriginalWhitespaceRange,
31 int Spaces, unsigned StartOfTokenColumn,
32 unsigned NewlinesBefore,
33 StringRef PreviousLinePostfix,
34 StringRef CurrentLinePrefix,
35 bool ContinuesPPDirective, bool IsInsideToken)
36 : Tok(&Tok), CreateReplacement(CreateReplacement),
37 OriginalWhitespaceRange(OriginalWhitespaceRange),
38 StartOfTokenColumn(StartOfTokenColumn), NewlinesBefore(NewlinesBefore),
39 PreviousLinePostfix(PreviousLinePostfix),
40 CurrentLinePrefix(CurrentLinePrefix),
41 ContinuesPPDirective(ContinuesPPDirective), Spaces(Spaces),
42 IsInsideToken(IsInsideToken), IsTrailingComment(false), TokenLength(0),
43 PreviousEndOfTokenColumn(0), EscapedNewlineColumn(0),
44 StartOfBlockComment(nullptr), IndentationOffset(0) {}
46 void WhitespaceManager::replaceWhitespace(FormatToken &Tok, unsigned Newlines,
48 unsigned StartOfTokenColumn,
52 Tok.Decision = (Newlines > 0) ? FD_Break : FD_Continue;
53 Changes.push_back(Change(Tok, /*CreateReplacement=*/true, Tok.WhitespaceRange,
54 Spaces, StartOfTokenColumn, Newlines, "", "",
55 InPPDirective && !Tok.IsFirst,
56 /*IsInsideToken=*/false));
59 void WhitespaceManager::addUntouchableToken(const FormatToken &Tok,
63 Changes.push_back(Change(Tok, /*CreateReplacement=*/false,
64 Tok.WhitespaceRange, /*Spaces=*/0,
65 Tok.OriginalColumn, Tok.NewlinesBefore, "", "",
66 InPPDirective && !Tok.IsFirst,
67 /*IsInsideToken=*/false));
70 void WhitespaceManager::replaceWhitespaceInToken(
71 const FormatToken &Tok, unsigned Offset, unsigned ReplaceChars,
72 StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective,
73 unsigned Newlines, int Spaces) {
76 SourceLocation Start = Tok.getStartOfNonWhitespace().getLocWithOffset(Offset);
78 Change(Tok, /*CreateReplacement=*/true,
79 SourceRange(Start, Start.getLocWithOffset(ReplaceChars)), Spaces,
80 std::max(0, Spaces), Newlines, PreviousPostfix, CurrentPrefix,
81 InPPDirective && !Tok.IsFirst, /*IsInsideToken=*/true));
84 const tooling::Replacements &WhitespaceManager::generateReplacements() {
88 std::sort(Changes.begin(), Changes.end(), Change::IsBeforeInFile(SourceMgr));
89 calculateLineBreakInformation();
90 alignConsecutiveDeclarations();
91 alignConsecutiveAssignments();
92 alignTrailingComments();
93 alignEscapedNewlines();
99 void WhitespaceManager::calculateLineBreakInformation() {
100 Changes[0].PreviousEndOfTokenColumn = 0;
101 Change *LastOutsideTokenChange = &Changes[0];
102 for (unsigned i = 1, e = Changes.size(); i != e; ++i) {
103 unsigned OriginalWhitespaceStart =
104 SourceMgr.getFileOffset(Changes[i].OriginalWhitespaceRange.getBegin());
105 unsigned PreviousOriginalWhitespaceEnd = SourceMgr.getFileOffset(
106 Changes[i - 1].OriginalWhitespaceRange.getEnd());
107 Changes[i - 1].TokenLength = OriginalWhitespaceStart -
108 PreviousOriginalWhitespaceEnd +
109 Changes[i].PreviousLinePostfix.size() +
110 Changes[i - 1].CurrentLinePrefix.size();
112 // If there are multiple changes in this token, sum up all the changes until
113 // the end of the line.
114 if (Changes[i - 1].IsInsideToken)
115 LastOutsideTokenChange->TokenLength +=
116 Changes[i - 1].TokenLength + Changes[i - 1].Spaces;
118 LastOutsideTokenChange = &Changes[i - 1];
120 Changes[i].PreviousEndOfTokenColumn =
121 Changes[i - 1].StartOfTokenColumn + Changes[i - 1].TokenLength;
123 Changes[i - 1].IsTrailingComment =
124 (Changes[i].NewlinesBefore > 0 || Changes[i].Tok->is(tok::eof) ||
125 (Changes[i].IsInsideToken && Changes[i].Tok->is(tok::comment))) &&
126 Changes[i - 1].Tok->is(tok::comment) &&
127 // FIXME: This is a dirty hack. The problem is that
128 // BreakableLineCommentSection does comment reflow changes and here is
129 // the aligning of trailing comments. Consider the case where we reflow
130 // the second line up in this example:
135 // That amounts to 2 changes by BreakableLineCommentSection:
136 // - the first, delimited by (), for the whitespace between the tokens,
137 // - and second, delimited by [], for the whitespace at the beginning
138 // of the second token:
143 // So in the end we have two changes like this:
145 // // line1()[ ]line 2
147 // Note that the OriginalWhitespaceStart of the second change is the
148 // same as the PreviousOriginalWhitespaceEnd of the first change.
149 // In this case, the below check ensures that the second change doesn't
150 // get treated as a trailing comment change here, since this might
151 // trigger additional whitespace to be wrongly inserted before "line 2"
152 // by the comment aligner here.
154 // For a proper solution we need a mechanism to say to WhitespaceManager
155 // that a particular change breaks the current sequence of trailing
157 OriginalWhitespaceStart != PreviousOriginalWhitespaceEnd;
159 // FIXME: The last token is currently not always an eof token; in those
160 // cases, setting TokenLength of the last token to 0 is wrong.
161 Changes.back().TokenLength = 0;
162 Changes.back().IsTrailingComment = Changes.back().Tok->is(tok::comment);
164 const WhitespaceManager::Change *LastBlockComment = nullptr;
165 for (auto &Change : Changes) {
166 // Reset the IsTrailingComment flag for changes inside of trailing comments
167 // so they don't get realigned later. Comment line breaks however still need
169 if (Change.IsInsideToken && Change.NewlinesBefore == 0)
170 Change.IsTrailingComment = false;
171 Change.StartOfBlockComment = nullptr;
172 Change.IndentationOffset = 0;
173 if (Change.Tok->is(tok::comment)) {
174 if (Change.Tok->is(TT_LineComment) || !Change.IsInsideToken)
175 LastBlockComment = &Change;
177 if ((Change.StartOfBlockComment = LastBlockComment))
178 Change.IndentationOffset =
179 Change.StartOfTokenColumn -
180 Change.StartOfBlockComment->StartOfTokenColumn;
183 LastBlockComment = nullptr;
188 // Align a single sequence of tokens, see AlignTokens below.
189 template <typename F>
191 AlignTokenSequence(unsigned Start, unsigned End, unsigned Column, F &&Matches,
192 SmallVector<WhitespaceManager::Change, 16> &Changes) {
193 bool FoundMatchOnLine = false;
196 // ScopeStack keeps track of the current scope depth. It contains indices of
197 // the first token on each scope.
198 // We only run the "Matches" function on tokens from the outer-most scope.
199 // However, we do need to pay special attention to one class of tokens
200 // that are not in the outer-most scope, and that is function parameters
201 // which are split across multiple lines, as illustrated by this example:
205 // In the above example, we need to take special care to ensure that
206 // 'double z' is indented along with it's owning function 'b'.
207 SmallVector<unsigned, 16> ScopeStack;
209 for (unsigned i = Start; i != End; ++i) {
210 if (ScopeStack.size() != 0 &&
211 Changes[i].nestingAndIndentLevel() <
212 Changes[ScopeStack.back()].nestingAndIndentLevel())
213 ScopeStack.pop_back();
215 if (i != Start && Changes[i].nestingAndIndentLevel() >
216 Changes[i - 1].nestingAndIndentLevel())
217 ScopeStack.push_back(i);
219 bool InsideNestedScope = ScopeStack.size() != 0;
221 if (Changes[i].NewlinesBefore > 0 && !InsideNestedScope) {
223 FoundMatchOnLine = false;
226 // If this is the first matching token to be aligned, remember by how many
227 // spaces it has to be shifted, so the rest of the changes on the line are
228 // shifted by the same amount
229 if (!FoundMatchOnLine && !InsideNestedScope && Matches(Changes[i])) {
230 FoundMatchOnLine = true;
231 Shift = Column - Changes[i].StartOfTokenColumn;
232 Changes[i].Spaces += Shift;
235 // This is for function parameters that are split across multiple lines,
236 // as mentioned in the ScopeStack comment.
237 if (InsideNestedScope && Changes[i].NewlinesBefore > 0) {
238 unsigned ScopeStart = ScopeStack.back();
239 if (Changes[ScopeStart - 1].Tok->is(TT_FunctionDeclarationName) ||
240 (ScopeStart > Start + 1 &&
241 Changes[ScopeStart - 2].Tok->is(TT_FunctionDeclarationName)))
242 Changes[i].Spaces += Shift;
246 Changes[i].StartOfTokenColumn += Shift;
247 if (i + 1 != Changes.size())
248 Changes[i + 1].PreviousEndOfTokenColumn += Shift;
252 // Walk through a subset of the changes, starting at StartAt, and find
253 // sequences of matching tokens to align. To do so, keep track of the lines and
254 // whether or not a matching token was found on a line. If a matching token is
255 // found, extend the current sequence. If the current line cannot be part of a
256 // sequence, e.g. because there is an empty line before it or it contains only
257 // non-matching tokens, finalize the previous sequence.
258 // The value returned is the token on which we stopped, either because we
259 // exhausted all items inside Changes, or because we hit a scope level higher
260 // than our initial scope.
261 // This function is recursive. Each invocation processes only the scope level
262 // equal to the initial level, which is the level of Changes[StartAt].
263 // If we encounter a scope level greater than the initial level, then we call
264 // ourselves recursively, thereby avoiding the pollution of the current state
265 // with the alignment requirements of the nested sub-level. This recursive
266 // behavior is necessary for aligning function prototypes that have one or more
268 // If this function encounters a scope level less than the initial level,
269 // it returns the current position.
270 // There is a non-obvious subtlety in the recursive behavior: Even though we
271 // defer processing of nested levels to recursive invocations of this
272 // function, when it comes time to align a sequence of tokens, we run the
273 // alignment on the entire sequence, including the nested levels.
274 // When doing so, most of the nested tokens are skipped, because their
275 // alignment was already handled by the recursive invocations of this function.
276 // However, the special exception is that we do NOT skip function parameters
277 // that are split across multiple lines. See the test case in FormatTest.cpp
278 // that mentions "split function parameter alignment" for an example of this.
279 template <typename F>
280 static unsigned AlignTokens(const FormatStyle &Style, F &&Matches,
281 SmallVector<WhitespaceManager::Change, 16> &Changes,
283 unsigned MinColumn = 0;
284 unsigned MaxColumn = UINT_MAX;
286 // Line number of the start and the end of the current token sequence.
287 unsigned StartOfSequence = 0;
288 unsigned EndOfSequence = 0;
290 // Measure the scope level (i.e. depth of (), [], {}) of the first token, and
291 // abort when we hit any token in a higher scope than the starting one.
292 auto NestingAndIndentLevel = StartAt < Changes.size()
293 ? Changes[StartAt].nestingAndIndentLevel()
294 : std::pair<unsigned, unsigned>(0, 0);
296 // Keep track of the number of commas before the matching tokens, we will only
297 // align a sequence of matching tokens if they are preceded by the same number
299 unsigned CommasBeforeLastMatch = 0;
300 unsigned CommasBeforeMatch = 0;
302 // Whether a matching token has been found on the current line.
303 bool FoundMatchOnLine = false;
305 // Aligns a sequence of matching tokens, on the MinColumn column.
307 // Sequences start from the first matching token to align, and end at the
308 // first token of the first line that doesn't need to be aligned.
310 // We need to adjust the StartOfTokenColumn of each Change that is on a line
311 // containing any matching token to be aligned and located after such token.
312 auto AlignCurrentSequence = [&] {
313 if (StartOfSequence > 0 && StartOfSequence < EndOfSequence)
314 AlignTokenSequence(StartOfSequence, EndOfSequence, MinColumn, Matches,
317 MaxColumn = UINT_MAX;
322 unsigned i = StartAt;
323 for (unsigned e = Changes.size(); i != e; ++i) {
324 if (Changes[i].nestingAndIndentLevel() < NestingAndIndentLevel)
327 if (Changes[i].NewlinesBefore != 0) {
328 CommasBeforeMatch = 0;
330 // If there is a blank line, or if the last line didn't contain any
331 // matching token, the sequence ends here.
332 if (Changes[i].NewlinesBefore > 1 || !FoundMatchOnLine)
333 AlignCurrentSequence();
335 FoundMatchOnLine = false;
338 if (Changes[i].Tok->is(tok::comma)) {
340 } else if (Changes[i].nestingAndIndentLevel() > NestingAndIndentLevel) {
341 // Call AlignTokens recursively, skipping over this scope block.
342 unsigned StoppedAt = AlignTokens(Style, Matches, Changes, i);
347 if (!Matches(Changes[i]))
350 // If there is more than one matching token per line, or if the number of
351 // preceding commas, do not match anymore, end the sequence.
352 if (FoundMatchOnLine || CommasBeforeMatch != CommasBeforeLastMatch)
353 AlignCurrentSequence();
355 CommasBeforeLastMatch = CommasBeforeMatch;
356 FoundMatchOnLine = true;
358 if (StartOfSequence == 0)
361 unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
362 int LineLengthAfter = -Changes[i].Spaces;
363 for (unsigned j = i; j != e && Changes[j].NewlinesBefore == 0; ++j)
364 LineLengthAfter += Changes[j].Spaces + Changes[j].TokenLength;
365 unsigned ChangeMaxColumn = Style.ColumnLimit - LineLengthAfter;
367 // If we are restricted by the maximum column width, end the sequence.
368 if (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn ||
369 CommasBeforeLastMatch != CommasBeforeMatch) {
370 AlignCurrentSequence();
374 MinColumn = std::max(MinColumn, ChangeMinColumn);
375 MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
379 AlignCurrentSequence();
383 void WhitespaceManager::alignConsecutiveAssignments() {
384 if (!Style.AlignConsecutiveAssignments)
388 [&](const Change &C) {
389 // Do not align on equal signs that are first on a line.
390 if (C.NewlinesBefore > 0)
393 // Do not align on equal signs that are last on a line.
394 if (&C != &Changes.back() && (&C + 1)->NewlinesBefore > 0)
397 return C.Tok->is(tok::equal);
399 Changes, /*StartAt=*/0);
402 void WhitespaceManager::alignConsecutiveDeclarations() {
403 if (!Style.AlignConsecutiveDeclarations)
406 // FIXME: Currently we don't handle properly the PointerAlignment: Right
407 // The * and & are not aligned and are left dangling. Something has to be done
408 // about it, but it raises the question of alignment of code like:
409 // const char* const* v1;
411 // SomeVeryLongType const& v3;
413 [](Change const &C) {
414 // tok::kw_operator is necessary for aligning operator overload
416 return C.Tok->is(TT_StartOfName) ||
417 C.Tok->is(TT_FunctionDeclarationName) ||
418 C.Tok->is(tok::kw_operator);
420 Changes, /*StartAt=*/0);
423 void WhitespaceManager::alignTrailingComments() {
424 unsigned MinColumn = 0;
425 unsigned MaxColumn = UINT_MAX;
426 unsigned StartOfSequence = 0;
427 bool BreakBeforeNext = false;
428 unsigned Newlines = 0;
429 for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
430 if (Changes[i].StartOfBlockComment)
432 Newlines += Changes[i].NewlinesBefore;
433 if (!Changes[i].IsTrailingComment)
436 unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
437 unsigned ChangeMaxColumn = Style.ColumnLimit - Changes[i].TokenLength;
439 // If we don't create a replacement for this change, we have to consider
440 // it to be immovable.
441 if (!Changes[i].CreateReplacement)
442 ChangeMaxColumn = ChangeMinColumn;
444 if (i + 1 != e && Changes[i + 1].ContinuesPPDirective)
445 ChangeMaxColumn -= 2;
446 // If this comment follows an } in column 0, it probably documents the
447 // closing of a namespace and we don't want to align it.
448 bool FollowsRBraceInColumn0 = i > 0 && Changes[i].NewlinesBefore == 0 &&
449 Changes[i - 1].Tok->is(tok::r_brace) &&
450 Changes[i - 1].StartOfTokenColumn == 0;
451 bool WasAlignedWithStartOfNextLine = false;
452 if (Changes[i].NewlinesBefore == 1) { // A comment on its own line.
453 unsigned CommentColumn = SourceMgr.getSpellingColumnNumber(
454 Changes[i].OriginalWhitespaceRange.getEnd());
455 for (unsigned j = i + 1; j != e; ++j) {
456 if (Changes[j].Tok->is(tok::comment))
459 unsigned NextColumn = SourceMgr.getSpellingColumnNumber(
460 Changes[j].OriginalWhitespaceRange.getEnd());
461 // The start of the next token was previously aligned with the
462 // start of this comment.
463 WasAlignedWithStartOfNextLine =
464 CommentColumn == NextColumn ||
465 CommentColumn == NextColumn + Style.IndentWidth;
469 if (!Style.AlignTrailingComments || FollowsRBraceInColumn0) {
470 alignTrailingComments(StartOfSequence, i, MinColumn);
471 MinColumn = ChangeMinColumn;
472 MaxColumn = ChangeMinColumn;
474 } else if (BreakBeforeNext || Newlines > 1 ||
475 (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) ||
476 // Break the comment sequence if the previous line did not end
477 // in a trailing comment.
478 (Changes[i].NewlinesBefore == 1 && i > 0 &&
479 !Changes[i - 1].IsTrailingComment) ||
480 WasAlignedWithStartOfNextLine) {
481 alignTrailingComments(StartOfSequence, i, MinColumn);
482 MinColumn = ChangeMinColumn;
483 MaxColumn = ChangeMaxColumn;
486 MinColumn = std::max(MinColumn, ChangeMinColumn);
487 MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
490 (i == 0) || (Changes[i].NewlinesBefore > 1) ||
491 // Never start a sequence with a comment at the beginning of
493 (Changes[i].NewlinesBefore == 1 && StartOfSequence == i);
496 alignTrailingComments(StartOfSequence, Changes.size(), MinColumn);
499 void WhitespaceManager::alignTrailingComments(unsigned Start, unsigned End,
501 for (unsigned i = Start; i != End; ++i) {
503 if (Changes[i].IsTrailingComment) {
504 Shift = Column - Changes[i].StartOfTokenColumn;
506 if (Changes[i].StartOfBlockComment) {
507 Shift = Changes[i].IndentationOffset +
508 Changes[i].StartOfBlockComment->StartOfTokenColumn -
509 Changes[i].StartOfTokenColumn;
512 Changes[i].Spaces += Shift;
513 if (i + 1 != Changes.size())
514 Changes[i + 1].PreviousEndOfTokenColumn += Shift;
515 Changes[i].StartOfTokenColumn += Shift;
519 void WhitespaceManager::alignEscapedNewlines() {
520 unsigned MaxEndOfLine =
521 Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit;
522 unsigned StartOfMacro = 0;
523 for (unsigned i = 1, e = Changes.size(); i < e; ++i) {
524 Change &C = Changes[i];
525 if (C.NewlinesBefore > 0) {
526 if (C.ContinuesPPDirective) {
527 MaxEndOfLine = std::max(C.PreviousEndOfTokenColumn + 2, MaxEndOfLine);
529 alignEscapedNewlines(StartOfMacro + 1, i, MaxEndOfLine);
530 MaxEndOfLine = Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit;
535 alignEscapedNewlines(StartOfMacro + 1, Changes.size(), MaxEndOfLine);
538 void WhitespaceManager::alignEscapedNewlines(unsigned Start, unsigned End,
540 for (unsigned i = Start; i < End; ++i) {
541 Change &C = Changes[i];
542 if (C.NewlinesBefore > 0) {
543 assert(C.ContinuesPPDirective);
544 if (C.PreviousEndOfTokenColumn + 1 > Column)
545 C.EscapedNewlineColumn = 0;
547 C.EscapedNewlineColumn = Column;
552 void WhitespaceManager::generateChanges() {
553 for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
554 const Change &C = Changes[i];
556 assert(Changes[i - 1].OriginalWhitespaceRange.getBegin() !=
557 C.OriginalWhitespaceRange.getBegin() &&
558 "Generating two replacements for the same location");
560 if (C.CreateReplacement) {
561 std::string ReplacementText = C.PreviousLinePostfix;
562 if (C.ContinuesPPDirective)
563 appendNewlineText(ReplacementText, C.NewlinesBefore,
564 C.PreviousEndOfTokenColumn, C.EscapedNewlineColumn);
566 appendNewlineText(ReplacementText, C.NewlinesBefore);
567 appendIndentText(ReplacementText, C.Tok->IndentLevel,
568 std::max(0, C.Spaces),
569 C.StartOfTokenColumn - std::max(0, C.Spaces));
570 ReplacementText.append(C.CurrentLinePrefix);
571 storeReplacement(C.OriginalWhitespaceRange, ReplacementText);
576 void WhitespaceManager::storeReplacement(SourceRange Range,
578 unsigned WhitespaceLength = SourceMgr.getFileOffset(Range.getEnd()) -
579 SourceMgr.getFileOffset(Range.getBegin());
580 // Don't create a replacement, if it does not change anything.
581 if (StringRef(SourceMgr.getCharacterData(Range.getBegin()),
582 WhitespaceLength) == Text)
584 auto Err = Replaces.add(tooling::Replacement(
585 SourceMgr, CharSourceRange::getCharRange(Range), Text));
586 // FIXME: better error handling. For now, just print an error message in the
589 llvm::errs() << llvm::toString(std::move(Err)) << "\n";
594 void WhitespaceManager::appendNewlineText(std::string &Text,
596 for (unsigned i = 0; i < Newlines; ++i)
597 Text.append(UseCRLF ? "\r\n" : "\n");
600 void WhitespaceManager::appendNewlineText(std::string &Text, unsigned Newlines,
601 unsigned PreviousEndOfTokenColumn,
602 unsigned EscapedNewlineColumn) {
605 std::min<int>(EscapedNewlineColumn - 1, PreviousEndOfTokenColumn);
606 for (unsigned i = 0; i < Newlines; ++i) {
607 Text.append(EscapedNewlineColumn - Offset - 1, ' ');
608 Text.append(UseCRLF ? "\\\r\n" : "\\\n");
614 void WhitespaceManager::appendIndentText(std::string &Text,
615 unsigned IndentLevel, unsigned Spaces,
616 unsigned WhitespaceStartColumn) {
617 switch (Style.UseTab) {
618 case FormatStyle::UT_Never:
619 Text.append(Spaces, ' ');
621 case FormatStyle::UT_Always: {
622 unsigned FirstTabWidth =
623 Style.TabWidth - WhitespaceStartColumn % Style.TabWidth;
624 // Indent with tabs only when there's at least one full tab.
625 if (FirstTabWidth + Style.TabWidth <= Spaces) {
626 Spaces -= FirstTabWidth;
629 Text.append(Spaces / Style.TabWidth, '\t');
630 Text.append(Spaces % Style.TabWidth, ' ');
633 case FormatStyle::UT_ForIndentation:
634 if (WhitespaceStartColumn == 0) {
635 unsigned Indentation = IndentLevel * Style.IndentWidth;
636 // This happens, e.g. when a line in a block comment is indented less than
638 if (Indentation > Spaces)
639 Indentation = Spaces;
640 unsigned Tabs = Indentation / Style.TabWidth;
641 Text.append(Tabs, '\t');
642 Spaces -= Tabs * Style.TabWidth;
644 Text.append(Spaces, ' ');
646 case FormatStyle::UT_ForContinuationAndIndentation:
647 if (WhitespaceStartColumn == 0) {
648 unsigned Tabs = Spaces / Style.TabWidth;
649 Text.append(Tabs, '\t');
650 Spaces -= Tabs * Style.TabWidth;
652 Text.append(Spaces, ' ');
657 } // namespace format