1 //===--- WhitespaceManager.cpp - Format C++ code --------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 ///
10 /// \file
11 /// \brief This file implements WhitespaceManager class.
12 ///
13 //===----------------------------------------------------------------------===//
14 
15 #include "WhitespaceManager.h"
16 #include "llvm/ADT/STLExtras.h"
17 
18 namespace clang {
19 namespace format {
20 
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());
26 }
27 
28 WhitespaceManager::Change::Change(
29     bool CreateReplacement, SourceRange OriginalWhitespaceRange,
30     unsigned IndentLevel, int Spaces, unsigned StartOfTokenColumn,
31     unsigned NewlinesBefore, StringRef PreviousLinePostfix,
32     StringRef CurrentLinePrefix, tok::TokenKind Kind, bool ContinuesPPDirective,
33     bool IsStartOfDeclName, bool IsInsideToken)
34     : CreateReplacement(CreateReplacement),
35       OriginalWhitespaceRange(OriginalWhitespaceRange),
36       StartOfTokenColumn(StartOfTokenColumn), NewlinesBefore(NewlinesBefore),
37       PreviousLinePostfix(PreviousLinePostfix),
38       CurrentLinePrefix(CurrentLinePrefix), Kind(Kind),
39       ContinuesPPDirective(ContinuesPPDirective),
40       IsStartOfDeclName(IsStartOfDeclName), IndentLevel(IndentLevel),
41       Spaces(Spaces), IsInsideToken(IsInsideToken), IsTrailingComment(false),
42       TokenLength(0), PreviousEndOfTokenColumn(0), EscapedNewlineColumn(0),
43       StartOfBlockComment(nullptr), IndentationOffset(0) {}
44 
45 void WhitespaceManager::replaceWhitespace(FormatToken &Tok, unsigned Newlines,
46                                           unsigned IndentLevel, unsigned Spaces,
47                                           unsigned StartOfTokenColumn,
48                                           bool InPPDirective) {
49   if (Tok.Finalized)
50     return;
51   Tok.Decision = (Newlines > 0) ? FD_Break : FD_Continue;
52   Changes.push_back(
53       Change(/*CreateReplacement=*/true, Tok.WhitespaceRange, IndentLevel,
54              Spaces, StartOfTokenColumn, Newlines, "", "", Tok.Tok.getKind(),
55              InPPDirective && !Tok.IsFirst,
56              Tok.is(TT_StartOfName) || Tok.is(TT_FunctionDeclarationName),
57              /*IsInsideToken=*/false));
58 }
59 
60 void WhitespaceManager::addUntouchableToken(const FormatToken &Tok,
61                                             bool InPPDirective) {
62   if (Tok.Finalized)
63     return;
64   Changes.push_back(Change(
65       /*CreateReplacement=*/false, Tok.WhitespaceRange, /*IndentLevel=*/0,
66       /*Spaces=*/0, Tok.OriginalColumn, Tok.NewlinesBefore, "", "",
67       Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst,
68       Tok.is(TT_StartOfName) || Tok.is(TT_FunctionDeclarationName),
69       /*IsInsideToken=*/false));
70 }
71 
72 void WhitespaceManager::replaceWhitespaceInToken(
73     const FormatToken &Tok, unsigned Offset, unsigned ReplaceChars,
74     StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective,
75     unsigned Newlines, unsigned IndentLevel, int Spaces) {
76   if (Tok.Finalized)
77     return;
78   SourceLocation Start = Tok.getStartOfNonWhitespace().getLocWithOffset(Offset);
79   Changes.push_back(Change(
80       true, SourceRange(Start, Start.getLocWithOffset(ReplaceChars)),
81       IndentLevel, Spaces, std::max(0, Spaces), Newlines, PreviousPostfix,
82       CurrentPrefix, Tok.is(TT_LineComment) ? tok::comment : tok::unknown,
83       InPPDirective && !Tok.IsFirst,
84       Tok.is(TT_StartOfName) || Tok.is(TT_FunctionDeclarationName),
85       /*IsInsideToken=*/Newlines == 0));
86 }
87 
88 const tooling::Replacements &WhitespaceManager::generateReplacements() {
89   if (Changes.empty())
90     return Replaces;
91 
92   std::sort(Changes.begin(), Changes.end(), Change::IsBeforeInFile(SourceMgr));
93   calculateLineBreakInformation();
94   alignConsecutiveDeclarations();
95   alignConsecutiveAssignments();
96   alignTrailingComments();
97   alignEscapedNewlines();
98   generateChanges();
99 
100   return Replaces;
101 }
102 
103 void WhitespaceManager::calculateLineBreakInformation() {
104   Changes[0].PreviousEndOfTokenColumn = 0;
105   Change *LastOutsideTokenChange = &Changes[0];
106   for (unsigned i = 1, e = Changes.size(); i != e; ++i) {
107     unsigned OriginalWhitespaceStart =
108         SourceMgr.getFileOffset(Changes[i].OriginalWhitespaceRange.getBegin());
109     unsigned PreviousOriginalWhitespaceEnd = SourceMgr.getFileOffset(
110         Changes[i - 1].OriginalWhitespaceRange.getEnd());
111     Changes[i - 1].TokenLength = OriginalWhitespaceStart -
112                                  PreviousOriginalWhitespaceEnd +
113                                  Changes[i].PreviousLinePostfix.size() +
114                                  Changes[i - 1].CurrentLinePrefix.size();
115 
116     // If there are multiple changes in this token, sum up all the changes until
117     // the end of the line.
118     if (Changes[i - 1].IsInsideToken)
119       LastOutsideTokenChange->TokenLength +=
120           Changes[i - 1].TokenLength + Changes[i - 1].Spaces;
121     else
122       LastOutsideTokenChange = &Changes[i - 1];
123 
124     Changes[i].PreviousEndOfTokenColumn =
125         Changes[i - 1].StartOfTokenColumn + Changes[i - 1].TokenLength;
126 
127     Changes[i - 1].IsTrailingComment =
128         (Changes[i].NewlinesBefore > 0 || Changes[i].Kind == tok::eof ||
129          (Changes[i].IsInsideToken && Changes[i].Kind == tok::comment)) &&
130         Changes[i - 1].Kind == tok::comment &&
131         // FIXME: This is a dirty hack. The problem is that
132         // BreakableLineCommentSection does comment reflow changes and here is
133         // the aligning of trailing comments. Consider the case where we reflow
134         // the second line up in this example:
135         //
136         // // line 1
137         // // line 2
138         //
139         // That amounts to 2 changes by BreakableLineCommentSection:
140         //  - the first, delimited by (), for the whitespace between the tokens,
141         //  - and second, delimited by [], for the whitespace at the beginning
142         //  of the second token:
143         //
144         // // line 1(
145         // )[// ]line 2
146         //
147         // So in the end we have two changes like this:
148         //
149         // // line1()[ ]line 2
150         //
151         // Note that the OriginalWhitespaceStart of the second change is the
152         // same as the PreviousOriginalWhitespaceEnd of the first change.
153         // In this case, the below check ensures that the second change doesn't
154         // get treated as a trailing comment change here, since this might
155         // trigger additional whitespace to be wrongly inserted before "line 2"
156         // by the comment aligner here.
157         //
158         // For a proper solution we need a mechanism to say to WhitespaceManager
159         // that a particular change breaks the current sequence of trailing
160         // comments.
161         OriginalWhitespaceStart != PreviousOriginalWhitespaceEnd;
162   }
163   // FIXME: The last token is currently not always an eof token; in those
164   // cases, setting TokenLength of the last token to 0 is wrong.
165   Changes.back().TokenLength = 0;
166   Changes.back().IsTrailingComment = Changes.back().Kind == tok::comment;
167 
168   const WhitespaceManager::Change *LastBlockComment = nullptr;
169   for (auto &Change : Changes) {
170     // Reset the IsTrailingComment flag for changes inside of trailing comments
171     // so they don't get realigned later.
172     if (Change.IsInsideToken)
173       Change.IsTrailingComment = false;
174     Change.StartOfBlockComment = nullptr;
175     Change.IndentationOffset = 0;
176     if (Change.Kind == tok::comment) {
177       LastBlockComment = &Change;
178     } else if (Change.Kind == tok::unknown) {
179       if ((Change.StartOfBlockComment = LastBlockComment))
180         Change.IndentationOffset =
181             Change.StartOfTokenColumn -
182             Change.StartOfBlockComment->StartOfTokenColumn;
183     } else {
184       LastBlockComment = nullptr;
185     }
186   }
187 }
188 
189 // Align a single sequence of tokens, see AlignTokens below.
190 template <typename F>
191 static void
192 AlignTokenSequence(unsigned Start, unsigned End, unsigned Column, F &&Matches,
193                    SmallVector<WhitespaceManager::Change, 16> &Changes) {
194   bool FoundMatchOnLine = false;
195   int Shift = 0;
196   for (unsigned i = Start; i != End; ++i) {
197     if (Changes[i].NewlinesBefore > 0) {
198       FoundMatchOnLine = false;
199       Shift = 0;
200     }
201 
202     // If this is the first matching token to be aligned, remember by how many
203     // spaces it has to be shifted, so the rest of the changes on the line are
204     // shifted by the same amount
205     if (!FoundMatchOnLine && Matches(Changes[i])) {
206       FoundMatchOnLine = true;
207       Shift = Column - Changes[i].StartOfTokenColumn;
208       Changes[i].Spaces += Shift;
209     }
210 
211     assert(Shift >= 0);
212     Changes[i].StartOfTokenColumn += Shift;
213     if (i + 1 != Changes.size())
214       Changes[i + 1].PreviousEndOfTokenColumn += Shift;
215   }
216 }
217 
218 // Walk through all of the changes and find sequences of matching tokens to
219 // align. To do so, keep track of the lines and whether or not a matching token
220 // was found on a line. If a matching token is found, extend the current
221 // sequence. If the current line cannot be part of a sequence, e.g. because
222 // there is an empty line before it or it contains only non-matching tokens,
223 // finalize the previous sequence.
224 template <typename F>
225 static void AlignTokens(const FormatStyle &Style, F &&Matches,
226                         SmallVector<WhitespaceManager::Change, 16> &Changes) {
227   unsigned MinColumn = 0;
228   unsigned MaxColumn = UINT_MAX;
229 
230   // Line number of the start and the end of the current token sequence.
231   unsigned StartOfSequence = 0;
232   unsigned EndOfSequence = 0;
233 
234   // Keep track of the nesting level of matching tokens, i.e. the number of
235   // surrounding (), [], or {}. We will only align a sequence of matching
236   // token that share the same scope depth.
237   //
238   // FIXME: This could use FormatToken::NestingLevel information, but there is
239   // an outstanding issue wrt the brace scopes.
240   unsigned NestingLevelOfLastMatch = 0;
241   unsigned NestingLevel = 0;
242 
243   // Keep track of the number of commas before the matching tokens, we will only
244   // align a sequence of matching tokens if they are preceded by the same number
245   // of commas.
246   unsigned CommasBeforeLastMatch = 0;
247   unsigned CommasBeforeMatch = 0;
248 
249   // Whether a matching token has been found on the current line.
250   bool FoundMatchOnLine = false;
251 
252   // Aligns a sequence of matching tokens, on the MinColumn column.
253   //
254   // Sequences start from the first matching token to align, and end at the
255   // first token of the first line that doesn't need to be aligned.
256   //
257   // We need to adjust the StartOfTokenColumn of each Change that is on a line
258   // containing any matching token to be aligned and located after such token.
259   auto AlignCurrentSequence = [&] {
260     if (StartOfSequence > 0 && StartOfSequence < EndOfSequence)
261       AlignTokenSequence(StartOfSequence, EndOfSequence, MinColumn, Matches,
262                          Changes);
263     MinColumn = 0;
264     MaxColumn = UINT_MAX;
265     StartOfSequence = 0;
266     EndOfSequence = 0;
267   };
268 
269   for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
270     if (Changes[i].NewlinesBefore != 0) {
271       CommasBeforeMatch = 0;
272       EndOfSequence = i;
273       // If there is a blank line, or if the last line didn't contain any
274       // matching token, the sequence ends here.
275       if (Changes[i].NewlinesBefore > 1 || !FoundMatchOnLine)
276         AlignCurrentSequence();
277 
278       FoundMatchOnLine = false;
279     }
280 
281     if (Changes[i].Kind == tok::comma) {
282       ++CommasBeforeMatch;
283     } else if (Changes[i].Kind == tok::r_brace ||
284                Changes[i].Kind == tok::r_paren ||
285                Changes[i].Kind == tok::r_square) {
286       --NestingLevel;
287     } else if (Changes[i].Kind == tok::l_brace ||
288                Changes[i].Kind == tok::l_paren ||
289                Changes[i].Kind == tok::l_square) {
290       // We want sequences to skip over child scopes if possible, but not the
291       // other way around.
292       NestingLevelOfLastMatch = std::min(NestingLevelOfLastMatch, NestingLevel);
293       ++NestingLevel;
294     }
295 
296     if (!Matches(Changes[i]))
297       continue;
298 
299     // If there is more than one matching token per line, or if the number of
300     // preceding commas, or the scope depth, do not match anymore, end the
301     // sequence.
302     if (FoundMatchOnLine || CommasBeforeMatch != CommasBeforeLastMatch ||
303         NestingLevel != NestingLevelOfLastMatch)
304       AlignCurrentSequence();
305 
306     CommasBeforeLastMatch = CommasBeforeMatch;
307     NestingLevelOfLastMatch = NestingLevel;
308     FoundMatchOnLine = true;
309 
310     if (StartOfSequence == 0)
311       StartOfSequence = i;
312 
313     unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
314     int LineLengthAfter = -Changes[i].Spaces;
315     for (unsigned j = i; j != e && Changes[j].NewlinesBefore == 0; ++j)
316       LineLengthAfter += Changes[j].Spaces + Changes[j].TokenLength;
317     unsigned ChangeMaxColumn = Style.ColumnLimit - LineLengthAfter;
318 
319     // If we are restricted by the maximum column width, end the sequence.
320     if (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn ||
321         CommasBeforeLastMatch != CommasBeforeMatch) {
322       AlignCurrentSequence();
323       StartOfSequence = i;
324     }
325 
326     MinColumn = std::max(MinColumn, ChangeMinColumn);
327     MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
328   }
329 
330   EndOfSequence = Changes.size();
331   AlignCurrentSequence();
332 }
333 
334 void WhitespaceManager::alignConsecutiveAssignments() {
335   if (!Style.AlignConsecutiveAssignments)
336     return;
337 
338   AlignTokens(Style,
339               [&](const Change &C) {
340                 // Do not align on equal signs that are first on a line.
341                 if (C.NewlinesBefore > 0)
342                   return false;
343 
344                 // Do not align on equal signs that are last on a line.
345                 if (&C != &Changes.back() && (&C + 1)->NewlinesBefore > 0)
346                   return false;
347 
348                 return C.Kind == tok::equal;
349               },
350               Changes);
351 }
352 
353 void WhitespaceManager::alignConsecutiveDeclarations() {
354   if (!Style.AlignConsecutiveDeclarations)
355     return;
356 
357   // FIXME: Currently we don't handle properly the PointerAlignment: Right
358   // The * and & are not aligned and are left dangling. Something has to be done
359   // about it, but it raises the question of alignment of code like:
360   //   const char* const* v1;
361   //   float const* v2;
362   //   SomeVeryLongType const& v3;
363 
364   AlignTokens(Style, [](Change const &C) { return C.IsStartOfDeclName; },
365               Changes);
366 }
367 
368 void WhitespaceManager::alignTrailingComments() {
369   unsigned MinColumn = 0;
370   unsigned MaxColumn = UINT_MAX;
371   unsigned StartOfSequence = 0;
372   bool BreakBeforeNext = false;
373   unsigned Newlines = 0;
374   for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
375     if (Changes[i].StartOfBlockComment)
376       continue;
377     Newlines += Changes[i].NewlinesBefore;
378     if (!Changes[i].IsTrailingComment)
379       continue;
380 
381     unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
382     unsigned ChangeMaxColumn = Style.ColumnLimit - Changes[i].TokenLength;
383 
384     // If we don't create a replacement for this change, we have to consider
385     // it to be immovable.
386     if (!Changes[i].CreateReplacement)
387       ChangeMaxColumn = ChangeMinColumn;
388 
389     if (i + 1 != e && Changes[i + 1].ContinuesPPDirective)
390       ChangeMaxColumn -= 2;
391     // If this comment follows an } in column 0, it probably documents the
392     // closing of a namespace and we don't want to align it.
393     bool FollowsRBraceInColumn0 = i > 0 && Changes[i].NewlinesBefore == 0 &&
394                                   Changes[i - 1].Kind == tok::r_brace &&
395                                   Changes[i - 1].StartOfTokenColumn == 0;
396     bool WasAlignedWithStartOfNextLine = false;
397     if (Changes[i].NewlinesBefore == 1) { // A comment on its own line.
398       unsigned CommentColumn = SourceMgr.getSpellingColumnNumber(
399           Changes[i].OriginalWhitespaceRange.getEnd());
400       for (unsigned j = i + 1; j != e; ++j) {
401         if (Changes[j].Kind == tok::comment ||
402             Changes[j].Kind == tok::unknown)
403           // Skip over comments and unknown tokens. "unknown tokens are used for
404           // the continuation of multiline comments.
405           continue;
406 
407         unsigned NextColumn = SourceMgr.getSpellingColumnNumber(
408             Changes[j].OriginalWhitespaceRange.getEnd());
409         // The start of the next token was previously aligned with the
410         // start of this comment.
411         WasAlignedWithStartOfNextLine =
412             CommentColumn == NextColumn ||
413             CommentColumn == NextColumn + Style.IndentWidth;
414         break;
415       }
416     }
417     if (!Style.AlignTrailingComments || FollowsRBraceInColumn0) {
418       alignTrailingComments(StartOfSequence, i, MinColumn);
419       MinColumn = ChangeMinColumn;
420       MaxColumn = ChangeMinColumn;
421       StartOfSequence = i;
422     } else if (BreakBeforeNext || Newlines > 1 ||
423                (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) ||
424                // Break the comment sequence if the previous line did not end
425                // in a trailing comment.
426                (Changes[i].NewlinesBefore == 1 && i > 0 &&
427                 !Changes[i - 1].IsTrailingComment) ||
428                WasAlignedWithStartOfNextLine) {
429       alignTrailingComments(StartOfSequence, i, MinColumn);
430       MinColumn = ChangeMinColumn;
431       MaxColumn = ChangeMaxColumn;
432       StartOfSequence = i;
433     } else {
434       MinColumn = std::max(MinColumn, ChangeMinColumn);
435       MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
436     }
437     BreakBeforeNext =
438         (i == 0) || (Changes[i].NewlinesBefore > 1) ||
439         // Never start a sequence with a comment at the beginning of
440         // the line.
441         (Changes[i].NewlinesBefore == 1 && StartOfSequence == i);
442     Newlines = 0;
443   }
444   alignTrailingComments(StartOfSequence, Changes.size(), MinColumn);
445 }
446 
447 void WhitespaceManager::alignTrailingComments(unsigned Start, unsigned End,
448                                               unsigned Column) {
449   for (unsigned i = Start; i != End; ++i) {
450     int Shift = 0;
451     if (Changes[i].IsTrailingComment) {
452       Shift = Column - Changes[i].StartOfTokenColumn;
453     }
454     if (Changes[i].StartOfBlockComment) {
455       Shift = Changes[i].IndentationOffset +
456               Changes[i].StartOfBlockComment->StartOfTokenColumn -
457               Changes[i].StartOfTokenColumn;
458     }
459     assert(Shift >= 0);
460     Changes[i].Spaces += Shift;
461     if (i + 1 != Changes.size())
462       Changes[i + 1].PreviousEndOfTokenColumn += Shift;
463     Changes[i].StartOfTokenColumn += Shift;
464   }
465 }
466 
467 void WhitespaceManager::alignEscapedNewlines() {
468   unsigned MaxEndOfLine =
469       Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit;
470   unsigned StartOfMacro = 0;
471   for (unsigned i = 1, e = Changes.size(); i < e; ++i) {
472     Change &C = Changes[i];
473     if (C.NewlinesBefore > 0) {
474       if (C.ContinuesPPDirective) {
475         MaxEndOfLine = std::max(C.PreviousEndOfTokenColumn + 2, MaxEndOfLine);
476       } else {
477         alignEscapedNewlines(StartOfMacro + 1, i, MaxEndOfLine);
478         MaxEndOfLine = Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit;
479         StartOfMacro = i;
480       }
481     }
482   }
483   alignEscapedNewlines(StartOfMacro + 1, Changes.size(), MaxEndOfLine);
484 }
485 
486 void WhitespaceManager::alignEscapedNewlines(unsigned Start, unsigned End,
487                                              unsigned Column) {
488   for (unsigned i = Start; i < End; ++i) {
489     Change &C = Changes[i];
490     if (C.NewlinesBefore > 0) {
491       assert(C.ContinuesPPDirective);
492       if (C.PreviousEndOfTokenColumn + 1 > Column)
493         C.EscapedNewlineColumn = 0;
494       else
495         C.EscapedNewlineColumn = Column;
496     }
497   }
498 }
499 
500 void WhitespaceManager::generateChanges() {
501   for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
502     const Change &C = Changes[i];
503     if (i > 0) {
504       assert(Changes[i - 1].OriginalWhitespaceRange.getBegin() !=
505                  C.OriginalWhitespaceRange.getBegin() &&
506              "Generating two replacements for the same location");
507     }
508     if (C.CreateReplacement) {
509       std::string ReplacementText = C.PreviousLinePostfix;
510       if (C.ContinuesPPDirective)
511         appendNewlineText(ReplacementText, C.NewlinesBefore,
512                           C.PreviousEndOfTokenColumn, C.EscapedNewlineColumn);
513       else
514         appendNewlineText(ReplacementText, C.NewlinesBefore);
515       appendIndentText(ReplacementText, C.IndentLevel, std::max(0, C.Spaces),
516                        C.StartOfTokenColumn - std::max(0, C.Spaces));
517       ReplacementText.append(C.CurrentLinePrefix);
518       storeReplacement(C.OriginalWhitespaceRange, ReplacementText);
519     }
520   }
521 }
522 
523 void WhitespaceManager::storeReplacement(SourceRange Range,
524                                          StringRef Text) {
525   unsigned WhitespaceLength = SourceMgr.getFileOffset(Range.getEnd()) -
526                               SourceMgr.getFileOffset(Range.getBegin());
527   // Don't create a replacement, if it does not change anything.
528   if (StringRef(SourceMgr.getCharacterData(Range.getBegin()),
529                 WhitespaceLength) == Text)
530     return;
531   auto Err = Replaces.add(tooling::Replacement(
532       SourceMgr, CharSourceRange::getCharRange(Range), Text));
533   // FIXME: better error handling. For now, just print an error message in the
534   // release version.
535   if (Err) {
536     llvm::errs() << llvm::toString(std::move(Err)) << "\n";
537     assert(false);
538   }
539 }
540 
541 void WhitespaceManager::appendNewlineText(std::string &Text,
542                                           unsigned Newlines) {
543   for (unsigned i = 0; i < Newlines; ++i)
544     Text.append(UseCRLF ? "\r\n" : "\n");
545 }
546 
547 void WhitespaceManager::appendNewlineText(std::string &Text, unsigned Newlines,
548                                           unsigned PreviousEndOfTokenColumn,
549                                           unsigned EscapedNewlineColumn) {
550   if (Newlines > 0) {
551     unsigned Offset =
552         std::min<int>(EscapedNewlineColumn - 1, PreviousEndOfTokenColumn);
553     for (unsigned i = 0; i < Newlines; ++i) {
554       Text.append(EscapedNewlineColumn - Offset - 1, ' ');
555       Text.append(UseCRLF ? "\\\r\n" : "\\\n");
556       Offset = 0;
557     }
558   }
559 }
560 
561 void WhitespaceManager::appendIndentText(std::string &Text,
562                                          unsigned IndentLevel, unsigned Spaces,
563                                          unsigned WhitespaceStartColumn) {
564   switch (Style.UseTab) {
565   case FormatStyle::UT_Never:
566     Text.append(Spaces, ' ');
567     break;
568   case FormatStyle::UT_Always: {
569     unsigned FirstTabWidth =
570         Style.TabWidth - WhitespaceStartColumn % Style.TabWidth;
571     // Indent with tabs only when there's at least one full tab.
572     if (FirstTabWidth + Style.TabWidth <= Spaces) {
573       Spaces -= FirstTabWidth;
574       Text.append("\t");
575     }
576     Text.append(Spaces / Style.TabWidth, '\t');
577     Text.append(Spaces % Style.TabWidth, ' ');
578     break;
579   }
580   case FormatStyle::UT_ForIndentation:
581     if (WhitespaceStartColumn == 0) {
582       unsigned Indentation = IndentLevel * Style.IndentWidth;
583       // This happens, e.g. when a line in a block comment is indented less than
584       // the first one.
585       if (Indentation > Spaces)
586         Indentation = Spaces;
587       unsigned Tabs = Indentation / Style.TabWidth;
588       Text.append(Tabs, '\t');
589       Spaces -= Tabs * Style.TabWidth;
590     }
591     Text.append(Spaces, ' ');
592     break;
593   case FormatStyle::UT_ForContinuationAndIndentation:
594     if (WhitespaceStartColumn == 0) {
595       unsigned Tabs = Spaces / Style.TabWidth;
596       Text.append(Tabs, '\t');
597       Spaces -= Tabs * Style.TabWidth;
598     }
599     Text.append(Spaces, ' ');
600     break;
601   }
602 }
603 
604 } // namespace format
605 } // namespace clang
606