]> granicus.if.org Git - clang/commitdiff
Add option to align escaped newlines left.
authorDaniel Jasper <djasper@google.com>
Thu, 25 Apr 2013 08:56:26 +0000 (08:56 +0000)
committerDaniel Jasper <djasper@google.com>
Thu, 25 Apr 2013 08:56:26 +0000 (08:56 +0000)
This enables formattings like:

  #define A   \
    int aaaa; \
    int b;    \
    int ccc;  \
    int dddddddddd;

Enabling this for Google/Chromium styles only as I don't know whether it
is desired for Clang/LLVM.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@180253 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Format/Format.h
lib/Format/Format.cpp
lib/Format/WhitespaceManager.cpp
lib/Format/WhitespaceManager.h
unittests/Format/FormatTest.cpp

index d6cc114e3cd1eb5e710f50abdebf09d4b85baab6..28aa6bd2ceda6afc022dbb97ce811871a5442fdd 100644 (file)
@@ -91,6 +91,10 @@ struct FormatStyle {
   /// \brief Add a space in front of an Objective-C protocol list, i.e. use
   /// Foo <Protocol> instead of Foo<Protocol>.
   bool ObjCSpaceBeforeProtocolList;
+
+  /// \brief If \c true, aligns escaped newlines as far left as possible.
+  /// Otherwise puts them into the right-most column.
+  bool AlignEscapedNewlinesLeft;
 };
 
 /// \brief Returns a format style complying with the LLVM coding standards:
index b483308a5a4cdb86b8628293ad26db56d6084cf9..7d2041cd3c21c5d23dcce160471c92f3f254864e 100644 (file)
@@ -51,6 +51,7 @@ FormatStyle getLLVMStyle() {
   LLVMStyle.ObjCSpaceBeforeProtocolList = true;
   LLVMStyle.PenaltyExcessCharacter = 1000000;
   LLVMStyle.PenaltyReturnTypeOnItsOwnLine = 75;
+  LLVMStyle.AlignEscapedNewlinesLeft = false;
   return LLVMStyle;
 }
 
@@ -71,6 +72,7 @@ FormatStyle getGoogleStyle() {
   GoogleStyle.ObjCSpaceBeforeProtocolList = false;
   GoogleStyle.PenaltyExcessCharacter = 1000000;
   GoogleStyle.PenaltyReturnTypeOnItsOwnLine = 200;
+  GoogleStyle.AlignEscapedNewlinesLeft = true;
   return GoogleStyle;
 }
 
