blob: b64506f39035f70681a7f8b0a565a9bd2590baf3 [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
Daniel Jasperb05a81d2014-05-09 13:11:16 +000021bool WhitespaceManager::Change::IsBeforeInFile::
22operator()(const Change &C1, const Change &C2) const {
Manuel Klimek4fe43002013-05-22 12:51:29 +000023 return SourceMgr.isBeforeInTranslationUnit(
24 C1.OriginalWhitespaceRange.getBegin(),
25 C2.OriginalWhitespaceRange.getBegin());
26}
Daniel Jasper6fe2f002013-04-25 08:56:26 +000027
Manuel Klimek4fe43002013-05-22 12:51:29 +000028WhitespaceManager::Change::Change(
Craig Toppere335f252015-10-04 04:53:55 +000029 bool CreateReplacement, SourceRange OriginalWhitespaceRange,
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +000030 unsigned IndentLevel, int Spaces, unsigned StartOfTokenColumn,
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +000031 unsigned NewlinesBefore, StringRef PreviousLinePostfix,
Daniel Jaspere12597c2015-10-01 10:06:54 +000032 StringRef CurrentLinePrefix, tok::TokenKind Kind, bool ContinuesPPDirective,
Benjamin Kramerdab50462016-01-11 16:27:16 +000033 bool IsStartOfDeclName, bool IsInsideToken)
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),
Daniel Jaspere12597c2015-10-01 10:06:54 +000039 ContinuesPPDirective(ContinuesPPDirective),
40 IsStartOfDeclName(IsStartOfDeclName), IndentLevel(IndentLevel),
Benjamin Kramerdab50462016-01-11 16:27:16 +000041 Spaces(Spaces), IsInsideToken(IsInsideToken), IsTrailingComment(false),
42 TokenLength(0), PreviousEndOfTokenColumn(0), EscapedNewlineColumn(0),
Manuel Klimek2d293402015-03-03 14:21:48 +000043 StartOfBlockComment(nullptr), IndentationOffset(0) {}
Manuel Klimek4fe43002013-05-22 12:51:29 +000044
Manuel Klimek71814b42013-10-11 21:25:45 +000045void WhitespaceManager::replaceWhitespace(FormatToken &Tok, unsigned Newlines,
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +000046 unsigned IndentLevel, unsigned Spaces,
Manuel Klimek4fe43002013-05-22 12:51:29 +000047 unsigned StartOfTokenColumn,
48 bool InPPDirective) {
Manuel Klimek71814b42013-10-11 21:25:45 +000049 if (Tok.Finalized)
50 return;
51 Tok.Decision = (Newlines > 0) ? FD_Break : FD_Continue;
Daniel Jaspere12597c2015-10-01 10:06:54 +000052 Changes.push_back(
Daniel Jasper417fc812016-01-09 15:56:53 +000053 Change(/*CreateReplacement=*/true, Tok.WhitespaceRange, IndentLevel,
54 Spaces, StartOfTokenColumn, Newlines, "", "", Tok.Tok.getKind(),
55 InPPDirective && !Tok.IsFirst,
Benjamin Kramerdab50462016-01-11 16:27:16 +000056 Tok.is(TT_StartOfName) || Tok.is(TT_FunctionDeclarationName),
57 /*IsInsideToken=*/false));
Alexander Kornienkocb45bc12013-04-15 14:28:00 +000058}
59
Manuel Klimek4fe43002013-05-22 12:51:29 +000060void WhitespaceManager::addUntouchableToken(const FormatToken &Tok,
61 bool InPPDirective) {
Manuel Klimek71814b42013-10-11 21:25:45 +000062 if (Tok.Finalized)
63 return;
Daniel Jasper417fc812016-01-09 15:56:53 +000064 Changes.push_back(Change(
65 /*CreateReplacement=*/false, Tok.WhitespaceRange, /*IndentLevel=*/0,
66 /*Spaces=*/0, Tok.OriginalColumn, Tok.NewlinesBefore, "", "",
67 Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst,
Benjamin Kramerdab50462016-01-11 16:27:16 +000068 Tok.is(TT_StartOfName) || Tok.is(TT_FunctionDeclarationName),
69 /*IsInsideToken=*/false));
Alexander Kornienkocb45bc12013-04-15 14:28:00 +000070}
71
Alexander Kornienko555efc32013-06-11 16:01:49 +000072void WhitespaceManager::replaceWhitespaceInToken(
73 const FormatToken &Tok, unsigned Offset, unsigned ReplaceChars,
74 StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective,
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +000075 unsigned Newlines, unsigned IndentLevel, int Spaces) {
Manuel Klimek71814b42013-10-11 21:25:45 +000076 if (Tok.Finalized)
77 return;
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +000078 SourceLocation Start = Tok.getStartOfNonWhitespace().getLocWithOffset(Offset);
Manuel Klimek4fe43002013-05-22 12:51:29 +000079 Changes.push_back(Change(
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +000080 true, SourceRange(Start, Start.getLocWithOffset(ReplaceChars)),
81 IndentLevel, Spaces, std::max(0, Spaces), Newlines, PreviousPostfix,
Benjamin Kramerdab50462016-01-11 16:27:16 +000082 CurrentPrefix, Tok.is(TT_LineComment) ? tok::comment : tok::unknown,
Daniel Jaspere12597c2015-10-01 10:06:54 +000083 InPPDirective && !Tok.IsFirst,
Benjamin Kramerdab50462016-01-11 16:27:16 +000084 Tok.is(TT_StartOfName) || Tok.is(TT_FunctionDeclarationName),
85 /*IsInsideToken=*/Newlines == 0));
Alexander Kornienkocb45bc12013-04-15 14:28:00 +000086}
87
Manuel Klimek4fe43002013-05-22 12:51:29 +000088const tooling::Replacements &WhitespaceManager::generateReplacements() {
89 if (Changes.empty())
90 return Replaces;
91
92 std::sort(Changes.begin(), Changes.end(), Change::IsBeforeInFile(SourceMgr));
93 calculateLineBreakInformation();
Daniel Jaspere12597c2015-10-01 10:06:54 +000094 alignConsecutiveDeclarations();
Daniel Jaspera44991332015-04-29 13:06:49 +000095 alignConsecutiveAssignments();
Manuel Klimek4fe43002013-05-22 12:51:29 +000096 alignTrailingComments();
97 alignEscapedNewlines();
98 generateChanges();
99
100 return Replaces;
101}
102
103void WhitespaceManager::calculateLineBreakInformation() {
104 Changes[0].PreviousEndOfTokenColumn = 0;
Benjamin Kramerdab50462016-01-11 16:27:16 +0000105 Change *LastOutsideTokenChange = &Changes[0];
Manuel Klimek4fe43002013-05-22 12:51:29 +0000106 for (unsigned i = 1, e = Changes.size(); i != e; ++i) {
107 unsigned OriginalWhitespaceStart =
108 SourceMgr.getFileOffset(Changes[i].OriginalWhitespaceRange.getBegin());
109 unsigned PreviousOriginalWhitespaceEnd = SourceMgr.getFileOffset(
110 Changes[i - 1].OriginalWhitespaceRange.getEnd());
Daniel Jasper3ac9b9e2013-07-08 14:34:09 +0000111 Changes[i - 1].TokenLength = OriginalWhitespaceStart -
112 PreviousOriginalWhitespaceEnd +
113 Changes[i].PreviousLinePostfix.size() +
114 Changes[i - 1].CurrentLinePrefix.size();
Manuel Klimek4fe43002013-05-22 12:51:29 +0000115
Benjamin Kramerdab50462016-01-11 16:27:16 +0000116 // If there are multiple changes in this token, sum up all the changes until
117 // the end of the line.
118 if (Changes[i - 1].IsInsideToken)
119 LastOutsideTokenChange->TokenLength +=
120 Changes[i - 1].TokenLength + Changes[i - 1].Spaces;
121 else
122 LastOutsideTokenChange = &Changes[i - 1];
123
Manuel Klimek4fe43002013-05-22 12:51:29 +0000124 Changes[i].PreviousEndOfTokenColumn =
125 Changes[i - 1].StartOfTokenColumn + Changes[i - 1].TokenLength;
126
127 Changes[i - 1].IsTrailingComment =
Benjamin Kramerdab50462016-01-11 16:27:16 +0000128 (Changes[i].NewlinesBefore > 0 || Changes[i].Kind == tok::eof ||
129 (Changes[i].IsInsideToken && Changes[i].Kind == tok::comment)) &&
Manuel Klimek4fe43002013-05-22 12:51:29 +0000130 Changes[i - 1].Kind == tok::comment;
131 }
Manuel Klimek05c67892013-05-22 14:01:08 +0000132 // FIXME: The last token is currently not always an eof token; in those
133 // cases, setting TokenLength of the last token to 0 is wrong.
134 Changes.back().TokenLength = 0;
Manuel Klimek4fe43002013-05-22 12:51:29 +0000135 Changes.back().IsTrailingComment = Changes.back().Kind == tok::comment;
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +0000136
137 const WhitespaceManager::Change *LastBlockComment = nullptr;
138 for (auto &Change : Changes) {
Benjamin Kramerdab50462016-01-11 16:27:16 +0000139 // Reset the IsTrailingComment flag for changes inside of trailing comments
140 // so they don't get realigned later.
141 if (Change.IsInsideToken)
142 Change.IsTrailingComment = false;
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +0000143 Change.StartOfBlockComment = nullptr;
144 Change.IndentationOffset = 0;
145 if (Change.Kind == tok::comment) {
146 LastBlockComment = &Change;
147 } else if (Change.Kind == tok::unknown) {
148 if ((Change.StartOfBlockComment = LastBlockComment))
149 Change.IndentationOffset =
150 Change.StartOfTokenColumn -
151 Change.StartOfBlockComment->StartOfTokenColumn;
152 } else {
153 LastBlockComment = nullptr;
154 }
155 }
Manuel Klimek4fe43002013-05-22 12:51:29 +0000156}
157
Daniel Jasperec90e512015-12-01 12:00:43 +0000158// Align a single sequence of tokens, see AlignTokens below.
159template <typename F>
160static void
161AlignTokenSequence(unsigned Start, unsigned End, unsigned Column, F &&Matches,
162 SmallVector<WhitespaceManager::Change, 16> &Changes) {
163 bool FoundMatchOnLine = false;
164 int Shift = 0;
165 for (unsigned i = Start; i != End; ++i) {
166 if (Changes[i].NewlinesBefore > 0) {
167 FoundMatchOnLine = false;
168 Shift = 0;
169 }
170
171 // If this is the first matching token to be aligned, remember by how many
172 // spaces it has to be shifted, so the rest of the changes on the line are
173 // shifted by the same amount
174 if (!FoundMatchOnLine && Matches(Changes[i])) {
175 FoundMatchOnLine = true;
176 Shift = Column - Changes[i].StartOfTokenColumn;
177 Changes[i].Spaces += Shift;
178 }
179
180 assert(Shift >= 0);
181 Changes[i].StartOfTokenColumn += Shift;
182 if (i + 1 != Changes.size())
183 Changes[i + 1].PreviousEndOfTokenColumn += Shift;
184 }
185}
186
187// Walk through all of the changes and find sequences of matching tokens to
188// align. To do so, keep track of the lines and whether or not a matching token
189// was found on a line. If a matching token is found, extend the current
190// sequence. If the current line cannot be part of a sequence, e.g. because
191// there is an empty line before it or it contains only non-matching tokens,
192// finalize the previous sequence.
193template <typename F>
194static void AlignTokens(const FormatStyle &Style, F &&Matches,
195 SmallVector<WhitespaceManager::Change, 16> &Changes) {
196 unsigned MinColumn = 0;
197 unsigned MaxColumn = UINT_MAX;
198
199 // Line number of the start and the end of the current token sequence.
200 unsigned StartOfSequence = 0;
201 unsigned EndOfSequence = 0;
202
203 // Keep track of the nesting level of matching tokens, i.e. the number of
204 // surrounding (), [], or {}. We will only align a sequence of matching
205 // token that share the same scope depth.
206 //
207 // FIXME: This could use FormatToken::NestingLevel information, but there is
208 // an outstanding issue wrt the brace scopes.
209 unsigned NestingLevelOfLastMatch = 0;
210 unsigned NestingLevel = 0;
211
212 // Keep track of the number of commas before the matching tokens, we will only
213 // align a sequence of matching tokens if they are preceded by the same number
214 // of commas.
215 unsigned CommasBeforeLastMatch = 0;
216 unsigned CommasBeforeMatch = 0;
217
218 // Whether a matching token has been found on the current line.
219 bool FoundMatchOnLine = false;
220
221 // Aligns a sequence of matching tokens, on the MinColumn column.
222 //
223 // Sequences start from the first matching token to align, and end at the
224 // first token of the first line that doesn't need to be aligned.
225 //
226 // We need to adjust the StartOfTokenColumn of each Change that is on a line
227 // containing any matching token to be aligned and located after such token.
228 auto AlignCurrentSequence = [&] {
229 if (StartOfSequence > 0 && StartOfSequence < EndOfSequence)
230 AlignTokenSequence(StartOfSequence, EndOfSequence, MinColumn, Matches,
231 Changes);
232 MinColumn = 0;
233 MaxColumn = UINT_MAX;
234 StartOfSequence = 0;
235 EndOfSequence = 0;
236 };
237
238 for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
239 if (Changes[i].NewlinesBefore != 0) {
240 CommasBeforeMatch = 0;
241 EndOfSequence = i;
242 // If there is a blank line, or if the last line didn't contain any
243 // matching token, the sequence ends here.
244 if (Changes[i].NewlinesBefore > 1 || !FoundMatchOnLine)
245 AlignCurrentSequence();
246
247 FoundMatchOnLine = false;
248 }
249
250 if (Changes[i].Kind == tok::comma) {
251 ++CommasBeforeMatch;
252 } else if (Changes[i].Kind == tok::r_brace ||
253 Changes[i].Kind == tok::r_paren ||
254 Changes[i].Kind == tok::r_square) {
255 --NestingLevel;
256 } else if (Changes[i].Kind == tok::l_brace ||
257 Changes[i].Kind == tok::l_paren ||
258 Changes[i].Kind == tok::l_square) {
259 // We want sequences to skip over child scopes if possible, but not the
260 // other way around.
261 NestingLevelOfLastMatch = std::min(NestingLevelOfLastMatch, NestingLevel);
262 ++NestingLevel;
263 }
264
265 if (!Matches(Changes[i]))
266 continue;
267
268 // If there is more than one matching token per line, or if the number of
269 // preceding commas, or the scope depth, do not match anymore, end the
270 // sequence.
271 if (FoundMatchOnLine || CommasBeforeMatch != CommasBeforeLastMatch ||
272 NestingLevel != NestingLevelOfLastMatch)
273 AlignCurrentSequence();
274
275 CommasBeforeLastMatch = CommasBeforeMatch;
276 NestingLevelOfLastMatch = NestingLevel;
277 FoundMatchOnLine = true;
278
279 if (StartOfSequence == 0)
280 StartOfSequence = i;
281
282 unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
283 int LineLengthAfter = -Changes[i].Spaces;
284 for (unsigned j = i; j != e && Changes[j].NewlinesBefore == 0; ++j)
285 LineLengthAfter += Changes[j].Spaces + Changes[j].TokenLength;
286 unsigned ChangeMaxColumn = Style.ColumnLimit - LineLengthAfter;
287
288 // If we are restricted by the maximum column width, end the sequence.
289 if (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn ||
290 CommasBeforeLastMatch != CommasBeforeMatch) {
291 AlignCurrentSequence();
292 StartOfSequence = i;
293 }
294
295 MinColumn = std::max(MinColumn, ChangeMinColumn);
296 MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
297 }
298
299 EndOfSequence = Changes.size();
300 AlignCurrentSequence();
301}
302
Daniel Jaspera44991332015-04-29 13:06:49 +0000303void WhitespaceManager::alignConsecutiveAssignments() {
304 if (!Style.AlignConsecutiveAssignments)
305 return;
306
Daniel Jasperec90e512015-12-01 12:00:43 +0000307 AlignTokens(Style,
308 [&](const Change &C) {
309 // Do not align on equal signs that are first on a line.
310 if (C.NewlinesBefore > 0)
311 return false;
Daniel Jaspera44991332015-04-29 13:06:49 +0000312
Daniel Jasperec90e512015-12-01 12:00:43 +0000313 // Do not align on equal signs that are last on a line.
314 if (&C != &Changes.back() && (&C + 1)->NewlinesBefore > 0)
315 return false;
Daniel Jaspera44991332015-04-29 13:06:49 +0000316
Daniel Jasperec90e512015-12-01 12:00:43 +0000317 return C.Kind == tok::equal;
318 },
319 Changes);
Daniel Jaspera44991332015-04-29 13:06:49 +0000320}
321
Daniel Jaspere12597c2015-10-01 10:06:54 +0000322void WhitespaceManager::alignConsecutiveDeclarations() {
323 if (!Style.AlignConsecutiveDeclarations)
324 return;
325
Daniel Jasperec90e512015-12-01 12:00:43 +0000326 // FIXME: Currently we don't handle properly the PointerAlignment: Right
327 // The * and & are not aligned and are left dangling. Something has to be done
328 // about it, but it raises the question of alignment of code like:
329 // const char* const* v1;
330 // float const* v2;
331 // SomeVeryLongType const& v3;
Daniel Jaspere12597c2015-10-01 10:06:54 +0000332
Daniel Jasperec90e512015-12-01 12:00:43 +0000333 AlignTokens(Style, [](Change const &C) { return C.IsStartOfDeclName; },
334 Changes);
Daniel Jaspere12597c2015-10-01 10:06:54 +0000335}
336
Manuel Klimek4fe43002013-05-22 12:51:29 +0000337void WhitespaceManager::alignTrailingComments() {
338 unsigned MinColumn = 0;
339 unsigned MaxColumn = UINT_MAX;
340 unsigned StartOfSequence = 0;
341 bool BreakBeforeNext = false;
342 unsigned Newlines = 0;
343 for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +0000344 if (Changes[i].StartOfBlockComment)
345 continue;
346 Newlines += Changes[i].NewlinesBefore;
347 if (!Changes[i].IsTrailingComment)
348 continue;
349
Manuel Klimek4fe43002013-05-22 12:51:29 +0000350 unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
Manuel Klimek4fe43002013-05-22 12:51:29 +0000351 unsigned ChangeMaxColumn = Style.ColumnLimit - Changes[i].TokenLength;
Daniel Jasper417fc812016-01-09 15:56:53 +0000352
353 // If we don't create a replacement for this change, we have to consider
354 // it to be immovable.
355 if (!Changes[i].CreateReplacement)
356 ChangeMaxColumn = ChangeMinColumn;
357
Daniel Jasper66935022014-04-27 10:03:19 +0000358 if (i + 1 != e && Changes[i + 1].ContinuesPPDirective)
359 ChangeMaxColumn -= 2;
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +0000360 // If this comment follows an } in column 0, it probably documents the
361 // closing of a namespace and we don't want to align it.
362 bool FollowsRBraceInColumn0 = i > 0 && Changes[i].NewlinesBefore == 0 &&
363 Changes[i - 1].Kind == tok::r_brace &&
364 Changes[i - 1].StartOfTokenColumn == 0;
365 bool WasAlignedWithStartOfNextLine = false;
366 if (Changes[i].NewlinesBefore == 1) { // A comment on its own line.
Daniel Jasper49532102015-01-07 14:00:11 +0000367 unsigned CommentColumn = SourceMgr.getSpellingColumnNumber(
368 Changes[i].OriginalWhitespaceRange.getEnd());
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +0000369 for (unsigned j = i + 1; j != e; ++j) {
Daniel Jasperbb37a2f2016-02-01 11:20:55 +0000370 if (Changes[j].Kind == tok::comment ||
371 Changes[j].Kind == tok::unknown)
372 // Skip over comments and unknown tokens. "unknown tokens are used for
373 // the continuation of multiline comments.
374 continue;
375
376 unsigned NextColumn = SourceMgr.getSpellingColumnNumber(
377 Changes[j].OriginalWhitespaceRange.getEnd());
378 // The start of the next token was previously aligned with the
379 // start of this comment.
380 WasAlignedWithStartOfNextLine =
381 CommentColumn == NextColumn ||
382 CommentColumn == NextColumn + Style.IndentWidth;
383 break;
Daniel Jasper0e93cdb2013-11-08 23:31:14 +0000384 }
Manuel Klimek4fe43002013-05-22 12:51:29 +0000385 }
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +0000386 if (!Style.AlignTrailingComments || FollowsRBraceInColumn0) {
387 alignTrailingComments(StartOfSequence, i, MinColumn);
388 MinColumn = ChangeMinColumn;
389 MaxColumn = ChangeMinColumn;
390 StartOfSequence = i;
391 } else if (BreakBeforeNext || Newlines > 1 ||
392 (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) ||
393 // Break the comment sequence if the previous line did not end
394 // in a trailing comment.
395 (Changes[i].NewlinesBefore == 1 && i > 0 &&
396 !Changes[i - 1].IsTrailingComment) ||
397 WasAlignedWithStartOfNextLine) {
398 alignTrailingComments(StartOfSequence, i, MinColumn);
399 MinColumn = ChangeMinColumn;
400 MaxColumn = ChangeMaxColumn;
401 StartOfSequence = i;
402 } else {
403 MinColumn = std::max(MinColumn, ChangeMinColumn);
404 MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
405 }
406 BreakBeforeNext =
407 (i == 0) || (Changes[i].NewlinesBefore > 1) ||
408 // Never start a sequence with a comment at the beginning of
409 // the line.
410 (Changes[i].NewlinesBefore == 1 && StartOfSequence == i);
411 Newlines = 0;
Manuel Klimek4fe43002013-05-22 12:51:29 +0000412 }
413 alignTrailingComments(StartOfSequence, Changes.size(), MinColumn);
414}
415
416void WhitespaceManager::alignTrailingComments(unsigned Start, unsigned End,
417 unsigned Column) {
418 for (unsigned i = Start; i != End; ++i) {
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +0000419 int Shift = 0;
Manuel Klimek4fe43002013-05-22 12:51:29 +0000420 if (Changes[i].IsTrailingComment) {
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +0000421 Shift = Column - Changes[i].StartOfTokenColumn;
Manuel Klimek4fe43002013-05-22 12:51:29 +0000422 }
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +0000423 if (Changes[i].StartOfBlockComment) {
424 Shift = Changes[i].IndentationOffset +
425 Changes[i].StartOfBlockComment->StartOfTokenColumn -
426 Changes[i].StartOfTokenColumn;
427 }
428 assert(Shift >= 0);
429 Changes[i].Spaces += Shift;
Andi-Bogdan Postelnicua9a8fde2016-10-26 07:44:51 +0000430 if (i + 1 != Changes.size())
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +0000431 Changes[i + 1].PreviousEndOfTokenColumn += Shift;
432 Changes[i].StartOfTokenColumn += Shift;
Manuel Klimek4fe43002013-05-22 12:51:29 +0000433 }
434}
435
436void WhitespaceManager::alignEscapedNewlines() {
Daniel Jaspera49393f2013-08-28 09:07:32 +0000437 unsigned MaxEndOfLine =
438 Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit;
Manuel Klimek4fe43002013-05-22 12:51:29 +0000439 unsigned StartOfMacro = 0;
440 for (unsigned i = 1, e = Changes.size(); i < e; ++i) {
441 Change &C = Changes[i];
442 if (C.NewlinesBefore > 0) {
443 if (C.ContinuesPPDirective) {
Daniel Jaspera49393f2013-08-28 09:07:32 +0000444 MaxEndOfLine = std::max(C.PreviousEndOfTokenColumn + 2, MaxEndOfLine);
Manuel Klimek4fe43002013-05-22 12:51:29 +0000445 } else {
446 alignEscapedNewlines(StartOfMacro + 1, i, MaxEndOfLine);
Daniel Jaspera49393f2013-08-28 09:07:32 +0000447 MaxEndOfLine = Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit;
Manuel Klimek4fe43002013-05-22 12:51:29 +0000448 StartOfMacro = i;
449 }
450 }
451 }
452 alignEscapedNewlines(StartOfMacro + 1, Changes.size(), MaxEndOfLine);
453}
454
455void WhitespaceManager::alignEscapedNewlines(unsigned Start, unsigned End,
456 unsigned Column) {
457 for (unsigned i = Start; i < End; ++i) {
458 Change &C = Changes[i];
459 if (C.NewlinesBefore > 0) {
460 assert(C.ContinuesPPDirective);
461 if (C.PreviousEndOfTokenColumn + 1 > Column)
462 C.EscapedNewlineColumn = 0;
463 else
464 C.EscapedNewlineColumn = Column;
465 }
466 }
467}
468
469void WhitespaceManager::generateChanges() {
470 for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
471 const Change &C = Changes[i];
Daniel Jasper47b35ae2015-01-29 10:47:14 +0000472 if (i > 0) {
473 assert(Changes[i - 1].OriginalWhitespaceRange.getBegin() !=
474 C.OriginalWhitespaceRange.getBegin() &&
475 "Generating two replacements for the same location");
476 }
Manuel Klimek4fe43002013-05-22 12:51:29 +0000477 if (C.CreateReplacement) {
Alexander Kornienko9e649af2013-09-11 12:25:57 +0000478 std::string ReplacementText = C.PreviousLinePostfix;
479 if (C.ContinuesPPDirective)
480 appendNewlineText(ReplacementText, C.NewlinesBefore,
481 C.PreviousEndOfTokenColumn, C.EscapedNewlineColumn);
482 else
483 appendNewlineText(ReplacementText, C.NewlinesBefore);
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +0000484 appendIndentText(ReplacementText, C.IndentLevel, std::max(0, C.Spaces),
485 C.StartOfTokenColumn - std::max(0, C.Spaces));
Alexander Kornienko9e649af2013-09-11 12:25:57 +0000486 ReplacementText.append(C.CurrentLinePrefix);
Manuel Klimek4fe43002013-05-22 12:51:29 +0000487 storeReplacement(C.OriginalWhitespaceRange, ReplacementText);
488 }
489 }
490}
491
Craig Toppere335f252015-10-04 04:53:55 +0000492void WhitespaceManager::storeReplacement(SourceRange Range,
Manuel Klimek4fe43002013-05-22 12:51:29 +0000493 StringRef Text) {
494 unsigned WhitespaceLength = SourceMgr.getFileOffset(Range.getEnd()) -
495 SourceMgr.getFileOffset(Range.getBegin());
496 // Don't create a replacement, if it does not change anything.
497 if (StringRef(SourceMgr.getCharacterData(Range.getBegin()),
Daniel Jasper3ac9b9e2013-07-08 14:34:09 +0000498 WhitespaceLength) == Text)
Manuel Klimek4fe43002013-05-22 12:51:29 +0000499 return;
Eric Liu40ef2fb2016-08-01 10:16:37 +0000500 auto Err = Replaces.add(tooling::Replacement(
Manuel Klimek4fe43002013-05-22 12:51:29 +0000501 SourceMgr, CharSourceRange::getCharRange(Range), Text));
Eric Liu40ef2fb2016-08-01 10:16:37 +0000502 // FIXME: better error handling. For now, just print an error message in the
503 // release version.
Piotr Padlewski1ec383c2016-12-23 11:40:44 +0000504 if (Err) {
Eric Liu40ef2fb2016-08-01 10:16:37 +0000505 llvm::errs() << llvm::toString(std::move(Err)) << "\n";
Piotr Padlewski1ec383c2016-12-23 11:40:44 +0000506 assert(false);
507 }
Alexander Kornienkocb45bc12013-04-15 14:28:00 +0000508}
509
Alexander Kornienko9e649af2013-09-11 12:25:57 +0000510void WhitespaceManager::appendNewlineText(std::string &Text,
511 unsigned Newlines) {
512 for (unsigned i = 0; i < Newlines; ++i)
513 Text.append(UseCRLF ? "\r\n" : "\n");
Alexander Kornienkocb45bc12013-04-15 14:28:00 +0000514}
515
Alexander Kornienko9e649af2013-09-11 12:25:57 +0000516void WhitespaceManager::appendNewlineText(std::string &Text, unsigned Newlines,
517 unsigned PreviousEndOfTokenColumn,
518 unsigned EscapedNewlineColumn) {
Alexander Kornienko555efc32013-06-11 16:01:49 +0000519 if (Newlines > 0) {
Alexander Kornienkocb45bc12013-04-15 14:28:00 +0000520 unsigned Offset =
Manuel Klimek4fe43002013-05-22 12:51:29 +0000521 std::min<int>(EscapedNewlineColumn - 1, PreviousEndOfTokenColumn);
Alexander Kornienko555efc32013-06-11 16:01:49 +0000522 for (unsigned i = 0; i < Newlines; ++i) {
Benjamin Kramerddf1cda2015-05-28 19:55:49 +0000523 Text.append(EscapedNewlineColumn - Offset - 1, ' ');
Alexander Kornienko9e649af2013-09-11 12:25:57 +0000524 Text.append(UseCRLF ? "\\\r\n" : "\\\n");
Alexander Kornienkocb45bc12013-04-15 14:28:00 +0000525 Offset = 0;
526 }
527 }
Manuel Klimekb9eae4c2013-05-13 09:22:11 +0000528}
529
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +0000530void WhitespaceManager::appendIndentText(std::string &Text,
531 unsigned IndentLevel, unsigned Spaces,
Alexander Kornienkodb4c21f2013-09-27 09:45:40 +0000532 unsigned WhitespaceStartColumn) {
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +0000533 switch (Style.UseTab) {
534 case FormatStyle::UT_Never:
Benjamin Kramerddf1cda2015-05-28 19:55:49 +0000535 Text.append(Spaces, ' ');
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +0000536 break;
537 case FormatStyle::UT_Always: {
Alexander Kornienkodb4c21f2013-09-27 09:45:40 +0000538 unsigned FirstTabWidth =
539 Style.TabWidth - WhitespaceStartColumn % Style.TabWidth;
540 // Indent with tabs only when there's at least one full tab.
541 if (FirstTabWidth + Style.TabWidth <= Spaces) {
542 Spaces -= FirstTabWidth;
543 Text.append("\t");
544 }
Benjamin Kramerddf1cda2015-05-28 19:55:49 +0000545 Text.append(Spaces / Style.TabWidth, '\t');
546 Text.append(Spaces % Style.TabWidth, ' ');
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +0000547 break;
548 }
549 case FormatStyle::UT_ForIndentation:
550 if (WhitespaceStartColumn == 0) {
551 unsigned Indentation = IndentLevel * Style.IndentWidth;
Alexander Kornienko45dc1b22013-09-27 16:40:11 +0000552 // This happens, e.g. when a line in a block comment is indented less than
553 // the first one.
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +0000554 if (Indentation > Spaces)
555 Indentation = Spaces;
556 unsigned Tabs = Indentation / Style.TabWidth;
Benjamin Kramerddf1cda2015-05-28 19:55:49 +0000557 Text.append(Tabs, '\t');
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +0000558 Spaces -= Tabs * Style.TabWidth;
559 }
Benjamin Kramerddf1cda2015-05-28 19:55:49 +0000560 Text.append(Spaces, ' ');
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +0000561 break;
Marianne Mailhot-Sarrasin51fe2792016-04-14 14:52:26 +0000562 case FormatStyle::UT_ForContinuationAndIndentation:
563 if (WhitespaceStartColumn == 0) {
564 unsigned Tabs = Spaces / Style.TabWidth;
565 Text.append(Tabs, '\t');
566 Spaces -= Tabs * Style.TabWidth;
567 }
568 Text.append(Spaces, ' ');
569 break;
Alexander Kornienko9e649af2013-09-11 12:25:57 +0000570 }
Alexander Kornienkocb45bc12013-04-15 14:28:00 +0000571}
572
Alexander Kornienkocb45bc12013-04-15 14:28:00 +0000573} // namespace format
574} // namespace clang