Hi klimek, djasper,

Changed UseTab to be a enum with three options: Never, Always,
ForIndentation (true/false are still supported when reading .clang-format).
IndentLevel should currently be propagated correctly for all tokens, except for
block comments. Please take a look at the general idea before I start dealing
with block comments.

http://llvm-reviews.chandlerc.com/D1770

Files:
  include/clang/Format/Format.h
  lib/Format/BreakableToken.cpp
  lib/Format/ContinuationIndenter.cpp
  lib/Format/Format.cpp
  lib/Format/WhitespaceManager.cpp
  lib/Format/WhitespaceManager.h
  unittests/Format/FormatTest.cpp
Index: include/clang/Format/Format.h
===================================================================
--- include/clang/Format/Format.h
+++ include/clang/Format/Format.h
@@ -166,9 +166,19 @@
   /// \brief If \c true, always break before multiline string literals.
   bool AlwaysBreakBeforeMultilineStrings;
 
-  /// \brief If \c true, \c IndentWidth consecutive spaces will be replaced
-  /// with tab characters.
-  bool UseTab;
+  /// \brief Different ways to use tab in formatting.
+  enum UseTabStyle {
+    /// Never use tab.
+    UT_Never,
+    /// Use tabs only for indentation.
+    UT_ForIndentation,
+    /// Use tabs whenever we need to fill whitespace that spans at least from
+    /// one tab stop to the next one.
+    UT_Always
+  };
+
+  /// \brief The way to use tab characters in the resulting file.
+  UseTabStyle UseTab;
 
   /// \brief If \c true, binary operators will be placed after line breaks.
   bool BreakBeforeBinaryOperators;
Index: lib/Format/BreakableToken.cpp
===================================================================
--- lib/Format/BreakableToken.cpp
+++ lib/Format/BreakableToken.cpp
@@ -173,9 +173,10 @@
 void BreakableStringLiteral::insertBreak(unsigned LineIndex,
                                          unsigned TailOffset, Split Split,
                                          WhitespaceManager &Whitespaces) {
+  // FIXME: Insert correct IndentLevel.
   Whitespaces.replaceWhitespaceInToken(
       Tok, Prefix.size() + TailOffset + Split.first, Split.second, Postfix,
-      Prefix, InPPDirective, 1, StartColumn);
+      Prefix, InPPDirective, 1, /*IndentLevel=*/0, StartColumn);
 }
 
 static StringRef getLineCommentPrefix(StringRef Comment) {
@@ -214,17 +215,18 @@
 void BreakableLineComment::insertBreak(unsigned LineIndex, unsigned TailOffset,
                                        Split Split,
                                        WhitespaceManager &Whitespaces) {
+  // FIXME: Insert correct IndentLevel.
   Whitespaces.replaceWhitespaceInToken(
       Tok, OriginalPrefix.size() + TailOffset + Split.first, Split.second,
-      Postfix, Prefix, InPPDirective, 1, StartColumn);
+      Postfix, Prefix, InPPDirective, 1, /*IndentLevel=*/0, StartColumn);
 }
 
 void
 BreakableLineComment::replaceWhitespaceBefore(unsigned LineIndex,
                                               WhitespaceManager &Whitespaces) {
   if (OriginalPrefix != Prefix) {
     Whitespaces.replaceWhitespaceInToken(Tok, OriginalPrefix.size(), 0, "", "",
-                                         false, 0, 1);
+                                         false, 0, /*IndentLevel=*/0, 1);
   }
 }
 
@@ -376,8 +378,10 @@
       Text.data() - Tok.TokenText.data() + Split.first;
   unsigned CharsToRemove = Split.second;
   assert(IndentAtLineBreak >= Decoration.size());
+  // FIXME: Insert correct IndentLevel.
   Whitespaces.replaceWhitespaceInToken(Tok, BreakOffsetInToken, CharsToRemove,
                                        "", Prefix, InPPDirective, 1,
+                                       /*IndentLevel=*/0,
                                        IndentAtLineBreak - Decoration.size());
 }
 
@@ -410,9 +414,11 @@
                                      Tok.TokenText.data() -
                                      LeadingWhitespace[LineIndex];
   assert(StartOfLineColumn[LineIndex] >= Prefix.size());
+  // FIXME: Insert correct IndentLevel.
   Whitespaces.replaceWhitespaceInToken(
       Tok, WhitespaceOffsetInToken, LeadingWhitespace[LineIndex], "", Prefix,
-      InPPDirective, 1, StartOfLineColumn[LineIndex] - Prefix.size());
+      InPPDirective, 1, /*IndentLevel=*/0,
+      StartOfLineColumn[LineIndex] - Prefix.size());
 }
 
 unsigned
