1251609Sdim//===--- BreakableToken.cpp - Format C++ code -----------------------------===// 2251609Sdim// 3251609Sdim// The LLVM Compiler Infrastructure 4251609Sdim// 5251609Sdim// This file is distributed under the University of Illinois Open Source 6251609Sdim// License. See LICENSE.TXT for details. 7251609Sdim// 8251609Sdim//===----------------------------------------------------------------------===// 9251609Sdim/// 10251609Sdim/// \file 11251609Sdim/// \brief Contains implementation of BreakableToken class and classes derived 12251609Sdim/// from it. 13251609Sdim/// 14251609Sdim//===----------------------------------------------------------------------===// 15251609Sdim 16251609Sdim#include "BreakableToken.h" 17261991Sdim#include "clang/Basic/CharInfo.h" 18261991Sdim#include "clang/Format/Format.h" 19251609Sdim#include "llvm/ADT/STLExtras.h" 20261991Sdim#include "llvm/Support/Debug.h" 21251609Sdim#include <algorithm> 22251609Sdim 23276479Sdim#define DEBUG_TYPE "format-token-breaker" 24276479Sdim 25251609Sdimnamespace clang { 26251609Sdimnamespace format { 27251609Sdim 28261991Sdimstatic const char *const Blanks = " \t\v\f\r"; 29261991Sdimstatic bool IsBlank(char C) { 30261991Sdim switch (C) { 31261991Sdim case ' ': 32261991Sdim case '\t': 33261991Sdim case '\v': 34261991Sdim case '\f': 35261991Sdim case '\r': 36261991Sdim return true; 37261991Sdim default: 38261991Sdim return false; 39261991Sdim } 40261991Sdim} 41261991Sdim 42261991Sdimstatic BreakableToken::Split getCommentSplit(StringRef Text, 43261991Sdim unsigned ContentStartColumn, 44261991Sdim unsigned ColumnLimit, 45261991Sdim unsigned TabWidth, 46261991Sdim encoding::Encoding Encoding) { 47251609Sdim if (ColumnLimit <= ContentStartColumn + 1) 48261991Sdim return BreakableToken::Split(StringRef::npos, 0); 49251609Sdim 50251609Sdim unsigned MaxSplit = ColumnLimit - ContentStartColumn + 1; 51261991Sdim unsigned MaxSplitBytes = 0; 52261991Sdim 53261991Sdim for (unsigned NumChars = 0; 54261991Sdim NumChars < MaxSplit && MaxSplitBytes < Text.size();) { 55261991Sdim unsigned BytesInChar = 56261991Sdim encoding::getCodePointNumBytes(Text[MaxSplitBytes], Encoding); 57261991Sdim NumChars += 58261991Sdim encoding::columnWidthWithTabs(Text.substr(MaxSplitBytes, BytesInChar), 59261991Sdim ContentStartColumn, TabWidth, Encoding); 60261991Sdim MaxSplitBytes += BytesInChar; 61261991Sdim } 62261991Sdim 63261991Sdim StringRef::size_type SpaceOffset = Text.find_last_of(Blanks, MaxSplitBytes); 64251609Sdim if (SpaceOffset == StringRef::npos || 65261991Sdim // Don't break at leading whitespace. 66261991Sdim Text.find_last_not_of(Blanks, SpaceOffset) == StringRef::npos) { 67261991Sdim // Make sure that we don't break at leading whitespace that 68261991Sdim // reaches past MaxSplit. 69261991Sdim StringRef::size_type FirstNonWhitespace = Text.find_first_not_of(Blanks); 70261991Sdim if (FirstNonWhitespace == StringRef::npos) 71261991Sdim // If the comment is only whitespace, we cannot split. 72261991Sdim return BreakableToken::Split(StringRef::npos, 0); 73261991Sdim SpaceOffset = Text.find_first_of( 74261991Sdim Blanks, std::max<unsigned>(MaxSplitBytes, FirstNonWhitespace)); 75251609Sdim } 76251609Sdim if (SpaceOffset != StringRef::npos && SpaceOffset != 0) { 77261991Sdim StringRef BeforeCut = Text.substr(0, SpaceOffset).rtrim(Blanks); 78261991Sdim StringRef AfterCut = Text.substr(SpaceOffset).ltrim(Blanks); 79251609Sdim return BreakableToken::Split(BeforeCut.size(), 80251609Sdim AfterCut.begin() - BeforeCut.end()); 81251609Sdim } 82251609Sdim return BreakableToken::Split(StringRef::npos, 0); 83251609Sdim} 84251609Sdim 85276479Sdimstatic BreakableToken::Split 86276479SdimgetStringSplit(StringRef Text, unsigned UsedColumns, unsigned ColumnLimit, 87276479Sdim unsigned TabWidth, encoding::Encoding Encoding) { 88261991Sdim // FIXME: Reduce unit test case. 89261991Sdim if (Text.empty()) 90261991Sdim return BreakableToken::Split(StringRef::npos, 0); 91261991Sdim if (ColumnLimit <= UsedColumns) 92261991Sdim return BreakableToken::Split(StringRef::npos, 0); 93276479Sdim unsigned MaxSplit = ColumnLimit - UsedColumns; 94261991Sdim StringRef::size_type SpaceOffset = 0; 95261991Sdim StringRef::size_type SlashOffset = 0; 96261991Sdim StringRef::size_type WordStartOffset = 0; 97261991Sdim StringRef::size_type SplitPoint = 0; 98261991Sdim for (unsigned Chars = 0;;) { 99261991Sdim unsigned Advance; 100261991Sdim if (Text[0] == '\\') { 101261991Sdim Advance = encoding::getEscapeSequenceLength(Text); 102261991Sdim Chars += Advance; 103261991Sdim } else { 104261991Sdim Advance = encoding::getCodePointNumBytes(Text[0], Encoding); 105261991Sdim Chars += encoding::columnWidthWithTabs( 106261991Sdim Text.substr(0, Advance), UsedColumns + Chars, TabWidth, Encoding); 107261991Sdim } 108261991Sdim 109288943Sdim if (Chars > MaxSplit || Text.size() <= Advance) 110261991Sdim break; 111261991Sdim 112261991Sdim if (IsBlank(Text[0])) 113261991Sdim SpaceOffset = SplitPoint; 114261991Sdim if (Text[0] == '/') 115261991Sdim SlashOffset = SplitPoint; 116261991Sdim if (Advance == 1 && !isAlphanumeric(Text[0])) 117261991Sdim WordStartOffset = SplitPoint; 118261991Sdim 119261991Sdim SplitPoint += Advance; 120261991Sdim Text = Text.substr(Advance); 121251609Sdim } 122251609Sdim 123261991Sdim if (SpaceOffset != 0) 124261991Sdim return BreakableToken::Split(SpaceOffset + 1, 0); 125261991Sdim if (SlashOffset != 0) 126261991Sdim return BreakableToken::Split(SlashOffset + 1, 0); 127261991Sdim if (WordStartOffset != 0) 128261991Sdim return BreakableToken::Split(WordStartOffset + 1, 0); 129261991Sdim if (SplitPoint != 0) 130261991Sdim return BreakableToken::Split(SplitPoint, 0); 131261991Sdim return BreakableToken::Split(StringRef::npos, 0); 132251609Sdim} 133251609Sdim 134261991Sdimunsigned BreakableSingleLineToken::getLineCount() const { return 1; } 135251609Sdim 136261991Sdimunsigned BreakableSingleLineToken::getLineLengthAfterSplit( 137261991Sdim unsigned LineIndex, unsigned Offset, StringRef::size_type Length) const { 138261991Sdim return StartColumn + Prefix.size() + Postfix.size() + 139261991Sdim encoding::columnWidthWithTabs(Line.substr(Offset, Length), 140261991Sdim StartColumn + Prefix.size(), 141261991Sdim Style.TabWidth, Encoding); 142261991Sdim} 143251609Sdim 144261991SdimBreakableSingleLineToken::BreakableSingleLineToken( 145261991Sdim const FormatToken &Tok, unsigned IndentLevel, unsigned StartColumn, 146261991Sdim StringRef Prefix, StringRef Postfix, bool InPPDirective, 147261991Sdim encoding::Encoding Encoding, const FormatStyle &Style) 148261991Sdim : BreakableToken(Tok, IndentLevel, InPPDirective, Encoding, Style), 149261991Sdim StartColumn(StartColumn), Prefix(Prefix), Postfix(Postfix) { 150276479Sdim assert(Tok.TokenText.endswith(Postfix)); 151261991Sdim Line = Tok.TokenText.substr( 152261991Sdim Prefix.size(), Tok.TokenText.size() - Prefix.size() - Postfix.size()); 153261991Sdim} 154261991Sdim 155261991SdimBreakableStringLiteral::BreakableStringLiteral( 156261991Sdim const FormatToken &Tok, unsigned IndentLevel, unsigned StartColumn, 157261991Sdim StringRef Prefix, StringRef Postfix, bool InPPDirective, 158261991Sdim encoding::Encoding Encoding, const FormatStyle &Style) 159261991Sdim : BreakableSingleLineToken(Tok, IndentLevel, StartColumn, Prefix, Postfix, 160261991Sdim InPPDirective, Encoding, Style) {} 161261991Sdim 162261991SdimBreakableToken::Split 163261991SdimBreakableStringLiteral::getSplit(unsigned LineIndex, unsigned TailOffset, 164261991Sdim unsigned ColumnLimit) const { 165261991Sdim return getStringSplit(Line.substr(TailOffset), 166261991Sdim StartColumn + Prefix.size() + Postfix.size(), 167261991Sdim ColumnLimit, Style.TabWidth, Encoding); 168261991Sdim} 169261991Sdim 170261991Sdimvoid BreakableStringLiteral::insertBreak(unsigned LineIndex, 171261991Sdim unsigned TailOffset, Split Split, 172261991Sdim WhitespaceManager &Whitespaces) { 173276479Sdim unsigned LeadingSpaces = StartColumn; 174276479Sdim // The '@' of an ObjC string literal (@"Test") does not become part of the 175276479Sdim // string token. 176276479Sdim // FIXME: It might be a cleaner solution to merge the tokens as a 177276479Sdim // precomputation step. 178276479Sdim if (Prefix.startswith("@")) 179276479Sdim --LeadingSpaces; 180261991Sdim Whitespaces.replaceWhitespaceInToken( 181261991Sdim Tok, Prefix.size() + TailOffset + Split.first, Split.second, Postfix, 182276479Sdim Prefix, InPPDirective, 1, IndentLevel, LeadingSpaces); 183261991Sdim} 184261991Sdim 185276479Sdimstatic StringRef getLineCommentIndentPrefix(StringRef Comment) { 186288943Sdim static const char *const KnownPrefixes[] = {"///", "//", "//!"}; 187276479Sdim StringRef LongestPrefix; 188276479Sdim for (StringRef KnownPrefix : KnownPrefixes) { 189276479Sdim if (Comment.startswith(KnownPrefix)) { 190276479Sdim size_t PrefixLength = KnownPrefix.size(); 191276479Sdim while (PrefixLength < Comment.size() && Comment[PrefixLength] == ' ') 192276479Sdim ++PrefixLength; 193276479Sdim if (PrefixLength > LongestPrefix.size()) 194276479Sdim LongestPrefix = Comment.substr(0, PrefixLength); 195276479Sdim } 196276479Sdim } 197276479Sdim return LongestPrefix; 198261991Sdim} 199261991Sdim 200261991SdimBreakableLineComment::BreakableLineComment( 201261991Sdim const FormatToken &Token, unsigned IndentLevel, unsigned StartColumn, 202261991Sdim bool InPPDirective, encoding::Encoding Encoding, const FormatStyle &Style) 203261991Sdim : BreakableSingleLineToken(Token, IndentLevel, StartColumn, 204276479Sdim getLineCommentIndentPrefix(Token.TokenText), "", 205261991Sdim InPPDirective, Encoding, Style) { 206261991Sdim OriginalPrefix = Prefix; 207261991Sdim if (Token.TokenText.size() > Prefix.size() && 208261991Sdim isAlphanumeric(Token.TokenText[Prefix.size()])) { 209261991Sdim if (Prefix == "//") 210261991Sdim Prefix = "// "; 211261991Sdim else if (Prefix == "///") 212261991Sdim Prefix = "/// "; 213288943Sdim else if (Prefix == "//!") 214288943Sdim Prefix = "//! "; 215261991Sdim } 216261991Sdim} 217261991Sdim 218261991SdimBreakableToken::Split 219261991SdimBreakableLineComment::getSplit(unsigned LineIndex, unsigned TailOffset, 220261991Sdim unsigned ColumnLimit) const { 221261991Sdim return getCommentSplit(Line.substr(TailOffset), StartColumn + Prefix.size(), 222261991Sdim ColumnLimit, Style.TabWidth, Encoding); 223261991Sdim} 224261991Sdim 225261991Sdimvoid BreakableLineComment::insertBreak(unsigned LineIndex, unsigned TailOffset, 226261991Sdim Split Split, 227261991Sdim WhitespaceManager &Whitespaces) { 228261991Sdim Whitespaces.replaceWhitespaceInToken( 229261991Sdim Tok, OriginalPrefix.size() + TailOffset + Split.first, Split.second, 230261991Sdim Postfix, Prefix, InPPDirective, /*Newlines=*/1, IndentLevel, StartColumn); 231261991Sdim} 232261991Sdim 233261991Sdimvoid BreakableLineComment::replaceWhitespace(unsigned LineIndex, 234261991Sdim unsigned TailOffset, Split Split, 235261991Sdim WhitespaceManager &Whitespaces) { 236261991Sdim Whitespaces.replaceWhitespaceInToken( 237261991Sdim Tok, OriginalPrefix.size() + TailOffset + Split.first, Split.second, "", 238261991Sdim "", /*InPPDirective=*/false, /*Newlines=*/0, /*IndentLevel=*/0, 239261991Sdim /*Spaces=*/1); 240261991Sdim} 241261991Sdim 242288943Sdimvoid BreakableLineComment::replaceWhitespaceBefore( 243288943Sdim unsigned LineIndex, WhitespaceManager &Whitespaces) { 244261991Sdim if (OriginalPrefix != Prefix) { 245261991Sdim Whitespaces.replaceWhitespaceInToken(Tok, OriginalPrefix.size(), 0, "", "", 246261991Sdim /*InPPDirective=*/false, 247261991Sdim /*Newlines=*/0, /*IndentLevel=*/0, 248261991Sdim /*Spaces=*/1); 249261991Sdim } 250261991Sdim} 251261991Sdim 252261991SdimBreakableBlockComment::BreakableBlockComment( 253261991Sdim const FormatToken &Token, unsigned IndentLevel, unsigned StartColumn, 254261991Sdim unsigned OriginalStartColumn, bool FirstInLine, bool InPPDirective, 255261991Sdim encoding::Encoding Encoding, const FormatStyle &Style) 256261991Sdim : BreakableToken(Token, IndentLevel, InPPDirective, Encoding, Style) { 257261991Sdim StringRef TokenText(Token.TokenText); 258261991Sdim assert(TokenText.startswith("/*") && TokenText.endswith("*/")); 259251609Sdim TokenText.substr(2, TokenText.size() - 4).split(Lines, "\n"); 260251609Sdim 261261991Sdim int IndentDelta = StartColumn - OriginalStartColumn; 262261991Sdim LeadingWhitespace.resize(Lines.size()); 263261991Sdim StartOfLineColumn.resize(Lines.size()); 264261991Sdim StartOfLineColumn[0] = StartColumn + 2; 265261991Sdim for (size_t i = 1; i < Lines.size(); ++i) 266261991Sdim adjustWhitespace(i, IndentDelta); 267261991Sdim 268261991Sdim Decoration = "* "; 269261991Sdim if (Lines.size() == 1 && !FirstInLine) { 270261991Sdim // Comments for which FirstInLine is false can start on arbitrary column, 271261991Sdim // and available horizontal space can be too small to align consecutive 272261991Sdim // lines with the first one. 273261991Sdim // FIXME: We could, probably, align them to current indentation level, but 274261991Sdim // now we just wrap them without stars. 275261991Sdim Decoration = ""; 276261991Sdim } 277261991Sdim for (size_t i = 1, e = Lines.size(); i < e && !Decoration.empty(); ++i) { 278261991Sdim // If the last line is empty, the closing "*/" will have a star. 279261991Sdim if (i + 1 == e && Lines[i].empty()) 280261991Sdim break; 281288943Sdim if (!Lines[i].empty() && i + 1 != e && Decoration.startswith(Lines[i])) 282288943Sdim continue; 283261991Sdim while (!Lines[i].startswith(Decoration)) 284261991Sdim Decoration = Decoration.substr(0, Decoration.size() - 1); 285261991Sdim } 286261991Sdim 287261991Sdim LastLineNeedsDecoration = true; 288261991Sdim IndentAtLineBreak = StartOfLineColumn[0] + 1; 289261991Sdim for (size_t i = 1; i < Lines.size(); ++i) { 290261991Sdim if (Lines[i].empty()) { 291261991Sdim if (i + 1 == Lines.size()) { 292261991Sdim // Empty last line means that we already have a star as a part of the 293261991Sdim // trailing */. We also need to preserve whitespace, so that */ is 294261991Sdim // correctly indented. 295261991Sdim LastLineNeedsDecoration = false; 296261991Sdim } else if (Decoration.empty()) { 297261991Sdim // For all other lines, set the start column to 0 if they're empty, so 298261991Sdim // we do not insert trailing whitespace anywhere. 299261991Sdim StartOfLineColumn[i] = 0; 300251609Sdim } 301261991Sdim continue; 302251609Sdim } 303288943Sdim 304261991Sdim // The first line already excludes the star. 305261991Sdim // For all other lines, adjust the line to exclude the star and 306261991Sdim // (optionally) the first whitespace. 307288943Sdim unsigned DecorationSize = 308288943Sdim Decoration.startswith(Lines[i]) ? Lines[i].size() : Decoration.size(); 309288943Sdim StartOfLineColumn[i] += DecorationSize; 310288943Sdim Lines[i] = Lines[i].substr(DecorationSize); 311288943Sdim LeadingWhitespace[i] += DecorationSize; 312288943Sdim if (!Decoration.startswith(Lines[i])) 313288943Sdim IndentAtLineBreak = 314288943Sdim std::min<int>(IndentAtLineBreak, std::max(0, StartOfLineColumn[i])); 315251609Sdim } 316261991Sdim IndentAtLineBreak = std::max<unsigned>(IndentAtLineBreak, Decoration.size()); 317261991Sdim DEBUG({ 318261991Sdim llvm::dbgs() << "IndentAtLineBreak " << IndentAtLineBreak << "\n"; 319261991Sdim for (size_t i = 0; i < Lines.size(); ++i) { 320261991Sdim llvm::dbgs() << i << " |" << Lines[i] << "| " << LeadingWhitespace[i] 321261991Sdim << "\n"; 322261991Sdim } 323261991Sdim }); 324261991Sdim} 325251609Sdim 326261991Sdimvoid BreakableBlockComment::adjustWhitespace(unsigned LineIndex, 327261991Sdim int IndentDelta) { 328261991Sdim // When in a preprocessor directive, the trailing backslash in a block comment 329261991Sdim // is not needed, but can serve a purpose of uniformity with necessary escaped 330261991Sdim // newlines outside the comment. In this case we remove it here before 331261991Sdim // trimming the trailing whitespace. The backslash will be re-added later when 332261991Sdim // inserting a line break. 333261991Sdim size_t EndOfPreviousLine = Lines[LineIndex - 1].size(); 334261991Sdim if (InPPDirective && Lines[LineIndex - 1].endswith("\\")) 335261991Sdim --EndOfPreviousLine; 336251609Sdim 337261991Sdim // Calculate the end of the non-whitespace text in the previous line. 338261991Sdim EndOfPreviousLine = 339261991Sdim Lines[LineIndex - 1].find_last_not_of(Blanks, EndOfPreviousLine); 340261991Sdim if (EndOfPreviousLine == StringRef::npos) 341261991Sdim EndOfPreviousLine = 0; 342261991Sdim else 343261991Sdim ++EndOfPreviousLine; 344261991Sdim // Calculate the start of the non-whitespace text in the current line. 345261991Sdim size_t StartOfLine = Lines[LineIndex].find_first_not_of(Blanks); 346261991Sdim if (StartOfLine == StringRef::npos) 347288943Sdim StartOfLine = Lines[LineIndex].rtrim("\r\n").size(); 348261991Sdim 349261991Sdim StringRef Whitespace = Lines[LineIndex].substr(0, StartOfLine); 350261991Sdim // Adjust Lines to only contain relevant text. 351261991Sdim Lines[LineIndex - 1] = Lines[LineIndex - 1].substr(0, EndOfPreviousLine); 352261991Sdim Lines[LineIndex] = Lines[LineIndex].substr(StartOfLine); 353261991Sdim // Adjust LeadingWhitespace to account all whitespace between the lines 354261991Sdim // to the current line. 355261991Sdim LeadingWhitespace[LineIndex] = 356261991Sdim Lines[LineIndex].begin() - Lines[LineIndex - 1].end(); 357261991Sdim 358276479Sdim // Adjust the start column uniformly across all lines. 359276479Sdim StartOfLineColumn[LineIndex] = 360261991Sdim encoding::columnWidthWithTabs(Whitespace, 0, Style.TabWidth, Encoding) + 361276479Sdim IndentDelta; 362251609Sdim} 363251609Sdim 364261991Sdimunsigned BreakableBlockComment::getLineCount() const { return Lines.size(); } 365261991Sdim 366261991Sdimunsigned BreakableBlockComment::getLineLengthAfterSplit( 367261991Sdim unsigned LineIndex, unsigned Offset, StringRef::size_type Length) const { 368261991Sdim unsigned ContentStartColumn = getContentStartColumn(LineIndex, Offset); 369261991Sdim return ContentStartColumn + 370261991Sdim encoding::columnWidthWithTabs(Lines[LineIndex].substr(Offset, Length), 371261991Sdim ContentStartColumn, Style.TabWidth, 372261991Sdim Encoding) + 373261991Sdim // The last line gets a "*/" postfix. 374261991Sdim (LineIndex + 1 == Lines.size() ? 2 : 0); 375261991Sdim} 376261991Sdim 377261991SdimBreakableToken::Split 378261991SdimBreakableBlockComment::getSplit(unsigned LineIndex, unsigned TailOffset, 379261991Sdim unsigned ColumnLimit) const { 380261991Sdim return getCommentSplit(Lines[LineIndex].substr(TailOffset), 381261991Sdim getContentStartColumn(LineIndex, TailOffset), 382261991Sdim ColumnLimit, Style.TabWidth, Encoding); 383261991Sdim} 384261991Sdim 385261991Sdimvoid BreakableBlockComment::insertBreak(unsigned LineIndex, unsigned TailOffset, 386261991Sdim Split Split, 387261991Sdim WhitespaceManager &Whitespaces) { 388261991Sdim StringRef Text = Lines[LineIndex].substr(TailOffset); 389261991Sdim StringRef Prefix = Decoration; 390261991Sdim if (LineIndex + 1 == Lines.size() && 391261991Sdim Text.size() == Split.first + Split.second) { 392261991Sdim // For the last line we need to break before "*/", but not to add "* ". 393261991Sdim Prefix = ""; 394251609Sdim } 395251609Sdim 396261991Sdim unsigned BreakOffsetInToken = 397261991Sdim Text.data() - Tok.TokenText.data() + Split.first; 398261991Sdim unsigned CharsToRemove = Split.second; 399261991Sdim assert(IndentAtLineBreak >= Decoration.size()); 400261991Sdim Whitespaces.replaceWhitespaceInToken( 401261991Sdim Tok, BreakOffsetInToken, CharsToRemove, "", Prefix, InPPDirective, 1, 402261991Sdim IndentLevel, IndentAtLineBreak - Decoration.size()); 403251609Sdim} 404251609Sdim 405261991Sdimvoid BreakableBlockComment::replaceWhitespace(unsigned LineIndex, 406261991Sdim unsigned TailOffset, Split Split, 407261991Sdim WhitespaceManager &Whitespaces) { 408251609Sdim StringRef Text = Lines[LineIndex].substr(TailOffset); 409261991Sdim unsigned BreakOffsetInToken = 410261991Sdim Text.data() - Tok.TokenText.data() + Split.first; 411261991Sdim unsigned CharsToRemove = Split.second; 412261991Sdim Whitespaces.replaceWhitespaceInToken( 413261991Sdim Tok, BreakOffsetInToken, CharsToRemove, "", "", /*InPPDirective=*/false, 414261991Sdim /*Newlines=*/0, /*IndentLevel=*/0, /*Spaces=*/1); 415261991Sdim} 416261991Sdim 417288943Sdimvoid BreakableBlockComment::replaceWhitespaceBefore( 418288943Sdim unsigned LineIndex, WhitespaceManager &Whitespaces) { 419261991Sdim if (LineIndex == 0) 420251609Sdim return; 421261991Sdim StringRef Prefix = Decoration; 422261991Sdim if (Lines[LineIndex].empty()) { 423261991Sdim if (LineIndex + 1 == Lines.size()) { 424261991Sdim if (!LastLineNeedsDecoration) { 425261991Sdim // If the last line was empty, we don't need a prefix, as the */ will 426261991Sdim // line up with the decoration (if it exists). 427261991Sdim Prefix = ""; 428261991Sdim } 429261991Sdim } else if (!Decoration.empty()) { 430261991Sdim // For other empty lines, if we do have a decoration, adapt it to not 431261991Sdim // contain a trailing whitespace. 432261991Sdim Prefix = Prefix.substr(0, 1); 433261991Sdim } 434261991Sdim } else { 435261991Sdim if (StartOfLineColumn[LineIndex] == 1) { 436261991Sdim // This line starts immediately after the decorating *. 437261991Sdim Prefix = Prefix.substr(0, 1); 438261991Sdim } 439261991Sdim } 440251609Sdim 441261991Sdim unsigned WhitespaceOffsetInToken = Lines[LineIndex].data() - 442261991Sdim Tok.TokenText.data() - 443261991Sdim LeadingWhitespace[LineIndex]; 444261991Sdim Whitespaces.replaceWhitespaceInToken( 445261991Sdim Tok, WhitespaceOffsetInToken, LeadingWhitespace[LineIndex], "", Prefix, 446261991Sdim InPPDirective, 1, IndentLevel, 447261991Sdim StartOfLineColumn[LineIndex] - Prefix.size()); 448251609Sdim} 449251609Sdim 450261991Sdimunsigned 451261991SdimBreakableBlockComment::getContentStartColumn(unsigned LineIndex, 452261991Sdim unsigned TailOffset) const { 453261991Sdim // If we break, we always break at the predefined indent. 454261991Sdim if (TailOffset != 0) 455261991Sdim return IndentAtLineBreak; 456276479Sdim return std::max(0, StartOfLineColumn[LineIndex]); 457251609Sdim} 458251609Sdim 459251609Sdim} // namespace format 460251609Sdim} // namespace clang 461