| //===--- 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) { |
| // 2+ newlines mean an empty line separating logic scopes. |
| if (NewLines >= 2) |
| alignComments(); |
| |
| SourceLocation TokenLoc = Tok.FormatTok.Tok.getLocation(); |
| bool LineExceedsColumnLimit = |
| Spaces + WhitespaceStartColumn + Tok.FormatTok.TokenLength > |
| Style.ColumnLimit; |
| |
| // Align line comments if they are trailing or if they continue other |
| // trailing comments. |
| if (Tok.isTrailingComment()) { |
| // Remove the comment's trailing whitespace. |
| if (Tok.FormatTok.Tok.getLength() != Tok.FormatTok.TokenLength) |
| Replaces.insert(tooling::Replacement( |
| SourceMgr, TokenLoc.getLocWithOffset(Tok.FormatTok.TokenLength), |
| Tok.FormatTok.Tok.getLength() - Tok.FormatTok.TokenLength, "")); |
| |
| // Align comment with other comments. |
| if ((Tok.Parent != NULL || !Comments.empty()) && !LineExceedsColumnLimit) { |
| StoredComment Comment; |
| Comment.Tok = Tok.FormatTok; |
| Comment.Spaces = Spaces; |
| Comment.NewLines = NewLines; |
| Comment.MinColumn = |
| NewLines > 0 ? Spaces : WhitespaceStartColumn + Spaces; |
| Comment.MaxColumn = Style.ColumnLimit - Tok.FormatTok.TokenLength; |
| Comment.Untouchable = false; |
| Comments.push_back(Comment); |
| return; |
| } |
| } |
| |
| // If this line does not have a trailing comment, align the stored comments. |
| if (Tok.Children.empty() && !Tok.isTrailingComment()) |
| alignComments(); |
| |
| if (Tok.Type == TT_LineComment && LineExceedsColumnLimit) { |
| StringRef Line(SourceMgr.getCharacterData(TokenLoc), |
| Tok.FormatTok.TokenLength); |
| int StartColumn = Spaces + (NewLines == 0 ? WhitespaceStartColumn : 0); |
| StringRef Prefix = getLineCommentPrefix(Line); |
| std::string NewPrefix = std::string(StartColumn, ' ') + Prefix.str(); |
| splitLineComment(Tok.FormatTok, Line.substr(Prefix.size()), |
| StartColumn + Prefix.size(), NewPrefix); |
| } |
| |
| storeReplacement(Tok.FormatTok, getNewLineText(NewLines, Spaces)); |
| } |
| |
| void WhitespaceManager::replacePPWhitespace(const AnnotatedToken &Tok, |
| unsigned NewLines, unsigned Spaces, |
| unsigned WhitespaceStartColumn) { |
| storeReplacement(Tok.FormatTok, |
| getNewLineText(NewLines, Spaces, WhitespaceStartColumn)); |
| } |
| |
| void WhitespaceManager::breakToken(const FormatToken &Tok, unsigned Offset, |
| unsigned ReplaceChars, StringRef Prefix, |
| StringRef Postfix, bool InPPDirective, |
| unsigned Spaces, |
| unsigned WhitespaceStartColumn) { |
| std::string NewLineText; |
| if (!InPPDirective) |
| NewLineText = getNewLineText(1, Spaces); |
| else |
| NewLineText = getNewLineText(1, Spaces, WhitespaceStartColumn); |
| std::string ReplacementText = (Prefix + NewLineText + Postfix).str(); |
| SourceLocation Location = Tok.Tok.getLocation().getLocWithOffset(Offset); |
| Replaces.insert( |
| tooling::Replacement(SourceMgr, Location, ReplaceChars, ReplacementText)); |
| } |
| |
| const tooling::Replacements &WhitespaceManager::generateReplacements() { |
| alignComments(); |
| 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) { |
| StoredComment Comment; |
| Comment.MinColumn = Column; |
| Comment.MaxColumn = Column; |
| Comment.Untouchable = true; |
| Comments.push_back(Comment); |
| } |
| |
| StringRef WhitespaceManager::getLineCommentPrefix(StringRef Comment) { |
| const char *KnownPrefixes[] = { "/// ", "///", "// ", "//" }; |
| for (size_t i = 0; i < llvm::array_lengthof(KnownPrefixes); ++i) |
| if (Comment.startswith(KnownPrefixes[i])) |
| return KnownPrefixes[i]; |
| return ""; |
| } |
| |
| void |
| WhitespaceManager::splitLineComment(const FormatToken &Tok, StringRef Line, |
| size_t StartColumn, StringRef LinePrefix, |
| const char *WhiteSpaceChars /*= " "*/) { |
| const char *TokenStart = SourceMgr.getCharacterData(Tok.Tok.getLocation()); |
| |
| StringRef TrimmedLine = Line.rtrim(); |
| // Don't touch leading whitespace. |
| Line = TrimmedLine.ltrim(); |
| StartColumn += TrimmedLine.size() - Line.size(); |
| |
| while (Line.size() + StartColumn > Style.ColumnLimit) { |
| // Try to break at the last whitespace before the column limit. |
| size_t SpacePos = |
| Line.find_last_of(WhiteSpaceChars, Style.ColumnLimit - StartColumn + 1); |
| if (SpacePos == StringRef::npos) { |
| // Try to find any whitespace in the line. |
| SpacePos = Line.find_first_of(WhiteSpaceChars); |
| if (SpacePos == StringRef::npos) // No whitespace found, give up. |
| break; |
| } |
| |
| StringRef NextCut = Line.substr(0, SpacePos).rtrim(); |
| StringRef RemainingLine = Line.substr(SpacePos).ltrim(); |
| if (RemainingLine.empty()) |
| break; |
| |
| Line = RemainingLine; |
| |
| size_t ReplaceChars = Line.begin() - NextCut.end(); |
| breakToken(Tok, NextCut.end() - TokenStart, ReplaceChars, "", LinePrefix, |
| false, 0, 0); |
| StartColumn = LinePrefix.size(); |
| } |
| } |
| |
| 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) { |
| std::string NewLineText; |
| if (NewLines > 0) { |
| unsigned Offset = |
| std::min<int>(Style.ColumnLimit - 1, WhitespaceStartColumn); |
| for (unsigned i = 0; i < NewLines; ++i) { |
| NewLineText += std::string(Style.ColumnLimit - Offset - 1, ' '); |
| NewLineText += "\\\n"; |
| Offset = 0; |
| } |
| } |
| return NewLineText + std::string(Spaces, ' '); |
| } |
| |
| void WhitespaceManager::alignComments() { |
| unsigned MinColumn = 0; |
| unsigned MaxColumn = UINT_MAX; |
| comment_iterator Start = Comments.begin(); |
| for (comment_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(comment_iterator I, comment_iterator E, |
| unsigned Column) { |
| while (I != E) { |
| if (!I->Untouchable) { |
| unsigned Spaces = I->Spaces + Column - I->MinColumn; |
| storeReplacement(I->Tok, getNewLineText(I->NewLines, Spaces)); |
| } |
| ++I; |
| } |
| } |
| |
| void WhitespaceManager::storeReplacement(const FormatToken &Tok, |
| const std::string Text) { |
| // Don't create a replacement, if it does not change anything. |
| if (StringRef(SourceMgr.getCharacterData(Tok.WhiteSpaceStart), |
| Tok.WhiteSpaceLength) == Text) |
| return; |
| |
| Replaces.insert(tooling::Replacement(SourceMgr, Tok.WhiteSpaceStart, |
| Tok.WhiteSpaceLength, Text)); |
| } |
| |
| } // namespace format |
| } // namespace clang |