]> granicus.if.org Git - clang/commitdiff
clang-format: [JS] Support different function literal style.
authorDaniel Jasper <djasper@google.com>
Wed, 21 May 2014 12:51:23 +0000 (12:51 +0000)
committerDaniel Jasper <djasper@google.com>
Wed, 21 May 2014 12:51:23 +0000 (12:51 +0000)
Before:
  goog.array.forEach(array, function() {
                              doSomething();
                              doSomething();
                            },
                     this);

After:
  goog.array.forEach(array, function() {
    doSomething();
    doSomething();
  }, this);

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

lib/Format/ContinuationIndenter.cpp
lib/Format/ContinuationIndenter.h
lib/Format/Format.cpp
unittests/Format/FormatTestJS.cpp

index 63bb5e9918b7c51e7c698ca3ec8a4bd9d464594a..389047945c45cc5f4efd52c8050dd7aaf27eeaf2 100644 (file)
@@ -420,9 +420,16 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State,
 
   // Any break on this level means that the parent level has been broken
   // and we need to avoid bin packing there.
-  for (unsigned i = 0, e = State.Stack.size() - 1; i != e; ++i) {
-    State.Stack[i].BreakBeforeParameter = true;
+  bool JavaScriptFormat = Style.Language == FormatStyle::LK_JavaScript &&
+                          Current.is(tok::r_brace) &&
+                          State.Stack.size() > 1 &&
+                          State.Stack[State.Stack.size() - 2].JSFunctionInlined;
+  if (!JavaScriptFormat) {
+    for (unsigned i = 0, e = State.Stack.size() - 1; i != e; ++i) {
+      State.Stack[i].BreakBeforeParameter = true;
+    }
   }
+
   if (PreviousNonComment &&
       !PreviousNonComment->isOneOf(tok::comma, tok::semi) &&
       PreviousNonComment->Type != TT_TemplateCloser &&
@@ -465,6 +472,9 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) {
     return Current.NestingLevel == 0 ? State.FirstIndent
                                      : State.Stack.back().Indent;
   if (Current.isOneOf(tok::r_brace, tok::r_square)) {
+    if (State.Stack.size() > 1 &&
+        State.Stack[State.Stack.size() - 2].JSFunctionInlined)
+      return State.FirstIndent;
     if (Current.closesBlockTypeList(Style) ||
         (Current.MatchingParen &&
          Current.MatchingParen->BlockKind == BK_BracedInit))
@@ -600,6 +610,27 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State,
 
   // Insert scopes created by fake parenthesis.
   const FormatToken *Previous = Current.getPreviousNonComment();
+
+  // Add special behavior to support a format commonly used for JavaScript
+  // closures:
+  //   SomeFunction(function() {
+  //     foo();
+  //     bar();
+  //   }, a, b, c);
+  if (Style.Language == FormatStyle::LK_JavaScript) {
+    if (Current.isNot(tok::comment) && Previous && Previous->is(tok::l_brace) &&
+        State.Stack.size() > 1) {
+      if (State.Stack[State.Stack.size() - 2].JSFunctionInlined && Newline) {
+        for (unsigned i = 0, e = State.Stack.size() - 1; i != e; ++i) {
+          State.Stack[i].NoLineBreak = true;
+        }
+      }
+      State.Stack[State.Stack.size() - 2].JSFunctionInlined = false;
+    }
+    if (Current.TokenText == "function")
+      State.Stack.back().JSFunctionInlined = !Newline;
+  }
+
   // Don't add extra indentation for the first fake parenthesis after
   // 'return', assignments or opening <({[. The indentation for these cases
   // is special cased.
index d81bf645aa803e5a13c59cf6fb984067c52f2791..dc3e3086fdf1d6068921941cb211e5a088055a0e 100644 (file)
@@ -139,7 +139,8 @@ struct ParenState {
         StartOfFunctionCall(0), StartOfArraySubscripts(0),
         NestedNameSpecifierContinuation(0), CallContinuation(0), VariablePos(0),
         ContainsLineBreak(false), ContainsUnwrappedBuilder(0),
-        AlignColons(true), ObjCSelectorNameFound(false), LambdasFound(0) {}
+        AlignColons(true), ObjCSelectorNameFound(false), LambdasFound(0),
+        JSFunctionInlined(false) {}
 
   /// \brief The position to which a specific parenthesis level needs to be
   /// indented.
@@ -240,6 +241,10 @@ struct ParenState {
   /// the same token.
   unsigned LambdasFound;
 
+  // \brief The previous JavaScript 'function' keyword is not wrapped to a new
+  // line.
+  bool JSFunctionInlined;
+
   bool operator<(const ParenState &Other) const {
     if (Indent != Other.Indent)
       return Indent < Other.Indent;
@@ -273,6 +278,8 @@ struct ParenState {
       return ContainsLineBreak < Other.ContainsLineBreak;
     if (ContainsUnwrappedBuilder != Other.ContainsUnwrappedBuilder)
       return ContainsUnwrappedBuilder < Other.ContainsUnwrappedBuilder;
+    if (JSFunctionInlined != Other.JSFunctionInlined)
+      return JSFunctionInlined < Other.JSFunctionInlined;
     return false;
   }
 };
index ae8c75ff6dc4a592090363ebe02d1568fdbb4aee..891a7188bbaf252ee83082d84b7c54f58227d358 100644 (file)
@@ -1146,8 +1146,13 @@ private:
       return true;
 
     if (NewLine) {
-      int AdditionalIndent = State.Stack.back().Indent -
-                             Previous.Children[0]->Level * Style.IndentWidth;
+      int AdditionalIndent = 0;
+      if (State.Stack.size() < 2 ||
+          !State.Stack[State.Stack.size() - 2].JSFunctionInlined) {
+        AdditionalIndent = State.Stack.back().Indent -
+                           Previous.Children[0]->Level * Style.IndentWidth;
+      }
+
       Penalty += format(Previous.Children, DryRun, AdditionalIndent,
                         /*FixBadIndentation=*/true);
       return true;
index 657f10874e022b0007aed5b4a89344c81fe0e7c8..257c247765b46ab4d8380100f91ef1ca90e254a9 100644 (file)
@@ -120,6 +120,18 @@ TEST_F(FormatTestJS, Closures) {
                "};");
   EXPECT_EQ("abc = xyz ? function() { return 1; } : function() { return -1; };",
             format("abc=xyz?function(){return 1;}:function(){return -1;};"));
+
+  verifyFormat("var closure = goog.bind(\n"
+               "    function() {  // comment\n"
+               "      foo();\n"
+               "      bar();\n"
+               "    },\n"
+               "    this, arg1IsReallyLongAndNeeedsLineBreaks,\n"
+               "    arg3IsReallyLongAndNeeedsLineBreaks);");
+  verifyFormat("var closure = goog.bind(function() {  // comment\n"
+               "  foo();\n"
+               "  bar();\n"
+               "}, this);");
 }
 
 TEST_F(FormatTestJS, ReturnStatements) {