blob: d1a925c5160ae217e43215ae0dee2c205ca39153 [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(
46 true, SourceRange(Tok.FormatTok.WhiteSpaceStart,
47 Tok.FormatTok.WhiteSpaceStart.getLocWithOffset(
48 Tok.FormatTok.WhiteSpaceLength)),
49 Spaces, StartOfTokenColumn, Newlines, "", "", Tok.FormatTok.Tok.getKind(),
50 InPPDirective && !Tok.FormatTok.IsFirst));
Alexander Kornienko70ce7882013-04-15 14:28:00 +000051
Alexander Kornienko70ce7882013-04-15 14:28:00 +000052 // Align line comments if they are trailing or if they continue other
53 // trailing comments.
Manuel Klimeke573c3f2013-05-22 12:51:29 +000054 // FIXME: Pull this out and generalize so it works the same way in broken
55 // comments and unbroken comments with trailing whitespace.
Alexander Kornienko70ce7882013-04-15 14:28:00 +000056 if (Tok.isTrailingComment()) {
Alexander Kornienko919398b2013-04-17 17:34:05 +000057 SourceLocation TokenEndLoc = Tok.FormatTok.getStartOfNonWhitespace()
58 .getLocWithOffset(Tok.FormatTok.TokenLength);
Alexander Kornienko70ce7882013-04-15 14:28:00 +000059 // Remove the comment's trailing whitespace.
Alexander Kornienko919398b2013-04-17 17:34:05 +000060 if (Tok.FormatTok.TrailingWhiteSpaceLength != 0)
Alexander Kornienko70ce7882013-04-15 14:28:00 +000061 Replaces.insert(tooling::Replacement(
Alexander Kornienko919398b2013-04-17 17:34:05 +000062 SourceMgr, TokenEndLoc, Tok.FormatTok.TrailingWhiteSpaceLength, ""));
Alexander Kornienko70ce7882013-04-15 14:28:00 +000063 }
Alexander Kornienko70ce7882013-04-15 14:28:00 +000064}
65
Manuel Klimeke573c3f2013-05-22 12:51:29 +000066void WhitespaceManager::addUntouchableToken(const FormatToken &Tok,
67 bool InPPDirective) {
68 Changes.push_back(Change(
69 false,
70 SourceRange(Tok.WhiteSpaceStart,
71 Tok.WhiteSpaceStart.getLocWithOffset(Tok.WhiteSpaceLength)),
72 Tok.WhiteSpaceLength - Tok.NewlinesBefore,
73 SourceMgr.getSpellingColumnNumber(Tok.Tok.getLocation()) - 1,
74 Tok.NewlinesBefore, "", "", Tok.Tok.getKind(),
75 InPPDirective && !Tok.IsFirst));
Alexander Kornienko70ce7882013-04-15 14:28:00 +000076}
77
78void WhitespaceManager::breakToken(const FormatToken &Tok, unsigned Offset,
Manuel Klimeke573c3f2013-05-22 12:51:29 +000079 unsigned ReplaceChars,
80 StringRef PreviousPostfix,
81 StringRef CurrentPrefix, bool InPPDirective,
82 unsigned Spaces) {
83 Changes.push_back(Change(
84 true, SourceRange(Tok.getStartOfNonWhitespace().getLocWithOffset(Offset),
85 Tok.getStartOfNonWhitespace().getLocWithOffset(
86 Offset + ReplaceChars)),
87 Spaces, Spaces, 1, PreviousPostfix, CurrentPrefix,
88 // FIXME: Unify token adjustment, so we don't split it between
89 // BreakableToken and the WhitespaceManager. That would also allow us to
90 // correctly store a tok::TokenKind instead of rolling our own enum.
91 tok::unknown, InPPDirective && !Tok.IsFirst));
Alexander Kornienko70ce7882013-04-15 14:28:00 +000092}
93
94void WhitespaceManager::addReplacement(const SourceLocation &SourceLoc,
95 unsigned ReplaceChars, StringRef Text) {
96 Replaces.insert(
97 tooling::Replacement(SourceMgr, SourceLoc, ReplaceChars, Text));
98}
99
Manuel Klimeke573c3f2013-05-22 12:51:29 +0000100const tooling::Replacements &WhitespaceManager::generateReplacements() {
101 if (Changes.empty())
102 return Replaces;
103
104 std::sort(Changes.begin(), Changes.end(), Change::IsBeforeInFile(SourceMgr));
105 calculateLineBreakInformation();
106 alignTrailingComments();
107 alignEscapedNewlines();
108 generateChanges();
109
110 return Replaces;
111}
112
113void WhitespaceManager::calculateLineBreakInformation() {
114 Changes[0].PreviousEndOfTokenColumn = 0;
115 for (unsigned i = 1, e = Changes.size(); i != e; ++i) {
116 unsigned OriginalWhitespaceStart =
117 SourceMgr.getFileOffset(Changes[i].OriginalWhitespaceRange.getBegin());
118 unsigned PreviousOriginalWhitespaceEnd = SourceMgr.getFileOffset(
119 Changes[i - 1].OriginalWhitespaceRange.getEnd());
120 Changes[i - 1].TokenLength =
121 OriginalWhitespaceStart - PreviousOriginalWhitespaceEnd +
122 Changes[i].PreviousLinePostfix.size() +
123 Changes[i - 1].CurrentLinePrefix.size();
124
125 Changes[i].PreviousEndOfTokenColumn =
126 Changes[i - 1].StartOfTokenColumn + Changes[i - 1].TokenLength;
127
128 Changes[i - 1].IsTrailingComment =
129 (Changes[i].NewlinesBefore > 0 || Changes[i].Kind == tok::eof) &&
130 Changes[i - 1].Kind == tok::comment;
131 }
Manuel Klimek0cd57b52013-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 Klimeke573c3f2013-05-22 12:51:29 +0000135 Changes.back().IsTrailingComment = Changes.back().Kind == tok::comment;
136}
137
138void WhitespaceManager::alignTrailingComments() {
139 unsigned MinColumn = 0;
140 unsigned MaxColumn = UINT_MAX;
141 unsigned StartOfSequence = 0;
142 bool BreakBeforeNext = false;
143 unsigned Newlines = 0;
144 for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
145 unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
146 // FIXME: Correctly handle ChangeMaxColumn in PP directives.
147 unsigned ChangeMaxColumn = Style.ColumnLimit - Changes[i].TokenLength;
148 Newlines += Changes[i].NewlinesBefore;
149 if (Changes[i].IsTrailingComment) {
150 if (BreakBeforeNext || Newlines > 1 ||
151 (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) ||
152 // Break the comment sequence if the previous line did not end
153 // in a trailing comment.
154 (Changes[i].NewlinesBefore == 1 && i > 0 &&
155 !Changes[i - 1].IsTrailingComment)) {
156 alignTrailingComments(StartOfSequence, i, MinColumn);
157 MinColumn = ChangeMinColumn;
158 MaxColumn = ChangeMaxColumn;
159 StartOfSequence = i;
160 } else {
161 MinColumn = std::max(MinColumn, ChangeMinColumn);
162 MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
163 }
164 BreakBeforeNext =
165 (i == 0) || (Changes[i].NewlinesBefore > 1) ||
166 (Changes[i].NewlinesBefore == 1 && !Changes[i - 1].IsTrailingComment);
167 Newlines = 0;
168 }
169 }
170 alignTrailingComments(StartOfSequence, Changes.size(), MinColumn);
171}
172
173void WhitespaceManager::alignTrailingComments(unsigned Start, unsigned End,
174 unsigned Column) {
175 for (unsigned i = Start; i != End; ++i) {
176 if (Changes[i].IsTrailingComment) {
177 assert(Column >= Changes[i].StartOfTokenColumn);
178 Changes[i].Spaces += Column - Changes[i].StartOfTokenColumn;
179 Changes[i].StartOfTokenColumn = Column;
180 }
181 }
182}
183
184void WhitespaceManager::alignEscapedNewlines() {
185 unsigned MaxEndOfLine = 0;
186 unsigned StartOfMacro = 0;
187 for (unsigned i = 1, e = Changes.size(); i < e; ++i) {
188 Change &C = Changes[i];
189 if (C.NewlinesBefore > 0) {
190 if (C.ContinuesPPDirective) {
191 if (Style.AlignEscapedNewlinesLeft)
192 MaxEndOfLine = std::max(C.PreviousEndOfTokenColumn + 2, MaxEndOfLine);
193 else
194 MaxEndOfLine = Style.ColumnLimit;
195 } else {
196 alignEscapedNewlines(StartOfMacro + 1, i, MaxEndOfLine);
197 MaxEndOfLine = 0;
198 StartOfMacro = i;
199 }
200 }
201 }
202 alignEscapedNewlines(StartOfMacro + 1, Changes.size(), MaxEndOfLine);
203}
204
205void WhitespaceManager::alignEscapedNewlines(unsigned Start, unsigned End,
206 unsigned Column) {
207 for (unsigned i = Start; i < End; ++i) {
208 Change &C = Changes[i];
209 if (C.NewlinesBefore > 0) {
210 assert(C.ContinuesPPDirective);
211 if (C.PreviousEndOfTokenColumn + 1 > Column)
212 C.EscapedNewlineColumn = 0;
213 else
214 C.EscapedNewlineColumn = Column;
215 }
216 }
217}
218
219void WhitespaceManager::generateChanges() {
220 for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
221 const Change &C = Changes[i];
222 if (C.CreateReplacement) {
223 std::string ReplacementText =
224 C.PreviousLinePostfix +
225 (C.ContinuesPPDirective
226 ? getNewLineText(C.NewlinesBefore, C.Spaces,
227 C.PreviousEndOfTokenColumn,
228 C.EscapedNewlineColumn)
229 : getNewLineText(C.NewlinesBefore, C.Spaces)) +
230 C.CurrentLinePrefix;
231 storeReplacement(C.OriginalWhitespaceRange, ReplacementText);
232 }
233 }
234}
235
236void WhitespaceManager::storeReplacement(const SourceRange &Range,
237 StringRef Text) {
238 unsigned WhitespaceLength = SourceMgr.getFileOffset(Range.getEnd()) -
239 SourceMgr.getFileOffset(Range.getBegin());
240 // Don't create a replacement, if it does not change anything.
241 if (StringRef(SourceMgr.getCharacterData(Range.getBegin()),
242 WhitespaceLength) ==
243 Text)
244 return;
245 Replaces.insert(tooling::Replacement(
246 SourceMgr, CharSourceRange::getCharRange(Range), Text));
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000247}
248
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000249std::string WhitespaceManager::getNewLineText(unsigned NewLines,
250 unsigned Spaces) {
Manuel Klimek7c9a93e2013-05-13 09:22:11 +0000251 return std::string(NewLines, '\n') + getIndentText(Spaces);
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000252}
253
254std::string WhitespaceManager::getNewLineText(unsigned NewLines,
255 unsigned Spaces,
Manuel Klimeke573c3f2013-05-22 12:51:29 +0000256 unsigned PreviousEndOfTokenColumn,
Daniel Jasper2972d042013-04-25 08:56:26 +0000257 unsigned EscapedNewlineColumn) {
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000258 std::string NewLineText;
259 if (NewLines > 0) {
260 unsigned Offset =
Manuel Klimeke573c3f2013-05-22 12:51:29 +0000261 std::min<int>(EscapedNewlineColumn - 1, PreviousEndOfTokenColumn);
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000262 for (unsigned i = 0; i < NewLines; ++i) {
Daniel Jasper2972d042013-04-25 08:56:26 +0000263 NewLineText += std::string(EscapedNewlineColumn - Offset - 1, ' ');
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000264 NewLineText += "\\\n";
265 Offset = 0;
266 }
267 }
Manuel Klimek7c9a93e2013-05-13 09:22:11 +0000268 return NewLineText + getIndentText(Spaces);
269}
270
271std::string WhitespaceManager::getIndentText(unsigned Spaces) {
Manuel Klimek967d9e92013-05-13 12:53:04 +0000272 if (!Style.UseTab)
Manuel Klimek7c9a93e2013-05-13 09:22:11 +0000273 return std::string(Spaces, ' ');
Manuel Klimek967d9e92013-05-13 12:53:04 +0000274
Manuel Klimek7c9a93e2013-05-13 09:22:11 +0000275 return std::string(Spaces / Style.IndentWidth, '\t') +
276 std::string(Spaces % Style.IndentWidth, ' ');
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000277}
278
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000279} // namespace format
280} // namespace clang