r209720 - clang-format: Format array and dict literals similar to blocks.
Daniel Jasper
djasper at google.com
Wed May 28 02:11:54 PDT 2014
Author: djasper
Date: Wed May 28 04:11:53 2014
New Revision: 209720
URL: http://llvm.org/viewvc/llvm-project?rev=209720&view=rev
Log:
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
}];
Modified:
cfe/trunk/lib/Format/ContinuationIndenter.cpp
cfe/trunk/unittests/Format/FormatTest.cpp
Modified: cfe/trunk/lib/Format/ContinuationIndenter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/ContinuationIndenter.cpp?rev=209720&r1=209719&r2=209720&view=diff
==============================================================================
--- cfe/trunk/lib/Format/ContinuationIndenter.cpp (original)
+++ cfe/trunk/lib/Format/ContinuationIndenter.cpp Wed May 28 04:11:53 2014
@@ -736,25 +736,58 @@ void ContinuationIndenter::moveStatePast
}
}
+// 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::moveStatePast
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::moveStatePast
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::moveStatePast
}
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 +
Modified: cfe/trunk/unittests/Format/FormatTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Format/FormatTest.cpp?rev=209720&r1=209719&r2=209720&view=diff
==============================================================================
--- cfe/trunk/unittests/Format/FormatTest.cpp (original)
+++ cfe/trunk/unittests/Format/FormatTest.cpp Wed May 28 04:11:53 2014
@@ -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, FormatsWithWebKitStyl
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"
More information about the cfe-commits
mailing list