ksuther created this revision.
ksuther added a reviewer: djasper.
ksuther added a subscriber: cfe-commits.
Herald added a subscriber: klimek.

Changes to clang-format's Objective-C block formatting over the past year have 
made clang-format's output deviate from what is expected (in my opinion).

There are three changes in particular:

- Method calls with multiple block parameters has each block indented further in
- Nested blocks get indented one level in
- Method calls that end with a block parameter always get forced to a new line 
and indented

The end of this message has examples of formatting with and without these 
changes.

This patch undoes one revision which causes methods with multiple block 
parameters to indent for each parameter (r236598) and adds two new options.
AllowNewlineBeforeBlockParameter makes the change in r235580 optional.
IndentNestedBlocks makes the change in r234304 optional.

Some relevant Bugzilla issues:
https://llvm.org/bugs/show_bug.cgi?id=23585
https://llvm.org/bugs/show_bug.cgi?id=23317

This patch came out of a discussion here 
https://github.com/square/spacecommander/issues/33, where we're using a custom 
version of clang-format with these options added. Based on the Bugzilla issues, 
it seems that we're not alone.

Thanks!

---

Current trunk:

bin/clang-format -style="{BasedOnStyle: WebKit, ColumnLimit: 0, IndentWidth: 
4}" ~/clang-format.m

```
- (void)test
{
    [self block:^(void) {
        doStuff();
    }
        completionHandler:^(void) {
            doStuff();

            [self block:^(void) {
                doStuff();
            }
                completionHandler:^(void) {
                    doStuff();
                }];
        }];

    [[SessionService sharedService] loadWindow:aWindow
                               completionBlock:^(SessionWindow* window) {
                                   if (window) {
                                       [self doStuff];
                                   }
                               }];
}
```

With this patch applied:

bin/clang-format -style="{BasedOnStyle: WebKit, ColumnLimit: 0, IndentWidth: 4, 
IndentNestedBlocks: false, AllowNewlineBeforeBlockParameter: 
false}"~/clang-format.m 

```
- (void)test
{
    [self block:^(void) {
        doStuff();
    } completionHandler:^(void) {
        doStuff();

        [self block:^(void) {
            doStuff();
        } completionHandler:^(void) {
            doStuff();
        }];
    }];

    [[SessionService sharedService] loadWindow:aWindow 
completionBlock:^(SessionWindow* window) {
        if (window) {
            [self doStuff];
        }
    }];
}
```

http://reviews.llvm.org/D17700

Files:
  include/clang/Format/Format.h
  lib/Format/ContinuationIndenter.cpp
  lib/Format/Format.cpp
  unittests/Format/FormatTest.cpp

Index: unittests/Format/FormatTest.cpp
===================================================================
--- unittests/Format/FormatTest.cpp
+++ unittests/Format/FormatTest.cpp
@@ -7606,13 +7606,6 @@
       "                 aaaaaaaaaaaaaaa | aaaaaaaaaaaaaaa | aaaaaaaaaaaaaaa |\n"
       "                 aaaaaaaaaaaaaaa | aaaaaaaaaaaaaaa];");
 
-  // FIXME: This violates the column limit.
-  verifyFormat(
-      "[aaaaaaaaaaaaaaaaaaaaaaaaa\n"
-      "    aaaaaaaaaaaaaaaaa:aaaaaaaa\n"
-      "                  aaa:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa];",
-      getLLVMStyleWithColumns(60));
-
   // Variadic parameters.
   verifyFormat(
       "NSArray *myStrings = [NSArray stringarray:@\"a\", @\"b\", nil];");
Index: lib/Format/Format.cpp
===================================================================
--- lib/Format/Format.cpp
+++ lib/Format/Format.cpp
@@ -296,6 +296,10 @@
     IO.mapOptional("IndentWidth", Style.IndentWidth);
     IO.mapOptional("IndentWrappedFunctionNames",
                    Style.IndentWrappedFunctionNames);
+    IO.mapOptional("IndentNestedBlocks",
+                   Style.IndentNestedBlocks);
+    IO.mapOptional("AllowNewlineBeforeBlockParameter",
+                   Style.AllowNewlineBeforeBlockParameter);
     IO.mapOptional("KeepEmptyLinesAtTheStartOfBlocks",
                    Style.KeepEmptyLinesAtTheStartOfBlocks);
     IO.mapOptional("MacroBlockBegin", Style.MacroBlockBegin);
