Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -222,6 +222,9 @@ /// on a single line. ShortFunctionStyle AllowShortFunctionsOnASingleLine; + /// \brief Compress the selector parts when more than two inline blocks are specified + bool ObjCXcodeBlockFormat; + /// \brief Add a space after \c @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 @@ -416,6 +416,7 @@ } else if (NextNonComment->is(TT_SelectorName)) { if (!State.Stack.back().ObjCSelectorNameFound) { if (NextNonComment->LongestObjCSelectorName == 0) { + // This catches keys in Literal NSDictionaries State.Stack.back().AlignColons = false; } else { State.Stack.back().ColonPos = @@ -853,7 +854,7 @@ Tok && Tok != Current.MatchingParen; Tok = Tok->Next) { if (Tok->MustBreakBefore || (Tok->CanBreakBefore && Tok->NewlinesBefore > 0)) { - BreakBeforeParameter = true; + BreakBeforeParameter = Style.ObjCXcodeBlockFormat == false; break; } } Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -218,6 +218,7 @@ Style.KeepEmptyLinesAtTheStartOfBlocks); IO.mapOptional("NamespaceIndentation", Style.NamespaceIndentation); IO.mapOptional("ObjCBlockIndentWidth", Style.ObjCBlockIndentWidth); + IO.mapOptional("ObjCXcodeBlockFormat", Style.ObjCXcodeBlockFormat); IO.mapOptional("ObjCSpaceAfterProperty", Style.ObjCSpaceAfterProperty); IO.mapOptional("ObjCSpaceBeforeProtocolList", Style.ObjCSpaceBeforeProtocolList); @@ -363,6 +364,7 @@ LLVMStyle.KeepEmptyLinesAtTheStartOfBlocks = true; LLVMStyle.NamespaceIndentation = FormatStyle::NI_None; LLVMStyle.ObjCBlockIndentWidth = 2; + LLVMStyle.ObjCXcodeBlockFormat = false; LLVMStyle.ObjCSpaceAfterProperty = false; LLVMStyle.ObjCSpaceBeforeProtocolList = true; LLVMStyle.PointerAlignment = FormatStyle::PAS_Right; Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -290,7 +290,8 @@ if (Contexts.back().FirstObjCSelectorName) { Contexts.back().FirstObjCSelectorName->LongestObjCSelectorName = Contexts.back().LongestObjCSelectorName; - if (Left->BlockParameterCount > 1) + // Compress blocks if there are multiple block arguments + if (Left->BlockParameterCount > 1 && Style.ObjCXcodeBlockFormat == false) Contexts.back().FirstObjCSelectorName->LongestObjCSelectorName = 0; } next(); Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -9799,6 +9799,86 @@ FourIndent); } +TEST_F(FormatTest, FormatsXcodeStyleBlocks) { + FormatStyle AppleStyle = getLLVMStyle(); + AppleStyle.ColumnLimit = 0; + AppleStyle.IndentWidth = 4; + AppleStyle.ObjCBlockIndentWidth = 4; + AppleStyle.ObjCXcodeBlockFormat = true; + verifyFormat("[operation setCompletionBlock:^(BOOL complete) {\n" + " [self onOperationDone];\n" + "}];", + AppleStyle); + + verifyFormat("[myObject doSomethingWith:arg1 // Comment to force colon alignment\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" + " }];", + AppleStyle); + verifyFormat("[myObject doSomethingWith:arg1 firstBlock:^(Foo *a) {\n" + " // ...\n" + " int i;\n" + "} secondBlock:^(Bar *b) {\n" + " // ...\n" + " int i;\n" + "} thirdBlock:^Foo(Bar *b) {\n" + " // ...\n" + " int i;\n" + "}];", + AppleStyle); + + verifyFormat("[[SessionService sharedService]" + " loadWindowWithCompletionBlock:^(SessionWindow *window) {\n" + " if (window) {\n" + " [self windowDidLoad:window];\n" + " } else {\n" + " [self errorLoadingWindow];\n" + " }\n" + "}];", + AppleStyle); + + verifyFormat("[UIView animateWithDuration:0 animations:^{ // This does not change behavior\n" + " view.center = CGPointZero;\n" + "} completion:^(BOOL finished) {\n" + " NSLog(complete);\n" + "}];", + AppleStyle); + verifyFormat("[UIView animateWithDuration:0 animations:^{\n" + " view.center = CGPointZero;\n" + "} completion:^(BOOL finished) {\n" + " NSLog(complete);\n" + "}];", + AppleStyle); + verifyFormat("[UIView animateWithDuration:0 // This will force colon alignment\n" + " animations:^{\n" + " view.center = CGPointZero;\n" + " }\n" + " completion:^(BOOL finished) {\n" + " NSLog(complete);\n" + " }];", + AppleStyle); + + verifyFormat("- (void)aMethod {\n" + " [self.magicalRecordStack saveWithBlock:^(NSManagedObjectContext *localContext) {\n" + " NSLog(format, localContext);\n" + " } completion:^(BOOL success, NSError *error) {\n" + " NSLog(format, localContext);\n" + " }];\n" + "}", + AppleStyle); + +} + + TEST_F(FormatTest, FormatsBlocksWithZeroColumnWidth) { FormatStyle ZeroColumn = getLLVMStyle(); ZeroColumn.ColumnLimit = 0;