diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index 6494c3d3898df22d90371935f72727d57a34748e..08096866bf39779680c9a050538935f4cda2aec9 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -92,16 +92,21 @@ public: } void format() { + // Format first token and initialize indent. unsigned Indent = formatFirstToken(); + + // Initialize state dependent on indent. IndentState State; - State.Column = Indent + Line.Tokens[0].Tok.getLength(); + State.Column = Indent; State.CtorInitializerOnNewLine = false; State.InCtorInitializer = false; - State.ConsumedTokens = 1; - - //State.UsedIndent.push_back(Line.Level * 2); + State.ConsumedTokens = 0; State.Indent.push_back(Indent + 4); State.LastSpace.push_back(Indent); + State.FirstLessLess.push_back(0); + + // The first token has already been indented and thus consumed. + moveStateToNextToken(State); // Start iterating at 1 as we have correctly formatted of Token #0 above. for (unsigned i = 1, n = Line.Tokens.size(); i != n; ++i) { @@ -126,8 +131,19 @@ private: /// indented. std::vector<unsigned> Indent; + /// \brief The position of the last space on each level. + /// + /// Used e.g. to break like: + /// functionCall(Parameter, otherCall( + /// OtherParameter)); std::vector<unsigned> LastSpace; + /// \brief The position the first "<<" operator encountered on each level. + /// + /// Used to align "<<" operators. 0 if no such operator has been encountered + /// on a level. + std::vector<unsigned> FirstLessLess; + bool CtorInitializerOnNewLine; bool InCtorInitializer; @@ -149,6 +165,12 @@ private: if (Other.LastSpace[i] != LastSpace[i]) return Other.LastSpace[i] > LastSpace[i]; } + if (Other.FirstLessLess.size() != FirstLessLess.size()) + return Other.FirstLessLess.size() > FirstLessLess.size(); + for (int i = 0, e = FirstLessLess.size(); i != e; ++i) { + if (Other.FirstLessLess[i] != FirstLessLess[i]) + return Other.FirstLessLess[i] > FirstLessLess[i]; + } return false; } }; @@ -171,6 +193,9 @@ private: if (Current.Tok.is(tok::string_literal) && Previous.Tok.is(tok::string_literal)) State.Column = State.Column - Previous.Tok.getLength(); + else if (Current.Tok.is(tok::lessless) && + State.FirstLessLess[ParenLevel] != 0) + State.Column = State.FirstLessLess[ParenLevel]; else if (Previous.Tok.is(tok::equal) && ParenLevel != 0) // Indent and extra 4 spaces after '=' as it continues an expression. // Don't do that on the top level, as we already indent 4 there. @@ -181,7 +206,6 @@ private: if (!DryRun) replaceWhitespace(Current, 1, State.Column); - State.Column += Current.Tok.getLength(); State.LastSpace[ParenLevel] = State.Indent[ParenLevel]; if (Current.Tok.is(tok::colon) && Annotations[Index].Type != TokenAnnotation::TT_ConditionalExpr) { @@ -205,9 +229,9 @@ private: State.InCtorInitializer = true; } // Top-level spaces are exempt as that mostly leads to better results. + State.Column += Spaces; if (Spaces > 0 && ParenLevel != 0) - State.LastSpace[ParenLevel] = State.Column + Spaces; - State.Column += Current.Tok.getLength() + Spaces; + State.LastSpace[ParenLevel] = State.Column; } moveStateToNextToken(State); } @@ -217,6 +241,12 @@ private: void moveStateToNextToken(IndentState &State) { unsigned Index = State.ConsumedTokens; const FormatToken &Current = Line.Tokens[Index]; + unsigned ParenLevel = State.Indent.size() - 1; + + if (Current.Tok.is(tok::lessless) && State.FirstLessLess[ParenLevel] == 0) + State.FirstLessLess[ParenLevel] = State.Column; + + State.Column += Current.Tok.getLength(); // If we encounter an opening (, [ or <, we add a level to our stacks to // prepare for the following tokens. @@ -224,6 +254,7 @@ private: Annotations[Index].Type == TokenAnnotation::TT_TemplateOpener) { State.Indent.push_back(4 + State.LastSpace.back()); State.LastSpace.push_back(State.LastSpace.back()); + State.FirstLessLess.push_back(0); } // If we encounter a closing ), ] or >, we can remove a level from our @@ -232,6 +263,7 @@ private: Annotations[Index].Type == TokenAnnotation::TT_TemplateCloser) { State.Indent.pop_back(); State.LastSpace.pop_back(); + State.FirstLessLess.pop_back(); } ++State.ConsumedTokens; @@ -687,6 +719,8 @@ private: return false; if (isBinaryOperator(Left)) return true; + if (Right.Tok.is(tok::lessless)) + return true; return Right.Tok.is(tok::colon) || Left.Tok.is(tok::comma) || Left.Tok.is(tok::semi) || Left.Tok.is(tok::equal) || Left.Tok.is(tok::ampamp) || Left.Tok.is(tok::pipepipe) || diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 05a6d334f55d78cc546dcbcc9bb18cb7b8144e32..7628b34d4b9c01be98688a630dc7928277475169 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -386,6 +386,11 @@ TEST_F(FormatTest, UnderstandsUsesOfStar) { verifyFormat("int a = b * *c;"); } +TEST_F(FormatTest, LineStartsWithSpecialCharacter) { + verifyFormat("(a)->b();"); + verifyFormat("--a;"); +} + TEST_F(FormatTest, HandlesIncludeDirectives) { EXPECT_EQ("#include <string>\n", format("#include <string>\n")); EXPECT_EQ("#include \"a/b/string\"\n", format("#include \"a/b/string\"\n")); @@ -434,5 +439,26 @@ TEST_F(FormatTest, IncorrectCodeErrorDetection) { } +TEST_F(FormatTest, AlignsPipes) { + verifyFormat( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " << aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " << aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;"); + verifyFormat( + "aaaaaaaaaaaaaaaaaaaa << aaaaaaaaaaaaaaaaaaaa << aaaaaaaaaaaaaaaaaaaa\n" + " << aaaaaaaaaaaaaaaaaaaa;"); + verifyFormat( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa << aaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " << aaaaaaaaaaaaaaaaaaaaaaaaaaaa;"); + verifyFormat( + "llvm::outs() << \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n" + " \"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"\n" + " << \"ccccccccccccccccccccccccccccccccccccccccccccccccc\";"); + verifyFormat( + "aaaaaaaa << (aaaaaaaaaaaaaaaaaaa << aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " << aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)\n" + " << aaaaaaaaaaaaaaaaaaaaaaaaaaaaa;"); +} + } // end namespace tooling } // end namespace clang