blob: c1303183d31246e17a3ad9098cb79081b89a1237 [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:
Alexander Kornienko919398b2013-04-17 17:34:05 +000029 BreakableToken(const SourceManager &SourceMgr, const FormatToken &Tok,
30 unsigned StartColumn)
31 : Tok(Tok), StartColumn(StartColumn),
32 TokenText(SourceMgr.getCharacterData(Tok.getStartOfNonWhitespace()),
33 Tok.TokenLength) {}
Alexander Kornienko70ce7882013-04-15 14:28:00 +000034 virtual ~BreakableToken() {}
35 virtual unsigned getLineCount() const = 0;
Alexander Kornienko919398b2013-04-17 17:34:05 +000036 virtual unsigned getLineSize(unsigned Index) const = 0;
Alexander Kornienko70ce7882013-04-15 14:28:00 +000037 virtual unsigned getLineLengthAfterSplit(unsigned LineIndex,
Alexander Kornienko919398b2013-04-17 17:34:05 +000038 unsigned TailOffset) const = 0;
Alexander Kornienko70ce7882013-04-15 14:28:00 +000039
40 // Contains starting character index and length of split.
41 typedef std::pair<StringRef::size_type, unsigned> Split;
42 virtual Split getSplit(unsigned LineIndex, unsigned TailOffset,
Alexander Kornienko919398b2013-04-17 17:34:05 +000043 unsigned ColumnLimit) const = 0;
Alexander Kornienko70ce7882013-04-15 14:28:00 +000044 virtual void insertBreak(unsigned LineIndex, unsigned TailOffset, Split Split,
45 bool InPPDirective,
46 WhitespaceManager &Whitespaces) = 0;
47 virtual void trimLine(unsigned LineIndex, unsigned TailOffset,
48 unsigned InPPDirective,
Alexander Kornienko919398b2013-04-17 17:34:05 +000049 WhitespaceManager &Whitespaces) {}
50protected:
51 const FormatToken &Tok;
52 unsigned StartColumn;
53 StringRef TokenText;
Alexander Kornienko70ce7882013-04-15 14:28:00 +000054};
55
56class BreakableStringLiteral : public BreakableToken {
57public:
Alexander Kornienko919398b2013-04-17 17:34:05 +000058 BreakableStringLiteral(const SourceManager &SourceMgr, const FormatToken &Tok,
59 unsigned StartColumn)
60 : BreakableToken(SourceMgr, Tok, StartColumn) {
61 assert(TokenText.startswith("\"") && TokenText.endswith("\""));
62 }
Alexander Kornienko70ce7882013-04-15 14:28:00 +000063
64 virtual unsigned getLineCount() const { return 1; }
65
Alexander Kornienko919398b2013-04-17 17:34:05 +000066 virtual unsigned getLineSize(unsigned Index) const {
Alexander Kornienko70ce7882013-04-15 14:28:00 +000067 return Tok.TokenLength - 2; // Should be in sync with getLine
68 }
69
70 virtual unsigned getLineLengthAfterSplit(unsigned LineIndex,
Alexander Kornienko919398b2013-04-17 17:34:05 +000071 unsigned TailOffset) const {
72 return getDecorationLength() + getLine().size() - TailOffset;
Alexander Kornienko70ce7882013-04-15 14:28:00 +000073 }
74
Alexander Kornienko70ce7882013-04-15 14:28:00 +000075 virtual Split getSplit(unsigned LineIndex, unsigned TailOffset,
Alexander Kornienko919398b2013-04-17 17:34:05 +000076 unsigned ColumnLimit) const {
77 StringRef Text = getLine().substr(TailOffset);
78 if (ColumnLimit <= getDecorationLength())
Alexander Kornienko70ce7882013-04-15 14:28:00 +000079 return Split(StringRef::npos, 0);
Alexander Kornienko919398b2013-04-17 17:34:05 +000080 unsigned MaxSplit = ColumnLimit - getDecorationLength();
Alexander Kornienko70ce7882013-04-15 14:28:00 +000081 assert(MaxSplit < Text.size());
82 StringRef::size_type SpaceOffset = Text.rfind(' ', MaxSplit);
83 if (SpaceOffset != StringRef::npos && SpaceOffset != 0)
84 return Split(SpaceOffset + 1, 0);
85 StringRef::size_type SlashOffset = Text.rfind('/', MaxSplit);
86 if (SlashOffset != StringRef::npos && SlashOffset != 0)
87 return Split(SlashOffset + 1, 0);
88 StringRef::size_type SplitPoint = getStartOfCharacter(Text, MaxSplit);
89 if (SplitPoint != StringRef::npos && SplitPoint > 1)
90 // Do not split at 0.
91 return Split(SplitPoint, 0);
92 return Split(StringRef::npos, 0);
93 }
94
95 virtual void insertBreak(unsigned LineIndex, unsigned TailOffset, Split Split,
96 bool InPPDirective, WhitespaceManager &Whitespaces) {
97 unsigned WhitespaceStartColumn = StartColumn + Split.first + 2;
Alexander Kornienko919398b2013-04-17 17:34:05 +000098 Whitespaces.breakToken(Tok, 1 + TailOffset + Split.first, Split.second,
Alexander Kornienko70ce7882013-04-15 14:28:00 +000099 "\"", "\"", InPPDirective, StartColumn,
100 WhitespaceStartColumn);
101 }
102
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000103private:
Alexander Kornienko919398b2013-04-17 17:34:05 +0000104 StringRef getLine() const {
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000105 // Get string without quotes.
106 // FIXME: Handle string prefixes.
Alexander Kornienko919398b2013-04-17 17:34:05 +0000107 return TokenText.substr(1, TokenText.size() - 2);
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000108 }
109
Alexander Kornienko919398b2013-04-17 17:34:05 +0000110 unsigned getDecorationLength() const { return StartColumn + 2; }
111
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000112 static StringRef::size_type getStartOfCharacter(StringRef Text,
113 StringRef::size_type Offset) {
114 StringRef::size_type NextEscape = Text.find('\\');
115 while (NextEscape != StringRef::npos && NextEscape < Offset) {
116 StringRef::size_type SequenceLength =
117 getEscapeSequenceLength(Text.substr(NextEscape));
118 if (Offset < NextEscape + SequenceLength)
119 return NextEscape;
120 NextEscape = Text.find('\\', NextEscape + SequenceLength);
121 }
122 return Offset;
123 }
124
125 static unsigned getEscapeSequenceLength(StringRef Text) {
126 assert(Text[0] == '\\');
127 if (Text.size() < 2)
128 return 1;
129
130 switch (Text[1]) {
131 case 'u':
132 return 6;
133 case 'U':
134 return 10;
135 case 'x':
136 return getHexLength(Text);
137 default:
138 if (Text[1] >= '0' && Text[1] <= '7')
139 return getOctalLength(Text);
140 return 2;
141 }
142 }
143
144 static unsigned getHexLength(StringRef Text) {
145 unsigned I = 2; // Point after '\x'.
146 while (I < Text.size() && ((Text[I] >= '0' && Text[I] <= '9') ||
147 (Text[I] >= 'a' && Text[I] <= 'f') ||
148 (Text[I] >= 'A' && Text[I] <= 'F'))) {
149 ++I;
150 }
151 return I;
152 }
153
154 static unsigned getOctalLength(StringRef Text) {
155 unsigned I = 1;
156 while (I < Text.size() && I < 4 && (Text[I] >= '0' && Text[I] <= '7')) {
157 ++I;
158 }
159 return I;
160 }
161
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000162};
163
Alexander Kornienko919398b2013-04-17 17:34:05 +0000164class BreakableComment : public BreakableToken {
165public:
166 virtual unsigned getLineSize(unsigned Index) const {
167 return getLine(Index).size();
168 }
169
170 virtual unsigned getLineCount() const { return Lines.size(); }
171
172 virtual unsigned getLineLengthAfterSplit(unsigned LineIndex,
173 unsigned TailOffset) const {
174 return getContentStartColumn(LineIndex, TailOffset) +
175 getLine(LineIndex).size() - TailOffset;
176 }
177
178 virtual Split getSplit(unsigned LineIndex, unsigned TailOffset,
179 unsigned ColumnLimit) const;
180 virtual void insertBreak(unsigned LineIndex, unsigned TailOffset, Split Split,
181 bool InPPDirective, WhitespaceManager &Whitespaces);
182
183protected:
184 BreakableComment(const SourceManager &SourceMgr, const FormatToken &Tok,
185 unsigned StartColumn)
186 : BreakableToken(SourceMgr, Tok, StartColumn) {}
187
188 // Get comment lines without /* */, common prefix and trailing whitespace.
189 // Last line is not trimmed, as it is terminated by */, so its trailing
190 // whitespace is not really trailing.
191 StringRef getLine(unsigned Index) const {
192 return Index < Lines.size() - 1 ? Lines[Index].rtrim() : Lines[Index];
193 }
194
195 unsigned getContentStartColumn(unsigned LineIndex,
196 unsigned TailOffset) const {
197 return (TailOffset == 0 && LineIndex == 0)
198 ? StartColumn
199 : IndentAtLineBreak + Decoration.size();
200 }
201
202 unsigned IndentAtLineBreak;
203 StringRef Decoration;
204 SmallVector<StringRef, 16> Lines;
205};
206
207class BreakableBlockComment : public BreakableComment {
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000208public:
209 BreakableBlockComment(const SourceManager &SourceMgr,
210 const AnnotatedToken &Token, unsigned StartColumn);
211
212 void alignLines(WhitespaceManager &Whitespaces);
213
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000214 virtual unsigned getLineLengthAfterSplit(unsigned LineIndex,
Alexander Kornienko919398b2013-04-17 17:34:05 +0000215 unsigned TailOffset) const {
216 return BreakableComment::getLineLengthAfterSplit(LineIndex, TailOffset) +
217 (LineIndex + 1 < Lines.size() ? 0 : 2);
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000218 }
219
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000220 virtual void trimLine(unsigned LineIndex, unsigned TailOffset,
221 unsigned InPPDirective, WhitespaceManager &Whitespaces);
222
223private:
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000224 unsigned OriginalStartColumn;
225 unsigned CommonPrefixLength;
Alexander Kornienko919398b2013-04-17 17:34:05 +0000226};
227
228class BreakableLineComment : public BreakableComment {
229public:
230 BreakableLineComment(const SourceManager &SourceMgr,
231 const AnnotatedToken &Token, unsigned StartColumn);
232
233private:
234 static StringRef getLineCommentPrefix(StringRef Comment);
Alexander Kornienko70ce7882013-04-15 14:28:00 +0000235};
236
237} // namespace format
238} // namespace clang
239
240#endif // LLVM_CLANG_FORMAT_BREAKABLETOKEN_H