WhitespaceManager.cpp revision 919398bb40d5d643f38b6595f5e8eac641e89d50
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  // 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
63void WhitespaceManager::replacePPWhitespace(const AnnotatedToken &Tok,
64                                            unsigned NewLines, unsigned Spaces,
65                                            unsigned WhitespaceStartColumn) {
66  storeReplacement(Tok.FormatTok,
67                   getNewLineText(NewLines, Spaces, WhitespaceStartColumn));
68}
69
70void 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
87const tooling::Replacements &WhitespaceManager::generateReplacements() {
88  alignComments();
89  return Replaces;
90}
91
92void WhitespaceManager::addReplacement(const SourceLocation &SourceLoc,
93                                       unsigned ReplaceChars, StringRef Text) {
94  Replaces.insert(
95      tooling::Replacement(SourceMgr, SourceLoc, ReplaceChars, Text));
96}
97
98void 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
106std::string WhitespaceManager::getNewLineText(unsigned NewLines,
107                                              unsigned Spaces) {
108  return std::string(NewLines, '\n') + std::string(Spaces, ' ');
109}
110
111std::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
127void 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
146void 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
157void 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