22
WhitespaceManager::Change::IsBeforeInFile::operator()(const Change &C1,
23
const Change &C2) const {
24
return SourceMgr.isBeforeInTranslationUnit(
25
C1.OriginalWhitespaceRange.getBegin(),
26
C2.OriginalWhitespaceRange.getBegin());
29
WhitespaceManager::Change::Change(
30
bool CreateReplacement, const SourceRange &OriginalWhitespaceRange,
31
unsigned Spaces, unsigned StartOfTokenColumn, unsigned NewlinesBefore,
32
StringRef PreviousLinePostfix, StringRef CurrentLinePrefix,
33
tok::TokenKind Kind, bool ContinuesPPDirective)
34
: CreateReplacement(CreateReplacement),
35
OriginalWhitespaceRange(OriginalWhitespaceRange),
36
StartOfTokenColumn(StartOfTokenColumn), NewlinesBefore(NewlinesBefore),
37
PreviousLinePostfix(PreviousLinePostfix),
38
CurrentLinePrefix(CurrentLinePrefix), Kind(Kind),
39
ContinuesPPDirective(ContinuesPPDirective), Spaces(Spaces) {}
21
41
void WhitespaceManager::replaceWhitespace(const AnnotatedToken &Tok,
22
unsigned NewLines, unsigned Spaces,
23
unsigned WhitespaceStartColumn) {
24
// 2+ newlines mean an empty line separating logic scopes.
42
unsigned Newlines, unsigned Spaces,
43
unsigned StartOfTokenColumn,
45
Changes.push_back(Change(
46
true, Tok.FormatTok.WhitespaceRange,
47
Spaces, StartOfTokenColumn, Newlines, "", "", Tok.FormatTok.Tok.getKind(),
48
InPPDirective && !Tok.FormatTok.IsFirst));
28
50
// Align line comments if they are trailing or if they continue other
29
51
// trailing comments.
52
// FIXME: Pull this out and generalize so it works the same way in broken
53
// comments and unbroken comments with trailing whitespace.
30
54
if (Tok.isTrailingComment()) {
31
55
SourceLocation TokenEndLoc = Tok.FormatTok.getStartOfNonWhitespace()
32
56
.getLocWithOffset(Tok.FormatTok.TokenLength);
34
58
if (Tok.FormatTok.TrailingWhiteSpaceLength != 0)
35
59
Replaces.insert(tooling::Replacement(
36
60
SourceMgr, TokenEndLoc, Tok.FormatTok.TrailingWhiteSpaceLength, ""));
38
bool LineExceedsColumnLimit =
39
Spaces + WhitespaceStartColumn + Tok.FormatTok.TokenLength >
41
// Align comment with other comments.
42
if ((Tok.Parent != NULL || !Comments.empty()) && !LineExceedsColumnLimit) {
43
StoredComment Comment;
44
Comment.Tok = Tok.FormatTok;
45
Comment.Spaces = Spaces;
46
Comment.NewLines = NewLines;
48
NewLines > 0 ? Spaces : WhitespaceStartColumn + Spaces;
49
Comment.MaxColumn = Style.ColumnLimit - Tok.FormatTok.TokenLength;
50
Comment.Untouchable = false;
51
Comments.push_back(Comment);
56
// If this line does not have a trailing comment, align the stored comments.
57
if (Tok.Children.empty() && !Tok.isTrailingComment())
60
storeReplacement(Tok.FormatTok, getNewLineText(NewLines, Spaces));
63
void WhitespaceManager::replacePPWhitespace(const AnnotatedToken &Tok,
64
unsigned NewLines, unsigned Spaces,
65
unsigned WhitespaceStartColumn) {
66
storeReplacement(Tok.FormatTok,
67
getNewLineText(NewLines, Spaces, WhitespaceStartColumn));
64
void WhitespaceManager::addUntouchableToken(const FormatToken &Tok,
67
Change(false, Tok.WhitespaceRange, /*Spaces=*/0,
68
SourceMgr.getSpellingColumnNumber(Tok.Tok.getLocation()) - 1,
69
Tok.NewlinesBefore, "", "", Tok.Tok.getKind(),
70
InPPDirective && !Tok.IsFirst));
70
73
void WhitespaceManager::breakToken(const FormatToken &Tok, unsigned Offset,
71
unsigned ReplaceChars, StringRef Prefix,
72
StringRef Postfix, bool InPPDirective,
74
unsigned WhitespaceStartColumn) {
75
std::string NewLineText;
77
NewLineText = getNewLineText(1, Spaces);
79
NewLineText = getNewLineText(1, Spaces, WhitespaceStartColumn);
80
std::string ReplacementText = (Prefix + NewLineText + Postfix).str();
81
SourceLocation Location =
82
Tok.getStartOfNonWhitespace().getLocWithOffset(Offset);
84
tooling::Replacement(SourceMgr, Location, ReplaceChars, ReplacementText));
87
const tooling::Replacements &WhitespaceManager::generateReplacements() {
74
unsigned ReplaceChars,
75
StringRef PreviousPostfix,
76
StringRef CurrentPrefix, bool InPPDirective,
78
Changes.push_back(Change(
79
true, SourceRange(Tok.getStartOfNonWhitespace().getLocWithOffset(Offset),
80
Tok.getStartOfNonWhitespace().getLocWithOffset(
81
Offset + ReplaceChars)),
82
Spaces, Spaces, 1, PreviousPostfix, CurrentPrefix,
83
// FIXME: Unify token adjustment, so we don't split it between
84
// BreakableToken and the WhitespaceManager. That would also allow us to
85
// correctly store a tok::TokenKind instead of rolling our own enum.
86
tok::unknown, InPPDirective && !Tok.IsFirst));
92
89
void WhitespaceManager::addReplacement(const SourceLocation &SourceLoc,
95
92
tooling::Replacement(SourceMgr, SourceLoc, ReplaceChars, Text));
98
void WhitespaceManager::addUntouchableComment(unsigned Column) {
99
StoredComment Comment;
100
Comment.MinColumn = Column;
101
Comment.MaxColumn = Column;
102
Comment.Untouchable = true;
103
Comments.push_back(Comment);
95
const tooling::Replacements &WhitespaceManager::generateReplacements() {
99
std::sort(Changes.begin(), Changes.end(), Change::IsBeforeInFile(SourceMgr));
100
calculateLineBreakInformation();
101
alignTrailingComments();
102
alignEscapedNewlines();
108
void WhitespaceManager::calculateLineBreakInformation() {
109
Changes[0].PreviousEndOfTokenColumn = 0;
110
for (unsigned i = 1, e = Changes.size(); i != e; ++i) {
111
unsigned OriginalWhitespaceStart =
112
SourceMgr.getFileOffset(Changes[i].OriginalWhitespaceRange.getBegin());
113
unsigned PreviousOriginalWhitespaceEnd = SourceMgr.getFileOffset(
114
Changes[i - 1].OriginalWhitespaceRange.getEnd());
115
Changes[i - 1].TokenLength =
116
OriginalWhitespaceStart - PreviousOriginalWhitespaceEnd +
117
Changes[i].PreviousLinePostfix.size() +
118
Changes[i - 1].CurrentLinePrefix.size();
120
Changes[i].PreviousEndOfTokenColumn =
121
Changes[i - 1].StartOfTokenColumn + Changes[i - 1].TokenLength;
123
Changes[i - 1].IsTrailingComment =
124
(Changes[i].NewlinesBefore > 0 || Changes[i].Kind == tok::eof) &&
125
Changes[i - 1].Kind == tok::comment;
127
// FIXME: The last token is currently not always an eof token; in those
128
// cases, setting TokenLength of the last token to 0 is wrong.
129
Changes.back().TokenLength = 0;
130
Changes.back().IsTrailingComment = Changes.back().Kind == tok::comment;
133
void WhitespaceManager::alignTrailingComments() {
134
unsigned MinColumn = 0;
135
unsigned MaxColumn = UINT_MAX;
136
unsigned StartOfSequence = 0;
137
bool BreakBeforeNext = false;
138
unsigned Newlines = 0;
139
for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
140
unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
141
// FIXME: Correctly handle ChangeMaxColumn in PP directives.
142
unsigned ChangeMaxColumn = Style.ColumnLimit - Changes[i].TokenLength;
143
Newlines += Changes[i].NewlinesBefore;
144
if (Changes[i].IsTrailingComment) {
145
bool WasAlignedWithStartOfNextLine =
146
// A comment on its own line.
147
Changes[i].NewlinesBefore == 1 &&
148
// Not the last line.
150
// The start of the next token was previously aligned with
151
// the start of this comment.
152
(SourceMgr.getSpellingColumnNumber(
153
Changes[i].OriginalWhitespaceRange.getEnd()) ==
154
SourceMgr.getSpellingColumnNumber(
155
Changes[i + 1].OriginalWhitespaceRange.getEnd())) &&
156
// Which is not a comment itself.
157
Changes[i + 1].Kind != tok::comment;
158
if (BreakBeforeNext || Newlines > 1 ||
159
(ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) ||
160
// Break the comment sequence if the previous line did not end
161
// in a trailing comment.
162
(Changes[i].NewlinesBefore == 1 && i > 0 &&
163
!Changes[i - 1].IsTrailingComment) ||
164
WasAlignedWithStartOfNextLine) {
165
alignTrailingComments(StartOfSequence, i, MinColumn);
166
MinColumn = ChangeMinColumn;
167
MaxColumn = ChangeMaxColumn;
170
MinColumn = std::max(MinColumn, ChangeMinColumn);
171
MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
174
(i == 0) || (Changes[i].NewlinesBefore > 1) ||
175
// Never start a sequence with a comment at the beginning of
177
(Changes[i].NewlinesBefore == 1 && StartOfSequence == i);
181
alignTrailingComments(StartOfSequence, Changes.size(), MinColumn);
184
void WhitespaceManager::alignTrailingComments(unsigned Start, unsigned End,
186
for (unsigned i = Start; i != End; ++i) {
187
if (Changes[i].IsTrailingComment) {
188
assert(Column >= Changes[i].StartOfTokenColumn);
189
Changes[i].Spaces += Column - Changes[i].StartOfTokenColumn;
190
Changes[i].StartOfTokenColumn = Column;
195
void WhitespaceManager::alignEscapedNewlines() {
196
unsigned MaxEndOfLine = 0;
197
unsigned StartOfMacro = 0;
198
for (unsigned i = 1, e = Changes.size(); i < e; ++i) {
199
Change &C = Changes[i];
200
if (C.NewlinesBefore > 0) {
201
if (C.ContinuesPPDirective) {
202
if (Style.AlignEscapedNewlinesLeft)
203
MaxEndOfLine = std::max(C.PreviousEndOfTokenColumn + 2, MaxEndOfLine);
205
MaxEndOfLine = Style.ColumnLimit;
207
alignEscapedNewlines(StartOfMacro + 1, i, MaxEndOfLine);
213
alignEscapedNewlines(StartOfMacro + 1, Changes.size(), MaxEndOfLine);
216
void WhitespaceManager::alignEscapedNewlines(unsigned Start, unsigned End,
218
for (unsigned i = Start; i < End; ++i) {
219
Change &C = Changes[i];
220
if (C.NewlinesBefore > 0) {
221
assert(C.ContinuesPPDirective);
222
if (C.PreviousEndOfTokenColumn + 1 > Column)
223
C.EscapedNewlineColumn = 0;
225
C.EscapedNewlineColumn = Column;
230
void WhitespaceManager::generateChanges() {
231
for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
232
const Change &C = Changes[i];
233
if (C.CreateReplacement) {
234
std::string ReplacementText =
235
C.PreviousLinePostfix +
236
(C.ContinuesPPDirective
237
? getNewLineText(C.NewlinesBefore, C.Spaces,
238
C.PreviousEndOfTokenColumn,
239
C.EscapedNewlineColumn)
240
: getNewLineText(C.NewlinesBefore, C.Spaces)) +
242
storeReplacement(C.OriginalWhitespaceRange, ReplacementText);
247
void WhitespaceManager::storeReplacement(const SourceRange &Range,
249
unsigned WhitespaceLength = SourceMgr.getFileOffset(Range.getEnd()) -
250
SourceMgr.getFileOffset(Range.getBegin());
251
// Don't create a replacement, if it does not change anything.
252
if (StringRef(SourceMgr.getCharacterData(Range.getBegin()),
256
Replaces.insert(tooling::Replacement(
257
SourceMgr, CharSourceRange::getCharRange(Range), Text));
106
260
std::string WhitespaceManager::getNewLineText(unsigned NewLines,
107
261
unsigned Spaces) {
108
return std::string(NewLines, '\n') + std::string(Spaces, ' ');
262
return std::string(NewLines, '\n') + getIndentText(Spaces);
111
265
std::string WhitespaceManager::getNewLineText(unsigned NewLines,
113
unsigned WhitespaceStartColumn) {
267
unsigned PreviousEndOfTokenColumn,
268
unsigned EscapedNewlineColumn) {
114
269
std::string NewLineText;
115
270
if (NewLines > 0) {
116
271
unsigned Offset =
117
std::min<int>(Style.ColumnLimit - 1, WhitespaceStartColumn);
272
std::min<int>(EscapedNewlineColumn - 1, PreviousEndOfTokenColumn);
118
273
for (unsigned i = 0; i < NewLines; ++i) {
119
NewLineText += std::string(Style.ColumnLimit - Offset - 1, ' ');
274
NewLineText += std::string(EscapedNewlineColumn - Offset - 1, ' ');
120
275
NewLineText += "\\\n";
124
return NewLineText + std::string(Spaces, ' ');
127
void WhitespaceManager::alignComments() {
128
unsigned MinColumn = 0;
129
unsigned MaxColumn = UINT_MAX;
130
comment_iterator Start = Comments.begin();
131
for (comment_iterator I = Start, E = Comments.end(); I != E; ++I) {
132
if (I->MinColumn > MaxColumn || I->MaxColumn < MinColumn) {
133
alignComments(Start, I, MinColumn);
134
MinColumn = I->MinColumn;
135
MaxColumn = I->MaxColumn;
138
MinColumn = std::max(MinColumn, I->MinColumn);
139
MaxColumn = std::min(MaxColumn, I->MaxColumn);
142
alignComments(Start, Comments.end(), MinColumn);
146
void WhitespaceManager::alignComments(comment_iterator I, comment_iterator E,
149
if (!I->Untouchable) {
150
unsigned Spaces = I->Spaces + Column - I->MinColumn;
151
storeReplacement(I->Tok, getNewLineText(I->NewLines, Spaces));
157
void WhitespaceManager::storeReplacement(const FormatToken &Tok,
158
const std::string Text) {
159
// Don't create a replacement, if it does not change anything.
160
if (StringRef(SourceMgr.getCharacterData(Tok.WhiteSpaceStart),
161
Tok.WhiteSpaceLength) == Text)
164
Replaces.insert(tooling::Replacement(SourceMgr, Tok.WhiteSpaceStart,
165
Tok.WhiteSpaceLength, Text));
279
return NewLineText + getIndentText(Spaces);
282
std::string WhitespaceManager::getIndentText(unsigned Spaces) {
284
return std::string(Spaces, ' ');
286
return std::string(Spaces / Style.IndentWidth, '\t') +
287
std::string(Spaces % Style.IndentWidth, ' ');
168
290
} // namespace format