Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -222,6 +222,14 @@ /// on a single line. ShortFunctionStyle AllowShortFunctionsOnASingleLine; + /// \brief Compress the selector parts to align left when more than two inline + /// blocks are specified. Default is true + bool ObjCLeftAlignMultipleBlocks; + + /// \brief Avoid inserting line breaks when calculating the length of lines + /// with inline Objective-C blocks. + bool ObjCAvoidLineBreaksForInlineBlocks; + /// \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 @@ -38,6 +38,22 @@ return End->TotalLength - Tok.TotalLength + 1; } +// Returns the length of everything up to the first possible line break after +// the ), ], } or > matching \c Tok, or the first token with children. +static unsigned getLengthToLineTerminator(const FormatToken &Tok) { + if (!Tok.MatchingParen) + return 0; + FormatToken *End = Tok.MatchingParen; + FormatToken *Cursor = Tok.Next; + while (Cursor->Children.size() == 0 && Cursor != End) { + Cursor = Cursor->Next; + } + while (Cursor->Next && !Cursor->Next->CanBreakBefore) { + Cursor = Cursor->Next; + } + return Cursor->TotalLength - Tok.TotalLength + 1; +} + // Returns \c true if \c Tok is the "." or "->" of a call and starts the next // segment of a builder type call. static bool startsSegmentOfBuilderTypeCall(const FormatToken &Tok) { @@ -843,8 +859,10 @@ if (Style.ColumnLimit) { // If this '[' opens an ObjC call, determine whether all parameters fit // into one line and put one per line if they don't. - if (getLengthToMatchingParen(Current) + State.Column > - getColumnLimit(State)) + unsigned length = (Style.ObjCAvoidLineBreaksForInlineBlocks ? + getLengthToLineTerminator(Current) : + getLengthToMatchingParen(Current)); + if (length + State.Column > getColumnLimit(State)) BreakBeforeParameter = true; } else { // For ColumnLimit = 0, we have to figure out whether there is or has to @@ -853,7 +871,7 @@ Tok && Tok != Current.MatchingParen; Tok = Tok->Next) { if (Tok->MustBreakBefore || (Tok->CanBreakBefore && Tok->NewlinesBefore > 0)) { - BreakBeforeParameter = true; + BreakBeforeParameter = !Style.ObjCAvoidLineBreaksForInlineBlocks; break; } } Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -218,6 +218,8 @@ Style.KeepEmptyLinesAtTheStartOfBlocks); IO.mapOptional("NamespaceIndentation", Style.NamespaceIndentation); IO.mapOptional("ObjCBlockIndentWidth", Style.ObjCBlockIndentWidth); + IO.mapOptional("ObjCAvoidLineBreaksForInlineBlocks", Style.ObjCAvoidLineBreaksForInlineBlocks); + IO.mapOptional("ObjCLeftAlignMultipleBlocks", Style.ObjCLeftAlignMultipleBlocks); IO.mapOptional("ObjCSpaceAfterProperty", Style.ObjCSpaceAfterProperty); IO.mapOptional("ObjCSpaceBeforeProtocolList", Style.ObjCSpaceBeforeProtocolList); @@ -363,6 +365,8 @@ LLVMStyle.KeepEmptyLinesAtTheStartOfBlocks = true; LLVMStyle.NamespaceIndentation = FormatStyle::NI_None; LLVMStyle.ObjCBlockIndentWidth = 2; + LLVMStyle.ObjCLeftAlignMultipleBlocks = true; + LLVMStyle.ObjCAvoidLineBreaksForInlineBlocks = 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,9 @@ 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.ObjCLeftAlignMultipleBlocks) Contexts.back().FirstObjCSelectorName->LongestObjCSelectorName = 0; } next(); Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -9797,6 +9797,61 @@ " [self onOperationDone];\n" "}];", FourIndent); + + FormatStyle AvoidBreaks = getLLVMStyle(); + AvoidBreaks.ObjCAvoidLineBreaksForInlineBlocks = true; + verifyFormat("[UIView animateWithDuration:0 animations:^{ // This does not change behavior\n" + " view.center = CGPointZero;\n" + "} completion:^(BOOL finished) {\n" + " NSLog(complete);\n" + "}];", + AvoidBreaks); + verifyFormat("[UIView animateWithDuration:0 animations:^{\n" + " view.center = CGPointZero;\n" + "} completion:^(BOOL finished) {\n" + " NSLog(complete);\n" + "}];", + AvoidBreaks); + verifyFormat("[UIView animateWithDuration:0 // Break\n" + " animations:^{\n" + " view.center = CGPointZero;\n" + " }\n" + " completion:^(BOOL finished) {\n" + " NSLog(complete);\n" + " }];", + AvoidBreaks); +} + +TEST_F(FormatTest, FormatsBlocksLeftAligned) { + FormatStyle DisableLeftAlignedStyle = getLLVMStyle(); + DisableLeftAlignedStyle.ColumnLimit = 0; + DisableLeftAlignedStyle.IndentWidth = 4; + DisableLeftAlignedStyle.ObjCBlockIndentWidth = 4; + DisableLeftAlignedStyle.ObjCLeftAlignMultipleBlocks = false; + + verifyFormat("[myObject doSomethingWith:arg1 // Comment\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" + " }];", + DisableLeftAlignedStyle); + verifyFormat("[UIView animateWithDuration:0\n" + " animations:^{\n" + " view.center = CGPointZero;\n" + " }\n" + " completion:^(BOOL finished) {\n" + " NSLog(complete);\n" + " }];", + DisableLeftAlignedStyle); + } TEST_F(FormatTest, FormatsBlocksWithZeroColumnWidth) { @@ -9865,6 +9920,29 @@ " int i;\n" "};", format("void (^largeBlock)(void) = ^{ int i; };", ZeroColumn)); + + ZeroColumn.ObjCAvoidLineBreaksForInlineBlocks = true; + verifyFormat("[UIView animateWithDuration:0 animations:^{ // This does not change behavior\n" + " view.center = CGPointZero;\n" + "} completion:^(BOOL finished) {\n" + " NSLog(complete);\n" + "}];", + ZeroColumn); + verifyFormat("[UIView animateWithDuration:0 animations:^{\n" + " view.center = CGPointZero;\n" + "} completion:^(BOOL finished) {\n" + " NSLog(complete);\n" + "}];", + ZeroColumn); + + verifyFormat("[UIView animateWithDuration:0 // Break\n" + " animations:^{\n" + " view.center = CGPointZero;\n" + " }\n" + " completion:^(BOOL finished) {\n" + " NSLog(complete);\n" + " }];", + ZeroColumn); } TEST_F(FormatTest, SupportsCRLF) {