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