From: Daniel Jasper Date: Thu, 23 Apr 2015 09:23:17 +0000 (+0000) Subject: clang-format: Support nested block formatting with ColumnLimit=0. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=eeee6f3fabff8a274e4f889ab7eeb610dd6021cc;p=clang clang-format: Support nested block formatting with ColumnLimit=0. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@235580 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Format/ContinuationIndenter.cpp b/lib/Format/ContinuationIndenter.cpp index a2a68bd6d4..b03a99a248 100644 --- a/lib/Format/ContinuationIndenter.cpp +++ b/lib/Format/ContinuationIndenter.cpp @@ -839,12 +839,26 @@ void ContinuationIndenter::moveStatePastScopeOpener(LineState &State, (Current.PackingKind == PPK_OnePerLine || (!BinPackInconclusiveFunctions && Current.PackingKind == PPK_Inconclusive))); - // If this '[' opens an ObjC call, determine whether all parameters fit - // into one line and put one per line if they don't. - if (Current.is(TT_ObjCMethodExpr) && Style.ColumnLimit != 0 && - getLengthToMatchingParen(Current) + State.Column > + if (Current.is(TT_ObjCMethodExpr) && Current.MatchingParen) { + 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)) - BreakBeforeParameter = true; + BreakBeforeParameter = true; + } else { + // For ColumnLimit = 0, we have to figure out whether there is or has to + // be a line break within this call. + for (const FormatToken *Tok = &Current; + Tok && Tok != Current.MatchingParen; Tok = Tok->Next) { + if (Tok->MustBreakBefore || + (Tok->CanBreakBefore && Tok->NewlinesBefore > 0)) { + BreakBeforeParameter = true; + break; + } + } + } + } } bool NoLineBreak = State.Stack.back().NoLineBreak || (Current.is(TT_TemplateOpener) && diff --git a/lib/Format/UnwrappedLineFormatter.cpp b/lib/Format/UnwrappedLineFormatter.cpp index 6c92d79d13..f5120b9f2f 100644 --- a/lib/Format/UnwrappedLineFormatter.cpp +++ b/lib/Format/UnwrappedLineFormatter.cpp @@ -303,7 +303,9 @@ private: class NoColumnLimitFormatter { public: - NoColumnLimitFormatter(ContinuationIndenter *Indenter) : Indenter(Indenter) {} + NoColumnLimitFormatter(ContinuationIndenter *Indenter, + UnwrappedLineFormatter *Formatter) + : Indenter(Indenter), Formatter(Formatter) {} /// \brief Formats the line starting at \p State, simply keeping all of the /// input's line breaking decisions. @@ -314,12 +316,15 @@ public: bool Newline = Indenter->mustBreak(State) || (Indenter->canBreak(State) && State.NextToken->NewlinesBefore > 0); + unsigned Penalty = 0; + Formatter->formatChildren(State, Newline, /*DryRun=*/false, Penalty); Indenter->addTokenToState(State, Newline, /*DryRun=*/false); } } private: ContinuationIndenter *Indenter; + UnwrappedLineFormatter *Formatter; }; @@ -426,8 +431,7 @@ UnwrappedLineFormatter::format(const SmallVectorImpl &Lines, Indenter->addTokenToState(State, /*Newline=*/false, DryRun); } } else if (Style.ColumnLimit == 0) { - // FIXME: Implement nested blocks for ColumnLimit = 0. - NoColumnLimitFormatter Formatter(Indenter); + NoColumnLimitFormatter Formatter(Indenter, this); if (!DryRun) Formatter.format(Indent, &TheLine); } else { diff --git a/lib/Format/UnwrappedLineFormatter.h b/lib/Format/UnwrappedLineFormatter.h index 7d5b01148b..35626338e3 100644 --- a/lib/Format/UnwrappedLineFormatter.h +++ b/lib/Format/UnwrappedLineFormatter.h @@ -40,6 +40,30 @@ public: unsigned format(const SmallVectorImpl &Lines, bool DryRun, int AdditionalIndent = 0, bool FixBadIndentation = false); + + /// \brief If the \p State's next token is an r_brace closing a nested block, + /// format the nested block before it. + /// + /// Returns \c true if all children could be placed successfully and adapts + /// \p Penalty as well as \p State. If \p DryRun is false, also directly + /// creates changes using \c Whitespaces. + /// + /// The crucial idea here is that children always get formatted upon + /// encountering the closing brace right after the nested block. Now, if we + /// are currently trying to keep the "}" on the same line (i.e. \p NewLine is + /// \c false), the entire block has to be kept on the same line (which is only + /// possible if it fits on the line, only contains a single statement, etc. + /// + /// If \p NewLine is true, we format the nested block on separate lines, i.e. + /// break after the "{", format all lines with correct indentation and the put + /// the closing "}" on yet another new line. + /// + /// This enables us to keep the simple structure of the + /// \c UnwrappedLineFormatter, where we only have two options for each token: + /// break or don't break. + bool formatChildren(LineState &State, bool NewLine, bool DryRun, + unsigned &Penalty); + private: /// \brief Formats an \c AnnotatedLine and returns the penalty. /// @@ -131,29 +155,6 @@ private: void addNextStateToQueue(unsigned Penalty, StateNode *PreviousNode, bool NewLine, unsigned *Count, QueueType *Queue); - /// \brief If the \p State's next token is an r_brace closing a nested block, - /// format the nested block before it. - /// - /// Returns \c true if all children could be placed successfully and adapts - /// \p Penalty as well as \p State. If \p DryRun is false, also directly - /// creates changes using \c Whitespaces. - /// - /// The crucial idea here is that children always get formatted upon - /// encountering the closing brace right after the nested block. Now, if we - /// are currently trying to keep the "}" on the same line (i.e. \p NewLine is - /// \c false), the entire block has to be kept on the same line (which is only - /// possible if it fits on the line, only contains a single statement, etc. - /// - /// If \p NewLine is true, we format the nested block on separate lines, i.e. - /// break after the "{", format all lines with correct indentation and the put - /// the closing "}" on yet another new line. - /// - /// This enables us to keep the simple structure of the - /// \c UnwrappedLineFormatter, where we only have two options for each token: - /// break or don't break. - bool formatChildren(LineState &State, bool NewLine, bool DryRun, - unsigned &Penalty); - ContinuationIndenter *Indenter; WhitespaceManager *Whitespaces; FormatStyle Style; diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index ed31a097b0..197357c6c3 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -9789,6 +9789,74 @@ TEST_F(FormatTest, FormatsBlocks) { FourIndent); } +TEST_F(FormatTest, FormatsBlocksWithZeroColumnWidth) { + FormatStyle ZeroColumn = getLLVMStyle(); + ZeroColumn.ColumnLimit = 0; + + verifyFormat("[[SessionService sharedService] " + "loadWindowWithCompletionBlock:^(SessionWindow *window) {\n" + " if (window) {\n" + " [self windowDidLoad:window];\n" + " } else {\n" + " [self errorLoadingWindow];\n" + " }\n" + "}];", + ZeroColumn); + EXPECT_EQ("[[SessionService sharedService]\n" + " loadWindowWithCompletionBlock:^(SessionWindow *window) {\n" + " if (window) {\n" + " [self windowDidLoad:window];\n" + " } else {\n" + " [self errorLoadingWindow];\n" + " }\n" + " }];", + format("[[SessionService sharedService]\n" + "loadWindowWithCompletionBlock:^(SessionWindow *window) {\n" + " if (window) {\n" + " [self windowDidLoad:window];\n" + " } else {\n" + " [self errorLoadingWindow];\n" + " }\n" + "}];", + ZeroColumn)); + verifyFormat("[myObject doSomethingWith:arg1\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" + " }];", + ZeroColumn); + verifyFormat("f(^{\n" + " @autoreleasepool {\n" + " if (a) {\n" + " g();\n" + " }\n" + " }\n" + "});", + ZeroColumn); + verifyFormat("void (^largeBlock)(void) = ^{\n" + " // ...\n" + "};", + ZeroColumn); + + ZeroColumn.AllowShortBlocksOnASingleLine = true; + EXPECT_EQ("void (^largeBlock)(void) = ^{ int i; };", + format("void (^largeBlock)(void) = ^{ int i; };", + ZeroColumn)); + ZeroColumn.AllowShortBlocksOnASingleLine = false; + EXPECT_EQ("void (^largeBlock)(void) = ^{\n" + " int i;\n" + "};", + format("void (^largeBlock)(void) = ^{ int i; };", ZeroColumn)); +} + TEST_F(FormatTest, SupportsCRLF) { EXPECT_EQ("int a;\r\n" "int b;\r\n"