blob: 6b14af3f75676ec43f907eaa9a4e73b903587d1e [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)) &&
Krasimir Georgiev91834222017-01-25 13:58:58 +0000130 Changes[i - 1].Kind == tok::comment &&
131 // FIXME: This is a dirty hack. The problem is that
132 // BreakableLineCommentSection does comment reflow changes and here is
133 // the aligning of trailing comments. Consider the case where we reflow
134 // the second line up in this example:
135 //
136 // // line 1
137 // // line 2
138 //
139 // That amounts to 2 changes by BreakableLineCommentSection:
140 // - the first, delimited by (), for the whitespace between the tokens,
141 // - and second, delimited by [], for the whitespace at the beginning
142 // of the second token:
143 //
144 // // line 1(
145 // )[// ]line 2
146 //
147 // So in the end we have two changes like this:
148 //
149 // // line1()[ ]line 2
150 //
151 // Note that the OriginalWhitespaceStart of the second change is the
152 // same as the PreviousOriginalWhitespaceEnd of the first change.
153 // In this case, the below check ensures that the second change doesn't
154 // get treated as a trailing comment change here, since this might
155 // trigger additional whitespace to be wrongly inserted before "line 2"
156 // by the comment aligner here.
157 //
158 // For a proper solution we need a mechanism to say to WhitespaceManager
159 // that a particular change breaks the current sequence of trailing
160 // comments.
161 OriginalWhitespaceStart != PreviousOriginalWhitespaceEnd;
Manuel Klimek4fe43002013-05-22 12:51:29 +0000162 }
Manuel Klimek05c67892013-05-22 14:01:08 +0000163 // FIXME: The last token is currently not always an eof token; in those
164 // cases, setting TokenLength of the last token to 0 is wrong.
165 Changes.back().TokenLength = 0;
Manuel Klimek4fe43002013-05-22 12:51:29 +0000166 Changes.back().IsTrailingComment = Changes.back().Kind == tok::comment;
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +0000167
168 const WhitespaceManager::Change *LastBlockComment = nullptr;
169 for (auto &Change : Changes) {
Benjamin Kramerdab50462016-01-11 16:27:16 +0000170 // Reset the IsTrailingComment flag for changes inside of trailing comments
171 // so they don't get realigned later.
172 if (Change.IsInsideToken)
173 Change.IsTrailingComment = false;
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +0000174 Change.StartOfBlockComment = nullptr;
175 Change.IndentationOffset = 0;
176 if (Change.Kind == tok::comment) {
177 LastBlockComment = &Change;
178 } else if (Change.Kind == tok::unknown) {
179 if ((Change.StartOfBlockComment = LastBlockComment))
180 Change.IndentationOffset =
181 Change.StartOfTokenColumn -
182 Change.StartOfBlockComment->StartOfTokenColumn;
183 } else {
184 LastBlockComment = nullptr;
185 }
186 }
Manuel Klimek4fe43002013-05-22 12:51:29 +0000187}
188
Daniel Jasperec90e512015-12-01 12:00:43 +0000189// Align a single sequence of tokens, see AlignTokens below.
190template <typename F>
191static void
192AlignTokenSequence(unsigned Start, unsigned End, unsigned Column, F &&Matches,
193 SmallVector<WhitespaceManager::Change, 16> &Changes) {
194 bool FoundMatchOnLine = false;
195 int Shift = 0;
196 for (unsigned i = Start; i != End; ++i) {
197 if (Changes[i].NewlinesBefore > 0) {
198 FoundMatchOnLine = false;
199 Shift = 0;
200 }
201
202 // If this is the first matching token to be aligned, remember by how many
203 // spaces it has to be shifted, so the rest of the changes on the line are
204 // shifted by the same amount
205 if (!FoundMatchOnLine && Matches(Changes[i])) {
206 FoundMatchOnLine = true;
207 Shift = Column - Changes[i].StartOfTokenColumn;
208 Changes[i].Spaces += Shift;
209 }
210
211 assert(Shift >= 0);
212 Changes[i].StartOfTokenColumn += Shift;
213 if (i + 1 != Changes.size())
214 Changes[i + 1].PreviousEndOfTokenColumn += Shift;
215 }
216}
217
218// Walk through all of the changes and find sequences of matching tokens to
219// align. To do so, keep track of the lines and whether or not a matching token
220// was found on a line. If a matching token is found, extend the current
221// sequence. If the current line cannot be part of a sequence, e.g. because
222// there is an empty line before it or it contains only non-matching tokens,
223// finalize the previous sequence.
224template <typename F>
225static void AlignTokens(const FormatStyle &Style, F &&Matches,
226 SmallVector<WhitespaceManager::Change, 16> &Changes) {
227 unsigned MinColumn = 0;
228 unsigned MaxColumn = UINT_MAX;
229
230 // Line number of the start and the end of the current token sequence.
231 unsigned StartOfSequence = 0;
232 unsigned EndOfSequence = 0;
233
234 // Keep track of the nesting level of matching tokens, i.e. the number of
235 // surrounding (), [], or {}. We will only align a sequence of matching
236 // token that share the same scope depth.
237 //
238 // FIXME: This could use FormatToken::NestingLevel information, but there is
239 // an outstanding issue wrt the brace scopes.
240 unsigned NestingLevelOfLastMatch = 0;
241 unsigned NestingLevel = 0;
242
243 // Keep track of the number of commas before the matching tokens, we will only
244 // align a sequence of matching tokens if they are preceded by the same number
245 // of commas.
246 unsigned CommasBeforeLastMatch = 0;
247 unsigned CommasBeforeMatch = 0;
248
249 // Whether a matching token has been found on the current line.
250 bool FoundMatchOnLine = false;
251
252 // Aligns a sequence of matching tokens, on the MinColumn column.
253 //
254 // Sequences start from the first matching token to align, and end at the
255 // first token of the first line that doesn't need to be aligned.
256 //
257 // We need to adjust the StartOfTokenColumn of each Change that is on a line
258 // containing any matching token to be aligned and located after such token.
259 auto AlignCurrentSequence = [&] {
260 if (StartOfSequence > 0 && StartOfSequence < EndOfSequence)
261 AlignTokenSequence(StartOfSequence, EndOfSequence, MinColumn, Matches,
262 Changes);
263 MinColumn = 0;
264 MaxColumn = UINT_MAX;
265 StartOfSequence = 0;
266 EndOfSequence = 0;
267 };
268
269 for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
270 if (Changes[i].NewlinesBefore != 0) {
271 CommasBeforeMatch = 0;
272 EndOfSequence = i;
273 // If there is a blank line, or if the last line didn't contain any
274 // matching token, the sequence ends here.
275 if (Changes[i].NewlinesBefore > 1 || !FoundMatchOnLine)
276 AlignCurrentSequence();
277
278 FoundMatchOnLine = false;
279 }
280
281 if (Changes[i].Kind == tok::comma) {
282 ++CommasBeforeMatch;
283 } else if (Changes[i].Kind == tok::r_brace ||
284 Changes[i].Kind == tok::r_paren ||
285 Changes[i].Kind == tok::r_square) {
286 --NestingLevel;
287 } else if (Changes[i].Kind == tok::l_brace ||
288 Changes[i].Kind == tok::l_paren ||
289 Changes[i].Kind == tok::l_square) {
290 // We want sequences to skip over child scopes if possible, but not the
291 // other way around.
292 NestingLevelOfLastMatch = std::min(NestingLevelOfLastMatch, NestingLevel);
293 ++NestingLevel;
294 }
295
296 if (!Matches(Changes[i]))
297 continue;
298
299 // If there is more than one matching token per line, or if the number of
300 // preceding commas, or the scope depth, do not match anymore, end the
301 // sequence.
302 if (FoundMatchOnLine || CommasBeforeMatch != CommasBeforeLastMatch ||
303 NestingLevel != NestingLevelOfLastMatch)
304 AlignCurrentSequence();
305
306 CommasBeforeLastMatch = CommasBeforeMatch;
307 NestingLevelOfLastMatch = NestingLevel;
308 FoundMatchOnLine = true;
309
310 if (StartOfSequence == 0)
311 StartOfSequence = i;
312
313 unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
314 int LineLengthAfter = -Changes[i].Spaces;
315 for (unsigned j = i; j != e && Changes[j].NewlinesBefore == 0; ++j)
316 LineLengthAfter += Changes[j].Spaces + Changes[j].TokenLength;
317 unsigned ChangeMaxColumn = Style.ColumnLimit - LineLengthAfter;
318
319 // If we are restricted by the maximum column width, end the sequence.
320 if (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn ||
321 CommasBeforeLastMatch != CommasBeforeMatch) {
322 AlignCurrentSequence();
323 StartOfSequence = i;
324 }
325
326 MinColumn = std::max(MinColumn, ChangeMinColumn);
327 MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
328 }
329
330 EndOfSequence = Changes.size();
331 AlignCurrentSequence();
332}
333
Daniel Jaspera44991332015-04-29 13:06:49 +0000334void WhitespaceManager::alignConsecutiveAssignments() {
335 if (!Style.AlignConsecutiveAssignments)
336 return;
337
Daniel Jasperec90e512015-12-01 12:00:43 +0000338 AlignTokens(Style,
339 [&](const Change &C) {
340 // Do not align on equal signs that are first on a line.
341 if (C.NewlinesBefore > 0)
342 return false;
Daniel Jaspera44991332015-04-29 13:06:49 +0000343
Daniel Jasperec90e512015-12-01 12:00:43 +0000344 // Do not align on equal signs that are last on a line.
345 if (&C != &Changes.back() && (&C + 1)->NewlinesBefore > 0)
346 return false;
Daniel Jaspera44991332015-04-29 13:06:49 +0000347
Daniel Jasperec90e512015-12-01 12:00:43 +0000348 return C.Kind == tok::equal;
349 },
350 Changes);
Daniel Jaspera44991332015-04-29 13:06:49 +0000351}
352
Daniel Jaspere12597c2015-10-01 10:06:54 +0000353void WhitespaceManager::alignConsecutiveDeclarations() {
354 if (!Style.AlignConsecutiveDeclarations)
355 return;
356
Daniel Jasperec90e512015-12-01 12:00:43 +0000357 // FIXME: Currently we don't handle properly the PointerAlignment: Right
358 // The * and & are not aligned and are left dangling. Something has to be done
359 // about it, but it raises the question of alignment of code like:
360 // const char* const* v1;
361 // float const* v2;
362 // SomeVeryLongType const& v3;
Daniel Jaspere12597c2015-10-01 10:06:54 +0000363
Daniel Jasperec90e512015-12-01 12:00:43 +0000364 AlignTokens(Style, [](Change const &C) { return C.IsStartOfDeclName; },
365 Changes);
Daniel Jaspere12597c2015-10-01 10:06:54 +0000366}
367
Manuel Klimek4fe43002013-05-22 12:51:29 +0000368void WhitespaceManager::alignTrailingComments() {
369 unsigned MinColumn = 0;
370 unsigned MaxColumn = UINT_MAX;
371 unsigned StartOfSequence = 0;
372 bool BreakBeforeNext = false;
373 unsigned Newlines = 0;
374 for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +0000375 if (Changes[i].StartOfBlockComment)
376 continue;
377 Newlines += Changes[i].NewlinesBefore;
378 if (!Changes[i].IsTrailingComment)
379 continue;
380
Manuel Klimek4fe43002013-05-22 12:51:29 +0000381 unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
Manuel Klimek4fe43002013-05-22 12:51:29 +0000382 unsigned ChangeMaxColumn = Style.ColumnLimit - Changes[i].TokenLength;
Daniel Jasper417fc812016-01-09 15:56:53 +0000383
384 // If we don't create a replacement for this change, we have to consider
385 // it to be immovable.
386 if (!Changes[i].CreateReplacement)
387 ChangeMaxColumn = ChangeMinColumn;
388
Daniel Jasper66935022014-04-27 10:03:19 +0000389 if (i + 1 != e && Changes[i + 1].ContinuesPPDirective)
390 ChangeMaxColumn -= 2;
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +0000391 // If this comment follows an } in column 0, it probably documents the
392 // closing of a namespace and we don't want to align it.
393 bool FollowsRBraceInColumn0 = i > 0 && Changes[i].NewlinesBefore == 0 &&
394 Changes[i - 1].Kind == tok::r_brace &&
395 Changes[i - 1].StartOfTokenColumn == 0;
396 bool WasAlignedWithStartOfNextLine = false;
397 if (Changes[i].NewlinesBefore == 1) { // A comment on its own line.
Daniel Jasper49532102015-01-07 14:00:11 +0000398 unsigned CommentColumn = SourceMgr.getSpellingColumnNumber(
399 Changes[i].OriginalWhitespaceRange.getEnd());
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +0000400 for (unsigned j = i + 1; j != e; ++j) {
Daniel Jasperbb37a2f2016-02-01 11:20:55 +0000401 if (Changes[j].Kind == tok::comment ||
402 Changes[j].Kind == tok::unknown)
403 // Skip over comments and unknown tokens. "unknown tokens are used for
404 // the continuation of multiline comments.
405 continue;
406
407 unsigned NextColumn = SourceMgr.getSpellingColumnNumber(
408 Changes[j].OriginalWhitespaceRange.getEnd());
409 // The start of the next token was previously aligned with the
410 // start of this comment.
411 WasAlignedWithStartOfNextLine =
412 CommentColumn == NextColumn ||
413 CommentColumn == NextColumn + Style.IndentWidth;
414 break;
Daniel Jasper0e93cdb2013-11-08 23:31:14 +0000415 }
Manuel Klimek4fe43002013-05-22 12:51:29 +0000416 }
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +0000417 if (!Style.AlignTrailingComments || FollowsRBraceInColumn0) {
418 alignTrailingComments(StartOfSequence, i, MinColumn);
419 MinColumn = ChangeMinColumn;
420 MaxColumn = ChangeMinColumn;
421 StartOfSequence = i;
422 } else if (BreakBeforeNext || Newlines > 1 ||
423 (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) ||
424 // Break the comment sequence if the previous line did not end
425 // in a trailing comment.
426 (Changes[i].NewlinesBefore == 1 && i > 0 &&
427 !Changes[i - 1].IsTrailingComment) ||
428 WasAlignedWithStartOfNextLine) {
429 alignTrailingComments(StartOfSequence, i, MinColumn);
430 MinColumn = ChangeMinColumn;
431 MaxColumn = ChangeMaxColumn;
432 StartOfSequence = i;
433 } else {
434 MinColumn = std::max(MinColumn, ChangeMinColumn);
435 MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
436 }
437 BreakBeforeNext =
438 (i == 0) || (Changes[i].NewlinesBefore > 1) ||
439 // Never start a sequence with a comment at the beginning of
440 // the line.
441 (Changes[i].NewlinesBefore == 1 && StartOfSequence == i);
442 Newlines = 0;
Manuel Klimek4fe43002013-05-22 12:51:29 +0000443 }
444 alignTrailingComments(StartOfSequence, Changes.size(), MinColumn);
445}
446
447void WhitespaceManager::alignTrailingComments(unsigned Start, unsigned End,
448 unsigned Column) {
449 for (unsigned i = Start; i != End; ++i) {
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +0000450 int Shift = 0;
Manuel Klimek4fe43002013-05-22 12:51:29 +0000451 if (Changes[i].IsTrailingComment) {
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +0000452 Shift = Column - Changes[i].StartOfTokenColumn;
Manuel Klimek4fe43002013-05-22 12:51:29 +0000453 }
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +0000454 if (Changes[i].StartOfBlockComment) {
455 Shift = Changes[i].IndentationOffset +
456 Changes[i].StartOfBlockComment->StartOfTokenColumn -
457 Changes[i].StartOfTokenColumn;
458 }
459 assert(Shift >= 0);
460 Changes[i].Spaces += Shift;
Andi-Bogdan Postelnicua9a8fde2016-10-26 07:44:51 +0000461 if (i + 1 != Changes.size())
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +0000462 Changes[i + 1].PreviousEndOfTokenColumn += Shift;
463 Changes[i].StartOfTokenColumn += Shift;
Manuel Klimek4fe43002013-05-22 12:51:29 +0000464 }
465}
466
467void WhitespaceManager::alignEscapedNewlines() {
Daniel Jaspera49393f2013-08-28 09:07:32 +0000468 unsigned MaxEndOfLine =
469 Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit;
Manuel Klimek4fe43002013-05-22 12:51:29 +0000470 unsigned StartOfMacro = 0;
471 for (unsigned i = 1, e = Changes.size(); i < e; ++i) {
472 Change &C = Changes[i];
473 if (C.NewlinesBefore > 0) {
474 if (C.ContinuesPPDirective) {
Daniel Jaspera49393f2013-08-28 09:07:32 +0000475 MaxEndOfLine = std::max(C.PreviousEndOfTokenColumn + 2, MaxEndOfLine);
Manuel Klimek4fe43002013-05-22 12:51:29 +0000476 } else {
477 alignEscapedNewlines(StartOfMacro + 1, i, MaxEndOfLine);
Daniel Jaspera49393f2013-08-28 09:07:32 +0000478 MaxEndOfLine = Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit;
Manuel Klimek4fe43002013-05-22 12:51:29 +0000479 StartOfMacro = i;
480 }
481 }
482 }
483 alignEscapedNewlines(StartOfMacro + 1, Changes.size(), MaxEndOfLine);
484}
485
486void WhitespaceManager::alignEscapedNewlines(unsigned Start, unsigned End,
487 unsigned Column) {
488 for (unsigned i = Start; i < End; ++i) {
489 Change &C = Changes[i];
490 if (C.NewlinesBefore > 0) {
491 assert(C.ContinuesPPDirective);
492 if (C.PreviousEndOfTokenColumn + 1 > Column)
493 C.EscapedNewlineColumn = 0;
494 else
495 C.EscapedNewlineColumn = Column;
496 }
497 }
498}
499
500void WhitespaceManager::generateChanges() {
501 for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
502 const Change &C = Changes[i];
Daniel Jasper47b35ae2015-01-29 10:47:14 +0000503 if (i > 0) {
504 assert(Changes[i - 1].OriginalWhitespaceRange.getBegin() !=
505 C.OriginalWhitespaceRange.getBegin() &&
506 "Generating two replacements for the same location");
507 }
Manuel Klimek4fe43002013-05-22 12:51:29 +0000508 if (C.CreateReplacement) {
Alexander Kornienko9e649af2013-09-11 12:25:57 +0000509 std::string ReplacementText = C.PreviousLinePostfix;
510 if (C.ContinuesPPDirective)
511 appendNewlineText(ReplacementText, C.NewlinesBefore,
512 C.PreviousEndOfTokenColumn, C.EscapedNewlineColumn);
513 else
514 appendNewlineText(ReplacementText, C.NewlinesBefore);
Alexander Kornienko67d9c8c2014-04-17 16:12:46 +0000515 appendIndentText(ReplacementText, C.IndentLevel, std::max(0, C.Spaces),
516 C.StartOfTokenColumn - std::max(0, C.Spaces));
Alexander Kornienko9e649af2013-09-11 12:25:57 +0000517 ReplacementText.append(C.CurrentLinePrefix);
Manuel Klimek4fe43002013-05-22 12:51:29 +0000518 storeReplacement(C.OriginalWhitespaceRange, ReplacementText);
519 }
520 }
521}
522
Craig Toppere335f252015-10-04 04:53:55 +0000523void WhitespaceManager::storeReplacement(SourceRange Range,
Manuel Klimek4fe43002013-05-22 12:51:29 +0000524 StringRef Text) {
525 unsigned WhitespaceLength = SourceMgr.getFileOffset(Range.getEnd()) -
526 SourceMgr.getFileOffset(Range.getBegin());
527 // Don't create a replacement, if it does not change anything.
528 if (StringRef(SourceMgr.getCharacterData(Range.getBegin()),
Daniel Jasper3ac9b9e2013-07-08 14:34:09 +0000529 WhitespaceLength) == Text)
Manuel Klimek4fe43002013-05-22 12:51:29 +0000530 return;
Eric Liu40ef2fb2016-08-01 10:16:37 +0000531 auto Err = Replaces.add(tooling::Replacement(
Manuel Klimek4fe43002013-05-22 12:51:29 +0000532 SourceMgr, CharSourceRange::getCharRange(Range), Text));
Eric Liu40ef2fb2016-08-01 10:16:37 +0000533 // FIXME: better error handling. For now, just print an error message in the
534 // release version.
Piotr Padlewski1ec383c2016-12-23 11:40:44 +0000535 if (Err) {
Eric Liu40ef2fb2016-08-01 10:16:37 +0000536 llvm::errs() << llvm::toString(std::move(Err)) << "\n";
Piotr Padlewski1ec383c2016-12-23 11:40:44 +0000537 assert(false);
538 }
Alexander Kornienkocb45bc12013-04-15 14:28:00 +0000539}
540
Alexander Kornienko9e649af2013-09-11 12:25:57 +0000541void WhitespaceManager::appendNewlineText(std::string &Text,
542 unsigned Newlines) {
543 for (unsigned i = 0; i < Newlines; ++i)
544 Text.append(UseCRLF ? "\r\n" : "\n");
Alexander Kornienkocb45bc12013-04-15 14:28:00 +0000545}
546
Alexander Kornienko9e649af2013-09-11 12:25:57 +0000547void WhitespaceManager::appendNewlineText(std::string &Text, unsigned Newlines,
548 unsigned PreviousEndOfTokenColumn,
549 unsigned EscapedNewlineColumn) {
Alexander Kornienko555efc32013-06-11 16:01:49 +0000550 if (Newlines > 0) {
Alexander Kornienkocb45bc12013-04-15 14:28:00 +0000551 unsigned Offset =
Manuel Klimek4fe43002013-05-22 12:51:29 +0000552 std::min<int>(EscapedNewlineColumn - 1, PreviousEndOfTokenColumn);
Alexander Kornienko555efc32013-06-11 16:01:49 +0000553 for (unsigned i = 0; i < Newlines; ++i) {
Benjamin Kramerddf1cda2015-05-28 19:55:49 +0000554 Text.append(EscapedNewlineColumn - Offset - 1, ' ');
Alexander Kornienko9e649af2013-09-11 12:25:57 +0000555 Text.append(UseCRLF ? "\\\r\n" : "\\\n");
Alexander Kornienkocb45bc12013-04-15 14:28:00 +0000556 Offset = 0;
557 }
558 }
Manuel Klimekb9eae4c2013-05-13 09:22:11 +0000559}
560
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +0000561void WhitespaceManager::appendIndentText(std::string &Text,
562 unsigned IndentLevel, unsigned Spaces,
Alexander Kornienkodb4c21f2013-09-27 09:45:40 +0000563 unsigned WhitespaceStartColumn) {
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +0000564 switch (Style.UseTab) {
565 case FormatStyle::UT_Never:
Benjamin Kramerddf1cda2015-05-28 19:55:49 +0000566 Text.append(Spaces, ' ');
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +0000567 break;
568 case FormatStyle::UT_Always: {
Alexander Kornienkodb4c21f2013-09-27 09:45:40 +0000569 unsigned FirstTabWidth =
570 Style.TabWidth - WhitespaceStartColumn % Style.TabWidth;
571 // Indent with tabs only when there's at least one full tab.
572 if (FirstTabWidth + Style.TabWidth <= Spaces) {
573 Spaces -= FirstTabWidth;
574 Text.append("\t");
575 }
Benjamin Kramerddf1cda2015-05-28 19:55:49 +0000576 Text.append(Spaces / Style.TabWidth, '\t');
577 Text.append(Spaces % Style.TabWidth, ' ');
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +0000578 break;
579 }
580 case FormatStyle::UT_ForIndentation:
581 if (WhitespaceStartColumn == 0) {
582 unsigned Indentation = IndentLevel * Style.IndentWidth;
Alexander Kornienko45dc1b22013-09-27 16:40:11 +0000583 // This happens, e.g. when a line in a block comment is indented less than
584 // the first one.
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +0000585 if (Indentation > Spaces)
586 Indentation = Spaces;
587 unsigned Tabs = Indentation / Style.TabWidth;
Benjamin Kramerddf1cda2015-05-28 19:55:49 +0000588 Text.append(Tabs, '\t');
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +0000589 Spaces -= Tabs * Style.TabWidth;
590 }
Benjamin Kramerddf1cda2015-05-28 19:55:49 +0000591 Text.append(Spaces, ' ');
Alexander Kornienko3c3d09c2013-09-27 16:14:22 +0000592 break;
Marianne Mailhot-Sarrasin51fe2792016-04-14 14:52:26 +0000593 case FormatStyle::UT_ForContinuationAndIndentation:
594 if (WhitespaceStartColumn == 0) {
595 unsigned Tabs = Spaces / Style.TabWidth;
596 Text.append(Tabs, '\t');
597 Spaces -= Tabs * Style.TabWidth;
598 }
599 Text.append(Spaces, ' ');
600 break;
Alexander Kornienko9e649af2013-09-11 12:25:57 +0000601 }
Alexander Kornienkocb45bc12013-04-15 14:28:00 +0000602}
603
Alexander Kornienkocb45bc12013-04-15 14:28:00 +0000604} // namespace format
605} // namespace clang