From: Daniel Jasper Date: Wed, 28 May 2014 09:11:53 +0000 (+0000) Subject: clang-format: Format array and dict literals similar to blocks. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d519dc975cf544718d9a46a63ec57cc7727cada2;p=clang clang-format: Format array and dict literals similar to blocks. Especially, reduce the amount of indentation if it doesn't increase readability. Before: NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithDictionary:@{ aaaaaaaaaaaaaaaaaaaaa : aaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbb : bbbbb, cccccccccccccccc : ccccccccccccccc }]; After: NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithDictionary:@{ aaaaaaaaaaaaaaaaaaaaa : aaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbb : bbbbb, cccccccccccccccc : ccccccccccccccc }]; git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@209720 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Format/ContinuationIndenter.cpp b/lib/Format/ContinuationIndenter.cpp index 7485e0d40a..01467f995d 100644 --- a/lib/Format/ContinuationIndenter.cpp +++ b/lib/Format/ContinuationIndenter.cpp @@ -736,25 +736,58 @@ void ContinuationIndenter::moveStatePastFakeLParens(LineState &State, } } +// Remove the fake r_parens after 'Tok'. +static void consumeRParens(LineState& State, const FormatToken &Tok) { + for (unsigned i = 0, e = Tok.FakeRParens; i != e; ++i) { + unsigned VariablePos = State.Stack.back().VariablePos; + assert(State.Stack.size() > 1); + if (State.Stack.size() == 1) { + // Do not pop the last element. + break; + } + State.Stack.pop_back(); + State.Stack.back().VariablePos = VariablePos; + } +} + +// Returns whether 'Tok' opens or closes a scope requiring special handling +// of the subsequent fake r_parens. +// +// For example, if this is an l_brace starting a nested block, we pretend (wrt. +// to indentation) that we already consumed the corresponding r_brace. Thus, we +// remove all ParenStates caused by fake parentheses that end at the r_brace. +// The net effect of this is that we don't indent relative to the l_brace, if +// the nested block is the last parameter of a function. This formats: +// +// SomeFunction(a, [] { +// f(); // break +// }); +// +// instead of: +// SomeFunction(a, [] { +// f(); // break +// }); +static bool fakeRParenSpecialCase(const FormatToken& Tok) { + if (!Tok.MatchingParen) + return false; + const FormatToken *Left = &Tok; + if (Tok.isOneOf(tok::r_brace, tok::r_square)) + Left = Tok.MatchingParen; + return Left->isOneOf(tok::l_brace, tok::l_square) && + (Left->BlockKind == BK_Block || + Left->Type == TT_ArrayInitializerLSquare || + Left->Type == TT_DictLiteral); +} + void ContinuationIndenter::moveStatePastFakeRParens(LineState &State) { const FormatToken &Current = *State.NextToken; - // Remove scopes created by fake parenthesis. - if (Current.isNot(tok::r_brace) || - (Current.MatchingParen && Current.MatchingParen->BlockKind != BK_Block)) { - // Don't remove FakeRParens attached to r_braces that surround nested blocks - // as they will have been removed early (see above). - for (unsigned i = 0, e = Current.FakeRParens; i != e; ++i) { - unsigned VariablePos = State.Stack.back().VariablePos; - assert(State.Stack.size() > 1); - if (State.Stack.size() == 1) { - // Do not pop the last element. - break; - } - State.Stack.pop_back(); - State.Stack.back().VariablePos = VariablePos; - } - } + // Don't remove FakeRParens attached to r_braces that surround nested blocks + // as they will have been removed early (see above). + if (fakeRParenSpecialCase(Current)) + return; + + consumeRParens(State, *State.NextToken); } void ContinuationIndenter::moveStatePastScopeOpener(LineState &State, @@ -773,6 +806,9 @@ void ContinuationIndenter::moveStatePastScopeOpener(LineState &State, bool AvoidBinPacking; bool BreakBeforeParameter = false; if (Current.is(tok::l_brace) || Current.Type == TT_ArrayInitializerLSquare) { + if (fakeRParenSpecialCase(Current)) + consumeRParens(State, *Current.MatchingParen); + NewIndent = State.Stack.back().LastSpace; if (Current.opensBlockTypeList(Style)) { NewIndent += Style.IndentWidth; @@ -824,9 +860,9 @@ void ContinuationIndenter::moveStatePastScopeCloser(LineState &State) { if (State.Stack.size() > 1 && (Current.isOneOf(tok::r_paren, tok::r_square) || (Current.is(tok::r_brace) && State.NextToken != State.Line->First) || - State.NextToken->Type == TT_TemplateCloser)) { + State.NextToken->Type == TT_TemplateCloser)) State.Stack.pop_back(); - } + if (Current.is(tok::r_square)) { // If this ends the array subscript expr, reset the corresponding value. const FormatToken *NextNonComment = Current.getNextNonComment(); @@ -836,35 +872,13 @@ void ContinuationIndenter::moveStatePastScopeCloser(LineState &State) { } void ContinuationIndenter::moveStateToNewBlock(LineState &State) { - // If this is an l_brace starting a nested block, we pretend (wrt. to - // indentation) that we already consumed the corresponding r_brace. Thus, we - // remove all ParenStates caused by fake parentheses that end at the r_brace. - // The net effect of this is that we don't indent relative to the l_brace, if - // the nested block is the last parameter of a function. For example, this - // formats: - // - // SomeFunction(a, [] { - // f(); // break - // }); - // - // instead of: - // SomeFunction(a, [] { - // f(); // break - // }); - // // If we have already found more than one lambda introducers on this level, we // opt out of this because similarity between the lambdas is more important. - if (State.Stack.back().LambdasFound <= 1) { - for (unsigned i = 0; i != State.NextToken->MatchingParen->FakeRParens; - ++i) { - assert(State.Stack.size() > 1); - if (State.Stack.size() == 1) { - // Do not pop the last element. - break; - } - State.Stack.pop_back(); - } - } + // FIXME: This should use fakeRParenSpecialCase() and fakeRParenSpecialCase() + // Needs to include the LambdasFound check. Otherwise the corresponding + // fake r_parens will never be consumed. + if (State.Stack.back().LambdasFound <= 1) + consumeRParens(State, *State.NextToken->MatchingParen); // For some reason, ObjC blocks are indented like continuations. unsigned NewIndent = State.Stack.back().LastSpace + diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 94d4bbb00e..8ad59cd93e 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -6491,6 +6491,12 @@ TEST_F(FormatTest, ObjCDictLiterals) { " @\"dte\" : [NSDate date],\n" " @\"processInfo\" : [NSProcessInfo processInfo]\n" "};"); + verifyFormat("NSMutableDictionary *dictionary =\n" + " [NSMutableDictionary dictionaryWithDictionary:@{\n" + " aaaaaaaaaaaaaaaaaaaaa : aaaaaaaaaaaaa,\n" + " bbbbbbbbbbbbbbbbbb : bbbbb,\n" + " cccccccccccccccc : ccccccccccccccc\n" + " }];"); } TEST_F(FormatTest, ObjCArrayLiterals) { @@ -8590,10 +8596,12 @@ TEST_F(FormatTest, FormatsWithWebKitStyle) { format("NSArray*a=[[NSArray alloc]initWithArray:@[ @\"a\" ]\n" " copyItems:YES];", Style)); + // FIXME: This does not seem right, there should be more indentation before + // the array literal's entries. Nested blocks have the same problem. EXPECT_EQ("NSArray* a = [[NSArray alloc] initWithArray:@[\n" - " @\"a\",\n" - " @\"a\"\n" - " ]\n" + " @\"a\",\n" + " @\"a\"\n" + "]\n" " copyItems:YES];", format("NSArray* a = [[NSArray alloc] initWithArray:@[\n" " @\"a\",\n"