WhitespaceManager.cpp revision 251662
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
18namespace clang {
19namespace format {
20
21void WhitespaceManager::replaceWhitespace(const AnnotatedToken &Tok,
22                                          unsigned NewLines, unsigned Spaces,
23                                          unsigned WhitespaceStartColumn) {
24  if (NewLines > 0)
25    alignEscapedNewlines();
26
27  // 2+ newlines mean an empty line separating logic scopes.
28  if (NewLines >= 2)
29    alignComments();
30
31  // Align line comments if they are trailing or if they continue other
32  // trailing comments.
33  if (Tok.isTrailingComment()) {
34    SourceLocation TokenEndLoc = Tok.FormatTok.getStartOfNonWhitespace()
35        .getLocWithOffset(Tok.FormatTok.TokenLength);
36    // Remove the comment's trailing whitespace.
37    if (Tok.FormatTok.TrailingWhiteSpaceLength != 0)
38      Replaces.insert(tooling::Replacement(
39          SourceMgr, TokenEndLoc, Tok.FormatTok.TrailingWhiteSpaceLength, ""));
40
41    bool LineExceedsColumnLimit =
42        Spaces + WhitespaceStartColumn + Tok.FormatTok.TokenLength >
43        Style.ColumnLimit;
44    // Align comment with other comments.
45    if ((Tok.Parent != NULL || !Comments.empty()) &&
46        !LineExceedsColumnLimit) {
47      unsigned MinColumn =
48          NewLines > 0 ? Spaces : WhitespaceStartColumn + Spaces;
49      unsigned MaxColumn = Style.ColumnLimit - Tok.FormatTok.TokenLength;
50      Comments.push_back(StoredToken(
51          Tok.FormatTok.WhiteSpaceStart, Tok.FormatTok.WhiteSpaceLength,
52          MinColumn, MaxColumn, NewLines, Spaces));
53      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
61  storeReplacement(Tok.FormatTok.WhiteSpaceStart,
62                   Tok.FormatTok.WhiteSpaceLength,
63                   getNewLineText(NewLines, Spaces));
64}
65
66void WhitespaceManager::replacePPWhitespace(const AnnotatedToken &Tok,
67                                            unsigned NewLines, unsigned Spaces,
68                                            unsigned WhitespaceStartColumn) {
69  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  }
79}
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) {
86  SourceLocation Location =
87      Tok.getStartOfNonWhitespace().getLocWithOffset(Offset);
88  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  }
103}
104
105const tooling::Replacements &WhitespaceManager::generateReplacements() {
106  alignComments();
107  alignEscapedNewlines();
108  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) {
118  StoredToken Tok = StoredToken(SourceLocation(), 0, Column, Column, 0, 0);
119  Tok.Untouchable = true;
120  Comments.push_back(Tok);
121}
122
123std::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,
130                                              unsigned WhitespaceStartColumn,
131                                              unsigned EscapedNewlineColumn) {
132  std::string NewLineText;
133  if (NewLines > 0) {
134    unsigned Offset =
135        std::min<int>(EscapedNewlineColumn - 1, WhitespaceStartColumn);
136    for (unsigned i = 0; i < NewLines; ++i) {
137      NewLineText += std::string(EscapedNewlineColumn - Offset - 1, ' ');
138      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;
148  token_iterator Start = Comments.begin();
149  for (token_iterator I = Start, E = Comments.end(); I != E; ++I) {
150    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
164void WhitespaceManager::alignComments(token_iterator I, token_iterator E,
165                                      unsigned Column) {
166  while (I != E) {
167    if (!I->Untouchable) {
168      unsigned Spaces = I->Spaces + Column - I->MinColumn;
169      storeReplacement(I->ReplacementLoc, I->ReplacementLength,
170                       getNewLineText(I->NewLines, Spaces));
171    }
172    ++I;
173  }
174}
175
176void 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,
203                                         const std::string Text) {
204  // Don't create a replacement, if it does not change anything.
205  if (StringRef(SourceMgr.getCharacterData(Loc), Length) == Text)
206    return;
207  Replaces.insert(tooling::Replacement(SourceMgr, Loc, Length, Text));
208}
209
210} // namespace format
211} // namespace clang
212