1 //===--- 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 
18 namespace clang {
19 namespace format {
20 
21 void WhitespaceManager::replaceWhitespace(const AnnotatedToken &Tok,
22                                           unsigned NewLines, unsigned Spaces,
23                                           unsigned WhitespaceStartColumn) {
24   // 2+ newlines mean an empty line separating logic scopes.
25   if (NewLines >= 2)
26     alignComments();
27 
28   // Align line comments if they are trailing or if they continue other
29   // trailing comments.
30   if (Tok.isTrailingComment()) {
31     SourceLocation TokenEndLoc = Tok.FormatTok.getStartOfNonWhitespace()
32         .getLocWithOffset(Tok.FormatTok.TokenLength);
33     // Remove the comment's trailing whitespace.
34     if (Tok.FormatTok.TrailingWhiteSpaceLength != 0)
35       Replaces.insert(tooling::Replacement(
36           SourceMgr, TokenEndLoc, Tok.FormatTok.TrailingWhiteSpaceLength, ""));
37 
38     bool LineExceedsColumnLimit =
39         Spaces + WhitespaceStartColumn + Tok.FormatTok.TokenLength >
40         Style.ColumnLimit;
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;
47       Comment.MinColumn =
48           NewLines > 0 ? Spaces : WhitespaceStartColumn + Spaces;
49       Comment.MaxColumn = Style.ColumnLimit - Tok.FormatTok.TokenLength;
50       Comment.Untouchable = false;
51       Comments.push_back(Comment);
52       return;
53     }
54   }
55 
56   // If this line does not have a trailing comment, align the stored comments.
57   if (Tok.Children.empty() && !Tok.isTrailingComment())
58     alignComments();
59 
60   storeReplacement(Tok.FormatTok, getNewLineText(NewLines, Spaces));
61 }
62 
63 void WhitespaceManager::replacePPWhitespace(const AnnotatedToken &Tok,
64                                             unsigned NewLines, unsigned Spaces,
65                                             unsigned WhitespaceStartColumn) {
66   storeReplacement(Tok.FormatTok,
67                    getNewLineText(NewLines, Spaces, WhitespaceStartColumn));
68 }
69 
70 void WhitespaceManager::breakToken(const FormatToken &Tok, unsigned Offset,
71                                    unsigned ReplaceChars, StringRef Prefix,
72                                    StringRef Postfix, bool InPPDirective,
73                                    unsigned Spaces,
74                                    unsigned WhitespaceStartColumn) {
75   std::string NewLineText;
76   if (!InPPDirective)
77     NewLineText = getNewLineText(1, Spaces);
78   else
79     NewLineText = getNewLineText(1, Spaces, WhitespaceStartColumn);
80   std::string ReplacementText = (Prefix + NewLineText + Postfix).str();
81   SourceLocation Location =
82       Tok.getStartOfNonWhitespace().getLocWithOffset(Offset);
83   Replaces.insert(
84       tooling::Replacement(SourceMgr, Location, ReplaceChars, ReplacementText));
85 }
86 
87 const tooling::Replacements &WhitespaceManager::generateReplacements() {
88   alignComments();
89   return Replaces;
90 }
91 
92 void WhitespaceManager::addReplacement(const SourceLocation &SourceLoc,
93                                        unsigned ReplaceChars, StringRef Text) {
94   Replaces.insert(
95       tooling::Replacement(SourceMgr, SourceLoc, ReplaceChars, Text));
96 }
97 
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);
104 }
105 
106 std::string WhitespaceManager::getNewLineText(unsigned NewLines,
107                                               unsigned Spaces) {
108   return std::string(NewLines, '\n') + std::string(Spaces, ' ');
109 }
110 
111 std::string WhitespaceManager::getNewLineText(unsigned NewLines,
112                                               unsigned Spaces,
113                                               unsigned WhitespaceStartColumn) {
114   std::string NewLineText;
115   if (NewLines > 0) {
116     unsigned Offset =
117         std::min<int>(Style.ColumnLimit - 1, WhitespaceStartColumn);
118     for (unsigned i = 0; i < NewLines; ++i) {
119       NewLineText += std::string(Style.ColumnLimit - Offset - 1, ' ');
120       NewLineText += "\\\n";
121       Offset = 0;
122     }
123   }
124   return NewLineText + std::string(Spaces, ' ');
125 }
126 
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;
136       Start = I;
137     } else {
138       MinColumn = std::max(MinColumn, I->MinColumn);
139       MaxColumn = std::min(MaxColumn, I->MaxColumn);
140     }
141   }
142   alignComments(Start, Comments.end(), MinColumn);
143   Comments.clear();
144 }
145 
146 void WhitespaceManager::alignComments(comment_iterator I, comment_iterator E,
147                                       unsigned Column) {
148   while (I != E) {
149     if (!I->Untouchable) {
150       unsigned Spaces = I->Spaces + Column - I->MinColumn;
151       storeReplacement(I->Tok, getNewLineText(I->NewLines, Spaces));
152     }
153     ++I;
154   }
155 }
156 
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)
162     return;
163 
164   Replaces.insert(tooling::Replacement(SourceMgr, Tok.WhiteSpaceStart,
165                                        Tok.WhiteSpaceLength, Text));
166 }
167 
168 } // namespace format
169 } // namespace clang
170