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