blob: 0609104a6fc57aba6cd493f9c5cf29dbb2e17c7d [file] [log] [blame]
Alexander Kornienko70ce7882013-04-15 14:28:00 +00001//===--- BreakableToken.h - 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 Declares BreakableToken, BreakableStringLiteral, and
12/// BreakableBlockComment classes, that contain token type-specific logic to
13/// break long lines in tokens.
14///
15//===----------------------------------------------------------------------===//
16
17#ifndef LLVM_CLANG_FORMAT_BREAKABLETOKEN_H
18#define LLVM_CLANG_FORMAT_BREAKABLETOKEN_H
19
20#include "TokenAnnotator.h"
21#include "WhitespaceManager.h"
22#include <utility>
23
24namespace clang {
25namespace format {
26
27class BreakableToken {
28public:
29 virtual ~BreakableToken() {}
30 virtual unsigned getLineCount() const = 0;
31 virtual unsigned getLineSize(unsigned Index) = 0;
32 virtual unsigned getLineLengthAfterSplit(unsigned LineIndex,
33 unsigned TailOffset) = 0;
34 virtual unsigned getPrefixLength() = 0;
35 virtual unsigned getSuffixLength(unsigned LineIndex) = 0;
36
37 // Contains starting character index and length of split.
38 typedef std::pair<StringRef::size_type, unsigned> Split;
39 virtual Split getSplit(unsigned LineIndex, unsigned TailOffset,
40 unsigned ColumnLimit) = 0;
41 virtual void insertBreak(unsigned LineIndex, unsigned TailOffset, Split Split,
42 bool InPPDirective,
43 WhitespaceManager &Whitespaces) = 0;
44 virtual void trimLine(unsigned LineIndex, unsigned TailOffset,
45 unsigned InPPDirective,
46 WhitespaceManager &Whitespaces) = 0;
47};
48
49class BreakableStringLiteral : public BreakableToken {
50public:
51 BreakableStringLiteral(const FormatToken &Tok, unsigned StartColumn)
52 : Tok(Tok), StartColumn(StartColumn) {}
53
54 virtual unsigned getLineCount() const { return 1; }
55
56 virtual unsigned getLineSize(unsigned Index) {
57 return Tok.TokenLength - 2; // Should be in sync with getLine
58 }
59
60 virtual unsigned getLineLengthAfterSplit(unsigned LineIndex,
61 unsigned TailOffset) {
62 return getPrefixLength() + getLine(LineIndex).size() - TailOffset +
63 getSuffixLength(LineIndex);
64 }
65
66 virtual unsigned getPrefixLength() { return StartColumn + 1; }
67
68 virtual unsigned getSuffixLength(unsigned LineIndex) { return 1; }
69
70 virtual Split getSplit(unsigned LineIndex, unsigned TailOffset,
71 unsigned ColumnLimit) {
72 StringRef Text = getLine(LineIndex).substr(TailOffset);
73 unsigned DecorationLength = getPrefixLength() + getSuffixLength(0);
74 if (ColumnLimit <= DecorationLength)
75 return Split(StringRef::npos, 0);
76 unsigned MaxSplit = ColumnLimit - DecorationLength;
77 assert(MaxSplit < Text.size());
78 StringRef::size_type SpaceOffset = Text.rfind(' ', MaxSplit);
79 if (SpaceOffset != StringRef::npos && SpaceOffset != 0)
80 return Split(SpaceOffset + 1, 0);
81 StringRef::size_type SlashOffset = Text.rfind('/', MaxSplit);
82 if (SlashOffset != StringRef::npos && SlashOffset != 0)
83 return Split(SlashOffset + 1, 0);
84 StringRef::size_type SplitPoint = getStartOfCharacter(Text, MaxSplit);
85 if (SplitPoint != StringRef::npos && SplitPoint > 1)
86 // Do not split at 0.
87 return Split(SplitPoint, 0);
88 return Split(StringRef::npos, 0);
89 }
90
91 virtual void insertBreak(unsigned LineIndex, unsigned TailOffset, Split Split,
92 bool InPPDirective, WhitespaceManager &Whitespaces) {
93 unsigned WhitespaceStartColumn = StartColumn + Split.first + 2;
94 Whitespaces.breakToken(Tok, TailOffset + Split.first + 1, Split.second,
95 "\"", "\"", InPPDirective, StartColumn,
96 WhitespaceStartColumn);
97 }
98
99 virtual void trimLine(unsigned LineIndex, unsigned TailOffset,
100 unsigned InPPDirective,
101 WhitespaceManager &Whitespaces) {}
102
103private:
104 StringRef getLine(unsigned Index) {
105 // Get string without quotes.
106 // FIXME: Handle string prefixes.
107 return StringRef(Tok.Tok.getLiteralData() + 1, Tok.TokenLength - 2);
108 }
109
110 static StringRef::size_type getStartOfCharacter(StringRef Text,
111 StringRef::size_type Offset) {
112 StringRef::size_type NextEscape = Text.find('\\');
113 while (NextEscape != StringRef::npos && NextEscape < Offset) {
114 StringRef::size_type SequenceLength =
115 getEscapeSequenceLength(Text.substr(NextEscape));
116 if (Offset < NextEscape + SequenceLength)
117 return NextEscape;
118 NextEscape = Text.find('\\', NextEscape + SequenceLength);
119 }
120 return Offset;
121 }
122
123 static unsigned getEscapeSequenceLength(StringRef Text) {
124 assert(Text[0] == '\\');
125 if (Text.size() < 2)
126 return 1;
127
128 switch (Text[1]) {
129 case 'u':
130 return 6;
131 case 'U':
132 return 10;
133 case 'x':
134 return getHexLength(Text);
135 default:
136 if (Text[1] >= '0' && Text[1] <= '7')
137 return getOctalLength(Text);
138 return 2;
139 }
140 }
141
142 static unsigned getHexLength(StringRef Text) {
143 unsigned I = 2; // Point after '\x'.
144 while (I < Text.size() && ((Text[I] >= '0' && Text[I] <= '9') ||
145 (Text[I] >= 'a' && Text[I] <= 'f') ||
146 (Text[I] >= 'A' && Text[I] <= 'F'))) {
147 ++I;
148 }
149 return I;
150 }
151
152 static unsigned getOctalLength(StringRef Text) {
153 unsigned I = 1;
154 while (I < Text.size() && I < 4 && (Text[I] >= '0' && Text[I] <= '7')) {
155 ++I;
156 }
157 return I;
158 }
159
160 const FormatToken &Tok;
161 unsigned StartColumn;
162};
163
164class BreakableBlockComment : public BreakableToken {
165public:
166 BreakableBlockComment(const SourceManager &SourceMgr,
167 const AnnotatedToken &Token, unsigned StartColumn);
168
169 void alignLines(WhitespaceManager &Whitespaces);
170
171 virtual unsigned getLineCount() const { return Lines.size(); }
172
173 virtual unsigned getLineSize(unsigned Index) {
174 return getLine(Index).size();
175 }
176
177 virtual unsigned getLineLengthAfterSplit(unsigned LineIndex,
178 unsigned TailOffset) {
179 unsigned ContentStartColumn = getPrefixLength();
180 if (TailOffset == 0 && LineIndex == 0)
181 ContentStartColumn = StartColumn + 2;
182 return ContentStartColumn + getLine(LineIndex).size() - TailOffset +
183 getSuffixLength(LineIndex);
184 }
185
186 virtual unsigned getPrefixLength() {
187 return IndentAtLineBreak + (NeedsStar ? 2 : 0);
188 }
189
190 virtual unsigned getSuffixLength(unsigned LineIndex) {
191 if (LineIndex + 1 < Lines.size())
192 return 0;
193 return 2;
194 }
195
196 virtual Split getSplit(unsigned LineIndex, unsigned TailOffset,
197 unsigned ColumnLimit);
198
199 virtual void insertBreak(unsigned LineIndex, unsigned TailOffset, Split Split,
200 bool InPPDirective, WhitespaceManager &Whitespaces);
201
202 virtual void trimLine(unsigned LineIndex, unsigned TailOffset,
203 unsigned InPPDirective, WhitespaceManager &Whitespaces);
204
205private:
206 // Get comment lines without /* */, common prefix and trailing whitespace.
207 // Last line is not trimmed, as it is terminated by */, so its trailing
208 // whitespace is not really trailing.
209 StringRef getLine(unsigned Index) {
210 return Index < Lines.size() - 1 ? Lines[Index].rtrim() : Lines[Index];
211 }
212
213 const FormatToken &Tok;
214 const unsigned StartColumn;
215 StringRef TokenText;
216 unsigned OriginalStartColumn;
217 unsigned CommonPrefixLength;
218 unsigned IndentAtLineBreak;
219 bool NeedsStar;
220 SmallVector<StringRef, 16> Lines;
221};
222
223} // namespace format
224} // namespace clang
225
226#endif // LLVM_CLANG_FORMAT_BREAKABLETOKEN_H