Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -132,16 +132,44 @@ /// \brief Options for aligning backslashes in escaped newlines. EscapedNewlineAlignmentStyle AlignEscapedNewlines; + enum OperandAlignmentStyle { + /// Do not align operands of binary and ternary expressions. + /// The wrapped lines are indented ``ContinuationIndentWidth`` spaces from + /// the start of the line. + OAS_DontAlign, + + /// Horizontally align operands of binary and ternary expressions. + /// + /// Specifically, this aligns operands of a single expression that needs + /// to be split over multiple lines, e.g.: + /// \code + /// int aaa = bbbbbbbbbbbbbbb + + /// ccccccccccccccc; + /// \endcode + /// + /// When ``BreakBeforeBinaryOperators`` is set, the wrapped operator is + /// aligned with the operand on the first line. + /// \code + /// int aaa = bbbbbbbbbbbbbbb + /// + ccccccccccccccc; + /// \endcode + OAS_Align, + + /// Horizontally align operands of binary and ternary expressions. + /// + /// This is similar to ``AO_Align``, except when + /// ``BreakBeforeBinaryOperators`` is set, the wrapped operands are + /// aligned with the operand on the first line. + /// \code + /// int aaa = bbbbbbbbbbbbbbb + /// + ccccccccccccccc; + /// \endcode + OAS_StrictAlign, + }; + /// \brief If ``true``, horizontally align operands of binary and ternary /// expressions. - /// - /// Specifically, this aligns operands of a single expression that needs to be - /// split over multiple lines, e.g.: - /// \code - /// int aaa = bbbbbbbbbbbbbbb + - /// ccccccccccccccc; - /// \endcode - bool AlignOperands; + OperandAlignmentStyle AlignOperands; /// \brief If ``true``, aligns trailing comments. /// \code Index: lib/Format/ContinuationIndenter.h =================================================================== --- lib/Format/ContinuationIndenter.h +++ lib/Format/ContinuationIndenter.h @@ -154,7 +154,8 @@ NoLineBreakInOperand(false), LastOperatorWrapped(true), ContainsLineBreak(false), ContainsUnwrappedBuilder(false), AlignColons(true), ObjCSelectorNameFound(false), - HasMultipleNestedBlocks(false), NestedBlockInlined(false) {} + HasMultipleNestedBlocks(false), NestedBlockInlined(false), + UnindentOperator(false) {} /// \brief The position to which a specific parenthesis level needs to be /// indented. @@ -264,6 +265,10 @@ // "function" in JavaScript) is not wrapped to a new line. bool NestedBlockInlined : 1; + /// \brief Indicates the indent should be reduced by the length of the + /// operator. + bool UnindentOperator : 1; + bool operator<(const ParenState &Other) const { if (Indent != Other.Indent) return Indent < Other.Indent; @@ -301,6 +306,8 @@ return ContainsUnwrappedBuilder; if (NestedBlockInlined != Other.NestedBlockInlined) return NestedBlockInlined; + if (UnindentOperator != Other.UnindentOperator) + return UnindentOperator; return false; } }; Index: lib/Format/ContinuationIndenter.cpp =================================================================== --- lib/Format/ContinuationIndenter.cpp +++ lib/Format/ContinuationIndenter.cpp @@ -445,7 +445,9 @@ // does not help. bool HasTwoOperands = P->OperatorIndex == 0 && !P->NextOperator && !P->is(TT_ConditionalExpr); - if ((!BreakBeforeOperator && !(HasTwoOperands && Style.AlignOperands)) || + if ((!BreakBeforeOperator && + !(HasTwoOperands && + Style.AlignOperands != FormatStyle::OAS_DontAlign)) || (!State.Stack.back().LastOperatorWrapped && BreakBeforeOperator)) State.Stack.back().NoLineBreakInOperand = true; } @@ -784,6 +786,9 @@ if (Previous.is(tok::r_paren) && !Current.isBinaryOperator() && !Current.isOneOf(tok::colon, tok::comment)) return ContinuationIndent; + if (Current.isBinaryOperator() && State.Stack.back().UnindentOperator) + return State.Stack.back().Indent - Current.Tok.getLength() - + Current.SpacesRequiredBefore; if (State.Stack.back().Indent == State.FirstIndent && PreviousNonComment && PreviousNonComment->isNot(tok::r_brace)) // Ensure that we fall back to the continuation indent width instead of @@ -950,7 +955,7 @@ (Previous && (Previous->opensScope() || Previous->isOneOf(tok::semi, tok::kw_return) || (Previous->getPrecedence() == prec::Assignment && - Style.AlignOperands) || + Style.AlignOperands != FormatStyle::OAS_DontAlign) || Previous->is(TT_ObjCMethodExpr))); for (SmallVectorImpl::const_reverse_iterator I = Current.FakeLParens.rbegin(), @@ -959,6 +964,7 @@ ParenState NewParenState = State.Stack.back(); NewParenState.ContainsLineBreak = false; NewParenState.LastOperatorWrapped = true; + NewParenState.UnindentOperator = false; NewParenState.NoLineBreak = NewParenState.NoLineBreak || State.Stack.back().NoLineBreakInOperand; @@ -970,14 +976,24 @@ // a builder type call after 'return' or, if the alignment after opening // brackets is disabled. if (!Current.isTrailingComment() && - (Style.AlignOperands || *I < prec::Assignment) && + (Style.AlignOperands != FormatStyle::OAS_DontAlign || + *I < prec::Assignment) && (!Previous || Previous->isNot(tok::kw_return) || (Style.Language != FormatStyle::LK_Java && *I > 0)) && (Style.AlignAfterOpenBracket != FormatStyle::BAS_DontAlign || - *I != prec::Comma || Current.NestingLevel == 0)) + *I != prec::Comma || Current.NestingLevel == 0)) { NewParenState.Indent = std::max(std::max(State.Column, NewParenState.Indent), State.Stack.back().LastSpace); + } + + // If BreakBeforeBinaryOperators is set, un-indent a bit to account for + // the operator and keep the operands aligned + if (Style.AlignOperands == FormatStyle::OAS_StrictAlign && Previous && + (Previous->getPrecedence() == prec::Assignment || + Previous->is(tok::kw_return)) && + !Newline) + NewParenState.UnindentOperator = true; // Do not indent relative to the fake parentheses inserted for "." or "->". // This is a special case to make the following to statements consistent: Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -191,6 +191,18 @@ } }; +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &IO, FormatStyle::OperandAlignmentStyle &Value) { + IO.enumCase(Value, "DontAlign", FormatStyle::OAS_DontAlign); + IO.enumCase(Value, "Align", FormatStyle::OAS_Align); + IO.enumCase(Value, "StrictAlign", FormatStyle::OAS_StrictAlign); + + // For backward compatibility. + IO.enumCase(Value, "true", FormatStyle::OAS_Align); + IO.enumCase(Value, "false", FormatStyle::OAS_DontAlign); + } +}; + template <> struct ScalarEnumerationTraits { static void enumeration(IO &IO, FormatStyle::PointerAlignmentStyle &Value) { IO.enumCase(Value, "Middle", FormatStyle::PAS_Middle); @@ -537,7 +549,7 @@ LLVMStyle.AccessModifierOffset = -2; LLVMStyle.AlignEscapedNewlines = FormatStyle::ENAS_Right; LLVMStyle.AlignAfterOpenBracket = FormatStyle::BAS_Align; - LLVMStyle.AlignOperands = true; + LLVMStyle.AlignOperands = FormatStyle::OAS_Align; LLVMStyle.AlignTrailingComments = true; LLVMStyle.AlignConsecutiveAssignments = false; LLVMStyle.AlignConsecutiveDeclarations = false; @@ -648,7 +660,7 @@ if (Language == FormatStyle::LK_Java) { GoogleStyle.AlignAfterOpenBracket = FormatStyle::BAS_DontAlign; - GoogleStyle.AlignOperands = false; + GoogleStyle.AlignOperands = FormatStyle::OAS_DontAlign; GoogleStyle.AlignTrailingComments = false; GoogleStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Empty; GoogleStyle.AllowShortIfStatementsOnASingleLine = false; @@ -659,7 +671,7 @@ GoogleStyle.SpacesBeforeTrailingComments = 1; } else if (Language == FormatStyle::LK_JavaScript) { GoogleStyle.AlignAfterOpenBracket = FormatStyle::BAS_AlwaysBreak; - GoogleStyle.AlignOperands = false; + GoogleStyle.AlignOperands = FormatStyle::OAS_DontAlign; GoogleStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Empty; GoogleStyle.AlwaysBreakBeforeMultilineStrings = false; GoogleStyle.BreakBeforeTernaryOperators = false; @@ -736,7 +748,7 @@ FormatStyle Style = getLLVMStyle(); Style.AccessModifierOffset = -4; Style.AlignAfterOpenBracket = FormatStyle::BAS_DontAlign; - Style.AlignOperands = false; + Style.AlignOperands = FormatStyle::OAS_DontAlign; Style.AlignTrailingComments = false; Style.BreakBeforeBinaryOperators = FormatStyle::BOS_All; Style.BreakBeforeBraces = FormatStyle::BS_WebKit; Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -2702,6 +2702,9 @@ " > ccccc) {\n" "}", Style); + verifyFormat("return aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " && bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;", + Style); verifyFormat("return (a)\n" " // comment\n" " + b;", @@ -2730,11 +2733,103 @@ Style.ColumnLimit = 60; verifyFormat("zzzzzzzzzz\n" - " = bbbbbbbbbbbbbbbbb\n" + " = bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n" " >> aaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaa);", Style); } +TEST_F(FormatTest, ExpressionIndentationStrictAlign) { + FormatStyle Style = getLLVMStyle(); + Style.BreakBeforeBinaryOperators = FormatStyle::BOS_All; + Style.AlignOperands = FormatStyle::OAS_StrictAlign; + + verifyFormat("bool value = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " == aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " * bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n" + " + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n" + " && aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " * aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " > ccccccccccccccccccccccccccccccccccccccccc;", + Style); + verifyFormat("if (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " * aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " == bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb) {\n}", + Style); + verifyFormat("if (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " * aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " == bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb) {\n}", + Style); + verifyFormat("if (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " == aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " * aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb) {\n}", + Style); + verifyFormat("if () {\n" + "} else if (aaaaa\n" + " && bbbbb // break\n" + " > ccccc) {\n" + "}", + Style); + verifyFormat("return aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " && bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;", + Style); + verifyFormat("return (a)\n" + " // comment\n" + " + b;", + Style); + verifyFormat( + "int aaaaaa = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " * bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n" + " + cc;", + Style); + + verifyFormat("aaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " = aaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaa;", + Style); + + verifyFormat("return boost::fusion::at_c<0>(iiii).second\n" + " == boost::fusion::at_c<1>(iiii).second;", + Style); + + Style.ColumnLimit = 60; + verifyFormat("zzzzzzzzzzzzz\n" + " = bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n" + " >> aaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaa);", + Style); + + // Forced by comments. + Style.ColumnLimit = 80; + verifyFormat( + "unsigned ContentSize\n" + " = sizeof(int16_t) // DWARF ARange version number\n" + " + sizeof(int32_t) // Offset of CU in the .debug_info section\n" + " + sizeof(int8_t) // Pointer Size (in bytes)\n" + " + sizeof(int8_t); // Segment Size (in bytes)", + Style); + + Style.BreakBeforeBinaryOperators = FormatStyle::BOS_NonAssignment; + verifyFormat( + "unsigned ContentSize =\n" + " sizeof(int16_t) // DWARF ARange version number\n" + " + sizeof(int32_t) // Offset of CU in the .debug_info section\n" + " + sizeof(int8_t) // Pointer Size (in bytes)\n" + " + sizeof(int8_t); // Segment Size (in bytes)", + Style); + + Style.BreakBeforeBinaryOperators = FormatStyle::BOS_None; + verifyFormat( + "unsigned ContentSize =\n" + " sizeof(int16_t) // DWARF ARange version number\n" + " + sizeof(int32_t) // Offset of CU in the .debug_info section\n" + " + sizeof(int8_t) // Pointer Size (in bytes)\n" + " + sizeof(int8_t); // Segment Size (in bytes)", + Style); +} + TEST_F(FormatTest, EnforcedOperatorWraps) { // Here we'd like to wrap after the || operators, but a comment is forcing an // earlier wrap. @@ -2746,7 +2841,7 @@ TEST_F(FormatTest, NoOperandAlignment) { FormatStyle Style = getLLVMStyle(); - Style.AlignOperands = false; + Style.AlignOperands = FormatStyle::OAS_DontAlign; verifyFormat("aaaaaaaaaaaaaa(aaaaaaaaaaaa,\n" " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +\n" " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);", @@ -3953,17 +4048,17 @@ " bbbbbbbbbbbbbbbbbbbbbb);", Style); Style.AlignAfterOpenBracket = FormatStyle::BAS_Align; - Style.AlignOperands = false; + Style.AlignOperands = FormatStyle::OAS_DontAlign; verifyFormat("int a = f(aaaaaaaaaaaaaaaaaaaaaa &&\n" " bbbbbbbbbbbbbbbbbbbbbb);", Style); Style.AlignAfterOpenBracket = FormatStyle::BAS_DontAlign; - Style.AlignOperands = true; + Style.AlignOperands = FormatStyle::OAS_Align; verifyFormat("int a = f(aaaaaaaaaaaaaaaaaaaaaa &&\n" " bbbbbbbbbbbbbbbbbbbbbb);", Style); Style.AlignAfterOpenBracket = FormatStyle::BAS_DontAlign; - Style.AlignOperands = false; + Style.AlignOperands = FormatStyle::OAS_DontAlign; verifyFormat("int a = f(aaaaaaaaaaaaaaaaaaaaaa &&\n" " bbbbbbbbbbbbbbbbbbbbbb);", Style); @@ -9258,7 +9353,6 @@ TEST_F(FormatTest, ParsesConfigurationBools) { FormatStyle Style = {}; Style.Language = FormatStyle::LK_Cpp; - CHECK_PARSE_BOOL(AlignOperands); CHECK_PARSE_BOOL(AlignTrailingComments); CHECK_PARSE_BOOL(AlignConsecutiveAssignments); CHECK_PARSE_BOOL(AlignConsecutiveDeclarations); @@ -9407,6 +9501,17 @@ CHECK_PARSE("AlignEscapedNewlinesLeft: false", AlignEscapedNewlines, FormatStyle::ENAS_Right); + Style.AlignOperands = FormatStyle::OAS_Align; + CHECK_PARSE("AlignOperands: DontAlign", AlignOperands, + FormatStyle::OAS_DontAlign); + CHECK_PARSE("AlignOperands: Align", AlignOperands, FormatStyle::OAS_Align); + CHECK_PARSE("AlignOperands: StrictAlign", AlignOperands, + FormatStyle::OAS_StrictAlign); + // For backward compatibility: + CHECK_PARSE("AlignOperands: false", AlignOperands, + FormatStyle::OAS_DontAlign); + CHECK_PARSE("AlignOperands: true", AlignOperands, FormatStyle::OAS_Align); + Style.UseTab = FormatStyle::UT_ForIndentation; CHECK_PARSE("UseTab: Never", UseTab, FormatStyle::UT_Never); CHECK_PARSE("UseTab: ForIndentation", UseTab, FormatStyle::UT_ForIndentation); Index: unittests/Format/FormatTestJS.cpp =================================================================== --- unittests/Format/FormatTestJS.cpp +++ unittests/Format/FormatTestJS.cpp @@ -102,7 +102,7 @@ verifyFormat("var x = aaaaaaaaaaaaaaaaaaaaaaaaa() in\n" " aaaa.aaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;"); FormatStyle Style = getGoogleJSStyleWithColumns(80); - Style.AlignOperands = true; + Style.AlignOperands = FormatStyle::OAS_Align; verifyFormat("var x = aaaaaaaaaaaaaaaaaaaaaaaaa() in\n" " aaaa.aaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;", Style);