Index: lib/Format/ContinuationIndenter.cpp
===================================================================
--- lib/Format/ContinuationIndenter.cpp
+++ lib/Format/ContinuationIndenter.cpp
@@ -303,12 +303,13 @@
       State.Stack.back().BreakBeforeParameter = false;
 
     if (!DryRun) {
-      unsigned NewLines = 1;
+      unsigned Newlines = 1;
       if (Current.is(tok::comment))
-        NewLines = std::max(NewLines, std::min(Current.NewlinesBefore,
+        Newlines = std::max(Newlines, std::min(Current.NewlinesBefore,
                                                Style.MaxEmptyLinesToKeep + 1));
-      Whitespaces.replaceWhitespace(Current, NewLines, State.Column,
-                                    State.Column, State.Line->InPPDirective);
+      Whitespaces.replaceWhitespace(Current, Newlines, State.Line->Level,
+                                    State.Column, State.Column,
+                                    State.Line->InPPDirective);
     }
 
     if (!Current.isTrailingComment())
@@ -363,7 +364,8 @@
     unsigned Spaces = State.NextToken->SpacesRequiredBefore + ExtraSpaces;
 
     if (!DryRun)
-      Whitespaces.replaceWhitespace(Current, 0, Spaces, State.Column + Spaces);
+      Whitespaces.replaceWhitespace(Current, /*Newlines=*/0, /*IndentLevel=*/0,
+                                    Spaces, State.Column + Spaces);
 
     if (Current.Type == TT_ObjCSelectorName &&
         State.Stack.back().ColonPos == 0) {
Index: lib/Format/Format.cpp
===================================================================
--- lib/Format/Format.cpp
+++ lib/Format/Format.cpp
@@ -45,6 +45,19 @@
 };
 
 template <>
+struct ScalarEnumerationTraits<clang::format::FormatStyle::UseTabStyle> {
+  static void
+  enumeration(IO &IO, clang::format::FormatStyle::UseTabStyle &Value) {
+    IO.enumCase(Value, "Never", clang::format::FormatStyle::UT_Never);
+    IO.enumCase(Value, "false", clang::format::FormatStyle::UT_Never);
+    IO.enumCase(Value, "Always", clang::format::FormatStyle::UT_Always);
+    IO.enumCase(Value, "true", clang::format::FormatStyle::UT_Always);
+    IO.enumCase(Value, "ForIndentation",
+                clang::format::FormatStyle::UT_ForIndentation);
+  }
+};
+
+template <>
 struct ScalarEnumerationTraits<clang::format::FormatStyle::BraceBreakingStyle> {
   static void
   enumeration(IO &IO, clang::format::FormatStyle::BraceBreakingStyle &Value) {
@@ -194,7 +207,7 @@
   LLVMStyle.PointerBindsToType = false;
   LLVMStyle.SpacesBeforeTrailingComments = 1;
   LLVMStyle.Standard = FormatStyle::LS_Cpp03;
-  LLVMStyle.UseTab = false;
+  LLVMStyle.UseTab = FormatStyle::UT_Never;
   LLVMStyle.SpacesInParentheses = false;
   LLVMStyle.SpaceInEmptyParentheses = false;
   LLVMStyle.SpacesInCStyleCastParentheses = false;
@@ -237,7 +250,7 @@
   GoogleStyle.PointerBindsToType = true;
   GoogleStyle.SpacesBeforeTrailingComments = 2;
   GoogleStyle.Standard = FormatStyle::LS_Auto;
-  GoogleStyle.UseTab = false;
+  GoogleStyle.UseTab = FormatStyle::UT_Never;
   GoogleStyle.SpacesInParentheses = false;
   GoogleStyle.SpaceInEmptyParentheses = false;
   GoogleStyle.SpacesInCStyleCastParentheses = false;
@@ -532,14 +545,14 @@
                I = LBrace.Children.begin(),
                E = LBrace.Children.end();
            I != E; ++I) {
-        unsigned Indent =
-            ParentIndent + ((*I)->Level - Line.Level - 1) * Style.IndentWidth;
+        unsigned IndentLevel = (*I)->Level - Line.Level - 1;
+        unsigned Indent = ParentIndent + IndentLevel * Style.IndentWidth;
         if (!DryRun) {
           unsigned Newlines = std::min((*I)->First->NewlinesBefore,
                                        Style.MaxEmptyLinesToKeep + 1);
           Newlines = std::max(1u, Newlines);
           Whitespaces->replaceWhitespace(
-              *(*I)->First, Newlines, /*Spaces=*/Indent,
+              *(*I)->First, Newlines, IndentLevel, /*Spaces=*/Indent,
               /*StartOfTokenColumn=*/Indent, Line.InPPDirective);
         }
         UnwrappedLineFormatter Formatter(Indenter, Whitespaces, Style, **I);
@@ -556,10 +569,10 @@
       return false;
 
     if (!DryRun) {
-      Whitespaces->replaceWhitespace(*LBrace.Children[0]->First,
-                                     /*Newlines=*/0, /*Spaces=*/1,
-                                     /*StartOfTokenColumn=*/State.Column,
-                                     State.Line->InPPDirective);
+      Whitespaces->replaceWhitespace(
+          *LBrace.Children[0]->First,
+          /*Newlines=*/0, /*IndentLevel=*/1, /*Spaces=*/1,
+          /*StartOfTokenColumn=*/State.Column, State.Line->InPPDirective);
       UnwrappedLineFormatter Formatter(Indenter, Whitespaces, Style,
                                        *LBrace.Children[0]);
       Penalty += Formatter.format(State.Column + 1, DryRun);
@@ -847,8 +860,9 @@
       if (TheLine.First->is(tok::eof)) {
         if (PreviousLineWasTouched) {
           unsigned NewLines = std::min(FirstTok->NewlinesBefore, 1u);
-          Whitespaces.replaceWhitespace(*TheLine.First, NewLines, /*Indent*/ 0,
-                                        /*TargetColumn*/ 0);
+          Whitespaces.replaceWhitespace(*TheLine.First, NewLines,
+                                        /*IndentLevel=*/0, /*Spaces=*/0,
+                                        /*TargetColumn=*/0);
         }
       } else if (TheLine.Type != LT_Invalid &&
                  (WasMoved || FormatPPDirective || touchesLine(TheLine))) {
@@ -1225,7 +1239,7 @@
       ++Newlines;
 
     Whitespaces.replaceWhitespace(
-        RootToken, Newlines, Indent, Indent,
+        RootToken, Newlines, Indent / Style.IndentWidth, Indent, Indent,
         InPPDirective && !RootToken.HasUnescapedNewline);
   }
 
Index: lib/Format/WhitespaceManager.cpp
===================================================================
--- lib/Format/WhitespaceManager.cpp
+++ lib/Format/WhitespaceManager.cpp
@@ -28,41 +28,44 @@
 
 WhitespaceManager::Change::Change(
     bool CreateReplacement, const SourceRange &OriginalWhitespaceRange,
-    unsigned Spaces, unsigned StartOfTokenColumn, unsigned NewlinesBefore,
-    StringRef PreviousLinePostfix, StringRef CurrentLinePrefix,
-    tok::TokenKind Kind, bool ContinuesPPDirective)
+    unsigned IndentLevel, unsigned Spaces, unsigned StartOfTokenColumn,
+    unsigned NewlinesBefore, StringRef PreviousLinePostfix,
+    StringRef CurrentLinePrefix, tok::TokenKind Kind, bool ContinuesPPDirective)
     : CreateReplacement(CreateReplacement),
       OriginalWhitespaceRange(OriginalWhitespaceRange),
       StartOfTokenColumn(StartOfTokenColumn), NewlinesBefore(NewlinesBefore),
       PreviousLinePostfix(PreviousLinePostfix),
       CurrentLinePrefix(CurrentLinePrefix), Kind(Kind),
-      ContinuesPPDirective(ContinuesPPDirective), Spaces(Spaces) {}
+      ContinuesPPDirective(ContinuesPPDirective), IndentLevel(IndentLevel),
+      Spaces(Spaces) {}
 
 void WhitespaceManager::replaceWhitespace(const FormatToken &Tok,
-                                          unsigned Newlines, unsigned Spaces,
+                                          unsigned Newlines,
+                                          unsigned IndentLevel, unsigned Spaces,
                                           unsigned StartOfTokenColumn,
                                           bool InPPDirective) {
-  Changes.push_back(Change(true, Tok.WhitespaceRange, Spaces,
+  Changes.push_back(Change(true, Tok.WhitespaceRange, IndentLevel, Spaces,
                            StartOfTokenColumn, Newlines, "", "",
                            Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst));
 }
 
 void WhitespaceManager::addUntouchableToken(const FormatToken &Tok,
                                             bool InPPDirective) {
-  Changes.push_back(Change(false, Tok.WhitespaceRange, /*Spaces=*/0,
-                           Tok.OriginalColumn, Tok.NewlinesBefore, "", "",
-                           Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst));
+  Changes.push_back(Change(false, Tok.WhitespaceRange, /*IndentLevel=*/0,
+                           /*Spaces=*/0, Tok.OriginalColumn, Tok.NewlinesBefore,
+                           "", "", Tok.Tok.getKind(),
+                           InPPDirective && !Tok.IsFirst));
 }
 
 void WhitespaceManager::replaceWhitespaceInToken(
     const FormatToken &Tok, unsigned Offset, unsigned ReplaceChars,
     StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective,
-    unsigned Newlines, unsigned Spaces) {
+    unsigned Newlines, unsigned IndentLevel, unsigned Spaces) {
   Changes.push_back(Change(
       true, SourceRange(Tok.getStartOfNonWhitespace().getLocWithOffset(Offset),
                         Tok.getStartOfNonWhitespace().getLocWithOffset(
                             Offset + ReplaceChars)),
-      Spaces, Spaces, Newlines, PreviousPostfix, CurrentPrefix,
+      IndentLevel, Spaces, Spaces, Newlines, PreviousPostfix, CurrentPrefix,
       // If we don't add a newline this change doesn't start a comment. Thus,
       // when we align line comments, we don't need to treat this change as one.
       // FIXME: We still need to take this change in account to properly
@@ -225,7 +228,8 @@
                           C.PreviousEndOfTokenColumn, C.EscapedNewlineColumn);
       else
         appendNewlineText(ReplacementText, C.NewlinesBefore);
-      appendIndentText(ReplacementText, C.Spaces, C.StartOfTokenColumn - C.Spaces);
+      appendIndentText(ReplacementText, C.IndentLevel, C.Spaces,
+                       C.StartOfTokenColumn - C.Spaces);
       ReplacementText.append(C.CurrentLinePrefix);
       storeReplacement(C.OriginalWhitespaceRange, ReplacementText);
     }
@@ -264,11 +268,14 @@
   }
 }
 
-void WhitespaceManager::appendIndentText(std::string &Text, unsigned Spaces,
+void WhitespaceManager::appendIndentText(std::string &Text,
+                                         unsigned IndentLevel, unsigned Spaces,
                                          unsigned WhitespaceStartColumn) {
-  if (!Style.UseTab) {
+  switch (Style.UseTab) {
+  case FormatStyle::UT_Never:
     Text.append(std::string(Spaces, ' '));
-  } else {
+    break;
+  case FormatStyle::UT_Always: {
     unsigned FirstTabWidth =
         Style.TabWidth - WhitespaceStartColumn % Style.TabWidth;
     // Indent with tabs only when there's at least one full tab.
@@ -278,6 +285,19 @@
     }
     Text.append(std::string(Spaces / Style.TabWidth, '\t'));
     Text.append(std::string(Spaces % Style.TabWidth, ' '));
+    break;
+  }
+  case FormatStyle::UT_ForIndentation:
+    if (WhitespaceStartColumn == 0) {
+      unsigned Indentation = IndentLevel * Style.IndentWidth;
+      if (Indentation > Spaces)
+        Indentation = Spaces;
+      unsigned Tabs = Indentation / Style.TabWidth;
+      Text.append(std::string(Tabs, '\t'));
+      Spaces -= Tabs * Style.TabWidth;
+    }
+    Text.append(std::string(Spaces, ' '));
+    break;
   }
 }
 
Index: lib/Format/WhitespaceManager.h
===================================================================
--- lib/Format/WhitespaceManager.h
+++ lib/Format/WhitespaceManager.h
@@ -44,7 +44,8 @@
   /// \brief Replaces the whitespace in front of \p Tok. Only call once for
   /// each \c AnnotatedToken.
   void replaceWhitespace(const FormatToken &Tok, unsigned Newlines,
-                         unsigned Spaces, unsigned StartOfTokenColumn,
+                         unsigned IndentLevel, unsigned Spaces,
+                         unsigned StartOfTokenColumn,
                          bool InPPDirective = false);
 
   /// \brief Adds information about an unchangable token's whitespace.
@@ -65,7 +66,8 @@
                                 unsigned ReplaceChars,
                                 StringRef PreviousPostfix,
                                 StringRef CurrentPrefix, bool InPPDirective,
-                                unsigned Newlines, unsigned Spaces);
+                                unsigned Newlines, unsigned IndentLevel,
+                                unsigned Spaces);
 
   /// \brief Returns all the \c Replacements created during formatting.
   const tooling::Replacements &generateReplacements();
@@ -96,7 +98,7 @@
     /// \p StartOfTokenColumn and \p InPPDirective will be used to lay out
     /// trailing comments and escaped newlines.
     Change(bool CreateReplacement, const SourceRange &OriginalWhitespaceRange,
-           unsigned Spaces, unsigned StartOfTokenColumn,
+           unsigned IndentLevel, unsigned Spaces, unsigned StartOfTokenColumn,
            unsigned NewlinesBefore, StringRef PreviousLinePostfix,
            StringRef CurrentLinePrefix, tok::TokenKind Kind,
            bool ContinuesPPDirective);
@@ -116,6 +118,11 @@
     tok::TokenKind Kind;
     bool ContinuesPPDirective;
 
+    // The number of nested blocks the token is in. This is used to add tabs
+    // only for the indentation, and not for alignment, when
+    // UseTab = US_ForIndentation.
+    unsigned IndentLevel;
+
     // The number of spaces in front of the token or broken part of the token.
     // This will be adapted when aligning tokens.
     unsigned Spaces;
@@ -157,8 +164,8 @@
   void appendNewlineText(std::string &Text, unsigned Newlines,
                          unsigned PreviousEndOfTokenColumn,
                          unsigned EscapedNewlineColumn);
-  void appendIndentText(std::string &Text, unsigned Spaces,
-                        unsigned WhitespaceStartColumn);
+  void appendIndentText(std::string &Text, unsigned IndentLevel,
+                        unsigned Spaces, unsigned WhitespaceStartColumn);
 
   SmallVector<Change, 16> Changes;
   SourceManager &SourceMgr;
Index: unittests/Format/FormatTest.cpp
===================================================================
--- unittests/Format/FormatTest.cpp
+++ unittests/Format/FormatTest.cpp
@@ -5758,7 +5758,7 @@
 TEST_F(FormatTest, ConfigurableUseOfTab) {
   FormatStyle Tab = getLLVMStyleWithColumns(42);
   Tab.IndentWidth = 8;
-  Tab.UseTab = true;
+  Tab.UseTab = FormatStyle::UT_Always;
   Tab.AlignEscapedNewlinesLeft = true;
 
   EXPECT_EQ("if (aaaaaaaa && // q\n"
@@ -5858,7 +5858,25 @@
                    " \t  */",
                    Tab));
 
-  Tab.UseTab = false;
+  Tab.UseTab = FormatStyle::UT_ForIndentation;
+  EXPECT_EQ("if (aaaaaaaa && // q\n"
+            "    bb)         // w\n"
+            "\t;",
+            format("if (aaaaaaaa &&// q\n"
+                   "bb)// w\n"
+                   ";",
+                   Tab));
+
+  verifyFormat("class X {\n"
+               "\tvoid f() {\n"
+               "\t\tsomeFunction(parameter1,\n"
+               "\t\t             parameter2);\n"
+               "\t}\n"
+               "};",
+               Tab);
+
+
+  Tab.UseTab = FormatStyle::UT_Never;
   EXPECT_EQ("/*\n"
             "              a\t\tcomment\n"
             "              in multiple lines\n"
@@ -6265,7 +6283,6 @@
   CHECK_PARSE_BOOL(ObjCSpaceBeforeProtocolList);
   CHECK_PARSE_BOOL(PointerBindsToType);
   CHECK_PARSE_BOOL(Cpp11BracedListStyle);
-  CHECK_PARSE_BOOL(UseTab);
   CHECK_PARSE_BOOL(IndentFunctionDeclarationAfterType);
   CHECK_PARSE_BOOL(SpacesInParentheses);
   CHECK_PARSE_BOOL(SpaceInEmptyParentheses);
@@ -6292,6 +6309,13 @@
   CHECK_PARSE("Standard: C++11", Standard, FormatStyle::LS_Cpp11);
   CHECK_PARSE("Standard: Auto", Standard, FormatStyle::LS_Auto);
 
+  Style.UseTab = FormatStyle::UT_ForIndentation;
+  CHECK_PARSE("UseTab: false", UseTab, FormatStyle::UT_Never);
+  CHECK_PARSE("UseTab: true", UseTab, FormatStyle::UT_Always);
+  CHECK_PARSE("UseTab: Never", UseTab, FormatStyle::UT_Never);
+  CHECK_PARSE("UseTab: ForIndentation", UseTab, FormatStyle::UT_ForIndentation);
+  CHECK_PARSE("UseTab: Always", UseTab, FormatStyle::UT_Always);
+
   Style.ColumnLimit = 123;
   FormatStyle BaseStyle = getLLVMStyle();
   CHECK_PARSE("BasedOnStyle: LLVM", ColumnLimit, BaseStyle.ColumnLimit);
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to