]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/llvm/tools/clang/lib/Format/BreakableToken.cpp
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / llvm / tools / clang / lib / Format / BreakableToken.cpp
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