Index: docs/ClangFormatStyleOptions.rst =================================================================== --- docs/ClangFormatStyleOptions.rst +++ docs/ClangFormatStyleOptions.rst @@ -256,17 +256,48 @@ -**AlignOperands** (``bool``) - If ``true``, horizontally align operands of binary and ternary - expressions. +**AlignOperands** (``OperandAlignmentStyle``) + Option to specify the style to use when aligning operands. - Specifically, this aligns operands of a single expression that needs to be - split over multiple lines, e.g.: + Possible values: + + * ``OAS_DontAlign`` (in configuration: ``DontAlign``) + Do not align operands of binary and ternary expressions. + The wrapped lines are indented ``ContinuationIndentWidth`` spaces from + the start of the line. + + * ``OAS_Align`` (in configuration: ``Align``) + 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-block:: c++ + + int aaa = bbbbbbbbbbbbbbb + + ccccccccccccccc; + + When ``BreakBeforeBinaryOperators`` is set, the wrapped operator is + aligned with the operand on the first line. + + .. code-block:: c++ + + int aaa = bbbbbbbbbbbbbbb + + ccccccccccccccc; + + * ``OAS_AlignAfterOperator`` (in configuration: ``AlignAfterOperator``) + Horizontally align operands of binary and ternary expressions. + + This is similar to ``AO_Align``, except when + ``BreakBeforeBinaryOperators`` is set, the operator is un-indented so + that the wrapped operand is aligned with the operand on the first line. + + .. code-block:: c++ + + int aaa = bbbbbbbbbbbbbbb + + ccccccccccccccc; - .. code-block:: c++ - int aaa = bbbbbbbbbbbbbbb + - ccccccccccccccc; **AlignTrailingComments** (``bool``) If ``true``, aligns trailing comments. Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -135,16 +135,42 @@ /// Options for aligning backslashes in escaped newlines. EscapedNewlineAlignmentStyle AlignEscapedNewlines; - /// 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; + /// Different styles for aligning operands. + 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 operator is un-indented so + /// that the wrapped operand is aligned with the operand on the first line. + /// \code + /// int aaa = bbbbbbbbbbbbbbb + /// + ccccccccccccccc; + /// \endcode + OAS_AlignAfterOperator, + }; + + /// Option to specify the style to use when aligning operands. + OperandAlignmentStyle AlignOperands; /// If ``true``, aligns trailing comments. /// \code Index: lib/Format/ContinuationIndenter.h =================================================================== --- lib/Format/ContinuationIndenter.h +++ lib/Format/ContinuationIndenter.h @@ -208,7 +208,8 @@ LastOperatorWrapped(true), ContainsLineBreak(false), ContainsUnwrappedBuilder(false), AlignColons(true), ObjCSelectorNameFound(false), HasMultipleNestedBlocks(false), - NestedBlockInlined(false), IsInsideObjCArrayLiteral(false) {} + NestedBlockInlined(false), IsInsideObjCArrayLiteral(false), + UnindentOperator(false) {} /// \brief The token opening this parenthesis level, or nullptr if this level /// is opened by fake parenthesis. @@ -329,6 +330,10 @@ /// array literal. bool IsInsideObjCArrayLiteral : 1; + /// 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; @@ -366,6 +371,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 @@ -668,7 +668,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; } @@ -1062,6 +1064,13 @@ return ContinuationIndent; if (Current.is(TT_ProtoExtensionLSquare)) return State.Stack.back().Indent; + if (Current.isBinaryOperator() && State.Stack.back().UnindentOperator) + return State.Stack.back().Indent - Current.Tok.getLength() - + Current.SpacesRequiredBefore; + if (Current.isOneOf(tok::comment, TT_BlockComment, TT_LineComment) && + NextNonComment->isBinaryOperator() && State.Stack.back().UnindentOperator) + return State.Stack.back().Indent - NextNonComment->Tok.getLength() - + NextNonComment->SpacesRequiredBefore; if (State.Stack.back().Indent == State.FirstIndent && PreviousNonComment && !PreviousNonComment->isOneOf(tok::r_brace, TT_CtorInitializerComma)) // Ensure that we fall back to the continuation indent width instead of @@ -1224,7 +1233,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(), @@ -1234,6 +1243,7 @@ NewParenState.Tok = nullptr; NewParenState.ContainsLineBreak = false; NewParenState.LastOperatorWrapped = true; + NewParenState.UnindentOperator = false; NewParenState.NoLineBreak = NewParenState.NoLineBreak || State.Stack.back().NoLineBreakInOperand; @@ -1245,14 +1255,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_AlignAfterOperator && 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 @@ -266,6 +266,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, "AlignAfterOperator", FormatStyle::OAS_AlignAfterOperator); + + // 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); @@ -662,7 +674,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; @@ -846,7 +858,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 = FormatStyle::SIS_Never; @@ -857,7 +869,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; @@ -962,7 +974,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 @@ -3837,6 +3837,9 @@ " > ccccc) {\n" "}", Style); + verifyFormat("return aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " && bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;", + Style); verifyFormat("return (a)\n" " // comment\n" " + b;", @@ -3865,7 +3868,7 @@ Style.ColumnLimit = 60; verifyFormat("zzzzzzzzzz\n" - " = bbbbbbbbbbbbbbbbb\n" + " = bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n" " >> aaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaa);", Style); @@ -3874,7 +3877,7 @@ Style.TabWidth = 4; Style.UseTab = FormatStyle::UT_Always; Style.AlignAfterOpenBracket = FormatStyle::BAS_DontAlign; - Style.AlignOperands = false; + Style.AlignOperands = FormatStyle::OAS_DontAlign; EXPECT_EQ("return someVeryVeryLongConditionThatBarelyFitsOnALine\n" "\t&& (someOtherLongishConditionPart1\n" "\t\t|| someOtherEvenLongerNestedConditionPart2);", @@ -3882,6 +3885,98 @@ Style)); } +TEST_F(FormatTest, ExpressionIndentationStrictAlign) { + FormatStyle Style = getLLVMStyle(); + Style.BreakBeforeBinaryOperators = FormatStyle::BOS_All; + Style.AlignOperands = FormatStyle::OAS_AlignAfterOperator; + + 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. @@ -3893,7 +3988,7 @@ TEST_F(FormatTest, NoOperandAlignment) { FormatStyle Style = getLLVMStyle(); - Style.AlignOperands = false; + Style.AlignOperands = FormatStyle::OAS_DontAlign; verifyFormat("aaaaaaaaaaaaaa(aaaaaaaaaaaa,\n" " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +\n" " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);", @@ -5359,17 +5454,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); @@ -11501,7 +11596,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); @@ -11672,6 +11766,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: AlignAfterOperator", AlignOperands, + FormatStyle::OAS_AlignAfterOperator); + // 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 @@ -277,7 +277,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);