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(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) {} 45 46 void WhitespaceManager::replaceWhitespace(FormatToken &Tok, unsigned Newlines, 47 unsigned Spaces, 48 unsigned StartOfTokenColumn, 49 bool InPPDirective) { 50 if (Tok.Finalized) 51 return; 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)); 57 } 58 59 void WhitespaceManager::addUntouchableToken(const FormatToken &Tok, 60 bool InPPDirective) { 61 if (Tok.Finalized) 62 return; 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)); 68 } 69 70 void WhitespaceManager::replaceWhitespaceInToken( 71 const FormatToken &Tok, unsigned Offset, unsigned ReplaceChars, 72 StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective, 73 unsigned Newlines, int Spaces) { 74 if (Tok.Finalized) 75 return; 76 SourceLocation Start = Tok.getStartOfNonWhitespace().getLocWithOffset(Offset); 77 Changes.push_back( 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)); 82 } 83 84 const tooling::Replacements &WhitespaceManager::generateReplacements() { 85 if (Changes.empty()) 86 return Replaces; 87 88 std::sort(Changes.begin(), Changes.end(), Change::IsBeforeInFile(SourceMgr)); 89 calculateLineBreakInformation(); 90 alignConsecutiveDeclarations(); 91 alignConsecutiveAssignments(); 92 alignTrailingComments(); 93 alignEscapedNewlines(); 94 generateChanges(); 95 96 return Replaces; 97 } 98 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(); 111 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; 117 else 118 LastOutsideTokenChange = &Changes[i - 1]; 119 120 Changes[i].PreviousEndOfTokenColumn = 121 Changes[i - 1].StartOfTokenColumn + Changes[i - 1].TokenLength; 122 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: 131 // 132 // // line 1 133 // // line 2 134 // 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: 139 // 140 // // line 1( 141 // )[// ]line 2 142 // 143 // So in the end we have two changes like this: 144 // 145 // // line1()[ ]line 2 146 // 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. 153 // 154 // For a proper solution we need a mechanism to say to WhitespaceManager 155 // that a particular change breaks the current sequence of trailing 156 // comments. 157 OriginalWhitespaceStart != PreviousOriginalWhitespaceEnd; 158 } 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); 163 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 168 // to be aligned. 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; 176 else { 177 if ((Change.StartOfBlockComment = LastBlockComment)) 178 Change.IndentationOffset = 179 Change.StartOfTokenColumn - 180 Change.StartOfBlockComment->StartOfTokenColumn; 181 } 182 } else { 183 LastBlockComment = nullptr; 184 } 185 } 186 } 187 188 // Align a single sequence of tokens, see AlignTokens below. 189 template <typename F> 190 static void 191 AlignTokenSequence(unsigned Start, unsigned End, unsigned Column, F &&Matches, 192 SmallVector<WhitespaceManager::Change, 16> &Changes) { 193 bool FoundMatchOnLine = false; 194 int Shift = 0; 195 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: 202 // double a(int x); 203 // int b(int y, 204 // double z); 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; 208 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(); 214 215 if (i != Start && Changes[i].nestingAndIndentLevel() > 216 Changes[i - 1].nestingAndIndentLevel()) 217 ScopeStack.push_back(i); 218 219 bool InsideNestedScope = ScopeStack.size() != 0; 220 221 if (Changes[i].NewlinesBefore > 0 && !InsideNestedScope) { 222 Shift = 0; 223 FoundMatchOnLine = false; 224 } 225 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; 233 } 234 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; 243 } 244 245 assert(Shift >= 0); 246 Changes[i].StartOfTokenColumn += Shift; 247 if (i + 1 != Changes.size()) 248 Changes[i + 1].PreviousEndOfTokenColumn += Shift; 249 } 250 } 251 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 267 // arguments. 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, 282 unsigned StartAt) { 283 unsigned MinColumn = 0; 284 unsigned MaxColumn = UINT_MAX; 285 286 // Line number of the start and the end of the current token sequence. 287 unsigned StartOfSequence = 0; 288 unsigned EndOfSequence = 0; 289 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); 295 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 298 // of commas. 299 unsigned CommasBeforeLastMatch = 0; 300 unsigned CommasBeforeMatch = 0; 301 302 // Whether a matching token has been found on the current line. 303 bool FoundMatchOnLine = false; 304 305 // Aligns a sequence of matching tokens, on the MinColumn column. 306 // 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. 309 // 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, 315 Changes); 316 MinColumn = 0; 317 MaxColumn = UINT_MAX; 318 StartOfSequence = 0; 319 EndOfSequence = 0; 320 }; 321 322 unsigned i = StartAt; 323 for (unsigned e = Changes.size(); i != e; ++i) { 324 if (Changes[i].nestingAndIndentLevel() < NestingAndIndentLevel) 325 break; 326 327 if (Changes[i].NewlinesBefore != 0) { 328 CommasBeforeMatch = 0; 329 EndOfSequence = i; 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(); 334 335 FoundMatchOnLine = false; 336 } 337 338 if (Changes[i].Tok->is(tok::comma)) { 339 ++CommasBeforeMatch; 340 } else if (Changes[i].nestingAndIndentLevel() > NestingAndIndentLevel) { 341 // Call AlignTokens recursively, skipping over this scope block. 342 unsigned StoppedAt = AlignTokens(Style, Matches, Changes, i); 343 i = StoppedAt - 1; 344 continue; 345 } 346 347 if (!Matches(Changes[i])) 348 continue; 349 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(); 354 355 CommasBeforeLastMatch = CommasBeforeMatch; 356 FoundMatchOnLine = true; 357 358 if (StartOfSequence == 0) 359 StartOfSequence = i; 360 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; 366 367 // If we are restricted by the maximum column width, end the sequence. 368 if (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn || 369 CommasBeforeLastMatch != CommasBeforeMatch) { 370 AlignCurrentSequence(); 371 StartOfSequence = i; 372 } 373 374 MinColumn = std::max(MinColumn, ChangeMinColumn); 375 MaxColumn = std::min(MaxColumn, ChangeMaxColumn); 376 } 377 378 EndOfSequence = i; 379 AlignCurrentSequence(); 380 return i; 381 } 382 383 void WhitespaceManager::alignConsecutiveAssignments() { 384 if (!Style.AlignConsecutiveAssignments) 385 return; 386 387 AlignTokens(Style, 388 [&](const Change &C) { 389 // Do not align on equal signs that are first on a line. 390 if (C.NewlinesBefore > 0) 391 return false; 392 393 // Do not align on equal signs that are last on a line. 394 if (&C != &Changes.back() && (&C + 1)->NewlinesBefore > 0) 395 return false; 396 397 return C.Tok->is(tok::equal); 398 }, 399 Changes, /*StartAt=*/0); 400 } 401 402 void WhitespaceManager::alignConsecutiveDeclarations() { 403 if (!Style.AlignConsecutiveDeclarations) 404 return; 405 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; 410 // float const* v2; 411 // SomeVeryLongType const& v3; 412 AlignTokens(Style, 413 [](Change const &C) { 414 // tok::kw_operator is necessary for aligning operator overload 415 // definitions. 416 return C.Tok->is(TT_StartOfName) || 417 C.Tok->is(TT_FunctionDeclarationName) || 418 C.Tok->is(tok::kw_operator); 419 }, 420 Changes, /*StartAt=*/0); 421 } 422 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) 431 continue; 432 Newlines += Changes[i].NewlinesBefore; 433 if (!Changes[i].IsTrailingComment) 434 continue; 435 436 unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn; 437 unsigned ChangeMaxColumn = Style.ColumnLimit - Changes[i].TokenLength; 438 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; 443 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)) 457 continue; 458 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; 466 break; 467 } 468 } 469 if (!Style.AlignTrailingComments || FollowsRBraceInColumn0) { 470 alignTrailingComments(StartOfSequence, i, MinColumn); 471 MinColumn = ChangeMinColumn; 472 MaxColumn = ChangeMinColumn; 473 StartOfSequence = i; 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; 484 StartOfSequence = i; 485 } else { 486 MinColumn = std::max(MinColumn, ChangeMinColumn); 487 MaxColumn = std::min(MaxColumn, ChangeMaxColumn); 488 } 489 BreakBeforeNext = 490 (i == 0) || (Changes[i].NewlinesBefore > 1) || 491 // Never start a sequence with a comment at the beginning of 492 // the line. 493 (Changes[i].NewlinesBefore == 1 && StartOfSequence == i); 494 Newlines = 0; 495 } 496 alignTrailingComments(StartOfSequence, Changes.size(), MinColumn); 497 } 498 499 void WhitespaceManager::alignTrailingComments(unsigned Start, unsigned End, 500 unsigned Column) { 501 for (unsigned i = Start; i != End; ++i) { 502 int Shift = 0; 503 if (Changes[i].IsTrailingComment) { 504 Shift = Column - Changes[i].StartOfTokenColumn; 505 } 506 if (Changes[i].StartOfBlockComment) { 507 Shift = Changes[i].IndentationOffset + 508 Changes[i].StartOfBlockComment->StartOfTokenColumn - 509 Changes[i].StartOfTokenColumn; 510 } 511 assert(Shift >= 0); 512 Changes[i].Spaces += Shift; 513 if (i + 1 != Changes.size()) 514 Changes[i + 1].PreviousEndOfTokenColumn += Shift; 515 Changes[i].StartOfTokenColumn += Shift; 516 } 517 } 518 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); 528 } else { 529 alignEscapedNewlines(StartOfMacro + 1, i, MaxEndOfLine); 530 MaxEndOfLine = Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit; 531 StartOfMacro = i; 532 } 533 } 534 } 535 alignEscapedNewlines(StartOfMacro + 1, Changes.size(), MaxEndOfLine); 536 } 537 538 void WhitespaceManager::alignEscapedNewlines(unsigned Start, unsigned End, 539 unsigned Column) { 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; 546 else 547 C.EscapedNewlineColumn = Column; 548 } 549 } 550 } 551 552 void WhitespaceManager::generateChanges() { 553 for (unsigned i = 0, e = Changes.size(); i != e; ++i) { 554 const Change &C = Changes[i]; 555 if (i > 0) { 556 assert(Changes[i - 1].OriginalWhitespaceRange.getBegin() != 557 C.OriginalWhitespaceRange.getBegin() && 558 "Generating two replacements for the same location"); 559 } 560 if (C.CreateReplacement) { 561 std::string ReplacementText = C.PreviousLinePostfix; 562 if (C.ContinuesPPDirective) 563 appendNewlineText(ReplacementText, C.NewlinesBefore, 564 C.PreviousEndOfTokenColumn, C.EscapedNewlineColumn); 565 else 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); 572 } 573 } 574 } 575 576 void WhitespaceManager::storeReplacement(SourceRange Range, 577 StringRef Text) { 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) 583 return; 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 587 // release version. 588 if (Err) { 589 llvm::errs() << llvm::toString(std::move(Err)) << "\n"; 590 assert(false); 591 } 592 } 593 594 void WhitespaceManager::appendNewlineText(std::string &Text, 595 unsigned Newlines) { 596 for (unsigned i = 0; i < Newlines; ++i) 597 Text.append(UseCRLF ? "\r\n" : "\n"); 598 } 599 600 void WhitespaceManager::appendNewlineText(std::string &Text, unsigned Newlines, 601 unsigned PreviousEndOfTokenColumn, 602 unsigned EscapedNewlineColumn) { 603 if (Newlines > 0) { 604 unsigned Offset = 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"); 609 Offset = 0; 610 } 611 } 612 } 613 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, ' '); 620 break; 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; 627 Text.append("\t"); 628 } 629 Text.append(Spaces / Style.TabWidth, '\t'); 630 Text.append(Spaces % Style.TabWidth, ' '); 631 break; 632 } 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 637 // the first one. 638 if (Indentation > Spaces) 639 Indentation = Spaces; 640 unsigned Tabs = Indentation / Style.TabWidth; 641 Text.append(Tabs, '\t'); 642 Spaces -= Tabs * Style.TabWidth; 643 } 644 Text.append(Spaces, ' '); 645 break; 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; 651 } 652 Text.append(Spaces, ' '); 653 break; 654 } 655 } 656 657 } // namespace format 658 } // namespace clang 659