1 //===--- BreakableToken.cpp - Format C++ code -----------------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
11 /// \brief Contains implementation of BreakableToken class and classes derived
14 //===----------------------------------------------------------------------===//
16 #include "BreakableToken.h"
17 #include "llvm/ADT/STLExtras.h"
23 BreakableToken::Split BreakableComment::getSplit(unsigned LineIndex,
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);
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);
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());
43 return BreakableToken::Split(StringRef::npos, 0);
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)
55 // For the last line we need to break before "*/", but not to add "* ".
56 AdditionalPrefix = "";
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);
68 BreakableBlockComment::BreakableBlockComment(const SourceManager &SourceMgr,
69 const AnnotatedToken &Token,
71 : BreakableComment(SourceMgr, Token.FormatTok, StartColumn + 2) {
72 assert(TokenText.startswith("/*") && TokenText.endswith("*/"));
75 SourceMgr.getSpellingColumnNumber(Tok.getStartOfNonWhitespace()) - 1;
77 TokenText.substr(2, TokenText.size() - 4).split(Lines, "\n");
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;
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
91 CommonPrefixLength = 0;
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] == '*');
100 std::min<unsigned>(CommonPrefixLength, FirstNonWhitespace);
104 if (CommonPrefixLength == UINT_MAX)
105 CommonPrefixLength = 0;
107 Decoration = NeedsStar ? "* " : "";
110 std::max<int>(StartColumn - OriginalStartColumn + CommonPrefixLength, 0);
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,
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))
130 for (size_t i = 1; i < Lines.size(); ++i) {
131 Whitespaces.addReplacement(
132 TokenLoc.getLocWithOffset(Lines[i].data() - TokenText.data()),
137 for (unsigned i = 1; i < Lines.size(); ++i)
138 Lines[i] = Lines[i].substr(CommonPrefixLength + Decoration.size());
141 void BreakableBlockComment::trimLine(unsigned LineIndex, unsigned TailOffset,
142 unsigned InPPDirective,
143 WhitespaceManager &Whitespaces) {
144 if (LineIndex == Lines.size() - 1)
146 StringRef Text = Lines[LineIndex].substr(TailOffset);
147 if (!Text.endswith(" ") && !InPPDirective)
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);
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.
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];
178 } // namespace format