blob: d29cd52b0eedc128c62858d51e409a27420d02d0 [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) {
24 // 2+ newlines mean an empty line separating logic scopes.
25 if (NewLines >= 2)
26 alignComments();
27
Alexander Kornienko70ce7882013-04-15 14:28:00 +000028 // Align line comments if they are trailing or if they continue other
29 // trailing comments.
30 if (Tok.isTrailingComment()) {
Alexander Kornienko919398b2013-04-17 17:34:05 +000031 SourceLocation TokenEndLoc = Tok.FormatTok.getStartOfNonWhitespace()
32 .getLocWithOffset(Tok.FormatTok.TokenLength);
Alexander Kornienko70ce7882013-04-15 14:28:00 +000033 // Remove the comment's trailing whitespace.
Alexander Kornienko919398b2013-04-17 17:34:05 +000034 if (Tok.FormatTok.TrailingWhiteSpaceLength != 0)
Alexander Kornienko70ce7882013-04-15 14:28:00 +000035 Replaces.insert(tooling::Replacement(
Alexander Kornienko919398b2013-04-17 17:34:05 +000036 SourceMgr, TokenEndLoc, Tok.FormatTok.TrailingWhiteSpaceLength, ""));
Alexander Kornienko70ce7882013-04-15 14:28:00 +000037
Alexander Kornienko919398b2013-04-17 17:34:05 +000038 bool LineExceedsColumnLimit =
39 Spaces + WhitespaceStartColumn + Tok.FormatTok.TokenLength >
40 Style.ColumnLimit;
Alexander Kornienko70ce7882013-04-15 14:28:00 +000041 // 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
Alexander Kornienko70ce7882013-04-15 14:28:00 +000060 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();
Alexander Kornienko919398b2013-04-17 17:34:05 +000081 SourceLocation Location =
82 Tok.getStartOfNonWhitespace().getLocWithOffset(Offset);
Alexander Kornienko70ce7882013-04-15 14:28:00 +000083 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
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000106std::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