1251609Sdim//===--- WhitespaceManager.cpp - Format C++ code --------------------------===// 2251609Sdim// 3251609Sdim// The LLVM Compiler Infrastructure 4251609Sdim// 5251609Sdim// This file is distributed under the University of Illinois Open Source 6251609Sdim// License. See LICENSE.TXT for details. 7251609Sdim// 8251609Sdim//===----------------------------------------------------------------------===// 9251609Sdim/// 10251609Sdim/// \file 11251609Sdim/// \brief This file implements WhitespaceManager class. 12251609Sdim/// 13251609Sdim//===----------------------------------------------------------------------===// 14251609Sdim 15251609Sdim#include "WhitespaceManager.h" 16251609Sdim#include "llvm/ADT/STLExtras.h" 17251609Sdim 18251609Sdimnamespace clang { 19251609Sdimnamespace format { 20251609Sdim 21263508Sdimbool 22263508SdimWhitespaceManager::Change::IsBeforeInFile::operator()(const Change &C1, 23263508Sdim const Change &C2) const { 24263508Sdim return SourceMgr.isBeforeInTranslationUnit( 25263508Sdim C1.OriginalWhitespaceRange.getBegin(), 26263508Sdim C2.OriginalWhitespaceRange.getBegin()); 27263508Sdim} 28251609Sdim 29263508SdimWhitespaceManager::Change::Change( 30263508Sdim bool CreateReplacement, const SourceRange &OriginalWhitespaceRange, 31263508Sdim unsigned IndentLevel, unsigned Spaces, unsigned StartOfTokenColumn, 32263508Sdim unsigned NewlinesBefore, StringRef PreviousLinePostfix, 33263508Sdim StringRef CurrentLinePrefix, tok::TokenKind Kind, bool ContinuesPPDirective) 34263508Sdim : CreateReplacement(CreateReplacement), 35263508Sdim OriginalWhitespaceRange(OriginalWhitespaceRange), 36263508Sdim StartOfTokenColumn(StartOfTokenColumn), NewlinesBefore(NewlinesBefore), 37263508Sdim PreviousLinePostfix(PreviousLinePostfix), 38263508Sdim CurrentLinePrefix(CurrentLinePrefix), Kind(Kind), 39263508Sdim ContinuesPPDirective(ContinuesPPDirective), IndentLevel(IndentLevel), 40263508Sdim Spaces(Spaces) {} 41251609Sdim 42263508Sdimvoid WhitespaceManager::reset() { 43263508Sdim Changes.clear(); 44263508Sdim Replaces.clear(); 45263508Sdim} 46251609Sdim 47263508Sdimvoid WhitespaceManager::replaceWhitespace(FormatToken &Tok, unsigned Newlines, 48263508Sdim unsigned IndentLevel, unsigned Spaces, 49263508Sdim unsigned StartOfTokenColumn, 50263508Sdim bool InPPDirective) { 51263508Sdim if (Tok.Finalized) 52263508Sdim return; 53263508Sdim Tok.Decision = (Newlines > 0) ? FD_Break : FD_Continue; 54263508Sdim Changes.push_back(Change(true, Tok.WhitespaceRange, IndentLevel, Spaces, 55263508Sdim StartOfTokenColumn, Newlines, "", "", 56263508Sdim Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst)); 57251609Sdim} 58251609Sdim 59263508Sdimvoid WhitespaceManager::addUntouchableToken(const FormatToken &Tok, 60263508Sdim bool InPPDirective) { 61263508Sdim if (Tok.Finalized) 62263508Sdim return; 63263508Sdim Changes.push_back(Change(false, Tok.WhitespaceRange, /*IndentLevel=*/0, 64263508Sdim /*Spaces=*/0, Tok.OriginalColumn, Tok.NewlinesBefore, 65263508Sdim "", "", Tok.Tok.getKind(), 66263508Sdim InPPDirective && !Tok.IsFirst)); 67251609Sdim} 68251609Sdim 69263508Sdimvoid WhitespaceManager::replaceWhitespaceInToken( 70263508Sdim const FormatToken &Tok, unsigned Offset, unsigned ReplaceChars, 71263508Sdim StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective, 72263508Sdim unsigned Newlines, unsigned IndentLevel, unsigned Spaces) { 73263508Sdim if (Tok.Finalized) 74263508Sdim return; 75263508Sdim Changes.push_back(Change( 76263508Sdim true, SourceRange(Tok.getStartOfNonWhitespace().getLocWithOffset(Offset), 77263508Sdim Tok.getStartOfNonWhitespace().getLocWithOffset( 78263508Sdim Offset + ReplaceChars)), 79263508Sdim IndentLevel, Spaces, Spaces, Newlines, PreviousPostfix, CurrentPrefix, 80263508Sdim // If we don't add a newline this change doesn't start a comment. Thus, 81263508Sdim // when we align line comments, we don't need to treat this change as one. 82263508Sdim // FIXME: We still need to take this change in account to properly 83263508Sdim // calculate the new length of the comment and to calculate the changes 84263508Sdim // for which to do the alignment when aligning comments. 85263508Sdim Tok.Type == TT_LineComment && Newlines > 0 ? tok::comment : tok::unknown, 86263508Sdim InPPDirective && !Tok.IsFirst)); 87251609Sdim} 88251609Sdim 89251609Sdimconst tooling::Replacements &WhitespaceManager::generateReplacements() { 90263508Sdim if (Changes.empty()) 91263508Sdim return Replaces; 92263508Sdim 93263508Sdim std::sort(Changes.begin(), Changes.end(), Change::IsBeforeInFile(SourceMgr)); 94263508Sdim calculateLineBreakInformation(); 95263508Sdim alignTrailingComments(); 96251609Sdim alignEscapedNewlines(); 97263508Sdim generateChanges(); 98263508Sdim 99251609Sdim return Replaces; 100251609Sdim} 101251609Sdim 102263508Sdimvoid WhitespaceManager::calculateLineBreakInformation() { 103263508Sdim Changes[0].PreviousEndOfTokenColumn = 0; 104263508Sdim for (unsigned i = 1, e = Changes.size(); i != e; ++i) { 105263508Sdim unsigned OriginalWhitespaceStart = 106263508Sdim SourceMgr.getFileOffset(Changes[i].OriginalWhitespaceRange.getBegin()); 107263508Sdim unsigned PreviousOriginalWhitespaceEnd = SourceMgr.getFileOffset( 108263508Sdim Changes[i - 1].OriginalWhitespaceRange.getEnd()); 109263508Sdim Changes[i - 1].TokenLength = OriginalWhitespaceStart - 110263508Sdim PreviousOriginalWhitespaceEnd + 111263508Sdim Changes[i].PreviousLinePostfix.size() + 112263508Sdim Changes[i - 1].CurrentLinePrefix.size(); 113251609Sdim 114263508Sdim Changes[i].PreviousEndOfTokenColumn = 115263508Sdim Changes[i - 1].StartOfTokenColumn + Changes[i - 1].TokenLength; 116251609Sdim 117263508Sdim Changes[i - 1].IsTrailingComment = 118263508Sdim (Changes[i].NewlinesBefore > 0 || Changes[i].Kind == tok::eof) && 119263508Sdim Changes[i - 1].Kind == tok::comment; 120251609Sdim } 121263508Sdim // FIXME: The last token is currently not always an eof token; in those 122263508Sdim // cases, setting TokenLength of the last token to 0 is wrong. 123263508Sdim Changes.back().TokenLength = 0; 124263508Sdim Changes.back().IsTrailingComment = Changes.back().Kind == tok::comment; 125251609Sdim} 126251609Sdim 127263508Sdimvoid WhitespaceManager::alignTrailingComments() { 128251609Sdim unsigned MinColumn = 0; 129251609Sdim unsigned MaxColumn = UINT_MAX; 130263508Sdim unsigned StartOfSequence = 0; 131263508Sdim bool BreakBeforeNext = false; 132263508Sdim unsigned Newlines = 0; 133263508Sdim for (unsigned i = 0, e = Changes.size(); i != e; ++i) { 134263508Sdim unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn; 135263508Sdim // FIXME: Correctly handle ChangeMaxColumn in PP directives. 136263508Sdim unsigned ChangeMaxColumn = Style.ColumnLimit - Changes[i].TokenLength; 137263508Sdim Newlines += Changes[i].NewlinesBefore; 138263508Sdim if (Changes[i].IsTrailingComment) { 139263508Sdim // If this comment follows an } in column 0, it probably documents the 140263508Sdim // closing of a namespace and we don't want to align it. 141263508Sdim bool FollowsRBraceInColumn0 = i > 0 && Changes[i].NewlinesBefore == 0 && 142263508Sdim Changes[i - 1].Kind == tok::r_brace && 143263508Sdim Changes[i - 1].StartOfTokenColumn == 0; 144263508Sdim bool WasAlignedWithStartOfNextLine = false; 145263508Sdim if (Changes[i].NewlinesBefore == 1) { // A comment on its own line. 146263508Sdim for (unsigned j = i + 1; j != e; ++j) { 147263508Sdim if (Changes[j].Kind != tok::comment) { // Skip over comments. 148263508Sdim // The start of the next token was previously aligned with the 149263508Sdim // start of this comment. 150263508Sdim WasAlignedWithStartOfNextLine = 151263508Sdim (SourceMgr.getSpellingColumnNumber( 152263508Sdim Changes[i].OriginalWhitespaceRange.getEnd()) == 153263508Sdim SourceMgr.getSpellingColumnNumber( 154263508Sdim Changes[j].OriginalWhitespaceRange.getEnd())); 155263508Sdim break; 156263508Sdim } 157263508Sdim } 158263508Sdim } 159263508Sdim if (!Style.AlignTrailingComments || FollowsRBraceInColumn0) { 160263508Sdim alignTrailingComments(StartOfSequence, i, MinColumn); 161263508Sdim MinColumn = ChangeMinColumn; 162263508Sdim MaxColumn = ChangeMinColumn; 163263508Sdim StartOfSequence = i; 164263508Sdim } else if (BreakBeforeNext || Newlines > 1 || 165263508Sdim (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) || 166263508Sdim // Break the comment sequence if the previous line did not end 167263508Sdim // in a trailing comment. 168263508Sdim (Changes[i].NewlinesBefore == 1 && i > 0 && 169263508Sdim !Changes[i - 1].IsTrailingComment) || 170263508Sdim WasAlignedWithStartOfNextLine) { 171263508Sdim alignTrailingComments(StartOfSequence, i, MinColumn); 172263508Sdim MinColumn = ChangeMinColumn; 173263508Sdim MaxColumn = ChangeMaxColumn; 174263508Sdim StartOfSequence = i; 175263508Sdim } else { 176263508Sdim MinColumn = std::max(MinColumn, ChangeMinColumn); 177263508Sdim MaxColumn = std::min(MaxColumn, ChangeMaxColumn); 178263508Sdim } 179263508Sdim BreakBeforeNext = 180263508Sdim (i == 0) || (Changes[i].NewlinesBefore > 1) || 181263508Sdim // Never start a sequence with a comment at the beginning of 182263508Sdim // the line. 183263508Sdim (Changes[i].NewlinesBefore == 1 && StartOfSequence == i); 184263508Sdim Newlines = 0; 185251609Sdim } 186251609Sdim } 187263508Sdim alignTrailingComments(StartOfSequence, Changes.size(), MinColumn); 188251609Sdim} 189251609Sdim 190263508Sdimvoid WhitespaceManager::alignTrailingComments(unsigned Start, unsigned End, 191263508Sdim unsigned Column) { 192263508Sdim for (unsigned i = Start; i != End; ++i) { 193263508Sdim if (Changes[i].IsTrailingComment) { 194263508Sdim assert(Column >= Changes[i].StartOfTokenColumn); 195263508Sdim Changes[i].Spaces += Column - Changes[i].StartOfTokenColumn; 196263508Sdim Changes[i].StartOfTokenColumn = Column; 197251609Sdim } 198251609Sdim } 199251609Sdim} 200251609Sdim 201251609Sdimvoid WhitespaceManager::alignEscapedNewlines() { 202263508Sdim unsigned MaxEndOfLine = 203263508Sdim Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit; 204263508Sdim unsigned StartOfMacro = 0; 205263508Sdim for (unsigned i = 1, e = Changes.size(); i < e; ++i) { 206263508Sdim Change &C = Changes[i]; 207263508Sdim if (C.NewlinesBefore > 0) { 208263508Sdim if (C.ContinuesPPDirective) { 209263508Sdim MaxEndOfLine = std::max(C.PreviousEndOfTokenColumn + 2, MaxEndOfLine); 210263508Sdim } else { 211263508Sdim alignEscapedNewlines(StartOfMacro + 1, i, MaxEndOfLine); 212263508Sdim MaxEndOfLine = Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit; 213263508Sdim StartOfMacro = i; 214263508Sdim } 215251609Sdim } 216251609Sdim } 217263508Sdim alignEscapedNewlines(StartOfMacro + 1, Changes.size(), MaxEndOfLine); 218263508Sdim} 219251609Sdim 220263508Sdimvoid WhitespaceManager::alignEscapedNewlines(unsigned Start, unsigned End, 221263508Sdim unsigned Column) { 222263508Sdim for (unsigned i = Start; i < End; ++i) { 223263508Sdim Change &C = Changes[i]; 224263508Sdim if (C.NewlinesBefore > 0) { 225263508Sdim assert(C.ContinuesPPDirective); 226263508Sdim if (C.PreviousEndOfTokenColumn + 1 > Column) 227263508Sdim C.EscapedNewlineColumn = 0; 228263508Sdim else 229263508Sdim C.EscapedNewlineColumn = Column; 230263508Sdim } 231263508Sdim } 232263508Sdim} 233251609Sdim 234263508Sdimvoid WhitespaceManager::generateChanges() { 235263508Sdim for (unsigned i = 0, e = Changes.size(); i != e; ++i) { 236263508Sdim const Change &C = Changes[i]; 237263508Sdim if (C.CreateReplacement) { 238263508Sdim std::string ReplacementText = C.PreviousLinePostfix; 239263508Sdim if (C.ContinuesPPDirective) 240263508Sdim appendNewlineText(ReplacementText, C.NewlinesBefore, 241263508Sdim C.PreviousEndOfTokenColumn, C.EscapedNewlineColumn); 242263508Sdim else 243263508Sdim appendNewlineText(ReplacementText, C.NewlinesBefore); 244263508Sdim appendIndentText(ReplacementText, C.IndentLevel, C.Spaces, 245263508Sdim C.StartOfTokenColumn - C.Spaces); 246263508Sdim ReplacementText.append(C.CurrentLinePrefix); 247263508Sdim storeReplacement(C.OriginalWhitespaceRange, ReplacementText); 248263508Sdim } 249251609Sdim } 250251609Sdim} 251251609Sdim 252263508Sdimvoid WhitespaceManager::storeReplacement(const SourceRange &Range, 253263508Sdim StringRef Text) { 254263508Sdim unsigned WhitespaceLength = SourceMgr.getFileOffset(Range.getEnd()) - 255263508Sdim SourceMgr.getFileOffset(Range.getBegin()); 256251609Sdim // Don't create a replacement, if it does not change anything. 257263508Sdim if (StringRef(SourceMgr.getCharacterData(Range.getBegin()), 258263508Sdim WhitespaceLength) == Text) 259251609Sdim return; 260263508Sdim Replaces.insert(tooling::Replacement( 261263508Sdim SourceMgr, CharSourceRange::getCharRange(Range), Text)); 262251609Sdim} 263251609Sdim 264263508Sdimvoid WhitespaceManager::appendNewlineText(std::string &Text, 265263508Sdim unsigned Newlines) { 266263508Sdim for (unsigned i = 0; i < Newlines; ++i) 267263508Sdim Text.append(UseCRLF ? "\r\n" : "\n"); 268263508Sdim} 269263508Sdim 270263508Sdimvoid WhitespaceManager::appendNewlineText(std::string &Text, unsigned Newlines, 271263508Sdim unsigned PreviousEndOfTokenColumn, 272263508Sdim unsigned EscapedNewlineColumn) { 273263508Sdim if (Newlines > 0) { 274263508Sdim unsigned Offset = 275263508Sdim std::min<int>(EscapedNewlineColumn - 1, PreviousEndOfTokenColumn); 276263508Sdim for (unsigned i = 0; i < Newlines; ++i) { 277263508Sdim Text.append(std::string(EscapedNewlineColumn - Offset - 1, ' ')); 278263508Sdim Text.append(UseCRLF ? "\\\r\n" : "\\\n"); 279263508Sdim Offset = 0; 280263508Sdim } 281263508Sdim } 282263508Sdim} 283263508Sdim 284263508Sdimvoid WhitespaceManager::appendIndentText(std::string &Text, 285263508Sdim unsigned IndentLevel, unsigned Spaces, 286263508Sdim unsigned WhitespaceStartColumn) { 287263508Sdim switch (Style.UseTab) { 288263508Sdim case FormatStyle::UT_Never: 289263508Sdim Text.append(std::string(Spaces, ' ')); 290263508Sdim break; 291263508Sdim case FormatStyle::UT_Always: { 292263508Sdim unsigned FirstTabWidth = 293263508Sdim Style.TabWidth - WhitespaceStartColumn % Style.TabWidth; 294263508Sdim // Indent with tabs only when there's at least one full tab. 295263508Sdim if (FirstTabWidth + Style.TabWidth <= Spaces) { 296263508Sdim Spaces -= FirstTabWidth; 297263508Sdim Text.append("\t"); 298263508Sdim } 299263508Sdim Text.append(std::string(Spaces / Style.TabWidth, '\t')); 300263508Sdim Text.append(std::string(Spaces % Style.TabWidth, ' ')); 301263508Sdim break; 302263508Sdim } 303263508Sdim case FormatStyle::UT_ForIndentation: 304263508Sdim if (WhitespaceStartColumn == 0) { 305263508Sdim unsigned Indentation = IndentLevel * Style.IndentWidth; 306263508Sdim // This happens, e.g. when a line in a block comment is indented less than 307263508Sdim // the first one. 308263508Sdim if (Indentation > Spaces) 309263508Sdim Indentation = Spaces; 310263508Sdim unsigned Tabs = Indentation / Style.TabWidth; 311263508Sdim Text.append(std::string(Tabs, '\t')); 312263508Sdim Spaces -= Tabs * Style.TabWidth; 313263508Sdim } 314263508Sdim Text.append(std::string(Spaces, ' ')); 315263508Sdim break; 316263508Sdim } 317263508Sdim} 318263508Sdim 319251609Sdim} // namespace format 320251609Sdim} // namespace clang 321