blob: daf63697b0ce07bde3e139d1b30dc57506c6df66 [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,
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) {}
40
41void WhitespaceManager::replaceWhitespace(const AnnotatedToken &Tok,
42 unsigned Newlines, unsigned Spaces,
43 unsigned StartOfTokenColumn,
44 bool InPPDirective) {
45 Changes.push_back(Change(
Manuel Klimekad3094b2013-05-23 10:56:37 +000046 true, Tok.FormatTok.WhitespaceRange,
Manuel Klimeke573c3f2013-05-22 12:51:29 +000047 Spaces, StartOfTokenColumn, Newlines, "", "", Tok.FormatTok.Tok.getKind(),
48 InPPDirective && !Tok.FormatTok.IsFirst));
Alexander Kornienko70ce7882013-04-15 14:28:00 +000049}
50
Manuel Klimeke573c3f2013-05-22 12:51:29 +000051void WhitespaceManager::addUntouchableToken(const FormatToken &Tok,
52 bool InPPDirective) {
Manuel Klimekad3094b2013-05-23 10:56:37 +000053 Changes.push_back(
54 Change(false, Tok.WhitespaceRange, /*Spaces=*/0,
55 SourceMgr.getSpellingColumnNumber(Tok.Tok.getLocation()) - 1,
56 Tok.NewlinesBefore, "", "", Tok.Tok.getKind(),
57 InPPDirective && !Tok.IsFirst));
Alexander Kornienko70ce7882013-04-15 14:28:00 +000058}
59
60void WhitespaceManager::breakToken(const FormatToken &Tok, unsigned Offset,
Manuel Klimeke573c3f2013-05-22 12:51:29 +000061 unsigned ReplaceChars,
62 StringRef PreviousPostfix,
63 StringRef CurrentPrefix, bool InPPDirective,
64 unsigned Spaces) {
65 Changes.push_back(Change(
66 true, SourceRange(Tok.getStartOfNonWhitespace().getLocWithOffset(Offset),
67 Tok.getStartOfNonWhitespace().getLocWithOffset(
68 Offset + ReplaceChars)),
69 Spaces, Spaces, 1, PreviousPostfix, CurrentPrefix,
70 // FIXME: Unify token adjustment, so we don't split it between
71 // BreakableToken and the WhitespaceManager. That would also allow us to
72 // correctly store a tok::TokenKind instead of rolling our own enum.
73 tok::unknown, InPPDirective && !Tok.IsFirst));
Alexander Kornienko70ce7882013-04-15 14:28:00 +000074}
75
Manuel Klimeke573c3f2013-05-22 12:51:29 +000076const tooling::Replacements &WhitespaceManager::generateReplacements() {
77 if (Changes.empty())
78 return Replaces;
79
80 std::sort(Changes.begin(), Changes.end(), Change::IsBeforeInFile(SourceMgr));
81 calculateLineBreakInformation();
82 alignTrailingComments();
83 alignEscapedNewlines();
84 generateChanges();
85
86 return Replaces;
87}
88
89void WhitespaceManager::calculateLineBreakInformation() {
90 Changes[0].PreviousEndOfTokenColumn = 0;
91 for (unsigned i = 1, e = Changes.size(); i != e; ++i) {
92 unsigned OriginalWhitespaceStart =
93 SourceMgr.getFileOffset(Changes[i].OriginalWhitespaceRange.getBegin());
94 unsigned PreviousOriginalWhitespaceEnd = SourceMgr.getFileOffset(
95 Changes[i - 1].OriginalWhitespaceRange.getEnd());
96 Changes[i - 1].TokenLength =
97 OriginalWhitespaceStart - PreviousOriginalWhitespaceEnd +
98 Changes[i].PreviousLinePostfix.size() +
99 Changes[i - 1].CurrentLinePrefix.size();
100
101 Changes[i].PreviousEndOfTokenColumn =
102 Changes[i - 1].StartOfTokenColumn + Changes[i - 1].TokenLength;
103
104 Changes[i - 1].IsTrailingComment =
105 (Changes[i].NewlinesBefore > 0 || Changes[i].Kind == tok::eof) &&
106 Changes[i - 1].Kind == tok::comment;
107 }
Manuel Klimek0cd57b52013-05-22 14:01:08 +0000108 // FIXME: The last token is currently not always an eof token; in those
109 // cases, setting TokenLength of the last token to 0 is wrong.
110 Changes.back().TokenLength = 0;
Manuel Klimeke573c3f2013-05-22 12:51:29 +0000111 Changes.back().IsTrailingComment = Changes.back().Kind == tok::comment;
112}
113
114void WhitespaceManager::alignTrailingComments() {
115 unsigned MinColumn = 0;
116 unsigned MaxColumn = UINT_MAX;
117 unsigned StartOfSequence = 0;
118 bool BreakBeforeNext = false;
119 unsigned Newlines = 0;
120 for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
121 unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
122 // FIXME: Correctly handle ChangeMaxColumn in PP directives.
123 unsigned ChangeMaxColumn = Style.ColumnLimit - Changes[i].TokenLength;
124 Newlines += Changes[i].NewlinesBefore;
125 if (Changes[i].IsTrailingComment) {
Manuel Klimekebfb88c2013-05-23 11:42:52 +0000126 bool WasAlignedWithStartOfNextLine =
127 // A comment on its own line.
128 Changes[i].NewlinesBefore == 1 &&
129 // Not the last line.
130 i + 1 != e &&
131 // The start of the next token was previously aligned with
132 // the start of this comment.
133 (SourceMgr.getSpellingColumnNumber(
134 Changes[i].OriginalWhitespaceRange.getEnd()) ==
135 SourceMgr.getSpellingColumnNumber(
136 Changes[i + 1].OriginalWhitespaceRange.getEnd())) &&
137 // Which is not a comment itself.
138 Changes[i + 1].Kind != tok::comment;
Manuel Klimeke573c3f2013-05-22 12:51:29 +0000139 if (BreakBeforeNext || Newlines > 1 ||
140 (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) ||
141 // Break the comment sequence if the previous line did not end
142 // in a trailing comment.
143 (Changes[i].NewlinesBefore == 1 && i > 0 &&
Manuel Klimekebfb88c2013-05-23 11:42:52 +0000144 !Changes[i - 1].IsTrailingComment) ||
145 WasAlignedWithStartOfNextLine) {
Manuel Klimeke573c3f2013-05-22 12:51:29 +0000146 alignTrailingComments(StartOfSequence, i, MinColumn);
147 MinColumn = ChangeMinColumn;
148 MaxColumn = ChangeMaxColumn;
149 StartOfSequence = i;
150 } else {
151 MinColumn = std::max(MinColumn, ChangeMinColumn);
152 MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
153 }
Manuel Klimek854ca792013-05-23 20:46:07 +0000154 BreakBeforeNext =
155 (i == 0) || (Changes[i].NewlinesBefore > 1) ||
156 // Never start a sequence with a comment at the beginning of
157 // the line.
158 (Changes[i].NewlinesBefore == 1 && StartOfSequence == i);
Manuel Klimeke573c3f2013-05-22 12:51:29 +0000159 Newlines = 0;
160 }
161 }
162 alignTrailingComments(StartOfSequence, Changes.size(), MinColumn);
163}
164
165void WhitespaceManager::alignTrailingComments(unsigned Start, unsigned End,
166 unsigned Column) {
167 for (unsigned i = Start; i != End; ++i) {
168 if (Changes[i].IsTrailingComment) {
169 assert(Column >= Changes[i].StartOfTokenColumn);
170 Changes[i].Spaces += Column - Changes[i].StartOfTokenColumn;
171 Changes[i].StartOfTokenColumn = Column;
172 }
173 }
174}
175
176void WhitespaceManager::alignEscapedNewlines() {
177 unsigned MaxEndOfLine = 0;
178 unsigned StartOfMacro = 0;
179 for (unsigned i = 1, e = Changes.size(); i < e; ++i) {
180 Change &C = Changes[i];
181 if (C.NewlinesBefore > 0) {
182 if (C.ContinuesPPDirective) {
183 if (Style.AlignEscapedNewlinesLeft)
184 MaxEndOfLine = std::max(C.PreviousEndOfTokenColumn + 2, MaxEndOfLine);
185 else
186 MaxEndOfLine = Style.ColumnLimit;
187 } else {
188 alignEscapedNewlines(StartOfMacro + 1, i, MaxEndOfLine);
189 MaxEndOfLine = 0;
190 StartOfMacro = i;
191 }
192 }
193 }
194 alignEscapedNewlines(StartOfMacro + 1, Changes.size(), MaxEndOfLine);
195}
196
197void WhitespaceManager::alignEscapedNewlines(unsigned Start, unsigned End,
198 unsigned Column) {
199 for (unsigned i = Start; i < End; ++i) {
200 Change &C = Changes[i];
201 if (C.NewlinesBefore > 0) {
202 assert(C.ContinuesPPDirective);
203 if (C.PreviousEndOfTokenColumn + 1 > Column)
204 C.EscapedNewlineColumn = 0;
205 else
206 C.EscapedNewlineColumn = Column;
207 }
208 }
209}
210
211void WhitespaceManager::generateChanges() {
212 for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
213 const Change &C = Changes[i];
214 if (C.CreateReplacement) {
215 std::string ReplacementText =
216 C.PreviousLinePostfix +
217 (C.ContinuesPPDirective
218 ? getNewLineText(C.NewlinesBefore, C.Spaces,
219 C.PreviousEndOfTokenColumn,
220 C.EscapedNewlineColumn)
221 : getNewLineText(C.NewlinesBefore, C.Spaces)) +
222 C.CurrentLinePrefix;
223 storeReplacement(C.OriginalWhitespaceRange, ReplacementText);
224 }
225 }
226}
227
228void WhitespaceManager::storeReplacement(const SourceRange &Range,
229 StringRef Text) {
230 unsigned WhitespaceLength = SourceMgr.getFileOffset(Range.getEnd()) -
231 SourceMgr.getFileOffset(Range.getBegin());
232 // Don't create a replacement, if it does not change anything.
233 if (StringRef(SourceMgr.getCharacterData(Range.getBegin()),
234 WhitespaceLength) ==
235 Text)
236 return;
237 Replaces.insert(tooling::Replacement(
238 SourceMgr, CharSourceRange::getCharRange(Range), Text));
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000239}
240
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000241std::string WhitespaceManager::getNewLineText(unsigned NewLines,
242 unsigned Spaces) {
Manuel Klimek7c9a93e2013-05-13 09:22:11 +0000243 return std::string(NewLines, '\n') + getIndentText(Spaces);
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000244}
245
246std::string WhitespaceManager::getNewLineText(unsigned NewLines,
247 unsigned Spaces,
Manuel Klimeke573c3f2013-05-22 12:51:29 +0000248 unsigned PreviousEndOfTokenColumn,
Daniel Jasper2972d042013-04-25 08:56:26 +0000249 unsigned EscapedNewlineColumn) {
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000250 std::string NewLineText;
251 if (NewLines > 0) {
252 unsigned Offset =
Manuel Klimeke573c3f2013-05-22 12:51:29 +0000253 std::min<int>(EscapedNewlineColumn - 1, PreviousEndOfTokenColumn);
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000254 for (unsigned i = 0; i < NewLines; ++i) {
Daniel Jasper2972d042013-04-25 08:56:26 +0000255 NewLineText += std::string(EscapedNewlineColumn - Offset - 1, ' ');
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000256 NewLineText += "\\\n";
257 Offset = 0;
258 }
259 }
Manuel Klimek7c9a93e2013-05-13 09:22:11 +0000260 return NewLineText + getIndentText(Spaces);
261}
262
263std::string WhitespaceManager::getIndentText(unsigned Spaces) {
Manuel Klimek967d9e92013-05-13 12:53:04 +0000264 if (!Style.UseTab)
Manuel Klimek7c9a93e2013-05-13 09:22:11 +0000265 return std::string(Spaces, ' ');
Manuel Klimek967d9e92013-05-13 12:53:04 +0000266
Manuel Klimek7c9a93e2013-05-13 09:22:11 +0000267 return std::string(Spaces / Style.IndentWidth, '\t') +
268 std::string(Spaces % Style.IndentWidth, ' ');
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000269}
270
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000271} // namespace format
272} // namespace clang