blob: c24ccdfee4d5d7a7d5e16f96af6998bab7fd8bd5 [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
Manuel Klimeke573c3f2013-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 Jasper2972d042013-04-25 08:56:26 +000028
Manuel Klimeke573c3f2013-05-22 12:51:29 +000029WhitespaceManager::Change::Change(
30 bool CreateReplacement, const SourceRange &OriginalWhitespaceRange,
Alexander Kornienko3d9ffcf2013-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 Klimeke573c3f2013-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 Kornienko3d9ffcf2013-09-27 16:14:22 +000039 ContinuesPPDirective(ContinuesPPDirective), IndentLevel(IndentLevel),
40 Spaces(Spaces) {}
Manuel Klimeke573c3f2013-05-22 12:51:29 +000041
Manuel Klimekae76f7f2013-10-11 21:25:45 +000042void WhitespaceManager::reset() {
43 Changes.clear();
44 Replaces.clear();
45}
46
47void WhitespaceManager::replaceWhitespace(FormatToken &Tok, unsigned Newlines,
Alexander Kornienko3d9ffcf2013-09-27 16:14:22 +000048 unsigned IndentLevel, unsigned Spaces,
Manuel Klimeke573c3f2013-05-22 12:51:29 +000049 unsigned StartOfTokenColumn,
50 bool InPPDirective) {
Manuel Klimekae76f7f2013-10-11 21:25:45 +000051 if (Tok.Finalized)
52 return;
53 Tok.Decision = (Newlines > 0) ? FD_Break : FD_Continue;
Alexander Kornienko3d9ffcf2013-09-27 16:14:22 +000054 Changes.push_back(Change(true, Tok.WhitespaceRange, IndentLevel, Spaces,
Daniel Jasper2a409b62013-07-08 14:34:09 +000055 StartOfTokenColumn, Newlines, "", "",
56 Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst));
Alexander Kornienko70ce7882013-04-15 14:28:00 +000057}
58
Manuel Klimeke573c3f2013-05-22 12:51:29 +000059void WhitespaceManager::addUntouchableToken(const FormatToken &Tok,
60 bool InPPDirective) {
Manuel Klimekae76f7f2013-10-11 21:25:45 +000061 if (Tok.Finalized)
62 return;
Alexander Kornienko3d9ffcf2013-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 Kornienko70ce7882013-04-15 14:28:00 +000067}
68
Alexander Kornienko2b2faa52013-06-11 16:01:49 +000069void WhitespaceManager::replaceWhitespaceInToken(
70 const FormatToken &Tok, unsigned Offset, unsigned ReplaceChars,
71 StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective,
Alexander Kornienko3d9ffcf2013-09-27 16:14:22 +000072 unsigned Newlines, unsigned IndentLevel, unsigned Spaces) {
Manuel Klimekae76f7f2013-10-11 21:25:45 +000073 if (Tok.Finalized)
74 return;
Manuel Klimeke573c3f2013-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 Kornienko3d9ffcf2013-09-27 16:14:22 +000079 IndentLevel, Spaces, Spaces, Newlines, PreviousPostfix, CurrentPrefix,
Alexander Kornienko22d0e292013-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 Kornienko70ce7882013-04-15 14:28:00 +000087}
88
Manuel Klimeke573c3f2013-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 Jasper2a409b62013-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 Klimeke573c3f2013-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 Klimek0cd57b52013-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 Klimeke573c3f2013-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 Jasper893ea8d2013-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 Jaspercbe86cc2013-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;
Manuel Klimekebfb88c2013-05-23 11:42:52 +0000144 bool WasAlignedWithStartOfNextLine =
145 // A comment on its own line.
146 Changes[i].NewlinesBefore == 1 &&
147 // Not the last line.
148 i + 1 != e &&
149 // The start of the next token was previously aligned with
150 // the start of this comment.
151 (SourceMgr.getSpellingColumnNumber(
152 Changes[i].OriginalWhitespaceRange.getEnd()) ==
153 SourceMgr.getSpellingColumnNumber(
154 Changes[i + 1].OriginalWhitespaceRange.getEnd())) &&
155 // Which is not a comment itself.
156 Changes[i + 1].Kind != tok::comment;
Daniel Jasper893ea8d2013-07-31 23:55:15 +0000157 if (!Style.AlignTrailingComments || FollowsRBraceInColumn0) {
Daniel Jaspercbe86cc2013-07-01 11:22:57 +0000158 alignTrailingComments(StartOfSequence, i, MinColumn);
159 MinColumn = ChangeMinColumn;
160 MaxColumn = ChangeMinColumn;
161 StartOfSequence = i;
162 } else if (BreakBeforeNext || Newlines > 1 ||
163 (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) ||
164 // Break the comment sequence if the previous line did not end
165 // in a trailing comment.
166 (Changes[i].NewlinesBefore == 1 && i > 0 &&
167 !Changes[i - 1].IsTrailingComment) ||
168 WasAlignedWithStartOfNextLine) {
Manuel Klimeke573c3f2013-05-22 12:51:29 +0000169 alignTrailingComments(StartOfSequence, i, MinColumn);
170 MinColumn = ChangeMinColumn;
171 MaxColumn = ChangeMaxColumn;
172 StartOfSequence = i;
173 } else {
174 MinColumn = std::max(MinColumn, ChangeMinColumn);
175 MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
176 }
Manuel Klimek854ca792013-05-23 20:46:07 +0000177 BreakBeforeNext =
178 (i == 0) || (Changes[i].NewlinesBefore > 1) ||
179 // Never start a sequence with a comment at the beginning of
180 // the line.
181 (Changes[i].NewlinesBefore == 1 && StartOfSequence == i);
Manuel Klimeke573c3f2013-05-22 12:51:29 +0000182 Newlines = 0;
183 }
184 }
185 alignTrailingComments(StartOfSequence, Changes.size(), MinColumn);
186}
187
188void WhitespaceManager::alignTrailingComments(unsigned Start, unsigned End,
189 unsigned Column) {
190 for (unsigned i = Start; i != End; ++i) {
191 if (Changes[i].IsTrailingComment) {
192 assert(Column >= Changes[i].StartOfTokenColumn);
193 Changes[i].Spaces += Column - Changes[i].StartOfTokenColumn;
194 Changes[i].StartOfTokenColumn = Column;
195 }
196 }
197}
198
199void WhitespaceManager::alignEscapedNewlines() {
Daniel Jasperc9346c92013-08-28 09:07:32 +0000200 unsigned MaxEndOfLine =
201 Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit;
Manuel Klimeke573c3f2013-05-22 12:51:29 +0000202 unsigned StartOfMacro = 0;
203 for (unsigned i = 1, e = Changes.size(); i < e; ++i) {
204 Change &C = Changes[i];
205 if (C.NewlinesBefore > 0) {
206 if (C.ContinuesPPDirective) {
Daniel Jasperc9346c92013-08-28 09:07:32 +0000207 MaxEndOfLine = std::max(C.PreviousEndOfTokenColumn + 2, MaxEndOfLine);
Manuel Klimeke573c3f2013-05-22 12:51:29 +0000208 } else {
209 alignEscapedNewlines(StartOfMacro + 1, i, MaxEndOfLine);
Daniel Jasperc9346c92013-08-28 09:07:32 +0000210 MaxEndOfLine = Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit;
Manuel Klimeke573c3f2013-05-22 12:51:29 +0000211 StartOfMacro = i;
212 }
213 }
214 }
215 alignEscapedNewlines(StartOfMacro + 1, Changes.size(), MaxEndOfLine);
216}
217
218void WhitespaceManager::alignEscapedNewlines(unsigned Start, unsigned End,
219 unsigned Column) {
220 for (unsigned i = Start; i < End; ++i) {
221 Change &C = Changes[i];
222 if (C.NewlinesBefore > 0) {
223 assert(C.ContinuesPPDirective);
224 if (C.PreviousEndOfTokenColumn + 1 > Column)
225 C.EscapedNewlineColumn = 0;
226 else
227 C.EscapedNewlineColumn = Column;
228 }
229 }
230}
231
232void WhitespaceManager::generateChanges() {
233 for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
234 const Change &C = Changes[i];
235 if (C.CreateReplacement) {
Alexander Kornienko73d845c2013-09-11 12:25:57 +0000236 std::string ReplacementText = C.PreviousLinePostfix;
237 if (C.ContinuesPPDirective)
238 appendNewlineText(ReplacementText, C.NewlinesBefore,
239 C.PreviousEndOfTokenColumn, C.EscapedNewlineColumn);
240 else
241 appendNewlineText(ReplacementText, C.NewlinesBefore);
Alexander Kornienko3d9ffcf2013-09-27 16:14:22 +0000242 appendIndentText(ReplacementText, C.IndentLevel, C.Spaces,
243 C.StartOfTokenColumn - C.Spaces);
Alexander Kornienko73d845c2013-09-11 12:25:57 +0000244 ReplacementText.append(C.CurrentLinePrefix);
Manuel Klimeke573c3f2013-05-22 12:51:29 +0000245 storeReplacement(C.OriginalWhitespaceRange, ReplacementText);
246 }
247 }
248}
249
250void WhitespaceManager::storeReplacement(const SourceRange &Range,
251 StringRef Text) {
252 unsigned WhitespaceLength = SourceMgr.getFileOffset(Range.getEnd()) -
253 SourceMgr.getFileOffset(Range.getBegin());
254 // Don't create a replacement, if it does not change anything.
255 if (StringRef(SourceMgr.getCharacterData(Range.getBegin()),
Daniel Jasper2a409b62013-07-08 14:34:09 +0000256 WhitespaceLength) == Text)
Manuel Klimeke573c3f2013-05-22 12:51:29 +0000257 return;
258 Replaces.insert(tooling::Replacement(
259 SourceMgr, CharSourceRange::getCharRange(Range), Text));
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000260}
261
Alexander Kornienko73d845c2013-09-11 12:25:57 +0000262void WhitespaceManager::appendNewlineText(std::string &Text,
263 unsigned Newlines) {
264 for (unsigned i = 0; i < Newlines; ++i)
265 Text.append(UseCRLF ? "\r\n" : "\n");
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000266}
267
Alexander Kornienko73d845c2013-09-11 12:25:57 +0000268void WhitespaceManager::appendNewlineText(std::string &Text, unsigned Newlines,
269 unsigned PreviousEndOfTokenColumn,
270 unsigned EscapedNewlineColumn) {
Alexander Kornienko2b2faa52013-06-11 16:01:49 +0000271 if (Newlines > 0) {
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000272 unsigned Offset =
Manuel Klimeke573c3f2013-05-22 12:51:29 +0000273 std::min<int>(EscapedNewlineColumn - 1, PreviousEndOfTokenColumn);
Alexander Kornienko2b2faa52013-06-11 16:01:49 +0000274 for (unsigned i = 0; i < Newlines; ++i) {
Alexander Kornienko73d845c2013-09-11 12:25:57 +0000275 Text.append(std::string(EscapedNewlineColumn - Offset - 1, ' '));
276 Text.append(UseCRLF ? "\\\r\n" : "\\\n");
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000277 Offset = 0;
278 }
279 }
Manuel Klimek7c9a93e2013-05-13 09:22:11 +0000280}
281
Alexander Kornienko3d9ffcf2013-09-27 16:14:22 +0000282void WhitespaceManager::appendIndentText(std::string &Text,
283 unsigned IndentLevel, unsigned Spaces,
Alexander Kornienkoacf8e902013-09-27 09:45:40 +0000284 unsigned WhitespaceStartColumn) {
Alexander Kornienko3d9ffcf2013-09-27 16:14:22 +0000285 switch (Style.UseTab) {
286 case FormatStyle::UT_Never:
Alexander Kornienko73d845c2013-09-11 12:25:57 +0000287 Text.append(std::string(Spaces, ' '));
Alexander Kornienko3d9ffcf2013-09-27 16:14:22 +0000288 break;
289 case FormatStyle::UT_Always: {
Alexander Kornienkoacf8e902013-09-27 09:45:40 +0000290 unsigned FirstTabWidth =
291 Style.TabWidth - WhitespaceStartColumn % Style.TabWidth;
292 // Indent with tabs only when there's at least one full tab.
293 if (FirstTabWidth + Style.TabWidth <= Spaces) {
294 Spaces -= FirstTabWidth;
295 Text.append("\t");
296 }
Alexander Kornienko73d845c2013-09-11 12:25:57 +0000297 Text.append(std::string(Spaces / Style.TabWidth, '\t'));
298 Text.append(std::string(Spaces % Style.TabWidth, ' '));
Alexander Kornienko3d9ffcf2013-09-27 16:14:22 +0000299 break;
300 }
301 case FormatStyle::UT_ForIndentation:
302 if (WhitespaceStartColumn == 0) {
303 unsigned Indentation = IndentLevel * Style.IndentWidth;
Alexander Kornienko6967e782013-09-27 16:40:11 +0000304 // This happens, e.g. when a line in a block comment is indented less than
305 // the first one.
Alexander Kornienko3d9ffcf2013-09-27 16:14:22 +0000306 if (Indentation > Spaces)
307 Indentation = Spaces;
308 unsigned Tabs = Indentation / Style.TabWidth;
309 Text.append(std::string(Tabs, '\t'));
310 Spaces -= Tabs * Style.TabWidth;
311 }
312 Text.append(std::string(Spaces, ' '));
313 break;
Alexander Kornienko73d845c2013-09-11 12:25:57 +0000314 }
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000315}
316
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000317} // namespace format
318} // namespace clang