@@ -510,6 +514,8 @@
                                  {".*", 1}};
   LLVMStyle.IndentCaseLabels = false;
   LLVMStyle.IndentWrappedFunctionNames = false;
+  LLVMStyle.IndentNestedBlocks = true;
+  LLVMStyle.AllowNewlineBeforeBlockParameter = true;
   LLVMStyle.IndentWidth = 2;
   LLVMStyle.TabWidth = 8;
   LLVMStyle.MaxEmptyLinesToKeep = 1;
Index: lib/Format/ContinuationIndenter.cpp
===================================================================
--- lib/Format/ContinuationIndenter.cpp
+++ lib/Format/ContinuationIndenter.cpp
@@ -178,9 +178,6 @@
       ((Style.AllowShortFunctionsOnASingleLine != FormatStyle::SFS_All) ||
        Style.BreakConstructorInitializersBeforeComma || Style.ColumnLimit != 0))
     return true;
-  if (Current.is(TT_SelectorName) && State.Stack.back().ObjCSelectorNameFound &&
-      State.Stack.back().BreakBeforeParameter)
-    return true;
 
   unsigned NewLineColumn = getNewLineColumn(State);
   if (Current.isMemberAccess() && Style.ColumnLimit != 0 &&
@@ -234,6 +231,9 @@
       State.Stack.back().FirstLessLess == 0)
     return true;
 
+  if (Current.is(TT_SelectorName) && State.Stack.back().ObjCSelectorNameFound &&
+      State.Stack.back().BreakBeforeParameter)
+    return true;
   if (Current.NestingLevel == 0 && !Current.isTrailingComment()) {
     // Always break after "template <...>" and leading annotations. This is only
     // for cases where the entire line does not fit on a single line as a
@@ -916,8 +916,13 @@
   unsigned LastSpace = State.Stack.back().LastSpace;
   bool AvoidBinPacking;
   bool BreakBeforeParameter = false;
-  unsigned NestedBlockIndent = std::max(State.Stack.back().StartOfFunctionCall,
-                                        State.Stack.back().NestedBlockIndent);
+  unsigned NestedBlockIndent;
+  if (Style.IndentNestedBlocks) {
+    NestedBlockIndent = std::max(State.Stack.back().StartOfFunctionCall,
+                                 State.Stack.back().NestedBlockIndent);
+  } else {
+    NestedBlockIndent = State.Stack.back().NestedBlockIndent;
+  }
   if (Current.isOneOf(tok::l_brace, TT_ArrayInitializerLSquare)) {
     if (Current.opensBlockOrBlockTypeList(Style)) {
       NewIndent = State.Stack.back().NestedBlockIndent + Style.IndentWidth;
@@ -967,7 +972,7 @@
         if (getLengthToMatchingParen(Current) + State.Column >
             getColumnLimit(State))
           BreakBeforeParameter = true;
-      } else {
+      } else if (Style.AllowNewlineBeforeBlockParameter) {
         // For ColumnLimit = 0, we have to figure out whether there is or has to
         // be a line break within this call.
         for (const FormatToken *Tok = &Current;
Index: include/clang/Format/Format.h
===================================================================
--- include/clang/Format/Format.h
+++ include/clang/Format/Format.h
@@ -414,6 +414,12 @@
   /// type.
   bool IndentWrappedFunctionNames;
 
+  /// \brief If true, indent nested blocks to the start of the nested function call
+  bool IndentNestedBlocks;
+
+  /// \brief If true, allow newlines before block parameters when ColumnLimit is 0.
+  bool AllowNewlineBeforeBlockParameter;
+
   /// \brief If true, empty lines at the start of blocks are kept.
   bool KeepEmptyLinesAtTheStartOfBlocks;
 
@@ -642,6 +648,8 @@
            IndentCaseLabels == R.IndentCaseLabels &&
            IndentWidth == R.IndentWidth && Language == R.Language &&
            IndentWrappedFunctionNames == R.IndentWrappedFunctionNames &&
+           IndentNestedBlocks == R.IndentNestedBlocks &&
+           AllowNewlineBeforeBlockParameter == R.AllowNewlineBeforeBlockParameter &&
            KeepEmptyLinesAtTheStartOfBlocks ==
                R.KeepEmptyLinesAtTheStartOfBlocks &&
            MacroBlockBegin == R.MacroBlockBegin &&
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to