1 //===--- BreakableToken.cpp - Format C++ code -----------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 /// 10 /// \file 11 /// \brief Contains implementation of BreakableToken class and classes derived 12 /// from it. 13 /// 14 //===----------------------------------------------------------------------===// 15 16 #include "BreakableToken.h" 17 #include "llvm/ADT/STLExtras.h" 18 #include <algorithm> 19 20 namespace clang { 21 namespace format { 22 23 BreakableToken::Split BreakableComment::getSplit(unsigned LineIndex, 24 unsigned TailOffset, 25 unsigned ColumnLimit) const { 26 StringRef Text = getLine(LineIndex).substr(TailOffset); 27 unsigned ContentStartColumn = getContentStartColumn(LineIndex, TailOffset); 28 if (ColumnLimit <= ContentStartColumn + 1) 29 return Split(StringRef::npos, 0); 30 31 unsigned MaxSplit = ColumnLimit - ContentStartColumn + 1; 32 StringRef::size_type SpaceOffset = Text.rfind(' ', MaxSplit); 33 if (SpaceOffset == StringRef::npos || 34 Text.find_last_not_of(' ', SpaceOffset) == StringRef::npos) { 35 SpaceOffset = Text.find(' ', MaxSplit); 36 } 37 if (SpaceOffset != StringRef::npos && SpaceOffset != 0) { 38 StringRef BeforeCut = Text.substr(0, SpaceOffset).rtrim(); 39 StringRef AfterCut = Text.substr(SpaceOffset).ltrim(); 40 return BreakableToken::Split(BeforeCut.size(), 41 AfterCut.begin() - BeforeCut.end()); 42 } 43 return BreakableToken::Split(StringRef::npos, 0); 44 } 45 46 void BreakableComment::insertBreak(unsigned LineIndex, unsigned TailOffset, 47 Split Split, bool InPPDirective, 48 WhitespaceManager &Whitespaces) { 49 StringRef Text = getLine(LineIndex).substr(TailOffset); 50 StringRef AdditionalPrefix = Decoration; 51 if (Text.size() == Split.first + Split.second) { 52 // For all but the last line handle trailing space in trimLine. 53 if (LineIndex < Lines.size() - 1) 54 return; 55 // For the last line we need to break before "*/", but not to add "* ". 56 AdditionalPrefix = ""; 57 } 58 59 unsigned WhitespaceStartColumn = 60 getContentStartColumn(LineIndex, TailOffset) + Split.first; 61 unsigned BreakOffset = Text.data() - TokenText.data() + Split.first; 62 unsigned CharsToRemove = Split.second; 63 Whitespaces.breakToken(Tok, BreakOffset, CharsToRemove, "", AdditionalPrefix, 64 InPPDirective, IndentAtLineBreak, 65 WhitespaceStartColumn); 66 } 67 68 BreakableBlockComment::BreakableBlockComment(const SourceManager &SourceMgr, 69 const AnnotatedToken &Token, 70 unsigned StartColumn) 71 : BreakableComment(SourceMgr, Token.FormatTok, StartColumn + 2) { 72 assert(TokenText.startswith("/*") && TokenText.endswith("*/")); 73 74 OriginalStartColumn = 75 SourceMgr.getSpellingColumnNumber(Tok.getStartOfNonWhitespace()) - 1; 76 77 TokenText.substr(2, TokenText.size() - 4).split(Lines, "\n"); 78 79 bool NeedsStar = true; 80 CommonPrefixLength = UINT_MAX; 81 if (Lines.size() == 1) { 82 if (Token.Parent == 0) { 83 // Standalone block comments will be aligned and prefixed with *s. 84 CommonPrefixLength = OriginalStartColumn + 1; 85 } else { 86 // Trailing comments can start on arbitrary column, and available 87 // horizontal space can be too small to align consecutive lines with 88 // the first one. We could, probably, align them to current 89 // indentation level, but now we just wrap them without indentation 90 // and stars. 91 CommonPrefixLength = 0; 92 NeedsStar = false; 93 } 94 } else { 95 for (size_t i = 1; i < Lines.size(); ++i) { 96 size_t FirstNonWhitespace = Lines[i].find_first_not_of(" "); 97 if (FirstNonWhitespace != StringRef::npos) { 98 NeedsStar = NeedsStar && (Lines[i][FirstNonWhitespace] == '*'); 99 CommonPrefixLength = 100 std::min<unsigned>(CommonPrefixLength, FirstNonWhitespace); 101 } 102 } 103 } 104 if (CommonPrefixLength == UINT_MAX) 105 CommonPrefixLength = 0; 106 107 Decoration = NeedsStar ? "* " : ""; 108 109 IndentAtLineBreak = 110 std::max<int>(StartColumn - OriginalStartColumn + CommonPrefixLength, 0); 111 } 112 113 void BreakableBlockComment::alignLines(WhitespaceManager &Whitespaces) { 114 SourceLocation TokenLoc = Tok.getStartOfNonWhitespace(); 115 int IndentDelta = (StartColumn - 2) - OriginalStartColumn; 116 if (IndentDelta > 0) { 117 std::string WhiteSpace(IndentDelta, ' '); 118 for (size_t i = 1; i < Lines.size(); ++i) { 119 Whitespaces.addReplacement( 120 TokenLoc.getLocWithOffset(Lines[i].data() - TokenText.data()), 0, 121 WhiteSpace); 122 } 123 } else if (IndentDelta < 0) { 124 std::string WhiteSpace(-IndentDelta, ' '); 125 // Check that the line is indented enough. 126 for (size_t i = 1; i < Lines.size(); ++i) { 127 if (!Lines[i].startswith(WhiteSpace)) 128 return; 129 } 130 for (size_t i = 1; i < Lines.size(); ++i) { 131 Whitespaces.addReplacement( 132 TokenLoc.getLocWithOffset(Lines[i].data() - TokenText.data()), 133 -IndentDelta, ""); 134 } 135 } 136 137 for (unsigned i = 1; i < Lines.size(); ++i) 138 Lines[i] = Lines[i].substr(CommonPrefixLength + Decoration.size()); 139 } 140 141 void BreakableBlockComment::trimLine(unsigned LineIndex, unsigned TailOffset, 142 unsigned InPPDirective, 143 WhitespaceManager &Whitespaces) { 144 if (LineIndex == Lines.size() - 1) 145 return; 146 StringRef Text = Lines[LineIndex].substr(TailOffset); 147 if (!Text.endswith(" ") && !InPPDirective) 148 return; 149 150 StringRef TrimmedLine = Text.rtrim(); 151 unsigned WhitespaceStartColumn = 152 getLineLengthAfterSplit(LineIndex, TailOffset); 153 unsigned BreakOffset = TrimmedLine.end() - TokenText.data(); 154 unsigned CharsToRemove = Text.size() - TrimmedLine.size() + 1; 155 Whitespaces.breakToken(Tok, BreakOffset, CharsToRemove, "", "", InPPDirective, 156 0, WhitespaceStartColumn); 157 } 158 159 BreakableLineComment::BreakableLineComment(const SourceManager &SourceMgr, 160 const AnnotatedToken &Token, 161 unsigned StartColumn) 162 : BreakableComment(SourceMgr, Token.FormatTok, StartColumn) { 163 assert(TokenText.startswith("//")); 164 Decoration = getLineCommentPrefix(TokenText); 165 Lines.push_back(TokenText.substr(Decoration.size())); 166 IndentAtLineBreak = StartColumn; 167 this->StartColumn += Decoration.size(); // Start column of the contents. 168 } 169 170 StringRef BreakableLineComment::getLineCommentPrefix(StringRef Comment) { 171 const char *KnownPrefixes[] = { "/// ", "///", "// ", "//" }; 172 for (size_t i = 0; i < llvm::array_lengthof(KnownPrefixes); ++i) 173 if (Comment.startswith(KnownPrefixes[i])) 174 return KnownPrefixes[i]; 175 return ""; 176 } 177 178 } // namespace format 179 } // namespace clang 180