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 "clang/Basic/CharInfo.h" 18 #include "clang/Format/Format.h" 19 #include "llvm/ADT/STLExtras.h" 20 #include "llvm/Support/Debug.h" 21 #include <algorithm> 22 23 #define DEBUG_TYPE "format-token-breaker" 24 25 namespace clang { 26 namespace format { 27 28 static const char *const Blanks = " \t\v\f\r"; 29 static bool IsBlank(char C) { 30 switch (C) { 31 case ' ': 32 case '\t': 33 case '\v': 34 case '\f': 35 case '\r': 36 return true; 37 default: 38 return false; 39 } 40 } 41 42 static BreakableToken::Split getCommentSplit(StringRef Text, 43 unsigned ContentStartColumn, 44 unsigned ColumnLimit, 45 unsigned TabWidth, 46 encoding::Encoding Encoding) { 47 if (ColumnLimit <= ContentStartColumn + 1) 48 return BreakableToken::Split(StringRef::npos, 0); 49 50 unsigned MaxSplit = ColumnLimit - ContentStartColumn + 1; 51 unsigned MaxSplitBytes = 0; 52 53 for (unsigned NumChars = 0; 54 NumChars < MaxSplit && MaxSplitBytes < Text.size();) { 55 unsigned BytesInChar = 56 encoding::getCodePointNumBytes(Text[MaxSplitBytes], Encoding); 57 NumChars += 58 encoding::columnWidthWithTabs(Text.substr(MaxSplitBytes, BytesInChar), 59 ContentStartColumn, TabWidth, Encoding); 60 MaxSplitBytes += BytesInChar; 61 } 62 63 StringRef::size_type SpaceOffset = Text.find_last_of(Blanks, MaxSplitBytes); 64 if (SpaceOffset == StringRef::npos || 65 // Don't break at leading whitespace. 66 Text.find_last_not_of(Blanks, SpaceOffset) == StringRef::npos) { 67 // Make sure that we don't break at leading whitespace that 68 // reaches past MaxSplit. 69 StringRef::size_type FirstNonWhitespace = Text.find_first_not_of(Blanks); 70 if (FirstNonWhitespace == StringRef::npos) 71 // If the comment is only whitespace, we cannot split. 72 return BreakableToken::Split(StringRef::npos, 0); 73 SpaceOffset = Text.find_first_of( 74 Blanks, std::max<unsigned>(MaxSplitBytes, FirstNonWhitespace)); 75 } 76 if (SpaceOffset != StringRef::npos && SpaceOffset != 0) { 77 StringRef BeforeCut = Text.substr(0, SpaceOffset).rtrim(Blanks); 78 StringRef AfterCut = Text.substr(SpaceOffset).ltrim(Blanks); 79 return BreakableToken::Split(BeforeCut.size(), 80 AfterCut.begin() - BeforeCut.end()); 81 } 82 return BreakableToken::Split(StringRef::npos, 0); 83 } 84 85 static BreakableToken::Split 86 getStringSplit(StringRef Text, unsigned UsedColumns, unsigned ColumnLimit, 87 unsigned TabWidth, encoding::Encoding Encoding) { 88 // FIXME: Reduce unit test case. 89 if (Text.empty()) 90 return BreakableToken::Split(StringRef::npos, 0); 91 if (ColumnLimit <= UsedColumns) 92 return BreakableToken::Split(StringRef::npos, 0); 93 unsigned MaxSplit = ColumnLimit - UsedColumns; 94 StringRef::size_type SpaceOffset = 0; 95 StringRef::size_type SlashOffset = 0; 96 StringRef::size_type WordStartOffset = 0; 97 StringRef::size_type SplitPoint = 0; 98 for (unsigned Chars = 0;;) { 99 unsigned Advance; 100 if (Text[0] == '\\') { 101 Advance = encoding::getEscapeSequenceLength(Text); 102 Chars += Advance; 103 } else { 104 Advance = encoding::getCodePointNumBytes(Text[0], Encoding); 105 Chars += encoding::columnWidthWithTabs( 106 Text.substr(0, Advance), UsedColumns + Chars, TabWidth, Encoding); 107 } 108 109 if (Chars > MaxSplit || Text.size() <= Advance) 110 break; 111 112 if (IsBlank(Text[0])) 113 SpaceOffset = SplitPoint; 114 if (Text[0] == '/') 115 SlashOffset = SplitPoint; 116 if (Advance == 1 && !isAlphanumeric(Text[0])) 117 WordStartOffset = SplitPoint; 118 119 SplitPoint += Advance; 120 Text = Text.substr(Advance); 121 } 122 123 if (SpaceOffset != 0) 124 return BreakableToken::Split(SpaceOffset + 1, 0); 125 if (SlashOffset != 0) 126 return BreakableToken::Split(SlashOffset + 1, 0); 127 if (WordStartOffset != 0) 128 return BreakableToken::Split(WordStartOffset + 1, 0); 129 if (SplitPoint != 0) 130 return BreakableToken::Split(SplitPoint, 0); 131 return BreakableToken::Split(StringRef::npos, 0); 132 } 133 134 unsigned BreakableSingleLineToken::getLineCount() const { return 1; } 135 136 unsigned BreakableSingleLineToken::getLineLengthAfterSplit( 137 unsigned LineIndex, unsigned Offset, StringRef::size_type Length) const { 138 return StartColumn + Prefix.size() + Postfix.size() + 139 encoding::columnWidthWithTabs(Line.substr(Offset, Length), 140 StartColumn + Prefix.size(), 141 Style.TabWidth, Encoding); 142 } 143 144 BreakableSingleLineToken::BreakableSingleLineToken( 145 const FormatToken &Tok, unsigned IndentLevel, unsigned StartColumn, 146 StringRef Prefix, StringRef Postfix, bool InPPDirective, 147 encoding::Encoding Encoding, const FormatStyle &Style) 148 : BreakableToken(Tok, IndentLevel, InPPDirective, Encoding, Style), 149 StartColumn(StartColumn), Prefix(Prefix), Postfix(Postfix) { 150 assert(Tok.TokenText.endswith(Postfix)); 151 Line = Tok.TokenText.substr( 152 Prefix.size(), Tok.TokenText.size() - Prefix.size() - Postfix.size()); 153 } 154 155 BreakableStringLiteral::BreakableStringLiteral( 156 const FormatToken &Tok, unsigned IndentLevel, unsigned StartColumn, 157 StringRef Prefix, StringRef Postfix, bool InPPDirective, 158 encoding::Encoding Encoding, const FormatStyle &Style) 159 : BreakableSingleLineToken(Tok, IndentLevel, StartColumn, Prefix, Postfix, 160 InPPDirective, Encoding, Style) {} 161 162 BreakableToken::Split 163 BreakableStringLiteral::getSplit(unsigned LineIndex, unsigned TailOffset, 164 unsigned ColumnLimit) const { 165 return getStringSplit(Line.substr(TailOffset), 166 StartColumn + Prefix.size() + Postfix.size(), 167 ColumnLimit, Style.TabWidth, Encoding); 168 } 169 170 void BreakableStringLiteral::insertBreak(unsigned LineIndex, 171 unsigned TailOffset, Split Split, 172 WhitespaceManager &Whitespaces) { 173 unsigned LeadingSpaces = StartColumn; 174 // The '@' of an ObjC string literal (@"Test") does not become part of the 175 // string token. 176 // FIXME: It might be a cleaner solution to merge the tokens as a 177 // precomputation step. 178 if (Prefix.startswith("@")) 179 --LeadingSpaces; 180 Whitespaces.replaceWhitespaceInToken( 181 Tok, Prefix.size() + TailOffset + Split.first, Split.second, Postfix, 182 Prefix, InPPDirective, 1, IndentLevel, LeadingSpaces); 183 } 184 185 static StringRef getLineCommentIndentPrefix(StringRef Comment) { 186 static const char *const KnownPrefixes[] = { "///", "//", "//!" }; 187 StringRef LongestPrefix; 188 for (StringRef KnownPrefix : KnownPrefixes) { 189 if (Comment.startswith(KnownPrefix)) { 190 size_t PrefixLength = KnownPrefix.size(); 191 while (PrefixLength < Comment.size() && Comment[PrefixLength] == ' ') 192 ++PrefixLength; 193 if (PrefixLength > LongestPrefix.size()) 194 LongestPrefix = Comment.substr(0, PrefixLength); 195 } 196 } 197 return LongestPrefix; 198 } 199 200 BreakableLineComment::BreakableLineComment( 201 const FormatToken &Token, unsigned IndentLevel, unsigned StartColumn, 202 bool InPPDirective, encoding::Encoding Encoding, const FormatStyle &Style) 203 : BreakableSingleLineToken(Token, IndentLevel, StartColumn, 204 getLineCommentIndentPrefix(Token.TokenText), "", 205 InPPDirective, Encoding, Style) { 206 OriginalPrefix = Prefix; 207 if (Token.TokenText.size() > Prefix.size() && 208 isAlphanumeric(Token.TokenText[Prefix.size()])) { 209 if (Prefix == "//") 210 Prefix = "// "; 211 else if (Prefix == "///") 212 Prefix = "/// "; 213 else if (Prefix == "//!") 214 Prefix = "//! "; 215 } 216 } 217 218 BreakableToken::Split 219 BreakableLineComment::getSplit(unsigned LineIndex, unsigned TailOffset, 220 unsigned ColumnLimit) const { 221 return getCommentSplit(Line.substr(TailOffset), StartColumn + Prefix.size(), 222 ColumnLimit, Style.TabWidth, Encoding); 223 } 224 225 void BreakableLineComment::insertBreak(unsigned LineIndex, unsigned TailOffset, 226 Split Split, 227 WhitespaceManager &Whitespaces) { 228 Whitespaces.replaceWhitespaceInToken( 229 Tok, OriginalPrefix.size() + TailOffset + Split.first, Split.second, 230 Postfix, Prefix, InPPDirective, /*Newlines=*/1, IndentLevel, StartColumn); 231 } 232 233 void BreakableLineComment::replaceWhitespace(unsigned LineIndex, 234 unsigned TailOffset, Split Split, 235 WhitespaceManager &Whitespaces) { 236 Whitespaces.replaceWhitespaceInToken( 237 Tok, OriginalPrefix.size() + TailOffset + Split.first, Split.second, "", 238 "", /*InPPDirective=*/false, /*Newlines=*/0, /*IndentLevel=*/0, 239 /*Spaces=*/1); 240 } 241 242 void 243 BreakableLineComment::replaceWhitespaceBefore(unsigned LineIndex, 244 WhitespaceManager &Whitespaces) { 245 if (OriginalPrefix != Prefix) { 246 Whitespaces.replaceWhitespaceInToken(Tok, OriginalPrefix.size(), 0, "", "", 247 /*InPPDirective=*/false, 248 /*Newlines=*/0, /*IndentLevel=*/0, 249 /*Spaces=*/1); 250 } 251 } 252 253 BreakableBlockComment::BreakableBlockComment( 254 const FormatToken &Token, unsigned IndentLevel, unsigned StartColumn, 255 unsigned OriginalStartColumn, bool FirstInLine, bool InPPDirective, 256 encoding::Encoding Encoding, const FormatStyle &Style) 257 : BreakableToken(Token, IndentLevel, InPPDirective, Encoding, Style) { 258 StringRef TokenText(Token.TokenText); 259 assert(TokenText.startswith("/*") && TokenText.endswith("*/")); 260 TokenText.substr(2, TokenText.size() - 4).split(Lines, "\n"); 261 262 int IndentDelta = StartColumn - OriginalStartColumn; 263 LeadingWhitespace.resize(Lines.size()); 264 StartOfLineColumn.resize(Lines.size()); 265 StartOfLineColumn[0] = StartColumn + 2; 266 for (size_t i = 1; i < Lines.size(); ++i) 267 adjustWhitespace(i, IndentDelta); 268 269 Decoration = "* "; 270 if (Lines.size() == 1 && !FirstInLine) { 271 // Comments for which FirstInLine is false can start on arbitrary column, 272 // and available horizontal space can be too small to align consecutive 273 // lines with the first one. 274 // FIXME: We could, probably, align them to current indentation level, but 275 // now we just wrap them without stars. 276 Decoration = ""; 277 } 278 for (size_t i = 1, e = Lines.size(); i < e && !Decoration.empty(); ++i) { 279 // If the last line is empty, the closing "*/" will have a star. 280 if (i + 1 == e && Lines[i].empty()) 281 break; 282 if (!Lines[i].empty() && i + 1 != e && Decoration.startswith(Lines[i])) 283 continue; 284 while (!Lines[i].startswith(Decoration)) 285 Decoration = Decoration.substr(0, Decoration.size() - 1); 286 } 287 288 LastLineNeedsDecoration = true; 289 IndentAtLineBreak = StartOfLineColumn[0] + 1; 290 for (size_t i = 1; i < Lines.size(); ++i) { 291 if (Lines[i].empty()) { 292 if (i + 1 == Lines.size()) { 293 // Empty last line means that we already have a star as a part of the 294 // trailing */. We also need to preserve whitespace, so that */ is 295 // correctly indented. 296 LastLineNeedsDecoration = false; 297 } else if (Decoration.empty()) { 298 // For all other lines, set the start column to 0 if they're empty, so 299 // we do not insert trailing whitespace anywhere. 300 StartOfLineColumn[i] = 0; 301 } 302 continue; 303 } 304 305 // The first line already excludes the star. 306 // For all other lines, adjust the line to exclude the star and 307 // (optionally) the first whitespace. 308 unsigned DecorationSize = 309 Decoration.startswith(Lines[i]) ? Lines[i].size() : Decoration.size(); 310 StartOfLineColumn[i] += DecorationSize; 311 Lines[i] = Lines[i].substr(DecorationSize); 312 LeadingWhitespace[i] += DecorationSize; 313 if (!Decoration.startswith(Lines[i])) 314 IndentAtLineBreak = 315 std::min<int>(IndentAtLineBreak, std::max(0, StartOfLineColumn[i])); 316 } 317 IndentAtLineBreak = std::max<unsigned>(IndentAtLineBreak, Decoration.size()); 318 DEBUG({ 319 llvm::dbgs() << "IndentAtLineBreak " << IndentAtLineBreak << "\n"; 320 for (size_t i = 0; i < Lines.size(); ++i) { 321 llvm::dbgs() << i << " |" << Lines[i] << "| " << LeadingWhitespace[i] 322 << "\n"; 323 } 324 }); 325 } 326 327 void BreakableBlockComment::adjustWhitespace(unsigned LineIndex, 328 int IndentDelta) { 329 // When in a preprocessor directive, the trailing backslash in a block comment 330 // is not needed, but can serve a purpose of uniformity with necessary escaped 331 // newlines outside the comment. In this case we remove it here before 332 // trimming the trailing whitespace. The backslash will be re-added later when 333 // inserting a line break. 334 size_t EndOfPreviousLine = Lines[LineIndex - 1].size(); 335 if (InPPDirective && Lines[LineIndex - 1].endswith("\\")) 336 --EndOfPreviousLine; 337 338 // Calculate the end of the non-whitespace text in the previous line. 339 EndOfPreviousLine = 340 Lines[LineIndex - 1].find_last_not_of(Blanks, EndOfPreviousLine); 341 if (EndOfPreviousLine == StringRef::npos) 342 EndOfPreviousLine = 0; 343 else 344 ++EndOfPreviousLine; 345 // Calculate the start of the non-whitespace text in the current line. 346 size_t StartOfLine = Lines[LineIndex].find_first_not_of(Blanks); 347 if (StartOfLine == StringRef::npos) 348 StartOfLine = Lines[LineIndex].size(); 349 350 StringRef Whitespace = Lines[LineIndex].substr(0, StartOfLine); 351 // Adjust Lines to only contain relevant text. 352 Lines[LineIndex - 1] = Lines[LineIndex - 1].substr(0, EndOfPreviousLine); 353 Lines[LineIndex] = Lines[LineIndex].substr(StartOfLine); 354 // Adjust LeadingWhitespace to account all whitespace between the lines 355 // to the current line. 356 LeadingWhitespace[LineIndex] = 357 Lines[LineIndex].begin() - Lines[LineIndex - 1].end(); 358 359 // Adjust the start column uniformly across all lines. 360 StartOfLineColumn[LineIndex] = 361 encoding::columnWidthWithTabs(Whitespace, 0, Style.TabWidth, Encoding) + 362 IndentDelta; 363 } 364 365 unsigned BreakableBlockComment::getLineCount() const { return Lines.size(); } 366 367 unsigned BreakableBlockComment::getLineLengthAfterSplit( 368 unsigned LineIndex, unsigned Offset, StringRef::size_type Length) const { 369 unsigned ContentStartColumn = getContentStartColumn(LineIndex, Offset); 370 return ContentStartColumn + 371 encoding::columnWidthWithTabs(Lines[LineIndex].substr(Offset, Length), 372 ContentStartColumn, Style.TabWidth, 373 Encoding) + 374 // The last line gets a "*/" postfix. 375 (LineIndex + 1 == Lines.size() ? 2 : 0); 376 } 377 378 BreakableToken::Split 379 BreakableBlockComment::getSplit(unsigned LineIndex, unsigned TailOffset, 380 unsigned ColumnLimit) const { 381 return getCommentSplit(Lines[LineIndex].substr(TailOffset), 382 getContentStartColumn(LineIndex, TailOffset), 383 ColumnLimit, Style.TabWidth, Encoding); 384 } 385 386 void BreakableBlockComment::insertBreak(unsigned LineIndex, unsigned TailOffset, 387 Split Split, 388 WhitespaceManager &Whitespaces) { 389 StringRef Text = Lines[LineIndex].substr(TailOffset); 390 StringRef Prefix = Decoration; 391 if (LineIndex + 1 == Lines.size() && 392 Text.size() == Split.first + Split.second) { 393 // For the last line we need to break before "*/", but not to add "* ". 394 Prefix = ""; 395 } 396 397 unsigned BreakOffsetInToken = 398 Text.data() - Tok.TokenText.data() + Split.first; 399 unsigned CharsToRemove = Split.second; 400 assert(IndentAtLineBreak >= Decoration.size()); 401 Whitespaces.replaceWhitespaceInToken( 402 Tok, BreakOffsetInToken, CharsToRemove, "", Prefix, InPPDirective, 1, 403 IndentLevel, IndentAtLineBreak - Decoration.size()); 404 } 405 406 void BreakableBlockComment::replaceWhitespace(unsigned LineIndex, 407 unsigned TailOffset, Split Split, 408 WhitespaceManager &Whitespaces) { 409 StringRef Text = Lines[LineIndex].substr(TailOffset); 410 unsigned BreakOffsetInToken = 411 Text.data() - Tok.TokenText.data() + Split.first; 412 unsigned CharsToRemove = Split.second; 413 Whitespaces.replaceWhitespaceInToken( 414 Tok, BreakOffsetInToken, CharsToRemove, "", "", /*InPPDirective=*/false, 415 /*Newlines=*/0, /*IndentLevel=*/0, /*Spaces=*/1); 416 } 417 418 void 419 BreakableBlockComment::replaceWhitespaceBefore(unsigned LineIndex, 420 WhitespaceManager &Whitespaces) { 421 if (LineIndex == 0) 422 return; 423 StringRef Prefix = Decoration; 424 if (Lines[LineIndex].empty()) { 425 if (LineIndex + 1 == Lines.size()) { 426 if (!LastLineNeedsDecoration) { 427 // If the last line was empty, we don't need a prefix, as the */ will 428 // line up with the decoration (if it exists). 429 Prefix = ""; 430 } 431 } else if (!Decoration.empty()) { 432 // For other empty lines, if we do have a decoration, adapt it to not 433 // contain a trailing whitespace. 434 Prefix = Prefix.substr(0, 1); 435 } 436 } else { 437 if (StartOfLineColumn[LineIndex] == 1) { 438 // This line starts immediately after the decorating *. 439 Prefix = Prefix.substr(0, 1); 440 } 441 } 442 443 unsigned WhitespaceOffsetInToken = Lines[LineIndex].data() - 444 Tok.TokenText.data() - 445 LeadingWhitespace[LineIndex]; 446 Whitespaces.replaceWhitespaceInToken( 447 Tok, WhitespaceOffsetInToken, LeadingWhitespace[LineIndex], "", Prefix, 448 InPPDirective, 1, IndentLevel, 449 StartOfLineColumn[LineIndex] - Prefix.size()); 450 } 451 452 unsigned 453 BreakableBlockComment::getContentStartColumn(unsigned LineIndex, 454 unsigned TailOffset) const { 455 // If we break, we always break at the predefined indent. 456 if (TailOffset != 0) 457 return IndentAtLineBreak; 458 return std::max(0, StartOfLineColumn[LineIndex]); 459 } 460 461 } // namespace format 462 } // namespace clang 463