16 #include "llvm/ADT/STLExtras.h"
34 : CreateReplacement(CreateReplacement),
35 OriginalWhitespaceRange(OriginalWhitespaceRange),
36 StartOfTokenColumn(StartOfTokenColumn), NewlinesBefore(NewlinesBefore),
37 PreviousLinePostfix(PreviousLinePostfix),
38 CurrentLinePrefix(CurrentLinePrefix), Kind(Kind),
39 ContinuesPPDirective(ContinuesPPDirective),
40 IsStartOfDeclName(IsStartOfDeclName), IndentLevel(IndentLevel),
51 unsigned IndentLevel,
unsigned Spaces,
52 unsigned StartOfTokenColumn,
59 Spaces, StartOfTokenColumn, Newlines,
"",
"", Tok.
Tok.
getKind(),
61 Tok.
is(TT_StartOfName) || Tok.
is(TT_FunctionDeclarationName),
73 Tok.
is(TT_StartOfName) || Tok.
is(TT_FunctionDeclarationName),
79 StringRef PreviousPostfix, StringRef CurrentPrefix,
bool InPPDirective,
80 unsigned Newlines,
unsigned IndentLevel,
int Spaces) {
86 IndentLevel, Spaces, std::max(0, Spaces), Newlines, PreviousPostfix,
87 CurrentPrefix, Tok.
is(TT_LineComment) ? tok::comment : tok::unknown,
89 Tok.
is(TT_StartOfName) || Tok.
is(TT_FunctionDeclarationName),
98 calculateLineBreakInformation();
99 alignConsecutiveDeclarations();
100 alignConsecutiveAssignments();
101 alignTrailingComments();
102 alignEscapedNewlines();
108 void WhitespaceManager::calculateLineBreakInformation() {
109 Changes[0].PreviousEndOfTokenColumn = 0;
110 Change *LastOutsideTokenChange = &Changes[0];
111 for (
unsigned i = 1, e = Changes.size(); i != e; ++i) {
112 unsigned OriginalWhitespaceStart =
113 SourceMgr.
getFileOffset(Changes[i].OriginalWhitespaceRange.getBegin());
114 unsigned PreviousOriginalWhitespaceEnd = SourceMgr.
getFileOffset(
115 Changes[i - 1].OriginalWhitespaceRange.getEnd());
116 Changes[i - 1].TokenLength = OriginalWhitespaceStart -
117 PreviousOriginalWhitespaceEnd +
118 Changes[i].PreviousLinePostfix.size() +
119 Changes[i - 1].CurrentLinePrefix.size();
123 if (Changes[i - 1].IsInsideToken)
124 LastOutsideTokenChange->TokenLength +=
125 Changes[i - 1].TokenLength + Changes[i - 1].Spaces;
127 LastOutsideTokenChange = &Changes[i - 1];
129 Changes[i].PreviousEndOfTokenColumn =
130 Changes[i - 1].StartOfTokenColumn + Changes[i - 1].TokenLength;
132 Changes[i - 1].IsTrailingComment =
133 (Changes[i].NewlinesBefore > 0 || Changes[i].Kind ==
tok::eof ||
134 (Changes[i].IsInsideToken && Changes[i].Kind == tok::comment)) &&
135 Changes[i - 1].Kind == tok::comment;
139 Changes.back().TokenLength = 0;
140 Changes.back().IsTrailingComment = Changes.back().Kind == tok::comment;
142 const WhitespaceManager::Change *LastBlockComment =
nullptr;
143 for (
auto &Change : Changes) {
146 if (Change.IsInsideToken)
147 Change.IsTrailingComment =
false;
148 Change.StartOfBlockComment =
nullptr;
149 Change.IndentationOffset = 0;
150 if (Change.Kind == tok::comment) {
151 LastBlockComment = &Change;
152 }
else if (Change.Kind == tok::unknown) {
153 if ((Change.StartOfBlockComment = LastBlockComment))
154 Change.IndentationOffset =
155 Change.StartOfTokenColumn -
156 Change.StartOfBlockComment->StartOfTokenColumn;
158 LastBlockComment =
nullptr;
164 template <
typename F>
168 bool FoundMatchOnLine =
false;
170 for (
unsigned i = Start; i !=
End; ++i) {
171 if (Changes[i].NewlinesBefore > 0) {
172 FoundMatchOnLine =
false;
179 if (!FoundMatchOnLine &&
Matches(Changes[i])) {
180 FoundMatchOnLine =
true;
181 Shift = Column - Changes[i].StartOfTokenColumn;
182 Changes[i].Spaces +=
Shift;
186 Changes[i].StartOfTokenColumn +=
Shift;
187 if (i + 1 != Changes.size())
188 Changes[i + 1].PreviousEndOfTokenColumn += Shift;
198 template <
typename F>
201 unsigned MinColumn = 0;
205 unsigned StartOfSequence = 0;
206 unsigned EndOfSequence = 0;
214 unsigned NestingLevelOfLastMatch = 0;
215 unsigned NestingLevel = 0;
220 unsigned CommasBeforeLastMatch = 0;
221 unsigned CommasBeforeMatch = 0;
224 bool FoundMatchOnLine =
false;
233 auto AlignCurrentSequence = [&] {
234 if (StartOfSequence > 0 && StartOfSequence < EndOfSequence)
243 for (
unsigned i = 0, e = Changes.size(); i != e; ++i) {
244 if (Changes[i].NewlinesBefore != 0) {
245 CommasBeforeMatch = 0;
249 if (Changes[i].NewlinesBefore > 1 || !FoundMatchOnLine)
250 AlignCurrentSequence();
252 FoundMatchOnLine =
false;
255 if (Changes[i].
Kind == tok::comma) {
257 }
else if (Changes[i].
Kind == tok::r_brace ||
258 Changes[i].
Kind == tok::r_paren ||
259 Changes[i].
Kind == tok::r_square) {
261 }
else if (Changes[i].
Kind == tok::l_brace ||
262 Changes[i].
Kind == tok::l_paren ||
263 Changes[i].
Kind == tok::l_square) {
266 NestingLevelOfLastMatch = std::min(NestingLevelOfLastMatch, NestingLevel);
276 if (FoundMatchOnLine || CommasBeforeMatch != CommasBeforeLastMatch ||
277 NestingLevel != NestingLevelOfLastMatch)
278 AlignCurrentSequence();
280 CommasBeforeLastMatch = CommasBeforeMatch;
281 NestingLevelOfLastMatch = NestingLevel;
282 FoundMatchOnLine =
true;
284 if (StartOfSequence == 0)
287 unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
288 int LineLengthAfter = -Changes[i].Spaces;
289 for (
unsigned j = i; j != e && Changes[j].NewlinesBefore == 0; ++j)
290 LineLengthAfter += Changes[j].Spaces + Changes[j].TokenLength;
291 unsigned ChangeMaxColumn = Style.
ColumnLimit - LineLengthAfter;
294 if (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn ||
295 CommasBeforeLastMatch != CommasBeforeMatch) {
296 AlignCurrentSequence();
300 MinColumn = std::max(MinColumn, ChangeMinColumn);
301 MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
304 EndOfSequence = Changes.size();
305 AlignCurrentSequence();
308 void WhitespaceManager::alignConsecutiveAssignments() {
313 [&](
const Change &
C) {
315 if (C.NewlinesBefore > 0)
319 if (&C != &Changes.back() && (&C + 1)->NewlinesBefore > 0)
322 return C.Kind == tok::equal;
327 void WhitespaceManager::alignConsecutiveDeclarations() {
338 AlignTokens(Style, [](Change
const &C) {
return C.IsStartOfDeclName; },
342 void WhitespaceManager::alignTrailingComments() {
343 unsigned MinColumn = 0;
345 unsigned StartOfSequence = 0;
346 bool BreakBeforeNext =
false;
347 unsigned Newlines = 0;
348 for (
unsigned i = 0, e = Changes.size(); i != e; ++i) {
349 if (Changes[i].StartOfBlockComment)
351 Newlines += Changes[i].NewlinesBefore;
352 if (!Changes[i].IsTrailingComment)
355 unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
356 unsigned ChangeMaxColumn = Style.
ColumnLimit - Changes[i].TokenLength;
360 if (!Changes[i].CreateReplacement)
361 ChangeMaxColumn = ChangeMinColumn;
363 if (i + 1 != e && Changes[i + 1].ContinuesPPDirective)
364 ChangeMaxColumn -= 2;
367 bool FollowsRBraceInColumn0 = i > 0 && Changes[i].NewlinesBefore == 0 &&
368 Changes[i - 1].Kind == tok::r_brace &&
369 Changes[i - 1].StartOfTokenColumn == 0;
370 bool WasAlignedWithStartOfNextLine =
false;
371 if (Changes[i].NewlinesBefore == 1) {
373 Changes[i].OriginalWhitespaceRange.getEnd());
374 for (
unsigned j = i + 1; j != e; ++j) {
375 if (Changes[j].
Kind != tok::comment) {
377 Changes[j].OriginalWhitespaceRange.getEnd());
380 WasAlignedWithStartOfNextLine =
381 CommentColumn == NextColumn ||
388 alignTrailingComments(StartOfSequence, i, MinColumn);
389 MinColumn = ChangeMinColumn;
390 MaxColumn = ChangeMinColumn;
392 }
else if (BreakBeforeNext || Newlines > 1 ||
393 (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) ||
396 (Changes[i].NewlinesBefore == 1 && i > 0 &&
397 !Changes[i - 1].IsTrailingComment) ||
398 WasAlignedWithStartOfNextLine) {
399 alignTrailingComments(StartOfSequence, i, MinColumn);
400 MinColumn = ChangeMinColumn;
401 MaxColumn = ChangeMaxColumn;
404 MinColumn = std::max(MinColumn, ChangeMinColumn);
405 MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
408 (i == 0) || (Changes[i].NewlinesBefore > 1) ||
411 (Changes[i].NewlinesBefore == 1 && StartOfSequence == i);
414 alignTrailingComments(StartOfSequence, Changes.size(), MinColumn);
417 void WhitespaceManager::alignTrailingComments(
unsigned Start,
unsigned End,
419 for (
unsigned i = Start; i !=
End; ++i) {
421 if (Changes[i].IsTrailingComment) {
422 Shift = Column - Changes[i].StartOfTokenColumn;
424 if (Changes[i].StartOfBlockComment) {
425 Shift = Changes[i].IndentationOffset +
426 Changes[i].StartOfBlockComment->StartOfTokenColumn -
427 Changes[i].StartOfTokenColumn;
430 Changes[i].Spaces +=
Shift;
432 Changes[i + 1].PreviousEndOfTokenColumn +=
Shift;
433 Changes[i].StartOfTokenColumn +=
Shift;
437 void WhitespaceManager::alignEscapedNewlines() {
438 unsigned MaxEndOfLine =
440 unsigned StartOfMacro = 0;
441 for (
unsigned i = 1, e = Changes.size(); i < e; ++i) {
442 Change &C = Changes[i];
443 if (C.NewlinesBefore > 0) {
444 if (C.ContinuesPPDirective) {
445 MaxEndOfLine = std::max(C.PreviousEndOfTokenColumn + 2, MaxEndOfLine);
447 alignEscapedNewlines(StartOfMacro + 1, i, MaxEndOfLine);
453 alignEscapedNewlines(StartOfMacro + 1, Changes.size(), MaxEndOfLine);
456 void WhitespaceManager::alignEscapedNewlines(
unsigned Start,
unsigned End,
458 for (
unsigned i = Start; i <
End; ++i) {
459 Change &C = Changes[i];
460 if (C.NewlinesBefore > 0) {
461 assert(C.ContinuesPPDirective);
462 if (C.PreviousEndOfTokenColumn + 1 > Column)
463 C.EscapedNewlineColumn = 0;
465 C.EscapedNewlineColumn =
Column;
470 void WhitespaceManager::generateChanges() {
471 for (
unsigned i = 0, e = Changes.size(); i != e; ++i) {
472 const Change &C = Changes[i];
474 assert(Changes[i - 1].OriginalWhitespaceRange.getBegin() !=
475 C.OriginalWhitespaceRange.getBegin() &&
476 "Generating two replacements for the same location");
478 if (C.CreateReplacement) {
479 std::string ReplacementText = C.PreviousLinePostfix;
480 if (C.ContinuesPPDirective)
481 appendNewlineText(ReplacementText, C.NewlinesBefore,
482 C.PreviousEndOfTokenColumn, C.EscapedNewlineColumn);
484 appendNewlineText(ReplacementText, C.NewlinesBefore);
485 appendIndentText(ReplacementText, C.IndentLevel, std::max(0, C.Spaces),
486 C.StartOfTokenColumn - std::max(0, C.Spaces));
487 ReplacementText.append(C.CurrentLinePrefix);
488 storeReplacement(C.OriginalWhitespaceRange, ReplacementText);
493 void WhitespaceManager::storeReplacement(SourceRange Range,
495 unsigned WhitespaceLength = SourceMgr.
getFileOffset(Range.getEnd()) -
499 WhitespaceLength) ==
Text)
501 Replaces.insert(tooling::Replacement(
505 void WhitespaceManager::appendNewlineText(std::string &Text,
507 for (
unsigned i = 0; i < Newlines; ++i)
508 Text.append(UseCRLF ?
"\r\n" :
"\n");
511 void WhitespaceManager::appendNewlineText(std::string &Text,
unsigned Newlines,
512 unsigned PreviousEndOfTokenColumn,
513 unsigned EscapedNewlineColumn) {
516 std::min<int>(EscapedNewlineColumn - 1, PreviousEndOfTokenColumn);
517 for (
unsigned i = 0; i < Newlines; ++i) {
518 Text.append(EscapedNewlineColumn - Offset - 1,
' ');
519 Text.append(UseCRLF ?
"\\\r\n" :
"\\\n");
525 void WhitespaceManager::appendIndentText(std::string &Text,
526 unsigned IndentLevel,
unsigned Spaces,
527 unsigned WhitespaceStartColumn) {
530 Text.append(Spaces,
' ');
533 unsigned FirstTabWidth =
536 if (FirstTabWidth + Style.
TabWidth <= Spaces) {
537 Spaces -= FirstTabWidth;
540 Text.append(Spaces / Style.
TabWidth,
'\t');
541 Text.append(Spaces % Style.
TabWidth,
' ');
545 if (WhitespaceStartColumn == 0) {
546 unsigned Indentation = IndentLevel * Style.
IndentWidth;
549 if (Indentation > Spaces)
550 Indentation = Spaces;
551 unsigned Tabs = Indentation / Style.
TabWidth;
552 Text.append(Tabs,
'\t');
555 Text.append(Spaces,
' ');
const char * getCharacterData(SourceLocation SL, bool *Invalid=nullptr) const
Return a pointer to the start of the specified location in the appropriate spelling MemoryBuffer...
SourceLocation getLocWithOffset(int Offset) const
Return a source location with the specified offset from this SourceLocation.
tok::TokenKind getKind() const
WhitespaceManager class manages whitespace around tokens and their replacements.
bool isBeforeInTranslationUnit(SourceLocation LHS, SourceLocation RHS) const
Determines the order of 2 source locations in the translation unit.
static CharSourceRange getCharRange(SourceRange R)
Encodes a location in the source.
unsigned getSpellingColumnNumber(SourceLocation Loc, bool *Invalid=nullptr) const
TokenKind
Provides a simple uniform namespace for tokens from all C languages.
SourceLocation getBegin() const
if(T->getSizeExpr()) TRY_TO(TraverseStmt(T-> getSizeExpr()))
unsigned getFileOffset(SourceLocation SpellingLoc) const
Returns the offset from the start of the file that the specified SourceLocation represents.
A trivial tuple used to represent a source range.