blob: 21e38b1e979e9e750f3f55e66878c40ccbbb8016 [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
21void WhitespaceManager::replaceWhitespace(const AnnotatedToken &Tok,
22 unsigned NewLines, unsigned Spaces,
23 unsigned WhitespaceStartColumn) {
Daniel Jasper2972d042013-04-25 08:56:26 +000024 if (NewLines > 0)
25 alignEscapedNewlines();
26
Alexander Kornienko70ce7882013-04-15 14:28:00 +000027 // 2+ newlines mean an empty line separating logic scopes.
28 if (NewLines >= 2)
29 alignComments();
30
Alexander Kornienko70ce7882013-04-15 14:28:00 +000031 // Align line comments if they are trailing or if they continue other
32 // trailing comments.
33 if (Tok.isTrailingComment()) {
Alexander Kornienko919398b2013-04-17 17:34:05 +000034 SourceLocation TokenEndLoc = Tok.FormatTok.getStartOfNonWhitespace()
35 .getLocWithOffset(Tok.FormatTok.TokenLength);
Alexander Kornienko70ce7882013-04-15 14:28:00 +000036 // Remove the comment's trailing whitespace.
Alexander Kornienko919398b2013-04-17 17:34:05 +000037 if (Tok.FormatTok.TrailingWhiteSpaceLength != 0)
Alexander Kornienko70ce7882013-04-15 14:28:00 +000038 Replaces.insert(tooling::Replacement(
Alexander Kornienko919398b2013-04-17 17:34:05 +000039 SourceMgr, TokenEndLoc, Tok.FormatTok.TrailingWhiteSpaceLength, ""));
Alexander Kornienko70ce7882013-04-15 14:28:00 +000040
Alexander Kornienko919398b2013-04-17 17:34:05 +000041 bool LineExceedsColumnLimit =
42 Spaces + WhitespaceStartColumn + Tok.FormatTok.TokenLength >
43 Style.ColumnLimit;
Alexander Kornienko70ce7882013-04-15 14:28:00 +000044 // Align comment with other comments.
Daniel Jasper2972d042013-04-25 08:56:26 +000045 if ((Tok.Parent != NULL || !Comments.empty()) &&
46 !LineExceedsColumnLimit) {
47 unsigned MinColumn =
Alexander Kornienko70ce7882013-04-15 14:28:00 +000048 NewLines > 0 ? Spaces : WhitespaceStartColumn + Spaces;
Daniel Jasper2972d042013-04-25 08:56:26 +000049 unsigned MaxColumn = Style.ColumnLimit - Tok.FormatTok.TokenLength;
50 Comments.push_back(StoredToken(
51 Tok.FormatTok.WhiteSpaceStart, Tok.FormatTok.WhiteSpaceLength,
52 MinColumn, MaxColumn, NewLines, Spaces));
Alexander Kornienko70ce7882013-04-15 14:28:00 +000053 return;
54 }
55 }
56
57 // If this line does not have a trailing comment, align the stored comments.
58 if (Tok.Children.empty() && !Tok.isTrailingComment())
59 alignComments();
60
Daniel Jasper2972d042013-04-25 08:56:26 +000061 storeReplacement(Tok.FormatTok.WhiteSpaceStart,
62 Tok.FormatTok.WhiteSpaceLength,
63 getNewLineText(NewLines, Spaces));
Alexander Kornienko70ce7882013-04-15 14:28:00 +000064}
65
66void WhitespaceManager::replacePPWhitespace(const AnnotatedToken &Tok,
67 unsigned NewLines, unsigned Spaces,
68 unsigned WhitespaceStartColumn) {
Daniel Jasper2972d042013-04-25 08:56:26 +000069 if (NewLines == 0) {
70 replaceWhitespace(Tok, NewLines, Spaces, WhitespaceStartColumn);
71 } else {
72 // The earliest position for "\" is 2 after the last token.
73 unsigned MinColumn = WhitespaceStartColumn + 2;
74 unsigned MaxColumn = Style.ColumnLimit;
75 EscapedNewlines.push_back(StoredToken(
76 Tok.FormatTok.WhiteSpaceStart, Tok.FormatTok.WhiteSpaceLength,
77 MinColumn, MaxColumn, NewLines, Spaces));
78 }
Alexander Kornienko70ce7882013-04-15 14:28:00 +000079}
80
81void WhitespaceManager::breakToken(const FormatToken &Tok, unsigned Offset,
82 unsigned ReplaceChars, StringRef Prefix,
83 StringRef Postfix, bool InPPDirective,
84 unsigned Spaces,
85 unsigned WhitespaceStartColumn) {
Alexander Kornienko919398b2013-04-17 17:34:05 +000086 SourceLocation Location =
87 Tok.getStartOfNonWhitespace().getLocWithOffset(Offset);
Daniel Jasper2972d042013-04-25 08:56:26 +000088 if (InPPDirective) {
89 // The earliest position for "\" is 2 after the last token.
90 unsigned MinColumn = WhitespaceStartColumn + 2;
91 unsigned MaxColumn = Style.ColumnLimit;
92 StoredToken StoredTok = StoredToken(Location, ReplaceChars, MinColumn,
93 MaxColumn, /*NewLines=*/ 1, Spaces);
94 StoredTok.Prefix = Prefix;
95 StoredTok.Postfix = Postfix;
96 EscapedNewlines.push_back(StoredTok);
97 } else {
98 std::string ReplacementText =
99 (Prefix + getNewLineText(1, Spaces) + Postfix).str();
100 Replaces.insert(tooling::Replacement(SourceMgr, Location, ReplaceChars,
101 ReplacementText));
102 }
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000103}
104
105const tooling::Replacements &WhitespaceManager::generateReplacements() {
106 alignComments();
Daniel Jasper2972d042013-04-25 08:56:26 +0000107 alignEscapedNewlines();
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000108 return Replaces;
109}
110
111void WhitespaceManager::addReplacement(const SourceLocation &SourceLoc,
112 unsigned ReplaceChars, StringRef Text) {
113 Replaces.insert(
114 tooling::Replacement(SourceMgr, SourceLoc, ReplaceChars, Text));
115}
116
117void WhitespaceManager::addUntouchableComment(unsigned Column) {
Daniel Jasper2972d042013-04-25 08:56:26 +0000118 StoredToken Tok = StoredToken(SourceLocation(), 0, Column, Column, 0, 0);
119 Tok.Untouchable = true;
120 Comments.push_back(Tok);
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000121}
122
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000123std::string WhitespaceManager::getNewLineText(unsigned NewLines,
124 unsigned Spaces) {
Manuel Klimek7c9a93e2013-05-13 09:22:11 +0000125 return std::string(NewLines, '\n') + getIndentText(Spaces);
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000126}
127
128std::string WhitespaceManager::getNewLineText(unsigned NewLines,
129 unsigned Spaces,
Daniel Jasper2972d042013-04-25 08:56:26 +0000130 unsigned WhitespaceStartColumn,
131 unsigned EscapedNewlineColumn) {
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000132 std::string NewLineText;
133 if (NewLines > 0) {
134 unsigned Offset =
Daniel Jasper2972d042013-04-25 08:56:26 +0000135 std::min<int>(EscapedNewlineColumn - 1, WhitespaceStartColumn);
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000136 for (unsigned i = 0; i < NewLines; ++i) {
Daniel Jasper2972d042013-04-25 08:56:26 +0000137 NewLineText += std::string(EscapedNewlineColumn - Offset - 1, ' ');
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000138 NewLineText += "\\\n";
139 Offset = 0;
140 }
141 }
Manuel Klimek7c9a93e2013-05-13 09:22:11 +0000142 return NewLineText + getIndentText(Spaces);
143}
144
145std::string WhitespaceManager::getIndentText(unsigned Spaces) {
146 if (!Style.UseTab) {
147 return std::string(Spaces, ' ');
148 }
149 return std::string(Spaces / Style.IndentWidth, '\t') +
150 std::string(Spaces % Style.IndentWidth, ' ');
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000151}
152
153void WhitespaceManager::alignComments() {
154 unsigned MinColumn = 0;
155 unsigned MaxColumn = UINT_MAX;
Daniel Jasper2972d042013-04-25 08:56:26 +0000156 token_iterator Start = Comments.begin();
157 for (token_iterator I = Start, E = Comments.end(); I != E; ++I) {
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000158 if (I->MinColumn > MaxColumn || I->MaxColumn < MinColumn) {
159 alignComments(Start, I, MinColumn);
160 MinColumn = I->MinColumn;
161 MaxColumn = I->MaxColumn;
162 Start = I;
163 } else {
164 MinColumn = std::max(MinColumn, I->MinColumn);
165 MaxColumn = std::min(MaxColumn, I->MaxColumn);
166 }
167 }
168 alignComments(Start, Comments.end(), MinColumn);
169 Comments.clear();
170}
171
Daniel Jasper2972d042013-04-25 08:56:26 +0000172void WhitespaceManager::alignComments(token_iterator I, token_iterator E,
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000173 unsigned Column) {
174 while (I != E) {
175 if (!I->Untouchable) {
176 unsigned Spaces = I->Spaces + Column - I->MinColumn;
Daniel Jasper2972d042013-04-25 08:56:26 +0000177 storeReplacement(I->ReplacementLoc, I->ReplacementLength,
178 getNewLineText(I->NewLines, Spaces));
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000179 }
180 ++I;
181 }
182}
183
Daniel Jasper2972d042013-04-25 08:56:26 +0000184void WhitespaceManager::alignEscapedNewlines() {
185 unsigned MinColumn;
186 if (Style.AlignEscapedNewlinesLeft) {
187 MinColumn = 0;
188 for (token_iterator I = EscapedNewlines.begin(), E = EscapedNewlines.end();
189 I != E; ++I) {
190 if (I->MinColumn > MinColumn)
191 MinColumn = I->MinColumn;
192 }
193 } else {
194 MinColumn = Style.ColumnLimit;
195 }
196
197 for (token_iterator I = EscapedNewlines.begin(), E = EscapedNewlines.end();
198 I != E; ++I) {
199 // I->MinColumn - 2 is the end of the previous token (i.e. the
200 // WhitespaceStartColumn).
201 storeReplacement(
202 I->ReplacementLoc, I->ReplacementLength,
203 I->Prefix + getNewLineText(I->NewLines, I->Spaces, I->MinColumn - 2,
204 MinColumn) + I->Postfix);
205
206 }
207 EscapedNewlines.clear();
208}
209
210void WhitespaceManager::storeReplacement(SourceLocation Loc, unsigned Length,
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000211 const std::string Text) {
212 // Don't create a replacement, if it does not change anything.
Daniel Jasper2972d042013-04-25 08:56:26 +0000213 if (StringRef(SourceMgr.getCharacterData(Loc), Length) == Text)
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000214 return;
Daniel Jasper2972d042013-04-25 08:56:26 +0000215 Replaces.insert(tooling::Replacement(SourceMgr, Loc, Length, Text));
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000216}
217
218} // namespace format
219} // namespace clang