blob: 6801e7e8c900100e0c86e795120df3a462609073 [file] [log] [blame]
Alexander Kornienko70ce7882013-04-15 14:28:00 +00001//===--- WhitespaceManager.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 This file implements WhitespaceManager class.
12///
13//===----------------------------------------------------------------------===//
14
15#include "WhitespaceManager.h"
16#include "llvm/ADT/STLExtras.h"
17
18namespace clang {
19namespace format {
20
21void WhitespaceManager::replaceWhitespace(const AnnotatedToken &Tok,
22 unsigned NewLines, unsigned Spaces,
23 unsigned WhitespaceStartColumn) {
24 // 2+ newlines mean an empty line separating logic scopes.
25 if (NewLines >= 2)
26 alignComments();
27
28 SourceLocation TokenLoc = Tok.FormatTok.Tok.getLocation();
29 bool LineExceedsColumnLimit =
30 Spaces + WhitespaceStartColumn + Tok.FormatTok.TokenLength >
31 Style.ColumnLimit;
32
33 // Align line comments if they are trailing or if they continue other
34 // trailing comments.
35 if (Tok.isTrailingComment()) {
36 // Remove the comment's trailing whitespace.
37 if (Tok.FormatTok.Tok.getLength() != Tok.FormatTok.TokenLength)
38 Replaces.insert(tooling::Replacement(
39 SourceMgr, TokenLoc.getLocWithOffset(Tok.FormatTok.TokenLength),
40 Tok.FormatTok.Tok.getLength() - Tok.FormatTok.TokenLength, ""));
41
42 // Align comment with other comments.
43 if ((Tok.Parent != NULL || !Comments.empty()) && !LineExceedsColumnLimit) {
44 StoredComment Comment;
45 Comment.Tok = Tok.FormatTok;
46 Comment.Spaces = Spaces;
47 Comment.NewLines = NewLines;
48 Comment.MinColumn =
49 NewLines > 0 ? Spaces : WhitespaceStartColumn + Spaces;
50 Comment.MaxColumn = Style.ColumnLimit - Tok.FormatTok.TokenLength;
51 Comment.Untouchable = false;
52 Comments.push_back(Comment);
53 return;
54 }
55 }
56
57 // If this line does not have a trailing comment, align the stored comments.
58 if (Tok.Children.empty() && !Tok.isTrailingComment())
59 alignComments();
60
61 if (Tok.Type == TT_LineComment && LineExceedsColumnLimit) {
62 StringRef Line(SourceMgr.getCharacterData(TokenLoc),
63 Tok.FormatTok.TokenLength);
64 int StartColumn = Spaces + (NewLines == 0 ? WhitespaceStartColumn : 0);
65 StringRef Prefix = getLineCommentPrefix(Line);
66 std::string NewPrefix = std::string(StartColumn, ' ') + Prefix.str();
67 splitLineComment(Tok.FormatTok, Line.substr(Prefix.size()),
68 StartColumn + Prefix.size(), NewPrefix);
69 }
70
71 storeReplacement(Tok.FormatTok, getNewLineText(NewLines, Spaces));
72}
73
74void WhitespaceManager::replacePPWhitespace(const AnnotatedToken &Tok,
75 unsigned NewLines, unsigned Spaces,
76 unsigned WhitespaceStartColumn) {
77 storeReplacement(Tok.FormatTok,
78 getNewLineText(NewLines, Spaces, WhitespaceStartColumn));
79}
80
81void WhitespaceManager::breakToken(const FormatToken &Tok, unsigned Offset,
82 unsigned ReplaceChars, StringRef Prefix,
83 StringRef Postfix, bool InPPDirective,
84 unsigned Spaces,
85 unsigned WhitespaceStartColumn) {
86 std::string NewLineText;
87 if (!InPPDirective)
88 NewLineText = getNewLineText(1, Spaces);
89 else
90 NewLineText = getNewLineText(1, Spaces, WhitespaceStartColumn);
91 std::string ReplacementText = (Prefix + NewLineText + Postfix).str();
92 SourceLocation Location = Tok.Tok.getLocation().getLocWithOffset(Offset);
93 Replaces.insert(
94 tooling::Replacement(SourceMgr, Location, ReplaceChars, ReplacementText));
95}
96
97const tooling::Replacements &WhitespaceManager::generateReplacements() {
98 alignComments();
99 return Replaces;
100}
101
102void WhitespaceManager::addReplacement(const SourceLocation &SourceLoc,
103 unsigned ReplaceChars, StringRef Text) {
104 Replaces.insert(
105 tooling::Replacement(SourceMgr, SourceLoc, ReplaceChars, Text));
106}
107
108void WhitespaceManager::addUntouchableComment(unsigned Column) {
109 StoredComment Comment;
110 Comment.MinColumn = Column;
111 Comment.MaxColumn = Column;
112 Comment.Untouchable = true;
113 Comments.push_back(Comment);
114}
115
116StringRef WhitespaceManager::getLineCommentPrefix(StringRef Comment) {
117 const char *KnownPrefixes[] = { "/// ", "///", "// ", "//" };
118 for (size_t i = 0; i < llvm::array_lengthof(KnownPrefixes); ++i)
119 if (Comment.startswith(KnownPrefixes[i]))
120 return KnownPrefixes[i];
121 return "";
122}
123
124void
125WhitespaceManager::splitLineComment(const FormatToken &Tok, StringRef Line,
126 size_t StartColumn, StringRef LinePrefix,
127 const char *WhiteSpaceChars /*= " "*/) {
128 const char *TokenStart = SourceMgr.getCharacterData(Tok.Tok.getLocation());
129
130 StringRef TrimmedLine = Line.rtrim();
131 // Don't touch leading whitespace.
132 Line = TrimmedLine.ltrim();
133 StartColumn += TrimmedLine.size() - Line.size();
134
135 while (Line.size() + StartColumn > Style.ColumnLimit) {
136 // Try to break at the last whitespace before the column limit.
137 size_t SpacePos =
138 Line.find_last_of(WhiteSpaceChars, Style.ColumnLimit - StartColumn + 1);
139 if (SpacePos == StringRef::npos) {
140 // Try to find any whitespace in the line.
141 SpacePos = Line.find_first_of(WhiteSpaceChars);
142 if (SpacePos == StringRef::npos) // No whitespace found, give up.
143 break;
144 }
145
146 StringRef NextCut = Line.substr(0, SpacePos).rtrim();
147 StringRef RemainingLine = Line.substr(SpacePos).ltrim();
148 if (RemainingLine.empty())
149 break;
150
151 Line = RemainingLine;
152
153 size_t ReplaceChars = Line.begin() - NextCut.end();
154 breakToken(Tok, NextCut.end() - TokenStart, ReplaceChars, "", LinePrefix,
155 false, 0, 0);
156 StartColumn = LinePrefix.size();
157 }
158}
159
160std::string WhitespaceManager::getNewLineText(unsigned NewLines,
161 unsigned Spaces) {
162 return std::string(NewLines, '\n') + std::string(Spaces, ' ');
163}
164
165std::string WhitespaceManager::getNewLineText(unsigned NewLines,
166 unsigned Spaces,
167 unsigned WhitespaceStartColumn) {
168 std::string NewLineText;
169 if (NewLines > 0) {
170 unsigned Offset =
171 std::min<int>(Style.ColumnLimit - 1, WhitespaceStartColumn);
172 for (unsigned i = 0; i < NewLines; ++i) {
173 NewLineText += std::string(Style.ColumnLimit - Offset - 1, ' ');
174 NewLineText += "\\\n";
175 Offset = 0;
176 }
177 }
178 return NewLineText + std::string(Spaces, ' ');
179}
180
181void WhitespaceManager::alignComments() {
182 unsigned MinColumn = 0;
183 unsigned MaxColumn = UINT_MAX;
184 comment_iterator Start = Comments.begin();
185 for (comment_iterator I = Start, E = Comments.end(); I != E; ++I) {
186 if (I->MinColumn > MaxColumn || I->MaxColumn < MinColumn) {
187 alignComments(Start, I, MinColumn);
188 MinColumn = I->MinColumn;
189 MaxColumn = I->MaxColumn;
190 Start = I;
191 } else {
192 MinColumn = std::max(MinColumn, I->MinColumn);
193 MaxColumn = std::min(MaxColumn, I->MaxColumn);
194 }
195 }
196 alignComments(Start, Comments.end(), MinColumn);
197 Comments.clear();
198}
199
200void WhitespaceManager::alignComments(comment_iterator I, comment_iterator E,
201 unsigned Column) {
202 while (I != E) {
203 if (!I->Untouchable) {
204 unsigned Spaces = I->Spaces + Column - I->MinColumn;
205 storeReplacement(I->Tok, getNewLineText(I->NewLines, Spaces));
206 }
207 ++I;
208 }
209}
210
211void WhitespaceManager::storeReplacement(const FormatToken &Tok,
212 const std::string Text) {
213 // Don't create a replacement, if it does not change anything.
214 if (StringRef(SourceMgr.getCharacterData(Tok.WhiteSpaceStart),
215 Tok.WhiteSpaceLength) == Text)
216 return;
217
218 Replaces.insert(tooling::Replacement(SourceMgr, Tok.WhiteSpaceStart,
219 Tok.WhiteSpaceLength, Text));
220}
221
222} // namespace format
223} // namespace clang