From: Jacek Olesiak Date: Mon, 9 Jul 2018 07:08:45 +0000 (+0000) Subject: [clang-format/ObjC] Put ObjC method arguments into one line when they fit X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=15e70438359dad2206c30f87afc1c4f6cb063b5e;p=clang [clang-format/ObjC] Put ObjC method arguments into one line when they fit Reapply D47195: Currently BreakBeforeParameter is set to true everytime message receiver spans multiple lines, e.g.: ``` [[object block:^{ return 42; }] aa:42 bb:42]; ``` will be formatted: ``` [[object block:^{ return 42; }] aa:42 bb:42]; ``` even though arguments could fit into one line. This change fixes this behavior. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@336521 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Format/ContinuationIndenter.cpp b/lib/Format/ContinuationIndenter.cpp index 0862a4e0f9..90d2a99971 100644 --- a/lib/Format/ContinuationIndenter.cpp +++ b/lib/Format/ContinuationIndenter.cpp @@ -1398,6 +1398,30 @@ void ContinuationIndenter::moveStatePastScopeCloser(LineState &State) { (Current.is(tok::greater) && Current.is(TT_DictLiteral)))) State.Stack.pop_back(); + // Reevaluate whether ObjC message arguments fit into one line. + // If a receiver spans multiple lines, e.g.: + // [[object block:^{ + // return 42; + // }] a:42 b:42]; + // BreakBeforeParameter is calculated based on an incorrect assumption + // (it is checked whether the whole expression fits into one line without + // considering a line break inside a message receiver). + // We check whether arguements fit after receiver scope closer (into the same + // line). + if (State.Stack.back().BreakBeforeParameter && Current.MatchingParen && + Current.MatchingParen->Previous) { + const FormatToken &CurrentScopeOpener = *Current.MatchingParen->Previous; + if (CurrentScopeOpener.is(TT_ObjCMethodExpr) && + CurrentScopeOpener.MatchingParen) { + int NecessarySpaceInLine = + getLengthToMatchingParen(CurrentScopeOpener, State.Stack) + + CurrentScopeOpener.TotalLength - Current.TotalLength - 1; + if (State.Column + Current.ColumnWidth + NecessarySpaceInLine <= + Style.ColumnLimit) + State.Stack.back().BreakBeforeParameter = false; + } + } + if (Current.is(tok::r_square)) { // If this ends the array subscript expr, reset the corresponding value. const FormatToken *NextNonComment = Current.getNextNonComment(); diff --git a/unittests/Format/FormatTestObjC.cpp b/unittests/Format/FormatTestObjC.cpp index 58e75363a5..e56df6d130 100644 --- a/unittests/Format/FormatTestObjC.cpp +++ b/unittests/Format/FormatTestObjC.cpp @@ -820,6 +820,48 @@ TEST_F(FormatTestObjC, FormatObjCMethodExpr) { verifyFormat("aaaaaa = [aa aa:aa\n" " aa:aa];"); + // Message receiver taking multiple lines. + // Non-corner case. + verifyFormat("[[object block:^{\n" + " return 42;\n" + "}] a:42 b:42];"); + // Arguments just fit into one line. + verifyFormat("[[object block:^{\n" + " return 42;\n" + "}] aaaaaaa:42 b:42];"); + // Arguments just over a column limit. + verifyFormat("[[object block:^{\n" + " return 42;\n" + "}] aaaaaaa:42\n" + " bb:42];"); + // Arguments just fit into one line. + Style.ColumnLimit = 23; + verifyFormat("[[obj a:42\n" + " b:42\n" + " c:42\n" + " d:42] e:42 f:42];"); + + // Arguments do not fit into one line with a receiver. + Style.ColumnLimit = 20; + verifyFormat("[[obj a:42] a:42\n" + " b:42];"); + verifyFormat("[[obj a:42] a:42\n" + " b:42\n" + " c:42];"); + verifyFormat("[[obj aaaaaa:42\n" + " b:42]\n" + " cc:42\n" + " d:42];"); + + // Avoid breaking receiver expression. + Style.ColumnLimit = 30; + verifyFormat("fooooooo =\n" + " [[obj fooo] aaa:42\n" + " aaa:42];"); + verifyFormat("[[[obj foo] bar] aa:42\n" + " bb:42\n" + " cc:42];"); + Style.ColumnLimit = 70; verifyFormat( "void f() {\n"