Index: docs/ClangFormatStyleOptions.rst =================================================================== --- docs/ClangFormatStyleOptions.rst +++ docs/ClangFormatStyleOptions.rst @@ -618,6 +618,10 @@ **ObjCBlockIndentWidth** (``unsigned``) The number of characters to use for indentation of ObjC blocks. +**ObjCBlockResetsIndent** (``bool``) + Reset the indent of an ObjC block to that of its owner statement + if the statement has only one embedded block. + **ObjCSpaceAfterProperty** (``bool``) Add a space after ``@property`` in Objective-C, i.e. use ``@property (readonly)`` instead of ``@property(readonly)``. Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -500,6 +500,10 @@ /// \brief The number of characters to use for indentation of ObjC blocks. unsigned ObjCBlockIndentWidth; + /// \brief Reset the indent of an ObjC block to that of its owner statement + /// if the statement has only one embedded block. + bool ObjCBlockResetsIndent = false; + /// \brief Add a space after ``@property`` in Objective-C, i.e. use /// ``@property (readonly)`` instead of ``@property(readonly)``. bool ObjCSpaceAfterProperty; Index: lib/Format/ContinuationIndenter.cpp =================================================================== --- lib/Format/ContinuationIndenter.cpp +++ lib/Format/ContinuationIndenter.cpp @@ -1037,6 +1037,16 @@ } void ContinuationIndenter::moveStateToNewBlock(LineState &State) { + // Reset the indent of an ObjC block to that of its owner statement if the + // statement has only one embedded block. + if (Style.ObjCBlockResetsIndent && + State.NextToken->is(TT_ObjCBlockLBrace) && + (State.Stack.size() < 2 || + !State.Stack[State.Stack.size() - 2].HasMultipleNestedBlocks)) { + State.Stack.back().Indent = State.Stack.front().Indent; + State.Stack.back().NestedBlockIndent = + State.Stack.front().NestedBlockIndent; + } unsigned NestedBlockIndent = State.Stack.back().NestedBlockIndent; // ObjC block sometimes follow special indentation rules. unsigned NewIndent = Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -322,6 +322,7 @@ IO.mapOptional("MaxEmptyLinesToKeep", Style.MaxEmptyLinesToKeep); IO.mapOptional("NamespaceIndentation", Style.NamespaceIndentation); IO.mapOptional("ObjCBlockIndentWidth", Style.ObjCBlockIndentWidth); + IO.mapOptional("ObjCBlockResetsIndent", Style.ObjCBlockResetsIndent); IO.mapOptional("ObjCSpaceAfterProperty", Style.ObjCSpaceAfterProperty); IO.mapOptional("ObjCSpaceBeforeProtocolList", Style.ObjCSpaceBeforeProtocolList); Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -11263,6 +11263,128 @@ FourIndent); } +TEST_F(FormatTest, FormatsBlocksWithIndentResetting) { + FormatStyle ShortBlocks = getLLVMStyle(); + ShortBlocks.AllowShortBlocksOnASingleLine = true; + ShortBlocks.ObjCBlockResetsIndent = true; + + verifyFormat("int (^Block)(int, int);", ShortBlocks); + verifyFormat("int (^Block1)(int, int) = ^(int i, int j)", ShortBlocks); + verifyFormat("void (^block)(int) = ^(id test) { int i; };", ShortBlocks); + verifyFormat("void (^block)(int) = ^(int test) { int i; };", ShortBlocks); + verifyFormat("void (^block)(int) = ^id(int test) { int i; };", ShortBlocks); + verifyFormat("void (^block)(int) = ^int(int test) { int i; };", ShortBlocks); + + verifyFormat("foo(^{ bar(); });", ShortBlocks); + verifyFormat("foo(a, ^{ bar(); });", ShortBlocks); + verifyFormat("{ void (^block)(Object *x); }", ShortBlocks); + + verifyFormat("[operation setCompletionBlock:^{\n" + " [self onOperationDone];\n" + "}];"); + verifyFormat("int i = {[operation setCompletionBlock:^{\n" + " [self onOperationDone];\n" + "}]};"); + + verifyFormat("[operation setCompletionBlock:^(int *i) {\n" + " f();\n" + "}];"); + + verifyFormat("int a = [operation block:^int(int *i) {\n" + " return 1;\n" + "}];"); + + FormatStyle ResetsIndent = getLLVMStyle(); + ResetsIndent.ObjCBlockResetsIndent = true; + verifyFormat("[myObject doSomethingWith:arg1\n" + " aaa:^int(int *a) {\n" + " return 1;\n" + "}\n" + " bbb:f(a * bbbbbbbb)];", + ResetsIndent); + + verifyFormat("[operation setCompletionBlock:^{\n" + " [self.delegate newDataAvailable];\n" + "}];", + ResetsIndent); + verifyFormat("dispatch_async(_fileIOQueue, ^{\n" + " NSString *path = [self sessionFilePath];\n" + " if (path) {\n" + " // ...\n" + " }\n" + "});", + ResetsIndent); + verifyFormat("[[SessionService sharedService]\n" + " loadWindowWithCompletionBlock:^(SessionWindow *window) {\n" + " if (window) {\n" + " [self windowDidLoad:window];\n" + " } else {\n" + " [self errorLoadingWindow];\n" + " }\n" + "}];", + ResetsIndent); + verifyFormat("void (^largeBlock)(void) = ^{\n" + " // ...\n" + "};\n", + ResetsIndent); + + FormatStyle ResetsIndent60 = getLLVMStyleWithColumns(60); + ResetsIndent60.ObjCBlockResetsIndent = true; + verifyFormat("[[SessionService sharedService]\n" + " loadWindowWithCompletionBlock: //\n" + " ^(SessionWindow *window) {\n" + " if (window) {\n" + " [self windowDidLoad:window];\n" + " } else {\n" + " [self errorLoadingWindow];\n" + " }\n" + "}];", + ResetsIndent60); + + verifyFormat("[myObject doSomethingWith:arg1\n" + " firstBlock:^(Foo *a) {\n" + " // ...\n" + " int i;\n" + " }\n" + " secondBlock:^(Bar *b) {\n" + " // ...\n" + " int i;\n" + " }\n" + " thirdBlock:^Foo(Bar *b) {\n" + " // ...\n" + " int i;\n" + " }];", + ResetsIndent); + verifyFormat("[myObject doSomethingWith:arg1\n" + " firstBlock:-1\n" + " secondBlock:^(Bar *b) {\n" + " // ...\n" + " int i;\n" + "}];", + ResetsIndent); + + verifyFormat("f(^{\n" + " @autoreleasepool {\n" + " if (a) {\n" + " g();\n" + " }\n" + " }\n" + "});", + ResetsIndent); + verifyFormat("Block b = ^int *(A *a, B *b) {}", ResetsIndent); + verifyFormat("BOOL (^aaa)(void) = ^BOOL {\n" + "};", + ResetsIndent); + + FormatStyle FourIndent = getLLVMStyle(); + FourIndent.ObjCBlockIndentWidth = 4; + FourIndent.ObjCBlockResetsIndent = true; + verifyFormat("[operation setCompletionBlock:^{\n" + " [self onOperationDone];\n" + "}];", + FourIndent); +} + TEST_F(FormatTest, FormatsBlocksWithZeroColumnWidth) { FormatStyle ZeroColumn = getLLVMStyle(); ZeroColumn.ColumnLimit = 0;