//===--- WhitespaceManager.cpp - Format C++ code --------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// \file /// \brief This file implements WhitespaceManager class. /// //===----------------------------------------------------------------------===// #include "WhitespaceManager.h" #include "llvm/ADT/STLExtras.h" namespace clang { namespace format { void WhitespaceManager::replaceWhitespace(const AnnotatedToken &Tok, unsigned NewLines, unsigned Spaces, unsigned WhitespaceStartColumn) { if (NewLines > 0) alignEscapedNewlines(); // 2+ newlines mean an empty line separating logic scopes. if (NewLines >= 2) alignComments(); // Align line comments if they are trailing or if they continue other // trailing comments. if (Tok.isTrailingComment()) { SourceLocation TokenEndLoc = Tok.FormatTok.getStartOfNonWhitespace() .getLocWithOffset(Tok.FormatTok.TokenLength); // Remove the comment's trailing whitespace. if (Tok.FormatTok.TrailingWhiteSpaceLength != 0) Replaces.insert(tooling::Replacement( SourceMgr, TokenEndLoc, Tok.FormatTok.TrailingWhiteSpaceLength, "")); bool LineExceedsColumnLimit = Spaces + WhitespaceStartColumn + Tok.FormatTok.TokenLength > Style.ColumnLimit; // Align comment with other comments. if ((Tok.Parent != NULL || !Comments.empty()) && !LineExceedsColumnLimit) { unsigned MinColumn = NewLines > 0 ? Spaces : WhitespaceStartColumn + Spaces; unsigned MaxColumn = Style.ColumnLimit - Tok.FormatTok.TokenLength; Comments.push_back(StoredToken( Tok.FormatTok.WhiteSpaceStart, Tok.FormatTok.WhiteSpaceLength, MinColumn, MaxColumn, NewLines, Spaces)); return; } } // If this line does not have a trailing comment, align the stored comments. if (Tok.Children.empty() && !Tok.isTrailingComment()) alignComments(); storeReplacement(Tok.FormatTok.WhiteSpaceStart, Tok.FormatTok.WhiteSpaceLength, getNewLineText(NewLines, Spaces)); } void WhitespaceManager::replacePPWhitespace(const AnnotatedToken &Tok, unsigned NewLines, unsigned Spaces, unsigned WhitespaceStartColumn) { if (NewLines == 0) { replaceWhitespace(Tok, NewLines, Spaces, WhitespaceStartColumn); } else { // The earliest position for "\" is 2 after the last token. unsigned MinColumn = WhitespaceStartColumn + 2; unsigned MaxColumn = Style.ColumnLimit; EscapedNewlines.push_back(StoredToken( Tok.FormatTok.WhiteSpaceStart, Tok.FormatTok.WhiteSpaceLength, MinColumn, MaxColumn, NewLines, Spaces)); } } void WhitespaceManager::breakToken(const FormatToken &Tok, unsigned Offset, unsigned ReplaceChars, StringRef Prefix, StringRef Postfix, bool InPPDirective, unsigned Spaces, unsigned WhitespaceStartColumn) { SourceLocation Location = Tok.getStartOfNonWhitespace().getLocWithOffset(Offset); if (InPPDirective) { // The earliest position for "\" is 2 after the last token. unsigned MinColumn = WhitespaceStartColumn + 2; unsigned MaxColumn = Style.ColumnLimit; StoredToken StoredTok = StoredToken(Location, ReplaceChars, MinColumn, MaxColumn, /*NewLines=*/ 1, Spaces); StoredTok.Prefix = Prefix; StoredTok.Postfix = Postfix; EscapedNewlines.push_back(StoredTok); } else { std::string ReplacementText = (Prefix + getNewLineText(1, Spaces) + Postfix).str(); Replaces.insert(tooling::Replacement(SourceMgr, Location, ReplaceChars, ReplacementText)); } } const tooling::Replacements &WhitespaceManager::generateReplacements() { alignComments(); alignEscapedNewlines(); return Replaces; } void WhitespaceManager::addReplacement(const SourceLocation &SourceLoc, unsigned ReplaceChars, StringRef Text) { Replaces.insert( tooling::Replacement(SourceMgr, SourceLoc, ReplaceChars, Text)); } void WhitespaceManager::addUntouchableComment(unsigned Column) { StoredToken Tok = StoredToken(SourceLocation(), 0, Column, Column, 0, 0); Tok.Untouchable = true; Comments.push_back(Tok); } std::string WhitespaceManager::getNewLineText(unsigned NewLines, unsigned Spaces) { return std::string(NewLines, '\n') + std::string(Spaces, ' '); } std::string WhitespaceManager::getNewLineText(unsigned NewLines, unsigned Spaces, unsigned WhitespaceStartColumn, unsigned EscapedNewlineColumn) { std::string NewLineText; if (NewLines > 0) { unsigned Offset = std::min(EscapedNewlineColumn - 1, WhitespaceStartColumn); for (unsigned i = 0; i < NewLines; ++i) { NewLineText += std::string(EscapedNewlineColumn - Offset - 1, ' '); NewLineText += "\\\n"; Offset = 0; } } return NewLineText + std::string(Spaces, ' '); } void WhitespaceManager::alignComments() { unsigned MinColumn = 0; unsigned MaxColumn = UINT_MAX; token_iterator Start = Comments.begin(); for (token_iterator I = Start, E = Comments.end(); I != E; ++I) { if (I->MinColumn > MaxColumn || I->MaxColumn < MinColumn) { alignComments(Start, I, MinColumn); MinColumn = I->MinColumn; MaxColumn = I->MaxColumn; Start = I; } else { MinColumn = std::max(MinColumn, I->MinColumn); MaxColumn = std::min(MaxColumn, I->MaxColumn); } } alignComments(Start, Comments.end(), MinColumn); Comments.clear(); } void WhitespaceManager::alignComments(token_iterator I, token_iterator E, unsigned Column) { while (I != E) { if (!I->Untouchable) { unsigned Spaces = I->Spaces + Column - I->MinColumn; storeReplacement(I->ReplacementLoc, I->ReplacementLength, getNewLineText(I->NewLines, Spaces)); } ++I; } } void WhitespaceManager::alignEscapedNewlines() { unsigned MinColumn; if (Style.AlignEscapedNewlinesLeft) { MinColumn = 0; for (token_iterator I = EscapedNewlines.begin(), E = EscapedNewlines.end(); I != E; ++I) { if (I->MinColumn > MinColumn) MinColumn = I->MinColumn; } } else { MinColumn = Style.ColumnLimit; } for (token_iterator I = EscapedNewlines.begin(), E = EscapedNewlines.end(); I != E; ++I) { // I->MinColumn - 2 is the end of the previous token (i.e. the // WhitespaceStartColumn). storeReplacement( I->ReplacementLoc, I->ReplacementLength, I->Prefix + getNewLineText(I->NewLines, I->Spaces, I->MinColumn - 2, MinColumn) + I->Postfix); } EscapedNewlines.clear(); } void WhitespaceManager::storeReplacement(SourceLocation Loc, unsigned Length, const std::string Text) { // Don't create a replacement, if it does not change anything. if (StringRef(SourceMgr.getCharacterData(Loc), Length) == Text) return; Replaces.insert(tooling::Replacement(SourceMgr, Loc, Length, Text)); } } // namespace format } // namespace clang