blob: 4c393ed4397633251c3e18ab336cd6a01be5be01 [file] [log] [blame]
Alexander Kornienkocb45bc12013-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
Manuel Klimek4fe43002013-05-22 12:51:29 +000021bool
22WhitespaceManager::Change::IsBeforeInFile::operator()(const Change &C1,
23 const Change &C2) const {
24 return SourceMgr.isBeforeInTranslationUnit(
25 C1.OriginalWhitespaceRange.getBegin(),
26 C2.OriginalWhitespaceRange.getBegin());
27}
Daniel Jasper6fe2f002013-04-25 08:56:26 +000028
Manuel Klimek4fe43002013-05-22 12:51:29 +000029WhitespaceManager::Change::Change(
30 bool CreateReplacement, const SourceRange &OriginalWhitespaceRange,
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +000031 unsigned IndentLevel, unsigned Spaces, unsigned StartOfTokenColumn,
32 unsigned NewlinesBefore, StringRef PreviousLinePostfix,
33 StringRef CurrentLinePrefix, tok::TokenKind Kind, bool ContinuesPPDirective)
Manuel Klimek4fe43002013-05-22 12:51:29 +000034 : CreateReplacement(CreateReplacement),
35 OriginalWhitespaceRange(OriginalWhitespaceRange),
36 StartOfTokenColumn(StartOfTokenColumn), NewlinesBefore(NewlinesBefore),
37 PreviousLinePostfix(PreviousLinePostfix),
38 CurrentLinePrefix(CurrentLinePrefix), Kind(Kind),
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +000039 ContinuesPPDirective(ContinuesPPDirective), IndentLevel(IndentLevel),
40 Spaces(Spaces) {}
Manuel Klimek4fe43002013-05-22 12:51:29 +000041
Manuel Klimek71814b42013-10-11 21:25:45 +000042void WhitespaceManager::reset() {
43 Changes.clear();
44 Replaces.clear();
45}
46
47void WhitespaceManager::replaceWhitespace(FormatToken &Tok, unsigned Newlines,
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +000048 unsigned IndentLevel, unsigned Spaces,
Manuel Klimek4fe43002013-05-22 12:51:29 +000049 unsigned StartOfTokenColumn,
50 bool InPPDirective) {
Manuel Klimek71814b42013-10-11 21:25:45 +000051 if (Tok.Finalized)
52 return;
53 Tok.Decision = (Newlines > 0) ? FD_Break : FD_Continue;
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +000054 Changes.push_back(Change(true, Tok.WhitespaceRange, IndentLevel, Spaces,
Daniel Jasper3ac9b9e2013-07-08 14:34:09 +000055 StartOfTokenColumn, Newlines, "", "",
56 Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst));
Alexander Kornienkocb45bc12013-04-15 14:28:00 +000057}
58
Manuel Klimek4fe43002013-05-22 12:51:29 +000059void WhitespaceManager::addUntouchableToken(const FormatToken &Tok,
60 bool InPPDirective) {
Manuel Klimek71814b42013-10-11 21:25:45 +000061 if (Tok.Finalized)
62 return;
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +000063 Changes.push_back(Change(false, Tok.WhitespaceRange, /*IndentLevel=*/0,
64 /*Spaces=*/0, Tok.OriginalColumn, Tok.NewlinesBefore,
65 "", "", Tok.Tok.getKind(),
66 InPPDirective && !Tok.IsFirst));
Alexander Kornienkocb45bc12013-04-15 14:28:00 +000067}
68
Alexander Kornienko555efc32013-06-11 16:01:49 +000069void WhitespaceManager::replaceWhitespaceInToken(
70 const FormatToken &Tok, unsigned Offset, unsigned ReplaceChars,
71 StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective,
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +000072 unsigned Newlines, unsigned IndentLevel, unsigned Spaces) {
Manuel Klimek71814b42013-10-11 21:25:45 +000073 if (Tok.Finalized)
74 return;
Manuel Klimek4fe43002013-05-22 12:51:29 +000075 Changes.push_back(Change(
76 true, SourceRange(Tok.getStartOfNonWhitespace().getLocWithOffset(Offset),
77 Tok.getStartOfNonWhitespace().getLocWithOffset(
78 Offset + ReplaceChars)),
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +000079 IndentLevel, Spaces, Spaces, Newlines, PreviousPostfix, CurrentPrefix,
Alexander Kornienko4d26b6e2013-06-17 12:59:44 +000080 // If we don't add a newline this change doesn't start a comment. Thus,
81 // when we align line comments, we don't need to treat this change as one.
82 // FIXME: We still need to take this change in account to properly
83 // calculate the new length of the comment and to calculate the changes
84 // for which to do the alignment when aligning comments.
85 Tok.Type == TT_LineComment && Newlines > 0 ? tok::comment : tok::unknown,
86 InPPDirective && !Tok.IsFirst));
Alexander Kornienkocb45bc12013-04-15 14:28:00 +000087}
88
Manuel Klimek4fe43002013-05-22 12:51:29 +000089const tooling::Replacements &WhitespaceManager::generateReplacements() {
90 if (Changes.empty())
91 return Replaces;
92
93 std::sort(Changes.begin(), Changes.end(), Change::IsBeforeInFile(SourceMgr));
94 calculateLineBreakInformation();
95 alignTrailingComments();
96 alignEscapedNewlines();
97 generateChanges();
98
99 return Replaces;
100}
101
102void WhitespaceManager::calculateLineBreakInformation() {
103 Changes[0].PreviousEndOfTokenColumn = 0;
104 for (unsigned i = 1, e = Changes.size(); i != e; ++i) {
105 unsigned OriginalWhitespaceStart =
106 SourceMgr.getFileOffset(Changes[i].OriginalWhitespaceRange.getBegin());
107 unsigned PreviousOriginalWhitespaceEnd = SourceMgr.getFileOffset(
108 Changes[i - 1].OriginalWhitespaceRange.getEnd());
Daniel Jasper3ac9b9e2013-07-08 14:34:09 +0000109 Changes[i - 1].TokenLength = OriginalWhitespaceStart -
110 PreviousOriginalWhitespaceEnd +
111 Changes[i].PreviousLinePostfix.size() +
112 Changes[i - 1].CurrentLinePrefix.size();
Manuel Klimek4fe43002013-05-22 12:51:29 +0000113
114 Changes[i].PreviousEndOfTokenColumn =
115 Changes[i - 1].StartOfTokenColumn + Changes[i - 1].TokenLength;
116
117 Changes[i - 1].IsTrailingComment =
118 (Changes[i].NewlinesBefore > 0 || Changes[i].Kind == tok::eof) &&
119 Changes[i - 1].Kind == tok::comment;
120 }
Manuel Klimek05c67892013-05-22 14:01:08 +0000121 // FIXME: The last token is currently not always an eof token; in those
122 // cases, setting TokenLength of the last token to 0 is wrong.
123 Changes.back().TokenLength = 0;
Manuel Klimek4fe43002013-05-22 12:51:29 +0000124 Changes.back().IsTrailingComment = Changes.back().Kind == tok::comment;
125}
126
127void WhitespaceManager::alignTrailingComments() {
128 unsigned MinColumn = 0;
129 unsigned MaxColumn = UINT_MAX;
130 unsigned StartOfSequence = 0;
131 bool BreakBeforeNext = false;
132 unsigned Newlines = 0;
133 for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
134 unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
135 // FIXME: Correctly handle ChangeMaxColumn in PP directives.
136 unsigned ChangeMaxColumn = Style.ColumnLimit - Changes[i].TokenLength;
137 Newlines += Changes[i].NewlinesBefore;
138 if (Changes[i].IsTrailingComment) {
Daniel Jasper552f4a72013-07-31 23:55:15 +0000139 // If this comment follows an } in column 0, it probably documents the
140 // closing of a namespace and we don't want to align it.
Daniel Jasper251b3c92013-07-01 11:22:57 +0000141 bool FollowsRBraceInColumn0 = i > 0 && Changes[i].NewlinesBefore == 0 &&
142 Changes[i - 1].Kind == tok::r_brace &&
143 Changes[i - 1].StartOfTokenColumn == 0;
Daniel Jasper0e93cdb2013-11-08 23:31:14 +0000144 bool WasAlignedWithStartOfNextLine = false;
145 if (Changes[i].NewlinesBefore == 1) { // A comment on its own line.
146 for (unsigned j = i + 1; j != e; ++j) {
147 if (Changes[j].Kind != tok::comment) { // Skip over comments.
148 // The start of the next token was previously aligned with the
149 // start of this comment.
150 WasAlignedWithStartOfNextLine =
151 (SourceMgr.getSpellingColumnNumber(
152 Changes[i].OriginalWhitespaceRange.getEnd()) ==
153 SourceMgr.getSpellingColumnNumber(
154 Changes[j].OriginalWhitespaceRange.getEnd()));
155 break;
156 }
157 }
158 }
Daniel Jasper552f4a72013-07-31 23:55:15 +0000159 if (!Style.AlignTrailingComments || FollowsRBraceInColumn0) {
Daniel Jasper251b3c92013-07-01 11:22:57 +0000160 alignTrailingComments(StartOfSequence, i, MinColumn);
161 MinColumn = ChangeMinColumn;
162 MaxColumn = ChangeMinColumn;
163 StartOfSequence = i;
164 } else if (BreakBeforeNext || Newlines > 1 ||
165 (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) ||
166 // Break the comment sequence if the previous line did not end
167 // in a trailing comment.
168 (Changes[i].NewlinesBefore == 1 && i > 0 &&
169 !Changes[i - 1].IsTrailingComment) ||
170 WasAlignedWithStartOfNextLine) {
Manuel Klimek4fe43002013-05-22 12:51:29 +0000171 alignTrailingComments(StartOfSequence, i, MinColumn);
172 MinColumn = ChangeMinColumn;
173 MaxColumn = ChangeMaxColumn;
174 StartOfSequence = i;
175 } else {
176 MinColumn = std::max(MinColumn, ChangeMinColumn);
177 MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
178 }
Manuel Klimek75ef31f2013-05-23 20:46:07 +0000179 BreakBeforeNext =
180 (i == 0) || (Changes[i].NewlinesBefore > 1) ||
181 // Never start a sequence with a comment at the beginning of
182 // the line.
183 (Changes[i].NewlinesBefore == 1 && StartOfSequence == i);
Manuel Klimek4fe43002013-05-22 12:51:29 +0000184 Newlines = 0;
185 }
186 }
187 alignTrailingComments(StartOfSequence, Changes.size(), MinColumn);
188}
189
190void WhitespaceManager::alignTrailingComments(unsigned Start, unsigned End,
191 unsigned Column) {
192 for (unsigned i = Start; i != End; ++i) {
193 if (Changes[i].IsTrailingComment) {
194 assert(Column >= Changes[i].StartOfTokenColumn);
195 Changes[i].Spaces += Column - Changes[i].StartOfTokenColumn;
Daniel Jasper03b1bc72014-03-28 15:06:01 +0000196 if (i + 1 != End) {
197 Changes[i + 1].PreviousEndOfTokenColumn +=
198 Column - Changes[i].StartOfTokenColumn;
199 }
Manuel Klimek4fe43002013-05-22 12:51:29 +0000200 Changes[i].StartOfTokenColumn = Column;
201 }
202 }
203}
204
205void WhitespaceManager::alignEscapedNewlines() {
Daniel Jaspera49393f2013-08-28 09:07:32 +0000206 unsigned MaxEndOfLine =
207 Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit;
Manuel Klimek4fe43002013-05-22 12:51:29 +0000208 unsigned StartOfMacro = 0;
209 for (unsigned i = 1, e = Changes.size(); i < e; ++i) {
210 Change &C = Changes[i];
211 if (C.NewlinesBefore > 0) {
212 if (C.ContinuesPPDirective) {
Daniel Jaspera49393f2013-08-28 09:07:32 +0000213 MaxEndOfLine = std::max(C.PreviousEndOfTokenColumn + 2, MaxEndOfLine);
Manuel Klimek4fe43002013-05-22 12:51:29 +0000214 } else {
215 alignEscapedNewlines(StartOfMacro + 1, i, MaxEndOfLine);
Daniel Jaspera49393f2013-08-28 09:07:32 +0000216 MaxEndOfLine = Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit;
Manuel Klimek4fe43002013-05-22 12:51:29 +0000217 StartOfMacro = i;
218 }
219 }
220 }
221 alignEscapedNewlines(StartOfMacro + 1, Changes.size(), MaxEndOfLine);
222}
223
224void WhitespaceManager::alignEscapedNewlines(unsigned Start, unsigned End,
225 unsigned Column) {
226 for (unsigned i = Start; i < End; ++i) {
227 Change &C = Changes[i];
228 if (C.NewlinesBefore > 0) {
229 assert(C.ContinuesPPDirective);
230 if (C.PreviousEndOfTokenColumn + 1 > Column)
231 C.EscapedNewlineColumn = 0;
232 else
233 C.EscapedNewlineColumn = Column;
234 }
235 }
236}
237
238void WhitespaceManager::generateChanges() {
239 for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
240 const Change &C = Changes[i];
241 if (C.CreateReplacement) {
Alexander Kornienko9e649af2013-09-11 12:25:57 +0000242 std::string ReplacementText = C.PreviousLinePostfix;
243 if (C.ContinuesPPDirective)
244 appendNewlineText(ReplacementText, C.NewlinesBefore,
245 C.PreviousEndOfTokenColumn, C.EscapedNewlineColumn);
246 else
247 appendNewlineText(ReplacementText, C.NewlinesBefore);
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +0000248 appendIndentText(ReplacementText, C.IndentLevel, C.Spaces,
249 C.StartOfTokenColumn - C.Spaces);
Alexander Kornienko9e649af2013-09-11 12:25:57 +0000250 ReplacementText.append(C.CurrentLinePrefix);
Manuel Klimek4fe43002013-05-22 12:51:29 +0000251 storeReplacement(C.OriginalWhitespaceRange, ReplacementText);
252 }
253 }
254}
255
256void WhitespaceManager::storeReplacement(const SourceRange &Range,
257 StringRef Text) {
258 unsigned WhitespaceLength = SourceMgr.getFileOffset(Range.getEnd()) -
259 SourceMgr.getFileOffset(Range.getBegin());
260 // Don't create a replacement, if it does not change anything.
261 if (StringRef(SourceMgr.getCharacterData(Range.getBegin()),
Daniel Jasper3ac9b9e2013-07-08 14:34:09 +0000262 WhitespaceLength) == Text)
Manuel Klimek4fe43002013-05-22 12:51:29 +0000263 return;
264 Replaces.insert(tooling::Replacement(
265 SourceMgr, CharSourceRange::getCharRange(Range), Text));
Alexander Kornienkocb45bc12013-04-15 14:28:00 +0000266}
267
Alexander Kornienko9e649af2013-09-11 12:25:57 +0000268void WhitespaceManager::appendNewlineText(std::string &Text,
269 unsigned Newlines) {
270 for (unsigned i = 0; i < Newlines; ++i)
271 Text.append(UseCRLF ? "\r\n" : "\n");
Alexander Kornienkocb45bc12013-04-15 14:28:00 +0000272}
273
Alexander Kornienko9e649af2013-09-11 12:25:57 +0000274void WhitespaceManager::appendNewlineText(std::string &Text, unsigned Newlines,
275 unsigned PreviousEndOfTokenColumn,
276 unsigned EscapedNewlineColumn) {
Alexander Kornienko555efc32013-06-11 16:01:49 +0000277 if (Newlines > 0) {
Alexander Kornienkocb45bc12013-04-15 14:28:00 +0000278 unsigned Offset =
Manuel Klimek4fe43002013-05-22 12:51:29 +0000279 std::min<int>(EscapedNewlineColumn - 1, PreviousEndOfTokenColumn);
Alexander Kornienko555efc32013-06-11 16:01:49 +0000280 for (unsigned i = 0; i < Newlines; ++i) {
Alexander Kornienko9e649af2013-09-11 12:25:57 +0000281 Text.append(std::string(EscapedNewlineColumn - Offset - 1, ' '));
282 Text.append(UseCRLF ? "\\\r\n" : "\\\n");
Alexander Kornienkocb45bc12013-04-15 14:28:00 +0000283 Offset = 0;
284 }
285 }
Manuel Klimekb9eae4c2013-05-13 09:22:11 +0000286}
287
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +0000288void WhitespaceManager::appendIndentText(std::string &Text,
289 unsigned IndentLevel, unsigned Spaces,
Alexander Kornienkodb4c21f2013-09-27 09:45:40 +0000290 unsigned WhitespaceStartColumn) {
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +0000291 switch (Style.UseTab) {
292 case FormatStyle::UT_Never:
Alexander Kornienko9e649af2013-09-11 12:25:57 +0000293 Text.append(std::string(Spaces, ' '));
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +0000294 break;
295 case FormatStyle::UT_Always: {
Alexander Kornienkodb4c21f2013-09-27 09:45:40 +0000296 unsigned FirstTabWidth =
297 Style.TabWidth - WhitespaceStartColumn % Style.TabWidth;
298 // Indent with tabs only when there's at least one full tab.
299 if (FirstTabWidth + Style.TabWidth <= Spaces) {
300 Spaces -= FirstTabWidth;
301 Text.append("\t");
302 }
Alexander Kornienko9e649af2013-09-11 12:25:57 +0000303 Text.append(std::string(Spaces / Style.TabWidth, '\t'));
304 Text.append(std::string(Spaces % Style.TabWidth, ' '));
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +0000305 break;
306 }
307 case FormatStyle::UT_ForIndentation:
308 if (WhitespaceStartColumn == 0) {
309 unsigned Indentation = IndentLevel * Style.IndentWidth;
Alexander Kornienko45dc1b22013-09-27 16:40:11 +0000310 // This happens, e.g. when a line in a block comment is indented less than
311 // the first one.
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +0000312 if (Indentation > Spaces)
313 Indentation = Spaces;
314 unsigned Tabs = Indentation / Style.TabWidth;
315 Text.append(std::string(Tabs, '\t'));
316 Spaces -= Tabs * Style.TabWidth;
317 }
318 Text.append(std::string(Spaces, ' '));
319 break;
Alexander Kornienko9e649af2013-09-11 12:25:57 +0000320 }
Alexander Kornienkocb45bc12013-04-15 14:28:00 +0000321}
322
Alexander Kornienkocb45bc12013-04-15 14:28:00 +0000323} // namespace format
324} // namespace clang