1 | //===--- BreakableToken.cpp - Format C++ code -----------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | /// |
9 | /// \file |
10 | /// Contains implementation of BreakableToken class and classes derived |
11 | /// from it. |
12 | /// |
13 | //===----------------------------------------------------------------------===// |
14 | |
15 | #include "BreakableToken.h" |
16 | #include "ContinuationIndenter.h" |
17 | #include "clang/Basic/CharInfo.h" |
18 | #include "clang/Format/Format.h" |
19 | #include "llvm/ADT/STLExtras.h" |
20 | #include "llvm/Support/Debug.h" |
21 | #include <algorithm> |
22 | |
23 | #define DEBUG_TYPE "format-token-breaker" |
24 | |
25 | namespace clang { |
26 | namespace format { |
27 | |
28 | static const char *const Blanks = " \t\v\f\r"; |
29 | static bool IsBlank(char C) { |
30 | switch (C) { |
31 | case ' ': |
32 | case '\t': |
33 | case '\v': |
34 | case '\f': |
35 | case '\r': |
36 | return true; |
37 | default: |
38 | return false; |
39 | } |
40 | } |
41 | |
42 | static StringRef getLineCommentIndentPrefix(StringRef Comment, |
43 | const FormatStyle &Style) { |
44 | static const char *const KnownCStylePrefixes[] = {"///<", "//!<", "///", "//", |
45 | "//!"}; |
46 | static const char *const KnownTextProtoPrefixes[] = {"//", "#", "##", "###", |
47 | "####"}; |
48 | ArrayRef<const char *> KnownPrefixes(KnownCStylePrefixes); |
49 | if (Style.Language == FormatStyle::LK_TextProto) |
50 | KnownPrefixes = KnownTextProtoPrefixes; |
51 | |
52 | StringRef LongestPrefix; |
53 | for (StringRef KnownPrefix : KnownPrefixes) { |
54 | if (Comment.startswith(KnownPrefix)) { |
55 | size_t PrefixLength = KnownPrefix.size(); |
56 | while (PrefixLength < Comment.size() && Comment[PrefixLength] == ' ') |
57 | ++PrefixLength; |
58 | if (PrefixLength > LongestPrefix.size()) |
59 | LongestPrefix = Comment.substr(0, PrefixLength); |
60 | } |
61 | } |
62 | return LongestPrefix; |
63 | } |
64 | |
65 | static BreakableToken::Split |
66 | getCommentSplit(StringRef Text, unsigned ContentStartColumn, |
67 | unsigned ColumnLimit, unsigned TabWidth, |
68 | encoding::Encoding Encoding, const FormatStyle &Style) { |
69 | LLVM_DEBUG(llvm::dbgs() << "Comment split: \"" << Text |
70 | << "\", Column limit: " << ColumnLimit |
71 | << ", Content start: " << ContentStartColumn << "\n"); |
72 | if (ColumnLimit <= ContentStartColumn + 1) |
73 | return BreakableToken::Split(StringRef::npos, 0); |
74 | |
75 | unsigned MaxSplit = ColumnLimit - ContentStartColumn + 1; |
76 | unsigned MaxSplitBytes = 0; |
77 | |
78 | for (unsigned NumChars = 0; |
79 | NumChars < MaxSplit && MaxSplitBytes < Text.size();) { |
80 | unsigned BytesInChar = |
81 | encoding::getCodePointNumBytes(Text[MaxSplitBytes], Encoding); |
82 | NumChars += |
83 | encoding::columnWidthWithTabs(Text.substr(MaxSplitBytes, BytesInChar), |
84 | ContentStartColumn, TabWidth, Encoding); |
85 | MaxSplitBytes += BytesInChar; |
86 | } |
87 | |
88 | StringRef::size_type SpaceOffset = Text.find_last_of(Blanks, MaxSplitBytes); |
89 | |
90 | static auto *const kNumberedListRegexp = new llvm::Regex("^[1-9][0-9]?\\."); |
91 | while (SpaceOffset != StringRef::npos) { |
92 | // Do not split before a number followed by a dot: this would be interpreted |
93 | // as a numbered list, which would prevent re-flowing in subsequent passes. |
94 | if (kNumberedListRegexp->match(Text.substr(SpaceOffset).ltrim(Blanks))) |
95 | SpaceOffset = Text.find_last_of(Blanks, SpaceOffset); |
96 | // In JavaScript, some @tags can be followed by {, and machinery that parses |
97 | // these comments will fail to understand the comment if followed by a line |
98 | // break. So avoid ever breaking before a {. |
99 | else if (Style.Language == FormatStyle::LK_JavaScript && |
100 | SpaceOffset + 1 < Text.size() && Text[SpaceOffset + 1] == '{') |
101 | SpaceOffset = Text.find_last_of(Blanks, SpaceOffset); |
102 | else |
103 | break; |
104 | } |
105 | |
106 | if (SpaceOffset == StringRef::npos || |
107 | // Don't break at leading whitespace. |
108 | Text.find_last_not_of(Blanks, SpaceOffset) == StringRef::npos) { |
109 | // Make sure that we don't break at leading whitespace that |
110 | // reaches past MaxSplit. |
111 | StringRef::size_type FirstNonWhitespace = Text.find_first_not_of(Blanks); |
112 | if (FirstNonWhitespace == StringRef::npos) |
113 | // If the comment is only whitespace, we cannot split. |
114 | return BreakableToken::Split(StringRef::npos, 0); |
115 | SpaceOffset = Text.find_first_of( |
116 | Blanks, std::max<unsigned>(MaxSplitBytes, FirstNonWhitespace)); |
117 | } |
118 | if (SpaceOffset != StringRef::npos && SpaceOffset != 0) { |
119 | // adaptStartOfLine will break after lines starting with /** if the comment |
120 | // is broken anywhere. Avoid emitting this break twice here. |
121 | // Example: in /** longtextcomesherethatbreaks */ (with ColumnLimit 20) will |
122 | // insert a break after /**, so this code must not insert the same break. |
123 | if (SpaceOffset == 1 && Text[SpaceOffset - 1] == '*') |
124 | return BreakableToken::Split(StringRef::npos, 0); |
125 | StringRef BeforeCut = Text.substr(0, SpaceOffset).rtrim(Blanks); |
126 | StringRef AfterCut = Text.substr(SpaceOffset).ltrim(Blanks); |
127 | return BreakableToken::Split(BeforeCut.size(), |
128 | AfterCut.begin() - BeforeCut.end()); |
129 | } |
130 | return BreakableToken::Split(StringRef::npos, 0); |
131 | } |
132 | |
133 | static BreakableToken::Split |
134 | getStringSplit(StringRef Text, unsigned UsedColumns, unsigned ColumnLimit, |
135 | unsigned TabWidth, encoding::Encoding Encoding) { |
136 | // FIXME: Reduce unit test case. |
137 | if (Text.empty()) |
138 | return BreakableToken::Split(StringRef::npos, 0); |
139 | if (ColumnLimit <= UsedColumns) |
140 | return BreakableToken::Split(StringRef::npos, 0); |
141 | unsigned MaxSplit = ColumnLimit - UsedColumns; |
142 | StringRef::size_type SpaceOffset = 0; |
143 | StringRef::size_type SlashOffset = 0; |
144 | StringRef::size_type WordStartOffset = 0; |
145 | StringRef::size_type SplitPoint = 0; |
146 | for (unsigned Chars = 0;;) { |
147 | unsigned Advance; |
148 | if (Text[0] == '\\') { |
149 | Advance = encoding::getEscapeSequenceLength(Text); |
150 | Chars += Advance; |
151 | } else { |
152 | Advance = encoding::getCodePointNumBytes(Text[0], Encoding); |
153 | Chars += encoding::columnWidthWithTabs( |
154 | Text.substr(0, Advance), UsedColumns + Chars, TabWidth, Encoding); |
155 | } |
156 | |
157 | if (Chars > MaxSplit || Text.size() <= Advance) |
158 | break; |
159 | |
160 | if (IsBlank(Text[0])) |
161 | SpaceOffset = SplitPoint; |
162 | if (Text[0] == '/') |
163 | SlashOffset = SplitPoint; |
164 | if (Advance == 1 && !isAlphanumeric(Text[0])) |
165 | WordStartOffset = SplitPoint; |
166 | |
167 | SplitPoint += Advance; |
168 | Text = Text.substr(Advance); |
169 | } |
170 | |
171 | if (SpaceOffset != 0) |
172 | return BreakableToken::Split(SpaceOffset + 1, 0); |
173 | if (SlashOffset != 0) |
174 | return BreakableToken::Split(SlashOffset + 1, 0); |
175 | if (WordStartOffset != 0) |
176 | return BreakableToken::Split(WordStartOffset + 1, 0); |
177 | if (SplitPoint != 0) |
178 | return BreakableToken::Split(SplitPoint, 0); |
179 | return BreakableToken::Split(StringRef::npos, 0); |
180 | } |
181 | |
182 | bool switchesFormatting(const FormatToken &Token) { |
183 | (0) . __assert_fail ("(Token.is(TT_BlockComment) || Token.is(TT_LineComment)) && \"formatting regions are switched by comment tokens\"", "/home/seafit/code_projects/clang_source/clang/lib/Format/BreakableToken.cpp", 184, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert((Token.is(TT_BlockComment) || Token.is(TT_LineComment)) && |
184 | (0) . __assert_fail ("(Token.is(TT_BlockComment) || Token.is(TT_LineComment)) && \"formatting regions are switched by comment tokens\"", "/home/seafit/code_projects/clang_source/clang/lib/Format/BreakableToken.cpp", 184, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true"> "formatting regions are switched by comment tokens"); |
185 | StringRef Content = Token.TokenText.substr(2).ltrim(); |
186 | return Content.startswith("clang-format on") || |
187 | Content.startswith("clang-format off"); |
188 | } |
189 | |
190 | unsigned |
191 | BreakableToken::getLengthAfterCompression(unsigned RemainingTokenColumns, |
192 | Split Split) const { |
193 | // Example: consider the content |
194 | // lala lala |
195 | // - RemainingTokenColumns is the original number of columns, 10; |
196 | // - Split is (4, 2), denoting the two spaces between the two words; |
197 | // |
198 | // We compute the number of columns when the split is compressed into a single |
199 | // space, like: |
200 | // lala lala |
201 | // |
202 | // FIXME: Correctly measure the length of whitespace in Split.second so it |
203 | // works with tabs. |
204 | return RemainingTokenColumns + 1 - Split.second; |
205 | } |
206 | |
207 | unsigned BreakableStringLiteral::getLineCount() const { return 1; } |
208 | |
209 | unsigned BreakableStringLiteral::getRangeLength(unsigned LineIndex, |
210 | unsigned Offset, |
211 | StringRef::size_type Length, |
212 | unsigned StartColumn) const { |
213 | llvm_unreachable("Getting the length of a part of the string literal " |
214 | "indicates that the code tries to reflow it."); |
215 | } |
216 | |
217 | unsigned |
218 | BreakableStringLiteral::getRemainingLength(unsigned LineIndex, unsigned Offset, |
219 | unsigned StartColumn) const { |
220 | return UnbreakableTailLength + Postfix.size() + |
221 | encoding::columnWidthWithTabs(Line.substr(Offset, StringRef::npos), |
222 | StartColumn, Style.TabWidth, Encoding); |
223 | } |
224 | |
225 | unsigned BreakableStringLiteral::getContentStartColumn(unsigned LineIndex, |
226 | bool Break) const { |
227 | return StartColumn + Prefix.size(); |
228 | } |
229 | |
230 | BreakableStringLiteral::BreakableStringLiteral( |
231 | const FormatToken &Tok, unsigned StartColumn, StringRef Prefix, |
232 | StringRef Postfix, unsigned UnbreakableTailLength, bool InPPDirective, |
233 | encoding::Encoding Encoding, const FormatStyle &Style) |
234 | : BreakableToken(Tok, InPPDirective, Encoding, Style), |
235 | StartColumn(StartColumn), Prefix(Prefix), Postfix(Postfix), |
236 | UnbreakableTailLength(UnbreakableTailLength) { |
237 | assert(Tok.TokenText.startswith(Prefix) && Tok.TokenText.endswith(Postfix)); |
238 | Line = Tok.TokenText.substr( |
239 | Prefix.size(), Tok.TokenText.size() - Prefix.size() - Postfix.size()); |
240 | } |
241 | |
242 | BreakableToken::Split BreakableStringLiteral::getSplit( |
243 | unsigned LineIndex, unsigned TailOffset, unsigned ColumnLimit, |
244 | unsigned ContentStartColumn, llvm::Regex &CommentPragmasRegex) const { |
245 | return getStringSplit(Line.substr(TailOffset), ContentStartColumn, |
246 | ColumnLimit - Postfix.size(), Style.TabWidth, Encoding); |
247 | } |
248 | |
249 | void BreakableStringLiteral::insertBreak(unsigned LineIndex, |
250 | unsigned TailOffset, Split Split, |
251 | unsigned ContentIndent, |
252 | WhitespaceManager &Whitespaces) const { |
253 | Whitespaces.replaceWhitespaceInToken( |
254 | Tok, Prefix.size() + TailOffset + Split.first, Split.second, Postfix, |
255 | Prefix, InPPDirective, 1, StartColumn); |
256 | } |
257 | |
258 | BreakableComment::BreakableComment(const FormatToken &Token, |
259 | unsigned StartColumn, bool InPPDirective, |
260 | encoding::Encoding Encoding, |
261 | const FormatStyle &Style) |
262 | : BreakableToken(Token, InPPDirective, Encoding, Style), |
263 | StartColumn(StartColumn) {} |
264 | |
265 | unsigned BreakableComment::getLineCount() const { return Lines.size(); } |
266 | |
267 | BreakableToken::Split |
268 | BreakableComment::getSplit(unsigned LineIndex, unsigned TailOffset, |
269 | unsigned ColumnLimit, unsigned ContentStartColumn, |
270 | llvm::Regex &CommentPragmasRegex) const { |
271 | // Don't break lines matching the comment pragmas regex. |
272 | if (CommentPragmasRegex.match(Content[LineIndex])) |
273 | return Split(StringRef::npos, 0); |
274 | return getCommentSplit(Content[LineIndex].substr(TailOffset), |
275 | ContentStartColumn, ColumnLimit, Style.TabWidth, |
276 | Encoding, Style); |
277 | } |
278 | |
279 | void BreakableComment::compressWhitespace( |
280 | unsigned LineIndex, unsigned TailOffset, Split Split, |
281 | WhitespaceManager &Whitespaces) const { |
282 | StringRef Text = Content[LineIndex].substr(TailOffset); |
283 | // Text is relative to the content line, but Whitespaces operates relative to |
284 | // the start of the corresponding token, so compute the start of the Split |
285 | // that needs to be compressed into a single space relative to the start of |
286 | // its token. |
287 | unsigned BreakOffsetInToken = |
288 | Text.data() - tokenAt(LineIndex).TokenText.data() + Split.first; |
289 | unsigned CharsToRemove = Split.second; |
290 | Whitespaces.replaceWhitespaceInToken( |
291 | tokenAt(LineIndex), BreakOffsetInToken, CharsToRemove, "", "", |
292 | /*InPPDirective=*/false, /*Newlines=*/0, /*Spaces=*/1); |
293 | } |
294 | |
295 | const FormatToken &BreakableComment::tokenAt(unsigned LineIndex) const { |
296 | return Tokens[LineIndex] ? *Tokens[LineIndex] : Tok; |
297 | } |
298 | |
299 | static bool mayReflowContent(StringRef Content) { |
300 | Content = Content.trim(Blanks); |
301 | // Lines starting with '@' commonly have special meaning. |
302 | // Lines starting with '-', '-#', '+' or '*' are bulleted/numbered lists. |
303 | bool hasSpecialMeaningPrefix = false; |
304 | for (StringRef Prefix : |
305 | {"@", "TODO", "FIXME", "XXX", "-# ", "- ", "+ ", "* "}) { |
306 | if (Content.startswith(Prefix)) { |
307 | hasSpecialMeaningPrefix = true; |
308 | break; |
309 | } |
310 | } |
311 | |
312 | // Numbered lists may also start with a number followed by '.' |
313 | // To avoid issues if a line starts with a number which is actually the end |
314 | // of a previous line, we only consider numbers with up to 2 digits. |
315 | static auto *const kNumberedListRegexp = new llvm::Regex("^[1-9][0-9]?\\. "); |
316 | hasSpecialMeaningPrefix = |
317 | hasSpecialMeaningPrefix || kNumberedListRegexp->match(Content); |
318 | |
319 | // Simple heuristic for what to reflow: content should contain at least two |
320 | // characters and either the first or second character must be |
321 | // non-punctuation. |
322 | return Content.size() >= 2 && !hasSpecialMeaningPrefix && |
323 | !Content.endswith("\\") && |
324 | // Note that this is UTF-8 safe, since if isPunctuation(Content[0]) is |
325 | // true, then the first code point must be 1 byte long. |
326 | (!isPunctuation(Content[0]) || !isPunctuation(Content[1])); |
327 | } |
328 | |
329 | BreakableBlockComment::BreakableBlockComment( |
330 | const FormatToken &Token, unsigned StartColumn, |
331 | unsigned OriginalStartColumn, bool FirstInLine, bool InPPDirective, |
332 | encoding::Encoding Encoding, const FormatStyle &Style) |
333 | : BreakableComment(Token, StartColumn, InPPDirective, Encoding, Style), |
334 | DelimitersOnNewline(false), |
335 | UnbreakableTailLength(Token.UnbreakableTailLength) { |
336 | (0) . __assert_fail ("Tok.is(TT_BlockComment) && \"block comment section must start with a block comment\"", "/home/seafit/code_projects/clang_source/clang/lib/Format/BreakableToken.cpp", 337, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(Tok.is(TT_BlockComment) && |
337 | (0) . __assert_fail ("Tok.is(TT_BlockComment) && \"block comment section must start with a block comment\"", "/home/seafit/code_projects/clang_source/clang/lib/Format/BreakableToken.cpp", 337, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true"> "block comment section must start with a block comment"); |
338 | |
339 | StringRef TokenText(Tok.TokenText); |
340 | (0) . __assert_fail ("TokenText.startswith(\"/*\") && TokenText.endswith(\"*/\")", "/home/seafit/code_projects/clang_source/clang/lib/Format/BreakableToken.cpp", 340, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(TokenText.startswith("/*") && TokenText.endswith("*/")); |
341 | TokenText.substr(2, TokenText.size() - 4).split(Lines, "\n"); |
342 | |
343 | int IndentDelta = StartColumn - OriginalStartColumn; |
344 | Content.resize(Lines.size()); |
345 | Content[0] = Lines[0]; |
346 | ContentColumn.resize(Lines.size()); |
347 | // Account for the initial '/*'. |
348 | ContentColumn[0] = StartColumn + 2; |
349 | Tokens.resize(Lines.size()); |
350 | for (size_t i = 1; i < Lines.size(); ++i) |
351 | adjustWhitespace(i, IndentDelta); |
352 | |
353 | // Align decorations with the column of the star on the first line, |
354 | // that is one column after the start "/*". |
355 | DecorationColumn = StartColumn + 1; |
356 | |
357 | // Account for comment decoration patterns like this: |
358 | // |
359 | // /* |
360 | // ** blah blah blah |
361 | // */ |
362 | if (Lines.size() >= 2 && Content[1].startswith("**") && |
363 | static_cast<unsigned>(ContentColumn[1]) == StartColumn) { |
364 | DecorationColumn = StartColumn; |
365 | } |
366 | |
367 | Decoration = "* "; |
368 | if (Lines.size() == 1 && !FirstInLine) { |
369 | // Comments for which FirstInLine is false can start on arbitrary column, |
370 | // and available horizontal space can be too small to align consecutive |
371 | // lines with the first one. |
372 | // FIXME: We could, probably, align them to current indentation level, but |
373 | // now we just wrap them without stars. |
374 | Decoration = ""; |
375 | } |
376 | for (size_t i = 1, e = Lines.size(); i < e && !Decoration.empty(); ++i) { |
377 | // If the last line is empty, the closing "*/" will have a star. |
378 | if (i + 1 == e && Content[i].empty()) |
379 | break; |
380 | if (!Content[i].empty() && i + 1 != e && Decoration.startswith(Content[i])) |
381 | continue; |
382 | while (!Content[i].startswith(Decoration)) |
383 | Decoration = Decoration.substr(0, Decoration.size() - 1); |
384 | } |
385 | |
386 | LastLineNeedsDecoration = true; |
387 | IndentAtLineBreak = ContentColumn[0] + 1; |
388 | for (size_t i = 1, e = Lines.size(); i < e; ++i) { |
389 | if (Content[i].empty()) { |
390 | if (i + 1 == e) { |
391 | // Empty last line means that we already have a star as a part of the |
392 | // trailing */. We also need to preserve whitespace, so that */ is |
393 | // correctly indented. |
394 | LastLineNeedsDecoration = false; |
395 | // Align the star in the last '*/' with the stars on the previous lines. |
396 | if (e >= 2 && !Decoration.empty()) { |
397 | ContentColumn[i] = DecorationColumn; |
398 | } |
399 | } else if (Decoration.empty()) { |
400 | // For all other lines, set the start column to 0 if they're empty, so |
401 | // we do not insert trailing whitespace anywhere. |
402 | ContentColumn[i] = 0; |
403 | } |
404 | continue; |
405 | } |
406 | |
407 | // The first line already excludes the star. |
408 | // The last line excludes the star if LastLineNeedsDecoration is false. |
409 | // For all other lines, adjust the line to exclude the star and |
410 | // (optionally) the first whitespace. |
411 | unsigned DecorationSize = Decoration.startswith(Content[i]) |
412 | ? Content[i].size() |
413 | : Decoration.size(); |
414 | if (DecorationSize) { |
415 | ContentColumn[i] = DecorationColumn + DecorationSize; |
416 | } |
417 | Content[i] = Content[i].substr(DecorationSize); |
418 | if (!Decoration.startswith(Content[i])) |
419 | IndentAtLineBreak = |
420 | std::min<int>(IndentAtLineBreak, std::max(0, ContentColumn[i])); |
421 | } |
422 | IndentAtLineBreak = std::max<unsigned>(IndentAtLineBreak, Decoration.size()); |
423 | |
424 | // Detect a multiline jsdoc comment and set DelimitersOnNewline in that case. |
425 | if (Style.Language == FormatStyle::LK_JavaScript || |
426 | Style.Language == FormatStyle::LK_Java) { |
427 | if ((Lines[0] == "*" || Lines[0].startswith("* ")) && Lines.size() > 1) { |
428 | // This is a multiline jsdoc comment. |
429 | DelimitersOnNewline = true; |
430 | } else if (Lines[0].startswith("* ") && Lines.size() == 1) { |
431 | // Detect a long single-line comment, like: |
432 | // /** long long long */ |
433 | // Below, '2' is the width of '*/'. |
434 | unsigned EndColumn = |
435 | ContentColumn[0] + |
436 | encoding::columnWidthWithTabs(Lines[0], ContentColumn[0], |
437 | Style.TabWidth, Encoding) + |
438 | 2; |
439 | DelimitersOnNewline = EndColumn > Style.ColumnLimit; |
440 | } |
441 | } |
442 | |
443 | LLVM_DEBUG({ |
444 | llvm::dbgs() << "IndentAtLineBreak " << IndentAtLineBreak << "\n"; |
445 | llvm::dbgs() << "DelimitersOnNewline " << DelimitersOnNewline << "\n"; |
446 | for (size_t i = 0; i < Lines.size(); ++i) { |
447 | llvm::dbgs() << i << " |" << Content[i] << "| " |
448 | << "CC=" << ContentColumn[i] << "| " |
449 | << "IN=" << (Content[i].data() - Lines[i].data()) << "\n"; |
450 | } |
451 | }); |
452 | } |
453 | |
454 | void BreakableBlockComment::adjustWhitespace(unsigned LineIndex, |
455 | int IndentDelta) { |
456 | // When in a preprocessor directive, the trailing backslash in a block comment |
457 | // is not needed, but can serve a purpose of uniformity with necessary escaped |
458 | // newlines outside the comment. In this case we remove it here before |
459 | // trimming the trailing whitespace. The backslash will be re-added later when |
460 | // inserting a line break. |
461 | size_t EndOfPreviousLine = Lines[LineIndex - 1].size(); |
462 | if (InPPDirective && Lines[LineIndex - 1].endswith("\\")) |
463 | --EndOfPreviousLine; |
464 | |
465 | // Calculate the end of the non-whitespace text in the previous line. |
466 | EndOfPreviousLine = |
467 | Lines[LineIndex - 1].find_last_not_of(Blanks, EndOfPreviousLine); |
468 | if (EndOfPreviousLine == StringRef::npos) |
469 | EndOfPreviousLine = 0; |
470 | else |
471 | ++EndOfPreviousLine; |
472 | // Calculate the start of the non-whitespace text in the current line. |
473 | size_t StartOfLine = Lines[LineIndex].find_first_not_of(Blanks); |
474 | if (StartOfLine == StringRef::npos) |
475 | StartOfLine = Lines[LineIndex].rtrim("\r\n").size(); |
476 | |
477 | StringRef Whitespace = Lines[LineIndex].substr(0, StartOfLine); |
478 | // Adjust Lines to only contain relevant text. |
479 | size_t PreviousContentOffset = |
480 | Content[LineIndex - 1].data() - Lines[LineIndex - 1].data(); |
481 | Content[LineIndex - 1] = Lines[LineIndex - 1].substr( |
482 | PreviousContentOffset, EndOfPreviousLine - PreviousContentOffset); |
483 | Content[LineIndex] = Lines[LineIndex].substr(StartOfLine); |
484 | |
485 | // Adjust the start column uniformly across all lines. |
486 | ContentColumn[LineIndex] = |
487 | encoding::columnWidthWithTabs(Whitespace, 0, Style.TabWidth, Encoding) + |
488 | IndentDelta; |
489 | } |
490 | |
491 | unsigned BreakableBlockComment::getRangeLength(unsigned LineIndex, |
492 | unsigned Offset, |
493 | StringRef::size_type Length, |
494 | unsigned StartColumn) const { |
495 | unsigned LineLength = |
496 | encoding::columnWidthWithTabs(Content[LineIndex].substr(Offset, Length), |
497 | StartColumn, Style.TabWidth, Encoding); |
498 | // FIXME: This should go into getRemainingLength instead, but we currently |
499 | // break tests when putting it there. Investigate how to fix those tests. |
500 | // The last line gets a "*/" postfix. |
501 | if (LineIndex + 1 == Lines.size()) { |
502 | LineLength += 2; |
503 | // We never need a decoration when breaking just the trailing "*/" postfix. |
504 | // Note that checking that Length == 0 is not enough, since Length could |
505 | // also be StringRef::npos. |
506 | if (Content[LineIndex].substr(Offset, StringRef::npos).empty()) { |
507 | LineLength -= Decoration.size(); |
508 | } |
509 | } |
510 | return LineLength; |
511 | } |
512 | |
513 | unsigned BreakableBlockComment::getRemainingLength(unsigned LineIndex, |
514 | unsigned Offset, |
515 | unsigned StartColumn) const { |
516 | return UnbreakableTailLength + |
517 | getRangeLength(LineIndex, Offset, StringRef::npos, StartColumn); |
518 | } |
519 | |
520 | unsigned BreakableBlockComment::getContentStartColumn(unsigned LineIndex, |
521 | bool Break) const { |
522 | if (Break) |
523 | return IndentAtLineBreak; |
524 | return std::max(0, ContentColumn[LineIndex]); |
525 | } |
526 | |
527 | const llvm::StringSet<> |
528 | BreakableBlockComment::ContentIndentingJavadocAnnotations = { |
529 | "@param", "@return", "@returns", "@throws", "@type", "@template", |
530 | "@see", "@deprecated", "@define", "@exports", "@mods", "@private", |
531 | }; |
532 | |
533 | unsigned BreakableBlockComment::getContentIndent(unsigned LineIndex) const { |
534 | if (Style.Language != FormatStyle::LK_Java && |
535 | Style.Language != FormatStyle::LK_JavaScript) |
536 | return 0; |
537 | // The content at LineIndex 0 of a comment like: |
538 | // /** line 0 */ |
539 | // is "* line 0", so we need to skip over the decoration in that case. |
540 | StringRef ContentWithNoDecoration = Content[LineIndex]; |
541 | if (LineIndex == 0 && ContentWithNoDecoration.startswith("*")) { |
542 | ContentWithNoDecoration = ContentWithNoDecoration.substr(1).ltrim(Blanks); |
543 | } |
544 | StringRef FirstWord = ContentWithNoDecoration.substr( |
545 | 0, ContentWithNoDecoration.find_first_of(Blanks)); |
546 | if (ContentIndentingJavadocAnnotations.find(FirstWord) != |
547 | ContentIndentingJavadocAnnotations.end()) |
548 | return Style.ContinuationIndentWidth; |
549 | return 0; |
550 | } |
551 | |
552 | void BreakableBlockComment::insertBreak(unsigned LineIndex, unsigned TailOffset, |
553 | Split Split, unsigned ContentIndent, |
554 | WhitespaceManager &Whitespaces) const { |
555 | StringRef Text = Content[LineIndex].substr(TailOffset); |
556 | StringRef Prefix = Decoration; |
557 | // We need this to account for the case when we have a decoration "* " for all |
558 | // the lines except for the last one, where the star in "*/" acts as a |
559 | // decoration. |
560 | unsigned LocalIndentAtLineBreak = IndentAtLineBreak; |
561 | if (LineIndex + 1 == Lines.size() && |
562 | Text.size() == Split.first + Split.second) { |
563 | // For the last line we need to break before "*/", but not to add "* ". |
564 | Prefix = ""; |
565 | if (LocalIndentAtLineBreak >= 2) |
566 | LocalIndentAtLineBreak -= 2; |
567 | } |
568 | // The split offset is from the beginning of the line. Convert it to an offset |
569 | // from the beginning of the token text. |
570 | unsigned BreakOffsetInToken = |
571 | Text.data() - tokenAt(LineIndex).TokenText.data() + Split.first; |
572 | unsigned CharsToRemove = Split.second; |
573 | = Prefix.size()", "/home/seafit/code_projects/clang_source/clang/lib/Format/BreakableToken.cpp", 573, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(LocalIndentAtLineBreak >= Prefix.size()); |
574 | std::string PrefixWithTrailingIndent = Prefix; |
575 | for (unsigned I = 0; I < ContentIndent; ++I) |
576 | PrefixWithTrailingIndent += " "; |
577 | Whitespaces.replaceWhitespaceInToken( |
578 | tokenAt(LineIndex), BreakOffsetInToken, CharsToRemove, "", |
579 | PrefixWithTrailingIndent, InPPDirective, /*Newlines=*/1, |
580 | /*Spaces=*/LocalIndentAtLineBreak + ContentIndent - |
581 | PrefixWithTrailingIndent.size()); |
582 | } |
583 | |
584 | BreakableToken::Split |
585 | BreakableBlockComment::getReflowSplit(unsigned LineIndex, |
586 | llvm::Regex &CommentPragmasRegex) const { |
587 | if (!mayReflow(LineIndex, CommentPragmasRegex)) |
588 | return Split(StringRef::npos, 0); |
589 | |
590 | // If we're reflowing into a line with content indent, only reflow the next |
591 | // line if its starting whitespace matches the content indent. |
592 | size_t Trimmed = Content[LineIndex].find_first_not_of(Blanks); |
593 | if (LineIndex) { |
594 | unsigned PreviousContentIndent = getContentIndent(LineIndex - 1); |
595 | if (PreviousContentIndent && Trimmed != StringRef::npos && |
596 | Trimmed != PreviousContentIndent) |
597 | return Split(StringRef::npos, 0); |
598 | } |
599 | |
600 | return Split(0, Trimmed != StringRef::npos ? Trimmed : 0); |
601 | } |
602 | |
603 | bool BreakableBlockComment::introducesBreakBeforeToken() const { |
604 | // A break is introduced when we want delimiters on newline. |
605 | return DelimitersOnNewline && |
606 | Lines[0].substr(1).find_first_not_of(Blanks) != StringRef::npos; |
607 | } |
608 | |
609 | void BreakableBlockComment::reflow(unsigned LineIndex, |
610 | WhitespaceManager &Whitespaces) const { |
611 | StringRef TrimmedContent = Content[LineIndex].ltrim(Blanks); |
612 | // Here we need to reflow. |
613 | (0) . __assert_fail ("Tokens[LineIndex - 1] == Tokens[LineIndex] && \"Reflowing whitespace within a token\"", "/home/seafit/code_projects/clang_source/clang/lib/Format/BreakableToken.cpp", 614, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(Tokens[LineIndex - 1] == Tokens[LineIndex] && |
614 | (0) . __assert_fail ("Tokens[LineIndex - 1] == Tokens[LineIndex] && \"Reflowing whitespace within a token\"", "/home/seafit/code_projects/clang_source/clang/lib/Format/BreakableToken.cpp", 614, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true"> "Reflowing whitespace within a token"); |
615 | // This is the offset of the end of the last line relative to the start of |
616 | // the token text in the token. |
617 | unsigned WhitespaceOffsetInToken = Content[LineIndex - 1].data() + |
618 | Content[LineIndex - 1].size() - |
619 | tokenAt(LineIndex).TokenText.data(); |
620 | unsigned WhitespaceLength = TrimmedContent.data() - |
621 | tokenAt(LineIndex).TokenText.data() - |
622 | WhitespaceOffsetInToken; |
623 | Whitespaces.replaceWhitespaceInToken( |
624 | tokenAt(LineIndex), WhitespaceOffsetInToken, |
625 | /*ReplaceChars=*/WhitespaceLength, /*PreviousPostfix=*/"", |
626 | /*CurrentPrefix=*/ReflowPrefix, InPPDirective, /*Newlines=*/0, |
627 | /*Spaces=*/0); |
628 | } |
629 | |
630 | void BreakableBlockComment::adaptStartOfLine( |
631 | unsigned LineIndex, WhitespaceManager &Whitespaces) const { |
632 | if (LineIndex == 0) { |
633 | if (DelimitersOnNewline) { |
634 | // Since we're breaking at index 1 below, the break position and the |
635 | // break length are the same. |
636 | // Note: this works because getCommentSplit is careful never to split at |
637 | // the beginning of a line. |
638 | size_t BreakLength = Lines[0].substr(1).find_first_not_of(Blanks); |
639 | if (BreakLength != StringRef::npos) |
640 | insertBreak(LineIndex, 0, Split(1, BreakLength), /*ContentIndent=*/0, |
641 | Whitespaces); |
642 | } |
643 | return; |
644 | } |
645 | // Here no reflow with the previous line will happen. |
646 | // Fix the decoration of the line at LineIndex. |
647 | StringRef Prefix = Decoration; |
648 | if (Content[LineIndex].empty()) { |
649 | if (LineIndex + 1 == Lines.size()) { |
650 | if (!LastLineNeedsDecoration) { |
651 | // If the last line was empty, we don't need a prefix, as the */ will |
652 | // line up with the decoration (if it exists). |
653 | Prefix = ""; |
654 | } |
655 | } else if (!Decoration.empty()) { |
656 | // For other empty lines, if we do have a decoration, adapt it to not |
657 | // contain a trailing whitespace. |
658 | Prefix = Prefix.substr(0, 1); |
659 | } |
660 | } else { |
661 | if (ContentColumn[LineIndex] == 1) { |
662 | // This line starts immediately after the decorating *. |
663 | Prefix = Prefix.substr(0, 1); |
664 | } |
665 | } |
666 | // This is the offset of the end of the last line relative to the start of the |
667 | // token text in the token. |
668 | unsigned WhitespaceOffsetInToken = Content[LineIndex - 1].data() + |
669 | Content[LineIndex - 1].size() - |
670 | tokenAt(LineIndex).TokenText.data(); |
671 | unsigned WhitespaceLength = Content[LineIndex].data() - |
672 | tokenAt(LineIndex).TokenText.data() - |
673 | WhitespaceOffsetInToken; |
674 | Whitespaces.replaceWhitespaceInToken( |
675 | tokenAt(LineIndex), WhitespaceOffsetInToken, WhitespaceLength, "", Prefix, |
676 | InPPDirective, /*Newlines=*/1, ContentColumn[LineIndex] - Prefix.size()); |
677 | } |
678 | |
679 | BreakableToken::Split |
680 | BreakableBlockComment::getSplitAfterLastLine(unsigned TailOffset) const { |
681 | if (DelimitersOnNewline) { |
682 | // Replace the trailing whitespace of the last line with a newline. |
683 | // In case the last line is empty, the ending '*/' is already on its own |
684 | // line. |
685 | StringRef Line = Content.back().substr(TailOffset); |
686 | StringRef TrimmedLine = Line.rtrim(Blanks); |
687 | if (!TrimmedLine.empty()) |
688 | return Split(TrimmedLine.size(), Line.size() - TrimmedLine.size()); |
689 | } |
690 | return Split(StringRef::npos, 0); |
691 | } |
692 | |
693 | bool BreakableBlockComment::mayReflow(unsigned LineIndex, |
694 | llvm::Regex &CommentPragmasRegex) const { |
695 | // Content[LineIndex] may exclude the indent after the '*' decoration. In that |
696 | // case, we compute the start of the comment pragma manually. |
697 | StringRef IndentContent = Content[LineIndex]; |
698 | if (Lines[LineIndex].ltrim(Blanks).startswith("*")) { |
699 | IndentContent = Lines[LineIndex].ltrim(Blanks).substr(1); |
700 | } |
701 | return LineIndex > 0 && !CommentPragmasRegex.match(IndentContent) && |
702 | mayReflowContent(Content[LineIndex]) && !Tok.Finalized && |
703 | !switchesFormatting(tokenAt(LineIndex)); |
704 | } |
705 | |
706 | BreakableLineCommentSection::BreakableLineCommentSection( |
707 | const FormatToken &Token, unsigned StartColumn, |
708 | unsigned OriginalStartColumn, bool FirstInLine, bool InPPDirective, |
709 | encoding::Encoding Encoding, const FormatStyle &Style) |
710 | : BreakableComment(Token, StartColumn, InPPDirective, Encoding, Style) { |
711 | (0) . __assert_fail ("Tok.is(TT_LineComment) && \"line comment section must start with a line comment\"", "/home/seafit/code_projects/clang_source/clang/lib/Format/BreakableToken.cpp", 712, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(Tok.is(TT_LineComment) && |
712 | (0) . __assert_fail ("Tok.is(TT_LineComment) && \"line comment section must start with a line comment\"", "/home/seafit/code_projects/clang_source/clang/lib/Format/BreakableToken.cpp", 712, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true"> "line comment section must start with a line comment"); |
713 | FormatToken *LineTok = nullptr; |
714 | for (const FormatToken *CurrentTok = &Tok; |
715 | CurrentTok && CurrentTok->is(TT_LineComment); |
716 | CurrentTok = CurrentTok->Next) { |
717 | LastLineTok = LineTok; |
718 | StringRef TokenText(CurrentTok->TokenText); |
719 | (0) . __assert_fail ("(TokenText.startswith(\"//\") || TokenText.startswith(\"#\")) && \"unsupported line comment prefix, '//' and '#' are supported\"", "/home/seafit/code_projects/clang_source/clang/lib/Format/BreakableToken.cpp", 720, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert((TokenText.startswith("//") || TokenText.startswith("#")) && |
720 | (0) . __assert_fail ("(TokenText.startswith(\"//\") || TokenText.startswith(\"#\")) && \"unsupported line comment prefix, '//' and '#' are supported\"", "/home/seafit/code_projects/clang_source/clang/lib/Format/BreakableToken.cpp", 720, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true"> "unsupported line comment prefix, '//' and '#' are supported"); |
721 | size_t FirstLineIndex = Lines.size(); |
722 | TokenText.split(Lines, "\n"); |
723 | Content.resize(Lines.size()); |
724 | ContentColumn.resize(Lines.size()); |
725 | OriginalContentColumn.resize(Lines.size()); |
726 | Tokens.resize(Lines.size()); |
727 | Prefix.resize(Lines.size()); |
728 | OriginalPrefix.resize(Lines.size()); |
729 | for (size_t i = FirstLineIndex, e = Lines.size(); i < e; ++i) { |
730 | Lines[i] = Lines[i].ltrim(Blanks); |
731 | // We need to trim the blanks in case this is not the first line in a |
732 | // multiline comment. Then the indent is included in Lines[i]. |
733 | StringRef IndentPrefix = |
734 | getLineCommentIndentPrefix(Lines[i].ltrim(Blanks), Style); |
735 | (0) . __assert_fail ("(TokenText.startswith(\"//\") || TokenText.startswith(\"#\")) && \"unsupported line comment prefix, '//' and '#' are supported\"", "/home/seafit/code_projects/clang_source/clang/lib/Format/BreakableToken.cpp", 736, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert((TokenText.startswith("//") || TokenText.startswith("#")) && |
736 | (0) . __assert_fail ("(TokenText.startswith(\"//\") || TokenText.startswith(\"#\")) && \"unsupported line comment prefix, '//' and '#' are supported\"", "/home/seafit/code_projects/clang_source/clang/lib/Format/BreakableToken.cpp", 736, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true"> "unsupported line comment prefix, '//' and '#' are supported"); |
737 | OriginalPrefix[i] = Prefix[i] = IndentPrefix; |
738 | if (Lines[i].size() > Prefix[i].size() && |
739 | isAlphanumeric(Lines[i][Prefix[i].size()])) { |
740 | if (Prefix[i] == "//") |
741 | Prefix[i] = "// "; |
742 | else if (Prefix[i] == "///") |
743 | Prefix[i] = "/// "; |
744 | else if (Prefix[i] == "//!") |
745 | Prefix[i] = "//! "; |
746 | else if (Prefix[i] == "///<") |
747 | Prefix[i] = "///< "; |
748 | else if (Prefix[i] == "//!<") |
749 | Prefix[i] = "//!< "; |
750 | else if (Prefix[i] == "#" && |
751 | Style.Language == FormatStyle::LK_TextProto) |
752 | Prefix[i] = "# "; |
753 | } |
754 | |
755 | Tokens[i] = LineTok; |
756 | Content[i] = Lines[i].substr(IndentPrefix.size()); |
757 | OriginalContentColumn[i] = |
758 | StartColumn + encoding::columnWidthWithTabs(OriginalPrefix[i], |
759 | StartColumn, |
760 | Style.TabWidth, Encoding); |
761 | ContentColumn[i] = |
762 | StartColumn + encoding::columnWidthWithTabs(Prefix[i], StartColumn, |
763 | Style.TabWidth, Encoding); |
764 | |
765 | // Calculate the end of the non-whitespace text in this line. |
766 | size_t EndOfLine = Content[i].find_last_not_of(Blanks); |
767 | if (EndOfLine == StringRef::npos) |
768 | EndOfLine = Content[i].size(); |
769 | else |
770 | ++EndOfLine; |
771 | Content[i] = Content[i].substr(0, EndOfLine); |
772 | } |
773 | LineTok = CurrentTok->Next; |
774 | if (CurrentTok->Next && !CurrentTok->Next->ContinuesLineCommentSection) { |
775 | // A line comment section needs to broken by a line comment that is |
776 | // preceded by at least two newlines. Note that we put this break here |
777 | // instead of breaking at a previous stage during parsing, since that |
778 | // would split the contents of the enum into two unwrapped lines in this |
779 | // example, which is undesirable: |
780 | // enum A { |
781 | // a, // comment about a |
782 | // |
783 | // // comment about b |
784 | // b |
785 | // }; |
786 | // |
787 | // FIXME: Consider putting separate line comment sections as children to |
788 | // the unwrapped line instead. |
789 | break; |
790 | } |
791 | } |
792 | } |
793 | |
794 | unsigned |
795 | BreakableLineCommentSection::getRangeLength(unsigned LineIndex, unsigned Offset, |
796 | StringRef::size_type Length, |
797 | unsigned StartColumn) const { |
798 | return encoding::columnWidthWithTabs( |
799 | Content[LineIndex].substr(Offset, Length), StartColumn, Style.TabWidth, |
800 | Encoding); |
801 | } |
802 | |
803 | unsigned BreakableLineCommentSection::getContentStartColumn(unsigned LineIndex, |
804 | bool Break) const { |
805 | if (Break) |
806 | return OriginalContentColumn[LineIndex]; |
807 | return ContentColumn[LineIndex]; |
808 | } |
809 | |
810 | void BreakableLineCommentSection::insertBreak( |
811 | unsigned LineIndex, unsigned TailOffset, Split Split, |
812 | unsigned ContentIndent, WhitespaceManager &Whitespaces) const { |
813 | StringRef Text = Content[LineIndex].substr(TailOffset); |
814 | // Compute the offset of the split relative to the beginning of the token |
815 | // text. |
816 | unsigned BreakOffsetInToken = |
817 | Text.data() - tokenAt(LineIndex).TokenText.data() + Split.first; |
818 | unsigned CharsToRemove = Split.second; |
819 | // Compute the size of the new indent, including the size of the new prefix of |
820 | // the newly broken line. |
821 | unsigned IndentAtLineBreak = OriginalContentColumn[LineIndex] + |
822 | Prefix[LineIndex].size() - |
823 | OriginalPrefix[LineIndex].size(); |
824 | = Prefix[LineIndex].size()", "/home/seafit/code_projects/clang_source/clang/lib/Format/BreakableToken.cpp", 824, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(IndentAtLineBreak >= Prefix[LineIndex].size()); |
825 | Whitespaces.replaceWhitespaceInToken( |
826 | tokenAt(LineIndex), BreakOffsetInToken, CharsToRemove, "", |
827 | Prefix[LineIndex], InPPDirective, /*Newlines=*/1, |
828 | /*Spaces=*/IndentAtLineBreak - Prefix[LineIndex].size()); |
829 | } |
830 | |
831 | BreakableComment::Split BreakableLineCommentSection::getReflowSplit( |
832 | unsigned LineIndex, llvm::Regex &CommentPragmasRegex) const { |
833 | if (!mayReflow(LineIndex, CommentPragmasRegex)) |
834 | return Split(StringRef::npos, 0); |
835 | |
836 | size_t Trimmed = Content[LineIndex].find_first_not_of(Blanks); |
837 | |
838 | // In a line comment section each line is a separate token; thus, after a |
839 | // split we replace all whitespace before the current line comment token |
840 | // (which does not need to be included in the split), plus the start of the |
841 | // line up to where the content starts. |
842 | return Split(0, Trimmed != StringRef::npos ? Trimmed : 0); |
843 | } |
844 | |
845 | void BreakableLineCommentSection::reflow(unsigned LineIndex, |
846 | WhitespaceManager &Whitespaces) const { |
847 | if (LineIndex > 0 && Tokens[LineIndex] != Tokens[LineIndex - 1]) { |
848 | // Reflow happens between tokens. Replace the whitespace between the |
849 | // tokens by the empty string. |
850 | Whitespaces.replaceWhitespace( |
851 | *Tokens[LineIndex], /*Newlines=*/0, /*Spaces=*/0, |
852 | /*StartOfTokenColumn=*/StartColumn, /*InPPDirective=*/false); |
853 | } else if (LineIndex > 0) { |
854 | // In case we're reflowing after the '\' in: |
855 | // |
856 | // // line comment \ |
857 | // // line 2 |
858 | // |
859 | // the reflow happens inside the single comment token (it is a single line |
860 | // comment with an unescaped newline). |
861 | // Replace the whitespace between the '\' and '//' with the empty string. |
862 | // |
863 | // Offset points to after the '\' relative to start of the token. |
864 | unsigned Offset = Lines[LineIndex - 1].data() + |
865 | Lines[LineIndex - 1].size() - |
866 | tokenAt(LineIndex - 1).TokenText.data(); |
867 | // WhitespaceLength is the number of chars between the '\' and the '//' on |
868 | // the next line. |
869 | unsigned WhitespaceLength = |
870 | Lines[LineIndex].data() - tokenAt(LineIndex).TokenText.data() - Offset; |
871 | Whitespaces.replaceWhitespaceInToken(*Tokens[LineIndex], Offset, |
872 | /*ReplaceChars=*/WhitespaceLength, |
873 | /*PreviousPostfix=*/"", |
874 | /*CurrentPrefix=*/"", |
875 | /*InPPDirective=*/false, |
876 | /*Newlines=*/0, |
877 | /*Spaces=*/0); |
878 | } |
879 | // Replace the indent and prefix of the token with the reflow prefix. |
880 | unsigned Offset = |
881 | Lines[LineIndex].data() - tokenAt(LineIndex).TokenText.data(); |
882 | unsigned WhitespaceLength = |
883 | Content[LineIndex].data() - Lines[LineIndex].data(); |
884 | Whitespaces.replaceWhitespaceInToken(*Tokens[LineIndex], Offset, |
885 | /*ReplaceChars=*/WhitespaceLength, |
886 | /*PreviousPostfix=*/"", |
887 | /*CurrentPrefix=*/ReflowPrefix, |
888 | /*InPPDirective=*/false, |
889 | /*Newlines=*/0, |
890 | /*Spaces=*/0); |
891 | } |
892 | |
893 | void BreakableLineCommentSection::adaptStartOfLine( |
894 | unsigned LineIndex, WhitespaceManager &Whitespaces) const { |
895 | // If this is the first line of a token, we need to inform Whitespace Manager |
896 | // about it: either adapt the whitespace range preceding it, or mark it as an |
897 | // untouchable token. |
898 | // This happens for instance here: |
899 | // // line 1 \ |
900 | // // line 2 |
901 | if (LineIndex > 0 && Tokens[LineIndex] != Tokens[LineIndex - 1]) { |
902 | // This is the first line for the current token, but no reflow with the |
903 | // previous token is necessary. However, we still may need to adjust the |
904 | // start column. Note that ContentColumn[LineIndex] is the expected |
905 | // content column after a possible update to the prefix, hence the prefix |
906 | // length change is included. |
907 | unsigned LineColumn = |
908 | ContentColumn[LineIndex] - |
909 | (Content[LineIndex].data() - Lines[LineIndex].data()) + |
910 | (OriginalPrefix[LineIndex].size() - Prefix[LineIndex].size()); |
911 | |
912 | // We always want to create a replacement instead of adding an untouchable |
913 | // token, even if LineColumn is the same as the original column of the |
914 | // token. This is because WhitespaceManager doesn't align trailing |
915 | // comments if they are untouchable. |
916 | Whitespaces.replaceWhitespace(*Tokens[LineIndex], |
917 | /*Newlines=*/1, |
918 | /*Spaces=*/LineColumn, |
919 | /*StartOfTokenColumn=*/LineColumn, |
920 | /*InPPDirective=*/false); |
921 | } |
922 | if (OriginalPrefix[LineIndex] != Prefix[LineIndex]) { |
923 | // Adjust the prefix if necessary. |
924 | |
925 | // Take care of the space possibly introduced after a decoration. |
926 | (0) . __assert_fail ("Prefix[LineIndex] == (OriginalPrefix[LineIndex] + \" \").str() && \"Expecting a line comment prefix to differ from original by at most \" \"a space\"", "/home/seafit/code_projects/clang_source/clang/lib/Format/BreakableToken.cpp", 928, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(Prefix[LineIndex] == (OriginalPrefix[LineIndex] + " ").str() && |
927 | (0) . __assert_fail ("Prefix[LineIndex] == (OriginalPrefix[LineIndex] + \" \").str() && \"Expecting a line comment prefix to differ from original by at most \" \"a space\"", "/home/seafit/code_projects/clang_source/clang/lib/Format/BreakableToken.cpp", 928, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true"> "Expecting a line comment prefix to differ from original by at most " |
928 | (0) . __assert_fail ("Prefix[LineIndex] == (OriginalPrefix[LineIndex] + \" \").str() && \"Expecting a line comment prefix to differ from original by at most \" \"a space\"", "/home/seafit/code_projects/clang_source/clang/lib/Format/BreakableToken.cpp", 928, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true"> "a space"); |
929 | Whitespaces.replaceWhitespaceInToken( |
930 | tokenAt(LineIndex), OriginalPrefix[LineIndex].size(), 0, "", "", |
931 | /*InPPDirective=*/false, /*Newlines=*/0, /*Spaces=*/1); |
932 | } |
933 | } |
934 | |
935 | void BreakableLineCommentSection::updateNextToken(LineState &State) const { |
936 | if (LastLineTok) { |
937 | State.NextToken = LastLineTok->Next; |
938 | } |
939 | } |
940 | |
941 | bool BreakableLineCommentSection::mayReflow( |
942 | unsigned LineIndex, llvm::Regex &CommentPragmasRegex) const { |
943 | // Line comments have the indent as part of the prefix, so we need to |
944 | // recompute the start of the line. |
945 | StringRef IndentContent = Content[LineIndex]; |
946 | if (Lines[LineIndex].startswith("//")) { |
947 | IndentContent = Lines[LineIndex].substr(2); |
948 | } |
949 | // FIXME: Decide whether we want to reflow non-regular indents: |
950 | // Currently, we only reflow when the OriginalPrefix[LineIndex] matches the |
951 | // OriginalPrefix[LineIndex-1]. That means we don't reflow |
952 | // // text that protrudes |
953 | // // into text with different indent |
954 | // We do reflow in that case in block comments. |
955 | return LineIndex > 0 && !CommentPragmasRegex.match(IndentContent) && |
956 | mayReflowContent(Content[LineIndex]) && !Tok.Finalized && |
957 | !switchesFormatting(tokenAt(LineIndex)) && |
958 | OriginalPrefix[LineIndex] == OriginalPrefix[LineIndex - 1]; |
959 | } |
960 | |
961 | } // namespace format |
962 | } // namespace clang |
963 | |