//===--- 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 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(CommonPrefixLength, FirstNonWhitespace); } } } if (CommonPrefixLength == UINT_MAX) CommonPrefixLength = 0; Decoration = NeedsStar ? "* " : ""; IndentAtLineBreak = std::max(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