blob: 4ec3de960899fe24e5dba8fb6e8f57878a77d589 [file] [log] [blame]
Alexander Kornienko70ce7882013-04-15 14:28:00 +00001//===--- BreakableToken.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 Contains implementation of BreakableToken class and classes derived
12/// from it.
13///
14//===----------------------------------------------------------------------===//
15
16#include "BreakableToken.h"
17#include <algorithm>
18
19namespace clang {
20namespace format {
21
22BreakableBlockComment::BreakableBlockComment(const SourceManager &SourceMgr,
23 const AnnotatedToken &Token,
24 unsigned StartColumn)
25 : Tok(Token.FormatTok), StartColumn(StartColumn) {
26
27 SourceLocation TokenLoc = Tok.Tok.getLocation();
28 TokenText = StringRef(SourceMgr.getCharacterData(TokenLoc), Tok.TokenLength);
29 assert(TokenText.startswith("/*") && TokenText.endswith("*/"));
30
31 OriginalStartColumn = SourceMgr.getSpellingColumnNumber(TokenLoc) - 1;
32
33 TokenText.substr(2, TokenText.size() - 4).split(Lines, "\n");
34
35 NeedsStar = true;
36 CommonPrefixLength = UINT_MAX;
37 if (Lines.size() == 1) {
38 if (Token.Parent == 0) {
39 // Standalone block comments will be aligned and prefixed with *s.
40 CommonPrefixLength = OriginalStartColumn + 1;
41 } else {
42 // Trailing comments can start on arbitrary column, and available
43 // horizontal space can be too small to align consecutive lines with
44 // the first one. We could, probably, align them to current
45 // indentation level, but now we just wrap them without indentation
46 // and stars.
47 CommonPrefixLength = 0;
48 NeedsStar = false;
49 }
50 } else {
51 for (size_t i = 1; i < Lines.size(); ++i) {
52 size_t FirstNonWhitespace = Lines[i].find_first_not_of(" ");
53 if (FirstNonWhitespace != StringRef::npos) {
54 NeedsStar = NeedsStar && (Lines[i][FirstNonWhitespace] == '*');
55 CommonPrefixLength =
56 std::min<unsigned>(CommonPrefixLength, FirstNonWhitespace);
57 }
58 }
59 }
60 if (CommonPrefixLength == UINT_MAX)
61 CommonPrefixLength = 0;
62
63 IndentAtLineBreak =
64 std::max<int>(StartColumn - OriginalStartColumn + CommonPrefixLength, 0);
65}
66
67void BreakableBlockComment::alignLines(WhitespaceManager &Whitespaces) {
68 SourceLocation TokenLoc = Tok.Tok.getLocation();
69 int IndentDelta = StartColumn - OriginalStartColumn;
70 if (IndentDelta > 0) {
71 std::string WhiteSpace(IndentDelta, ' ');
72 for (size_t i = 1; i < Lines.size(); ++i) {
73 Whitespaces.addReplacement(
74 TokenLoc.getLocWithOffset(Lines[i].data() - TokenText.data()), 0,
75 WhiteSpace);
76 }
77 } else if (IndentDelta < 0) {
78 std::string WhiteSpace(-IndentDelta, ' ');
79 // Check that the line is indented enough.
80 for (size_t i = 1; i < Lines.size(); ++i) {
81 if (!Lines[i].startswith(WhiteSpace))
82 return;
83 }
84 for (size_t i = 1; i < Lines.size(); ++i) {
85 Whitespaces.addReplacement(
86 TokenLoc.getLocWithOffset(Lines[i].data() - TokenText.data()),
87 -IndentDelta, "");
88 }
89 }
90
91 for (unsigned i = 1; i < Lines.size(); ++i)
92 Lines[i] = Lines[i].substr(CommonPrefixLength + (NeedsStar ? 2 : 0));
93}
94
95BreakableToken::Split BreakableBlockComment::getSplit(unsigned LineIndex,
96 unsigned TailOffset,
97 unsigned ColumnLimit) {
98 StringRef Text = getLine(LineIndex).substr(TailOffset);
99 unsigned DecorationLength =
100 (TailOffset == 0 && LineIndex == 0) ? StartColumn + 2 : getPrefixLength();
101 if (ColumnLimit <= DecorationLength + 1)
102 return Split(StringRef::npos, 0);
103
104 unsigned MaxSplit = ColumnLimit - DecorationLength + 1;
105 StringRef::size_type SpaceOffset = Text.rfind(' ', MaxSplit);
106 if (SpaceOffset == StringRef::npos ||
107 Text.find_last_not_of(' ', SpaceOffset) == StringRef::npos) {
108 SpaceOffset = Text.find(' ', MaxSplit);
109 }
110 if (SpaceOffset != StringRef::npos && SpaceOffset != 0) {
111 StringRef BeforeCut = Text.substr(0, SpaceOffset).rtrim();
112 StringRef AfterCut = Text.substr(SpaceOffset).ltrim();
113 return BreakableToken::Split(BeforeCut.size(),
114 AfterCut.begin() - BeforeCut.end());
115 }
116 return BreakableToken::Split(StringRef::npos, 0);
117}
118
119void BreakableBlockComment::insertBreak(unsigned LineIndex, unsigned TailOffset,
120 Split Split, bool InPPDirective,
121 WhitespaceManager &Whitespaces) {
122 StringRef Text = getLine(LineIndex).substr(TailOffset);
123 StringRef AdditionalPrefix = NeedsStar ? "* " : "";
124 if (Text.size() == Split.first + Split.second) {
125 // For all but the last line handle trailing space separately.
126 if (LineIndex < Lines.size() - 1)
127 return;
128 // For the last line we need to break before "*/", but not to add "* ".
129 AdditionalPrefix = "";
130 }
131
132 unsigned WhitespaceStartColumn =
133 Split.first +
134 (LineIndex == 0 && TailOffset == 0 ? StartColumn + 2 : getPrefixLength());
135 unsigned BreakOffset = Text.data() - TokenText.data() + Split.first;
136 unsigned CharsToRemove = Split.second;
137 Whitespaces.breakToken(Tok, BreakOffset, CharsToRemove, "", AdditionalPrefix,
138 InPPDirective, IndentAtLineBreak,
139 WhitespaceStartColumn);
140}
141
142void BreakableBlockComment::trimLine(unsigned LineIndex, unsigned TailOffset,
143 unsigned InPPDirective,
144 WhitespaceManager &Whitespaces) {
145 if (LineIndex == Lines.size() - 1)
146 return;
147 StringRef Text = Lines[LineIndex].substr(TailOffset);
148 if (!Text.endswith(" ") && !InPPDirective)
149 return;
150
151 StringRef TrimmedLine = Text.rtrim();
152 unsigned WhitespaceStartColumn =
153 getLineLengthAfterSplit(LineIndex, TailOffset);
154 unsigned BreakOffset = TrimmedLine.end() - TokenText.data();
155 unsigned CharsToRemove = Text.size() - TrimmedLine.size() + 1;
156 Whitespaces.breakToken(Tok, BreakOffset, CharsToRemove, "", "", InPPDirective,
157 0, WhitespaceStartColumn);
158}
159
160} // namespace format
161} // namespace clang