BreakableToken.cpp revision 251609
133965Sjdp//===--- BreakableToken.cpp - Format C++ code -----------------------------===//
238889Sjdp//
333965Sjdp//                     The LLVM Compiler Infrastructure
433965Sjdp//
533965Sjdp// This file is distributed under the University of Illinois Open Source
633965Sjdp// License. See LICENSE.TXT for details.
733965Sjdp//
833965Sjdp//===----------------------------------------------------------------------===//
933965Sjdp///
1033965Sjdp/// \file
1133965Sjdp/// \brief Contains implementation of BreakableToken class and classes derived
1233965Sjdp/// from it.
1333965Sjdp///
1433965Sjdp//===----------------------------------------------------------------------===//
1533965Sjdp
1633965Sjdp#include "BreakableToken.h"
1733965Sjdp#include "llvm/ADT/STLExtras.h"
1833965Sjdp#include <algorithm>
1933965Sjdp
2033965Sjdpnamespace clang {
2133965Sjdpnamespace format {
2233965Sjdp
2333965SjdpBreakableToken::Split BreakableComment::getSplit(unsigned LineIndex,
2433965Sjdp                                                 unsigned TailOffset,
2533965Sjdp                                                 unsigned ColumnLimit) const {
2633965Sjdp  StringRef Text = getLine(LineIndex).substr(TailOffset);
2733965Sjdp  unsigned ContentStartColumn = getContentStartColumn(LineIndex, TailOffset);
2833965Sjdp  if (ColumnLimit <= ContentStartColumn + 1)
2933965Sjdp    return Split(StringRef::npos, 0);
3033965Sjdp
3133965Sjdp  unsigned MaxSplit = ColumnLimit - ContentStartColumn + 1;
3233965Sjdp  StringRef::size_type SpaceOffset = Text.rfind(' ', MaxSplit);
3333965Sjdp  if (SpaceOffset == StringRef::npos ||
3433965Sjdp      Text.find_last_not_of(' ', SpaceOffset) == StringRef::npos) {
3533965Sjdp    SpaceOffset = Text.find(' ', MaxSplit);
3633965Sjdp  }
3733965Sjdp  if (SpaceOffset != StringRef::npos && SpaceOffset != 0) {
3833965Sjdp    StringRef BeforeCut = Text.substr(0, SpaceOffset).rtrim();
3933965Sjdp    StringRef AfterCut = Text.substr(SpaceOffset).ltrim();
4033965Sjdp    return BreakableToken::Split(BeforeCut.size(),
4133965Sjdp                                 AfterCut.begin() - BeforeCut.end());
4233965Sjdp  }
4333965Sjdp  return BreakableToken::Split(StringRef::npos, 0);
4433965Sjdp}
4533965Sjdp
4633965Sjdpvoid BreakableComment::insertBreak(unsigned LineIndex, unsigned TailOffset,
4733965Sjdp                                   Split Split, bool InPPDirective,
4833965Sjdp                                   WhitespaceManager &Whitespaces) {
4933965Sjdp  StringRef Text = getLine(LineIndex).substr(TailOffset);
5033965Sjdp  StringRef AdditionalPrefix = Decoration;
5133965Sjdp  if (Text.size() == Split.first + Split.second) {
5233965Sjdp    // For all but the last line handle trailing space in trimLine.
5333965Sjdp    if (LineIndex < Lines.size() - 1)
5433965Sjdp      return;
5533965Sjdp    // For the last line we need to break before "*/", but not to add "* ".
5633965Sjdp    AdditionalPrefix = "";
5733965Sjdp  }
5833965Sjdp
5933965Sjdp  unsigned WhitespaceStartColumn =
6033965Sjdp      getContentStartColumn(LineIndex, TailOffset) + Split.first;
6133965Sjdp  unsigned BreakOffset = Text.data() - TokenText.data() + Split.first;
6233965Sjdp  unsigned CharsToRemove = Split.second;
6333965Sjdp  Whitespaces.breakToken(Tok, BreakOffset, CharsToRemove, "", AdditionalPrefix,
6433965Sjdp                         InPPDirective, IndentAtLineBreak,
6533965Sjdp                         WhitespaceStartColumn);
6633965Sjdp}
6733965Sjdp
6833965SjdpBreakableBlockComment::BreakableBlockComment(const SourceManager &SourceMgr,
6933965Sjdp                                             const AnnotatedToken &Token,
7033965Sjdp                                             unsigned StartColumn)
7133965Sjdp    : BreakableComment(SourceMgr, Token.FormatTok, StartColumn + 2) {
7233965Sjdp  assert(TokenText.startswith("/*") && TokenText.endswith("*/"));
7333965Sjdp
7433965Sjdp  OriginalStartColumn =
7533965Sjdp      SourceMgr.getSpellingColumnNumber(Tok.getStartOfNonWhitespace()) - 1;
7633965Sjdp
7733965Sjdp  TokenText.substr(2, TokenText.size() - 4).split(Lines, "\n");
7833965Sjdp
7933965Sjdp  bool NeedsStar = true;
8033965Sjdp  CommonPrefixLength = UINT_MAX;
8133965Sjdp  if (Lines.size() == 1) {
8233965Sjdp    if (Token.Parent == 0) {
8333965Sjdp      // Standalone block comments will be aligned and prefixed with *s.
8433965Sjdp      CommonPrefixLength = OriginalStartColumn + 1;
8533965Sjdp    } else {
8633965Sjdp      // Trailing comments can start on arbitrary column, and available
8733965Sjdp      // horizontal space can be too small to align consecutive lines with
8833965Sjdp      // the first one. We could, probably, align them to current
8933965Sjdp      // indentation level, but now we just wrap them without indentation
9033965Sjdp      // and stars.
9133965Sjdp      CommonPrefixLength = 0;
9233965Sjdp      NeedsStar = false;
9333965Sjdp    }
9433965Sjdp  } else {
9533965Sjdp    for (size_t i = 1; i < Lines.size(); ++i) {
9633965Sjdp      size_t FirstNonWhitespace = Lines[i].find_first_not_of(" ");
9733965Sjdp      if (FirstNonWhitespace != StringRef::npos) {
9833965Sjdp        NeedsStar = NeedsStar && (Lines[i][FirstNonWhitespace] == '*');
9933965Sjdp        CommonPrefixLength =
10033965Sjdp            std::min<unsigned>(CommonPrefixLength, FirstNonWhitespace);
10133965Sjdp      }
10233965Sjdp    }
10333965Sjdp  }
10433965Sjdp  if (CommonPrefixLength == UINT_MAX)
10560484Sobrien    CommonPrefixLength = 0;
10633965Sjdp
10733965Sjdp  Decoration = NeedsStar ? "* " : "";
10833965Sjdp
10933965Sjdp  IndentAtLineBreak =
11033965Sjdp      std::max<int>(StartColumn - OriginalStartColumn + CommonPrefixLength, 0);
11133965Sjdp}
11233965Sjdp
11333965Sjdpvoid BreakableBlockComment::alignLines(WhitespaceManager &Whitespaces) {
11433965Sjdp  SourceLocation TokenLoc = Tok.getStartOfNonWhitespace();
11533965Sjdp  int IndentDelta = (StartColumn - 2) - OriginalStartColumn;
11633965Sjdp  if (IndentDelta > 0) {
11733965Sjdp    std::string WhiteSpace(IndentDelta, ' ');
11833965Sjdp    for (size_t i = 1; i < Lines.size(); ++i) {
11933965Sjdp      Whitespaces.addReplacement(
12033965Sjdp          TokenLoc.getLocWithOffset(Lines[i].data() - TokenText.data()), 0,
12133965Sjdp          WhiteSpace);
12233965Sjdp    }
12333965Sjdp  } else if (IndentDelta < 0) {
12433965Sjdp    std::string WhiteSpace(-IndentDelta, ' ');
12533965Sjdp    // Check that the line is indented enough.
12633965Sjdp    for (size_t i = 1; i < Lines.size(); ++i) {
12733965Sjdp      if (!Lines[i].startswith(WhiteSpace))
12833965Sjdp        return;
12933965Sjdp    }
13033965Sjdp    for (size_t i = 1; i < Lines.size(); ++i) {
13133965Sjdp      Whitespaces.addReplacement(
13233965Sjdp          TokenLoc.getLocWithOffset(Lines[i].data() - TokenText.data()),
13333965Sjdp          -IndentDelta, "");
13433965Sjdp    }
13533965Sjdp  }
13633965Sjdp
13733965Sjdp  for (unsigned i = 1; i < Lines.size(); ++i)
13833965Sjdp    Lines[i] = Lines[i].substr(CommonPrefixLength + Decoration.size());
13933965Sjdp}
14033965Sjdp
14133965Sjdpvoid BreakableBlockComment::trimLine(unsigned LineIndex, unsigned TailOffset,
14233965Sjdp                                     unsigned InPPDirective,
14333965Sjdp                                     WhitespaceManager &Whitespaces) {
14433965Sjdp  if (LineIndex == Lines.size() - 1)
14533965Sjdp    return;
14633965Sjdp  StringRef Text = Lines[LineIndex].substr(TailOffset);
14733965Sjdp  if (!Text.endswith(" ") && !InPPDirective)
14833965Sjdp    return;
14933965Sjdp
15033965Sjdp  StringRef TrimmedLine = Text.rtrim();
15133965Sjdp  unsigned WhitespaceStartColumn =
15233965Sjdp      getLineLengthAfterSplit(LineIndex, TailOffset);
15333965Sjdp  unsigned BreakOffset = TrimmedLine.end() - TokenText.data();
15433965Sjdp  unsigned CharsToRemove = Text.size() - TrimmedLine.size() + 1;
15533965Sjdp  Whitespaces.breakToken(Tok, BreakOffset, CharsToRemove, "", "", InPPDirective,
15633965Sjdp                         0, WhitespaceStartColumn);
15733965Sjdp}
15833965Sjdp
15933965SjdpBreakableLineComment::BreakableLineComment(const SourceManager &SourceMgr,
16033965Sjdp                                           const AnnotatedToken &Token,
16133965Sjdp                                           unsigned StartColumn)
16233965Sjdp    : BreakableComment(SourceMgr, Token.FormatTok, StartColumn) {
16333965Sjdp  assert(TokenText.startswith("//"));
16433965Sjdp  Decoration = getLineCommentPrefix(TokenText);
16533965Sjdp  Lines.push_back(TokenText.substr(Decoration.size()));
16633965Sjdp  IndentAtLineBreak = StartColumn;
16733965Sjdp  this->StartColumn += Decoration.size(); // Start column of the contents.
16833965Sjdp}
16933965Sjdp
17033965SjdpStringRef BreakableLineComment::getLineCommentPrefix(StringRef Comment) {
17133965Sjdp  const char *KnownPrefixes[] = { "/// ", "///", "// ", "//" };
17233965Sjdp  for (size_t i = 0; i < llvm::array_lengthof(KnownPrefixes); ++i)
17333965Sjdp    if (Comment.startswith(KnownPrefixes[i]))
17433965Sjdp      return KnownPrefixes[i];
17533965Sjdp  return "";
17633965Sjdp}
17733965Sjdp
17833965Sjdp} // namespace format
17933965Sjdp} // namespace clang
18033965Sjdp