blob: a75c592bfeda6b574afc17641c9da452e4d75382 [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) {
125 return std::string(NewLines, '\n') + std::string(Spaces, ' ');
126}
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 }
142 return NewLineText + std::string(Spaces, ' ');
143}
144
145void WhitespaceManager::alignComments() {
146 unsigned MinColumn = 0;
147 unsigned MaxColumn = UINT_MAX;
Daniel Jasper2972d042013-04-25 08:56:26 +0000148 token_iterator Start = Comments.begin();
149 for (token_iterator I = Start, E = Comments.end(); I != E; ++I) {
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000150 if (I->MinColumn > MaxColumn || I->MaxColumn < MinColumn) {
151 alignComments(Start, I, MinColumn);
152 MinColumn = I->MinColumn;
153 MaxColumn = I->MaxColumn;
154 Start = I;
155 } else {
156 MinColumn = std::max(MinColumn, I->MinColumn);
157 MaxColumn = std::min(MaxColumn, I->MaxColumn);
158 }
159 }
160 alignComments(Start, Comments.end(), MinColumn);
161 Comments.clear();
162}
163
Daniel Jasper2972d042013-04-25 08:56:26 +0000164void WhitespaceManager::alignComments(token_iterator I, token_iterator E,
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000165 unsigned Column) {
166 while (I != E) {
167 if (!I->Untouchable) {
168 unsigned Spaces = I->Spaces + Column - I->MinColumn;
Daniel Jasper2972d042013-04-25 08:56:26 +0000169 storeReplacement(I->ReplacementLoc, I->ReplacementLength,
170 getNewLineText(I->NewLines, Spaces));
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000171 }
172 ++I;
173 }
174}
175
Daniel Jasper2972d042013-04-25 08:56:26 +0000176void WhitespaceManager::alignEscapedNewlines() {
177 unsigned MinColumn;
178 if (Style.AlignEscapedNewlinesLeft) {
179 MinColumn = 0;
180 for (token_iterator I = EscapedNewlines.begin(), E = EscapedNewlines.end();
181 I != E; ++I) {
182 if (I->MinColumn > MinColumn)
183 MinColumn = I->MinColumn;
184 }
185 } else {
186 MinColumn = Style.ColumnLimit;
187 }
188
189 for (token_iterator I = EscapedNewlines.begin(), E = EscapedNewlines.end();
190 I != E; ++I) {
191 // I->MinColumn - 2 is the end of the previous token (i.e. the
192 // WhitespaceStartColumn).
193 storeReplacement(
194 I->ReplacementLoc, I->ReplacementLength,
195 I->Prefix + getNewLineText(I->NewLines, I->Spaces, I->MinColumn - 2,
196 MinColumn) + I->Postfix);
197
198 }
199 EscapedNewlines.clear();
200}
201
202void WhitespaceManager::storeReplacement(SourceLocation Loc, unsigned Length,
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000203 const std::string Text) {
204 // Don't create a replacement, if it does not change anything.
Daniel Jasper2972d042013-04-25 08:56:26 +0000205 if (StringRef(SourceMgr.getCharacterData(Loc), Length) == Text)
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000206 return;
Daniel Jasper2972d042013-04-25 08:56:26 +0000207 Replaces.insert(tooling::Replacement(SourceMgr, Loc, Length, Text));
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000208}
209
210} // namespace format
211} // namespace clang