index d29cd52b0eedc128c62858d51e409a27420d02d0..a75c592bfeda6b574afc17641c9da452e4d75382 100644 (file)
@@ -21,6 +21,9 @@ namespace format {
 void WhitespaceManager::replaceWhitespace(const AnnotatedToken &Tok,
                                           unsigned NewLines, unsigned Spaces,
                                           unsigned WhitespaceStartColumn) {
+  if (NewLines > 0)
+    alignEscapedNewlines();
+
   // 2+ newlines mean an empty line separating logic scopes.
   if (NewLines >= 2)
     alignComments();
@@ -39,16 +42,14 @@ void WhitespaceManager::replaceWhitespace(const AnnotatedToken &Tok,
         Spaces + WhitespaceStartColumn + Tok.FormatTok.TokenLength >
         Style.ColumnLimit;
     // Align comment with other comments.
-    if ((Tok.Parent != NULL || !Comments.empty()) && !LineExceedsColumnLimit) {
-      StoredComment Comment;
-      Comment.Tok = Tok.FormatTok;
-      Comment.Spaces = Spaces;
-      Comment.NewLines = NewLines;
-      Comment.MinColumn =
+    if ((Tok.Parent != NULL || !Comments.empty()) &&
+        !LineExceedsColumnLimit) {
+      unsigned MinColumn =
           NewLines > 0 ? Spaces : WhitespaceStartColumn + Spaces;
-      Comment.MaxColumn = Style.ColumnLimit - Tok.FormatTok.TokenLength;
-      Comment.Untouchable = false;
-      Comments.push_back(Comment);
+      unsigned MaxColumn = Style.ColumnLimit - Tok.FormatTok.TokenLength;
+      Comments.push_back(StoredToken(
+          Tok.FormatTok.WhiteSpaceStart, Tok.FormatTok.WhiteSpaceLength,
+          MinColumn, MaxColumn, NewLines, Spaces));
       return;
     }
   }
@@ -57,14 +58,24 @@ void WhitespaceManager::replaceWhitespace(const AnnotatedToken &Tok,
   if (Tok.Children.empty() && !Tok.isTrailingComment())
     alignComments();
 
-  storeReplacement(Tok.FormatTok, getNewLineText(NewLines, Spaces));
+  storeReplacement(Tok.FormatTok.WhiteSpaceStart,
+                   Tok.FormatTok.WhiteSpaceLength,
+                   getNewLineText(NewLines, Spaces));
 }
 
 void WhitespaceManager::replacePPWhitespace(const AnnotatedToken &Tok,
                                             unsigned NewLines, unsigned Spaces,
                                             unsigned WhitespaceStartColumn) {
-  storeReplacement(Tok.FormatTok,
-                   getNewLineText(NewLines, Spaces, WhitespaceStartColumn));
+  if (NewLines == 0) {
+    replaceWhitespace(Tok, NewLines, Spaces, WhitespaceStartColumn);
+  } else {
+    // The earliest position for "\" is 2 after the last token.
+    unsigned MinColumn = WhitespaceStartColumn + 2;
+    unsigned MaxColumn = Style.ColumnLimit;
+    EscapedNewlines.push_back(StoredToken(
+        Tok.FormatTok.WhiteSpaceStart, Tok.FormatTok.WhiteSpaceLength,
+        MinColumn, MaxColumn, NewLines, Spaces));
+  }
 }
 
 void WhitespaceManager::breakToken(const FormatToken &Tok, unsigned Offset,
@@ -72,20 +83,28 @@ void WhitespaceManager::breakToken(const FormatToken &Tok, unsigned Offset,
                                    StringRef Postfix, bool InPPDirective,
                                    unsigned Spaces,
                                    unsigned WhitespaceStartColumn) {
-  std::string NewLineText;
-  if (!InPPDirective)
-    NewLineText = getNewLineText(1, Spaces);
-  else
-    NewLineText = getNewLineText(1, Spaces, WhitespaceStartColumn);
-  std::string ReplacementText = (Prefix + NewLineText + Postfix).str();
   SourceLocation Location =
       Tok.getStartOfNonWhitespace().getLocWithOffset(Offset);
-  Replaces.insert(
-      tooling::Replacement(SourceMgr, Location, ReplaceChars, ReplacementText));
+  if (InPPDirective) {
+    // The earliest position for "\" is 2 after the last token.
+    unsigned MinColumn = WhitespaceStartColumn + 2;
+    unsigned MaxColumn = Style.ColumnLimit;
+    StoredToken StoredTok = StoredToken(Location, ReplaceChars, MinColumn,
+                                        MaxColumn, /*NewLines=*/ 1, Spaces);
+    StoredTok.Prefix = Prefix;
+    StoredTok.Postfix = Postfix;
+    EscapedNewlines.push_back(StoredTok);
+  } else {
+    std::string ReplacementText =
+        (Prefix + getNewLineText(1, Spaces) + Postfix).str();
+    Replaces.insert(tooling::Replacement(SourceMgr, Location, ReplaceChars,
+                                         ReplacementText));
+  }
 }
 
 const tooling::Replacements &WhitespaceManager::generateReplacements() {
   alignComments();
+  alignEscapedNewlines();
   return Replaces;
 }
 
@@ -96,11 +115,9 @@ void WhitespaceManager::addReplacement(const SourceLocation &SourceLoc,
 }
 
 void WhitespaceManager::addUntouchableComment(unsigned Column) {
-  StoredComment Comment;
-  Comment.MinColumn = Column;
-  Comment.MaxColumn = Column;
-  Comment.Untouchable = true;
-  Comments.push_back(Comment);
+  StoredToken Tok = StoredToken(SourceLocation(), 0, Column, Column, 0, 0);
+  Tok.Untouchable = true;
+  Comments.push_back(Tok);
 }
 
 std::string WhitespaceManager::getNewLineText(unsigned NewLines,
@@ -110,13 +127,14 @@ std::string WhitespaceManager::getNewLineText(unsigned NewLines,
 
 std::string WhitespaceManager::getNewLineText(unsigned NewLines,
                                               unsigned Spaces,
-                                              unsigned WhitespaceStartColumn) {
+                                              unsigned WhitespaceStartColumn,
+                                              unsigned EscapedNewlineColumn) {
   std::string NewLineText;
   if (NewLines > 0) {
     unsigned Offset =
-        std::min<int>(Style.ColumnLimit - 1, WhitespaceStartColumn);
+        std::min<int>(EscapedNewlineColumn - 1, WhitespaceStartColumn);
     for (unsigned i = 0; i < NewLines; ++i) {
-      NewLineText += std::string(Style.ColumnLimit - Offset - 1, ' ');
+      NewLineText += std::string(EscapedNewlineColumn - Offset - 1, ' ');
       NewLineText += "\\\n";
       Offset = 0;
     }
@@ -127,8 +145,8 @@ std::string WhitespaceManager::getNewLineText(unsigned NewLines,
 void WhitespaceManager::alignComments() {
   unsigned MinColumn = 0;
   unsigned MaxColumn = UINT_MAX;
-  comment_iterator Start = Comments.begin();
-  for (comment_iterator I = Start, E = Comments.end(); I != E; ++I) {
+  token_iterator Start = Comments.begin();
+  for (token_iterator I = Start, E = Comments.end(); I != E; ++I) {
     if (I->MinColumn > MaxColumn || I->MaxColumn < MinColumn) {
       alignComments(Start, I, MinColumn);
       MinColumn = I->MinColumn;
@@ -143,26 +161,50 @@ void WhitespaceManager::alignComments() {
   Comments.clear();
 }
 
-void WhitespaceManager::alignComments(comment_iterator I, comment_iterator E,
+void WhitespaceManager::alignComments(token_iterator I, token_iterator E,
                                       unsigned Column) {
   while (I != E) {
     if (!I->Untouchable) {
       unsigned Spaces = I->Spaces + Column - I->MinColumn;
-      storeReplacement(I->Tok, getNewLineText(I->NewLines, Spaces));
+      storeReplacement(I->ReplacementLoc, I->ReplacementLength,
+                       getNewLineText(I->NewLines, Spaces));
     }
     ++I;
   }
 }
 
-void WhitespaceManager::storeReplacement(const FormatToken &Tok,
+void WhitespaceManager::alignEscapedNewlines() {
+  unsigned MinColumn;
+  if (Style.AlignEscapedNewlinesLeft) {
+    MinColumn = 0;
+    for (token_iterator I = EscapedNewlines.begin(), E = EscapedNewlines.end();
+         I != E; ++I) {
+      if (I->MinColumn > MinColumn)
+        MinColumn = I->MinColumn;
+    }
+  } else {
+    MinColumn = Style.ColumnLimit;
+  }
+
+  for (token_iterator I = EscapedNewlines.begin(), E = EscapedNewlines.end();
+       I != E; ++I) {
+    // I->MinColumn - 2 is the end of the previous token (i.e. the
+    // WhitespaceStartColumn).
+    storeReplacement(
+        I->ReplacementLoc, I->ReplacementLength,
+        I->Prefix + getNewLineText(I->NewLines, I->Spaces, I->MinColumn - 2,
+                                   MinColumn) + I->Postfix);
+
+  }
+  EscapedNewlines.clear();
+}
+
+void WhitespaceManager::storeReplacement(SourceLocation Loc, unsigned Length,
                                          const std::string Text) {
   // Don't create a replacement, if it does not change anything.
-  if (StringRef(SourceMgr.getCharacterData(Tok.WhiteSpaceStart),
-                Tok.WhiteSpaceLength) == Text)
+  if (StringRef(SourceMgr.getCharacterData(Loc), Length) == Text)
     return;
-
-  Replaces.insert(tooling::Replacement(SourceMgr, Tok.WhiteSpaceStart,
-                                       Tok.WhiteSpaceLength, Text));
+  Replaces.insert(tooling::Replacement(SourceMgr, Loc, Length, Text));
 }
 
 } // namespace format
index 2833e249c426ec065041b10dd70d97f220e3617e..5f3dc55edaccb28cd13d86f43c75052d5798aeeb 100644 (file)
@@ -1,4 +1,4 @@
-//===--- WhitespaceManager.h - Format C++ code ----------------------------===//
+//===--- WhitespaceManager.h - Format C++ code ------------------*- C++ -*-===//
 //
 //                     The LLVM Compiler Infrastructure
 //
@@ -68,31 +68,45 @@ public:
 
   /// \brief Try to align all stashed comments.
   void alignComments();
+  /// \brief Try to align all stashed escaped newlines.
+  void alignEscapedNewlines();
 
 private:
   std::string getNewLineText(unsigned NewLines, unsigned Spaces);
 
   std::string getNewLineText(unsigned NewLines, unsigned Spaces,
-                             unsigned WhitespaceStartColumn);
-
-  /// \brief Structure to store a comment for later layout and alignment.
-  struct StoredComment {
-    FormatToken Tok;
+                             unsigned WhitespaceStartColumn,
+                             unsigned EscapedNewlineColumn);
+
+  /// \brief Structure to store tokens for later layout and alignment.
+  struct StoredToken {
+    StoredToken(SourceLocation ReplacementLoc, unsigned ReplacementLength,
+                unsigned MinColumn, unsigned MaxColumn, unsigned NewLines,
+                unsigned Spaces)
+        : ReplacementLoc(ReplacementLoc), ReplacementLength(ReplacementLength),
+          MinColumn(MinColumn), MaxColumn(MaxColumn), NewLines(NewLines),
+          Spaces(Spaces), Untouchable(false) {}
+    SourceLocation ReplacementLoc;
+    unsigned ReplacementLength;
     unsigned MinColumn;
     unsigned MaxColumn;
     unsigned NewLines;
     unsigned Spaces;
     bool Untouchable;
+    std::string Prefix;
+    std::string Postfix;
   };
-  SmallVector<StoredComment, 16> Comments;
-  typedef SmallVector<StoredComment, 16>::iterator comment_iterator;
+  SmallVector<StoredToken, 16> Comments;
+  SmallVector<StoredToken, 16> EscapedNewlines;
+  typedef SmallVector<StoredToken, 16>::iterator token_iterator;
 
   /// \brief Put all the comments between \p I and \p E into \p Column.
-  void alignComments(comment_iterator I, comment_iterator E, unsigned Column);
+  void alignComments(token_iterator I, token_iterator E, unsigned Column);
 
   /// \brief Stores \p Text as the replacement for the whitespace in front of
   /// \p Tok.
-  void storeReplacement(const FormatToken &Tok, const std::string Text);
+  void storeReplacement(SourceLocation Loc, unsigned Length,
+                        const std::string Text);
 
   SourceManager &SourceMgr;
   tooling::Replacements Replaces;
index 97539d91a61ed673da8d218b42d88e002375ccb2..97d4df8e5e5d8ca01503e8f17109a8c80fba3cfd 100644 (file)
@@ -2974,16 +2974,16 @@ TEST_F(FormatTest, MergeHandlingInTheFaceOfPreprocessorDirectives) {
             format("if (true)\nreturn 42;", AllowsMergedIf));
   FormatStyle ShortMergedIf = AllowsMergedIf;
   ShortMergedIf.ColumnLimit = 25;
-  verifyFormat("#define A               \\\n"
+  verifyFormat("#define A \\\n"
                "  if (true) return 42;",
                ShortMergedIf);
-  verifyFormat("#define A               \\\n"
-               "  f();                  \\\n"
+  verifyFormat("#define A \\\n"
+               "  f();    \\\n"
                "  if (true)\n"
                "#define B",
                ShortMergedIf);
-  verifyFormat("#define A               \\\n"
-               "  f();                  \\\n"
+  verifyFormat("#define A \\\n"
+               "  f();    \\\n"
                "  if (true)\n"
                "g();",
                ShortMergedIf);
@@ -3800,6 +3800,15 @@ TEST_F(FormatTest, BreakStringLiterals) {
       "\"pathat/\"\n"
       "\"slashes\"",
       format("\"split/pathat/slashes\"", getLLVMStyleWithColumns(10)));
+
+  FormatStyle AlignLeft = getLLVMStyleWithColumns(12);
+  AlignLeft.AlignEscapedNewlinesLeft = true;
+  EXPECT_EQ(
+      "#define A \\\n"
+      "  \"some \" \\\n"
+      "  \"text \" \\\n"
+      "  \"other\";",
+      format("#define A \"some text other\";", AlignLeft));
 }
 
 TEST_F(FormatTest, DoNotBreakStringLiteralsInEscapeSequence) {