BreakableToken.cpp revision 70ce7881fc30a39b795b2873f008e7eca72ba669
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 <algorithm> 18 19namespace clang { 20namespace format { 21 22BreakableBlockComment::BreakableBlockComment(const SourceManager &SourceMgr, 23 const AnnotatedToken &Token, 24 unsigned StartColumn) 25 : Tok(Token.FormatTok), StartColumn(StartColumn) { 26 27 SourceLocation TokenLoc = Tok.Tok.getLocation(); 28 TokenText = StringRef(SourceMgr.getCharacterData(TokenLoc), Tok.TokenLength); 29 assert(TokenText.startswith("/*") && TokenText.endswith("*/")); 30 31 OriginalStartColumn = SourceMgr.getSpellingColumnNumber(TokenLoc) - 1; 32 33 TokenText.substr(2, TokenText.size() - 4).split(Lines, "\n"); 34 35 NeedsStar = true; 36 CommonPrefixLength = UINT_MAX; 37 if (Lines.size() == 1) { 38 if (Token.Parent == 0) { 39 // Standalone block comments will be aligned and prefixed with *s. 40 CommonPrefixLength = OriginalStartColumn + 1; 41 } else { 42 // Trailing comments can start on arbitrary column, and available 43 // horizontal space can be too small to align consecutive lines with 44 // the first one. We could, probably, align them to current 45 // indentation level, but now we just wrap them without indentation 46 // and stars. 47 CommonPrefixLength = 0; 48 NeedsStar = false; 49 } 50 } else { 51 for (size_t i = 1; i < Lines.size(); ++i) { 52 size_t FirstNonWhitespace = Lines[i].find_first_not_of(" "); 53 if (FirstNonWhitespace != StringRef::npos) { 54 NeedsStar = NeedsStar && (Lines[i][FirstNonWhitespace] == '*'); 55 CommonPrefixLength = 56 std::min<unsigned>(CommonPrefixLength, FirstNonWhitespace); 57 } 58 } 59 } 60 if (CommonPrefixLength == UINT_MAX) 61 CommonPrefixLength = 0; 62 63 IndentAtLineBreak = 64 std::max<int>(StartColumn - OriginalStartColumn + CommonPrefixLength, 0); 65} 66 67void BreakableBlockComment::alignLines(WhitespaceManager &Whitespaces) { 68 SourceLocation TokenLoc = Tok.Tok.getLocation(); 69 int IndentDelta = StartColumn - OriginalStartColumn; 70 if (IndentDelta > 0) { 71 std::string WhiteSpace(IndentDelta, ' '); 72 for (size_t i = 1; i < Lines.size(); ++i) { 73 Whitespaces.addReplacement( 74 TokenLoc.getLocWithOffset(Lines[i].data() - TokenText.data()), 0, 75 WhiteSpace); 76 } 77 } else if (IndentDelta < 0) { 78 std::string WhiteSpace(-IndentDelta, ' '); 79 // Check that the line is indented enough. 80 for (size_t i = 1; i < Lines.size(); ++i) { 81 if (!Lines[i].startswith(WhiteSpace)) 82 return; 83 } 84 for (size_t i = 1; i < Lines.size(); ++i) { 85 Whitespaces.addReplacement( 86 TokenLoc.getLocWithOffset(Lines[i].data() - TokenText.data()), 87 -IndentDelta, ""); 88 } 89 } 90 91 for (unsigned i = 1; i < Lines.size(); ++i) 92 Lines[i] = Lines[i].substr(CommonPrefixLength + (NeedsStar ? 2 : 0)); 93} 94 95BreakableToken::Split BreakableBlockComment::getSplit(unsigned LineIndex, 96 unsigned TailOffset, 97 unsigned ColumnLimit) { 98 StringRef Text = getLine(LineIndex).substr(TailOffset); 99 unsigned DecorationLength = 100 (TailOffset == 0 && LineIndex == 0) ? StartColumn + 2 : getPrefixLength(); 101 if (ColumnLimit <= DecorationLength + 1) 102 return Split(StringRef::npos, 0); 103 104 unsigned MaxSplit = ColumnLimit - DecorationLength + 1; 105 StringRef::size_type SpaceOffset = Text.rfind(' ', MaxSplit); 106 if (SpaceOffset == StringRef::npos || 107 Text.find_last_not_of(' ', SpaceOffset) == StringRef::npos) { 108 SpaceOffset = Text.find(' ', MaxSplit); 109 } 110 if (SpaceOffset != StringRef::npos && SpaceOffset != 0) { 111 StringRef BeforeCut = Text.substr(0, SpaceOffset).rtrim(); 112 StringRef AfterCut = Text.substr(SpaceOffset).ltrim(); 113 return BreakableToken::Split(BeforeCut.size(), 114 AfterCut.begin() - BeforeCut.end()); 115 } 116 return BreakableToken::Split(StringRef::npos, 0); 117} 118 119void BreakableBlockComment::insertBreak(unsigned LineIndex, unsigned TailOffset, 120 Split Split, bool InPPDirective, 121 WhitespaceManager &Whitespaces) { 122 StringRef Text = getLine(LineIndex).substr(TailOffset); 123 StringRef AdditionalPrefix = NeedsStar ? "* " : ""; 124 if (Text.size() == Split.first + Split.second) { 125 // For all but the last line handle trailing space separately. 126 if (LineIndex < Lines.size() - 1) 127 return; 128 // For the last line we need to break before "*/", but not to add "* ". 129 AdditionalPrefix = ""; 130 } 131 132 unsigned WhitespaceStartColumn = 133 Split.first + 134 (LineIndex == 0 && TailOffset == 0 ? StartColumn + 2 : getPrefixLength()); 135 unsigned BreakOffset = Text.data() - TokenText.data() + Split.first; 136 unsigned CharsToRemove = Split.second; 137 Whitespaces.breakToken(Tok, BreakOffset, CharsToRemove, "", AdditionalPrefix, 138 InPPDirective, IndentAtLineBreak, 139 WhitespaceStartColumn); 140} 141 142void BreakableBlockComment::trimLine(unsigned LineIndex, unsigned TailOffset, 143 unsigned InPPDirective, 144 WhitespaceManager &Whitespaces) { 145 if (LineIndex == Lines.size() - 1) 146 return; 147 StringRef Text = Lines[LineIndex].substr(TailOffset); 148 if (!Text.endswith(" ") && !InPPDirective) 149 return; 150 151 StringRef TrimmedLine = Text.rtrim(); 152 unsigned WhitespaceStartColumn = 153 getLineLengthAfterSplit(LineIndex, TailOffset); 154 unsigned BreakOffset = TrimmedLine.end() - TokenText.data(); 155 unsigned CharsToRemove = Text.size() - TrimmedLine.size() + 1; 156 Whitespaces.breakToken(Tok, BreakOffset, CharsToRemove, "", "", InPPDirective, 157 0, WhitespaceStartColumn); 158} 159 160} // namespace format 161} // namespace clang 162