Unified token breaking logic: support for line comments.

Summary:
Added BreakableLineComment, moved common code from
BreakableBlockComment to newly added BreakableComment. As a side-effect of the
rewrite, found another problem with escaped newlines and had to change
code which removes trailing whitespace from line comments not to break after
this patch.

Reviewers: klimek, djasper

Reviewed By: klimek

CC: cfe-commits

Differential Revision: http://llvm-reviews.chandlerc.com/D682

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@179693 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Format/BreakableToken.cpp b/lib/Format/BreakableToken.cpp
index 4ec3de9..3e2e0ce 100644
--- a/lib/Format/BreakableToken.cpp
+++ b/lib/Format/BreakableToken.cpp
@@ -14,25 +14,69 @@
 //===----------------------------------------------------------------------===//
 
 #include "BreakableToken.h"
+#include "llvm/ADT/STLExtras.h"
 #include <algorithm>
 
 namespace clang {
 namespace format {
 
+BreakableToken::Split BreakableComment::getSplit(unsigned LineIndex,
+                                                 unsigned TailOffset,
+                                                 unsigned ColumnLimit) const {
+  StringRef Text = getLine(LineIndex).substr(TailOffset);
+  unsigned ContentStartColumn = getContentStartColumn(LineIndex, TailOffset);
+  if (ColumnLimit <= ContentStartColumn + 1)
+    return Split(StringRef::npos, 0);
+
+  unsigned MaxSplit = ColumnLimit - ContentStartColumn + 1;
+  StringRef::size_type SpaceOffset = Text.rfind(' ', MaxSplit);
+  if (SpaceOffset == StringRef::npos ||
+      Text.find_last_not_of(' ', SpaceOffset) == StringRef::npos) {
+    SpaceOffset = Text.find(' ', MaxSplit);
+  }
+  if (SpaceOffset != StringRef::npos && SpaceOffset != 0) {
+    StringRef BeforeCut = Text.substr(0, SpaceOffset).rtrim();
+    StringRef AfterCut = Text.substr(SpaceOffset).ltrim();
+    return BreakableToken::Split(BeforeCut.size(),
+                                 AfterCut.begin() - BeforeCut.end());
+  }
+  return BreakableToken::Split(StringRef::npos, 0);
+}
+
+void BreakableComment::insertBreak(unsigned LineIndex, unsigned TailOffset,
+                                   Split Split, bool InPPDirective,
+                                   WhitespaceManager &Whitespaces) {
+  StringRef Text = getLine(LineIndex).substr(TailOffset);
+  StringRef AdditionalPrefix = Decoration;
+  if (Text.size() == Split.first + Split.second) {
+    // For all but the last line handle trailing space in trimLine.
+    if (LineIndex < Lines.size() - 1)
+      return;
+    // For the last line we need to break before "*/", but not to add "* ".
+    AdditionalPrefix = "";
+  }
+
+  unsigned WhitespaceStartColumn =
+      getContentStartColumn(LineIndex, TailOffset) + Split.first;
+  unsigned BreakOffset = Text.data() - TokenText.data() + Split.first;
+  unsigned CharsToRemove = Split.second;
+  Whitespaces.breakToken(Tok, BreakOffset, CharsToRemove, "", AdditionalPrefix,
+                         InPPDirective, IndentAtLineBreak,
+                         WhitespaceStartColumn);
+}
+
 BreakableBlockComment::BreakableBlockComment(const SourceManager &SourceMgr,
                                              const AnnotatedToken &Token,
                                              unsigned StartColumn)
-    : Tok(Token.FormatTok), StartColumn(StartColumn) {
-
-  SourceLocation TokenLoc = Tok.Tok.getLocation();
-  TokenText = StringRef(SourceMgr.getCharacterData(TokenLoc), Tok.TokenLength);
+    : BreakableComment(SourceMgr, Token.FormatTok, StartColumn + 2) {
   assert(TokenText.startswith("/*") && TokenText.endswith("*/"));
 
-  OriginalStartColumn = SourceMgr.getSpellingColumnNumber(TokenLoc) - 1;
+  OriginalStartColumn =
+      SourceMgr.getSpellingColumnNumber(Tok.getStartOfNonWhitespace()) - 1;
 
   TokenText.substr(2, TokenText.size() - 4).split(Lines, "\n");
 
-  NeedsStar = true;
+  bool NeedsStar = true;
   CommonPrefixLength = UINT_MAX;
   if (Lines.size() == 1) {
     if (Token.Parent == 0) {
@@ -60,13 +104,15 @@
   if (CommonPrefixLength == UINT_MAX)
     CommonPrefixLength = 0;
 
+  Decoration = NeedsStar ? "* " : "";
+
   IndentAtLineBreak =
       std::max<int>(StartColumn - OriginalStartColumn + CommonPrefixLength, 0);
 }
 
 void BreakableBlockComment::alignLines(WhitespaceManager &Whitespaces) {
-  SourceLocation TokenLoc = Tok.Tok.getLocation();
-  int IndentDelta = StartColumn - OriginalStartColumn;
+  SourceLocation TokenLoc = Tok.getStartOfNonWhitespace();
+  int IndentDelta = (StartColumn - 2) - OriginalStartColumn;
   if (IndentDelta > 0) {
     std::string WhiteSpace(IndentDelta, ' ');
     for (size_t i = 1; i < Lines.size(); ++i) {
@@ -89,54 +135,7 @@
   }
 
   for (unsigned i = 1; i < Lines.size(); ++i)
-    Lines[i] = Lines[i].substr(CommonPrefixLength + (NeedsStar ? 2 : 0));
-}
-
-BreakableToken::Split BreakableBlockComment::getSplit(unsigned LineIndex,
-                                                      unsigned TailOffset,
-                                                      unsigned ColumnLimit) {
-  StringRef Text = getLine(LineIndex).substr(TailOffset);
-  unsigned DecorationLength =
-      (TailOffset == 0 && LineIndex == 0) ? StartColumn + 2 : getPrefixLength();
-  if (ColumnLimit <= DecorationLength + 1)
-    return Split(StringRef::npos, 0);
-
-  unsigned MaxSplit = ColumnLimit - DecorationLength + 1;
-  StringRef::size_type SpaceOffset = Text.rfind(' ', MaxSplit);
-  if (SpaceOffset == StringRef::npos ||
-      Text.find_last_not_of(' ', SpaceOffset) == StringRef::npos) {
-    SpaceOffset = Text.find(' ', MaxSplit);
-  }
-  if (SpaceOffset != StringRef::npos && SpaceOffset != 0) {
-    StringRef BeforeCut = Text.substr(0, SpaceOffset).rtrim();
-    StringRef AfterCut = Text.substr(SpaceOffset).ltrim();
-    return BreakableToken::Split(BeforeCut.size(),
-                                 AfterCut.begin() - BeforeCut.end());
-  }
-  return BreakableToken::Split(StringRef::npos, 0);
-}
-
-void BreakableBlockComment::insertBreak(unsigned LineIndex, unsigned TailOffset,
-                                        Split Split, bool InPPDirective,
-                                        WhitespaceManager &Whitespaces) {
-  StringRef Text = getLine(LineIndex).substr(TailOffset);
-  StringRef AdditionalPrefix = NeedsStar ? "* " : "";
-  if (Text.size() == Split.first + Split.second) {
-    // For all but the last line handle trailing space separately.
-    if (LineIndex < Lines.size() - 1)
-      return;
-    // For the last line we need to break before "*/", but not to add "* ".
-    AdditionalPrefix = "";
-  }
-
-  unsigned WhitespaceStartColumn =
-      Split.first +
-      (LineIndex == 0 && TailOffset == 0 ? StartColumn + 2 : getPrefixLength());
-  unsigned BreakOffset = Text.data() - TokenText.data() + Split.first;
-  unsigned CharsToRemove = Split.second;
-  Whitespaces.breakToken(Tok, BreakOffset, CharsToRemove, "", AdditionalPrefix,
-                         InPPDirective, IndentAtLineBreak,
-                         WhitespaceStartColumn);
+    Lines[i] = Lines[i].substr(CommonPrefixLength + Decoration.size());
 }
 
 void BreakableBlockComment::trimLine(unsigned LineIndex, unsigned TailOffset,
@@ -157,5 +156,24 @@
                          0, WhitespaceStartColumn);
 }
 
+BreakableLineComment::BreakableLineComment(const SourceManager &SourceMgr,
+                                           const AnnotatedToken &Token,
+                                           unsigned StartColumn)
+    : BreakableComment(SourceMgr, Token.FormatTok, StartColumn) {
+  assert(TokenText.startswith("//"));
+  Decoration = getLineCommentPrefix(TokenText);
+  Lines.push_back(TokenText.substr(Decoration.size()));
+  IndentAtLineBreak = StartColumn;
+  this->StartColumn += Decoration.size(); // Start column of the contents.
+}
+
+StringRef BreakableLineComment::getLineCommentPrefix(StringRef Comment) {
+  const char *KnownPrefixes[] = { "/// ", "///", "// ", "//" };
+  for (size_t i = 0; i < llvm::array_lengthof(KnownPrefixes); ++i)
+    if (Comment.startswith(KnownPrefixes[i]))
+      return KnownPrefixes[i];
+  return "";
+}
+
 } // namespace format
 } // namespace clang