BreakableToken.cpp revision 70ce7881fc30a39b795b2873f008e7eca72ba669
1//===--- 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
162