| //===--- BreakableToken.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 Contains implementation of BreakableToken class and classes derived |
| /// from it. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #include "BreakableToken.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include <algorithm> |
| |
| namespace clang { |
| namespace format { |
| |
| BreakableToken::Split BreakableComment::getSplit(unsigned LineIndex, |
| unsigned TailOffset, |
| unsigned ColumnLimit) const { |
| StringRef Text = getLine(LineIndex).substr(TailOffset); |
| unsigned ContentStartColumn = getContentStartColumn(LineIndex, TailOffset); |
| if (ColumnLimit <= ContentStartColumn + 1) |
| return Split(StringRef::npos, 0); |
| |
| unsigned MaxSplit = ColumnLimit - ContentStartColumn + 1; |
| StringRef::size_type SpaceOffset = Text.rfind(' ', MaxSplit); |
| if (SpaceOffset == StringRef::npos || |
| Text.find_last_not_of(' ', SpaceOffset) == StringRef::npos) { |
| SpaceOffset = Text.find(' ', MaxSplit); |
| } |
| if (SpaceOffset != StringRef::npos && SpaceOffset != 0) { |
| StringRef BeforeCut = Text.substr(0, SpaceOffset).rtrim(); |
| StringRef AfterCut = Text.substr(SpaceOffset).ltrim(); |
| return BreakableToken::Split(BeforeCut.size(), |
| AfterCut.begin() - BeforeCut.end()); |
| } |
| return BreakableToken::Split(StringRef::npos, 0); |
| } |
| |
| void BreakableComment::insertBreak(unsigned LineIndex, unsigned TailOffset, |
| Split Split, bool InPPDirective, |
| WhitespaceManager &Whitespaces) { |
| StringRef Text = getLine(LineIndex).substr(TailOffset); |
| StringRef AdditionalPrefix = Decoration; |
| if (Text.size() == Split.first + Split.second) { |
| // For all but the last line handle trailing space in trimLine. |
| if (LineIndex < Lines.size() - 1) |
| return; |
| // For the last line we need to break before "*/", but not to add "* ". |
| AdditionalPrefix = ""; |
| } |
| |
| unsigned WhitespaceStartColumn = |
| getContentStartColumn(LineIndex, TailOffset) + Split.first; |
| unsigned BreakOffset = Text.data() - TokenText.data() + Split.first; |
| unsigned CharsToRemove = Split.second; |
| Whitespaces.breakToken(Tok, BreakOffset, CharsToRemove, "", AdditionalPrefix, |
| InPPDirective, IndentAtLineBreak, |
| WhitespaceStartColumn); |
| } |
| |
| BreakableBlockComment::BreakableBlockComment(const SourceManager &SourceMgr, |
| const AnnotatedToken &Token, |
| unsigned StartColumn) |
| : BreakableComment(SourceMgr, Token.FormatTok, StartColumn + 2) { |
| assert(TokenText.startswith("/*") && TokenText.endswith("*/")); |
| |
| OriginalStartColumn = |
| SourceMgr.getSpellingColumnNumber(Tok.getStartOfNonWhitespace()) - 1; |
| |
| TokenText.substr(2, TokenText.size() - 4).split(Lines, "\n"); |
| |
| bool NeedsStar = true; |
| CommonPrefixLength = UINT_MAX; |
| if (Lines.size() == 1) { |
| if (Token.Parent == 0) { |
| // Standalone block comments will be aligned and prefixed with *s. |
| CommonPrefixLength = OriginalStartColumn + 1; |
| } else { |
| // Trailing comments can start on arbitrary column, and available |
| // horizontal space can be too small to align consecutive lines with |
| // the first one. We could, probably, align them to current |
| // indentation level, but now we just wrap them without indentation |
| // and stars. |
| CommonPrefixLength = 0; |
| NeedsStar = false; |
| } |
| } else { |
| for (size_t i = 1; i < Lines.size(); ++i) { |
| size_t FirstNonWhitespace = Lines[i].find_first_not_of(" "); |
| if (FirstNonWhitespace != StringRef::npos) { |
| NeedsStar = NeedsStar && (Lines[i][FirstNonWhitespace] == '*'); |
| CommonPrefixLength = |
| std::min<unsigned>(CommonPrefixLength, FirstNonWhitespace); |
| } |
| } |
| } |
| if (CommonPrefixLength == UINT_MAX) |
| CommonPrefixLength = 0; |
| |
| Decoration = NeedsStar ? "* " : ""; |
| |
| IndentAtLineBreak = |
| std::max<int>(StartColumn - OriginalStartColumn + CommonPrefixLength, 0); |
| } |
| |
| void BreakableBlockComment::alignLines(WhitespaceManager &Whitespaces) { |
| SourceLocation TokenLoc = Tok.getStartOfNonWhitespace(); |
| int IndentDelta = (StartColumn - 2) - OriginalStartColumn; |
| if (IndentDelta > 0) { |
| std::string WhiteSpace(IndentDelta, ' '); |
| for (size_t i = 1; i < Lines.size(); ++i) { |
| Whitespaces.addReplacement( |
| TokenLoc.getLocWithOffset(Lines[i].data() - TokenText.data()), 0, |
| WhiteSpace); |
| } |
| } else if (IndentDelta < 0) { |
| std::string WhiteSpace(-IndentDelta, ' '); |
| // Check that the line is indented enough. |
| for (size_t i = 1; i < Lines.size(); ++i) { |
| if (!Lines[i].startswith(WhiteSpace)) |
| return; |
| } |
| for (size_t i = 1; i < Lines.size(); ++i) { |
| Whitespaces.addReplacement( |
| TokenLoc.getLocWithOffset(Lines[i].data() - TokenText.data()), |
| -IndentDelta, ""); |
| } |
| } |
| |
| for (unsigned i = 1; i < Lines.size(); ++i) |
| Lines[i] = Lines[i].substr(CommonPrefixLength + Decoration.size()); |
| } |
| |
| void BreakableBlockComment::trimLine(unsigned LineIndex, unsigned TailOffset, |
| unsigned InPPDirective, |
| WhitespaceManager &Whitespaces) { |
| if (LineIndex == Lines.size() - 1) |
| return; |
| StringRef Text = Lines[LineIndex].substr(TailOffset); |
| if (!Text.endswith(" ") && !InPPDirective) |
| return; |
| |
| StringRef TrimmedLine = Text.rtrim(); |
| unsigned WhitespaceStartColumn = |
| getLineLengthAfterSplit(LineIndex, TailOffset); |
| unsigned BreakOffset = TrimmedLine.end() - TokenText.data(); |
| unsigned CharsToRemove = Text.size() - TrimmedLine.size() + 1; |
| Whitespaces.breakToken(Tok, BreakOffset, CharsToRemove, "", "", InPPDirective, |
| 0, WhitespaceStartColumn); |
| } |
| |
| BreakableLineComment::BreakableLineComment(const SourceManager &SourceMgr, |
| const AnnotatedToken &Token, |
| unsigned StartColumn) |
| : BreakableComment(SourceMgr, Token.FormatTok, StartColumn) { |
| assert(TokenText.startswith("//")); |
| Decoration = getLineCommentPrefix(TokenText); |
| Lines.push_back(TokenText.substr(Decoration.size())); |
| IndentAtLineBreak = StartColumn; |
| this->StartColumn += Decoration.size(); // Start column of the contents. |
| } |
| |
| StringRef BreakableLineComment::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 ""; |
| } |
| |
| } // namespace format |
| } // namespace clang |