Index: clang/docs/ClangFormatStyleOptions.rst =================================================================== --- clang/docs/ClangFormatStyleOptions.rst +++ clang/docs/ClangFormatStyleOptions.rst @@ -239,6 +239,34 @@ +**AlignAfterOpenBracket** (``CloseBracketAlignmentStyle``) + Style of aligning close brackets. + + This applies to round brackets (parentheses), angle brackets and square + brackets. + + Possible values: + + * ``CBAS_DontAlign`` (in configuration: ``DontAlign``) + Don't align, e.g.: + + .. code-block:: c++ + + someLongFunction(argument1, + argument2); + + * ``CBAS_AlwaysBreak`` (in configuration: ``AlwaysBreak``) + Always break after a close bracket, if the parameters don't fit + on a single line, e.g.: + + .. code-block:: c++ + + someLongFunction( + argument1, argument2 + ); + + + **AlignConsecutiveAssignments** (``AlignConsecutiveStyle``) Style of aligning consecutive assignments. Index: clang/include/clang/Format/Format.h =================================================================== --- clang/include/clang/Format/Format.h +++ clang/include/clang/Format/Format.h @@ -119,6 +119,30 @@ /// aligns the fields into columns. ArrayInitializerAlignmentStyle AlignArrayOfStructures; + /// Different styles for aligning after close brackets. + enum CloseBracketAlignmentStyle : unsigned char { + /// Don't align, e.g.: + /// \code + /// someLongFunction(argument1, + /// argument2); + /// \endcode + CBAS_DontAlign, + /// Always break before a close bracket, if the parameters don't fit + /// on a single line, e.g.: + /// \code + /// someLongFunction( + /// argument1, argument2 + /// ); + /// \endcode + CBAS_AlwaysBreak, + }; + + /// Style of aligning close brackets. + /// + /// This applies to round brackets (parentheses), angle brackets and square + /// brackets. + CloseBracketAlignmentStyle AlignCloseBracket; + /// Styles for alignment of consecutive tokens. Tokens can be assignment signs /// (see /// ``AlignConsecutiveAssignments``), bitfield member separators (see Index: clang/lib/Format/ContinuationIndenter.h =================================================================== --- clang/lib/Format/ContinuationIndenter.h +++ clang/lib/Format/ContinuationIndenter.h @@ -203,15 +203,15 @@ bool AvoidBinPacking, bool NoLineBreak) : Tok(Tok), Indent(Indent), LastSpace(LastSpace), NestedBlockIndent(Indent), IsAligned(false), - BreakBeforeClosingBrace(false), AvoidBinPacking(AvoidBinPacking), - BreakBeforeParameter(false), NoLineBreak(NoLineBreak), - NoLineBreakInOperand(false), LastOperatorWrapped(true), - ContainsLineBreak(false), ContainsUnwrappedBuilder(false), - AlignColons(true), ObjCSelectorNameFound(false), - HasMultipleNestedBlocks(false), NestedBlockInlined(false), - IsInsideObjCArrayLiteral(false), IsCSharpGenericTypeConstraint(false), - IsChainedConditional(false), IsWrappedConditional(false), - UnindentOperator(false) {} + BreakBeforeClosingBrace(false), BreakBeforeClosingParen(false), + AvoidBinPacking(AvoidBinPacking), BreakBeforeParameter(false), + NoLineBreak(NoLineBreak), NoLineBreakInOperand(false), + LastOperatorWrapped(true), ContainsLineBreak(false), + ContainsUnwrappedBuilder(false), AlignColons(true), + ObjCSelectorNameFound(false), HasMultipleNestedBlocks(false), + NestedBlockInlined(false), IsInsideObjCArrayLiteral(false), + IsCSharpGenericTypeConstraint(false), IsChainedConditional(false), + IsWrappedConditional(false), UnindentOperator(false) {} /// \brief The token opening this parenthesis level, or nullptr if this level /// is opened by fake parenthesis. @@ -277,6 +277,13 @@ /// was a newline after the beginning left brace. bool BreakBeforeClosingBrace : 1; + /// Whether a newline needs to be inserted before the block's closing + /// paren. + /// + /// We only want to insert a newline before the closing paren if there also + /// was a newline after the beginning left paren. + bool BreakBeforeClosingParen : 1; + /// Avoid bin packing, i.e. multiple parameters/elements on multiple /// lines, in this context. bool AvoidBinPacking : 1; @@ -362,6 +369,8 @@ return IsAligned; if (BreakBeforeClosingBrace != Other.BreakBeforeClosingBrace) return BreakBeforeClosingBrace; + if (BreakBeforeClosingParen != Other.BreakBeforeClosingParen) + return BreakBeforeClosingParen; if (QuestionColumn != Other.QuestionColumn) return QuestionColumn < Other.QuestionColumn; if (AvoidBinPacking != Other.AvoidBinPacking) Index: clang/lib/Format/ContinuationIndenter.cpp =================================================================== --- clang/lib/Format/ContinuationIndenter.cpp +++ clang/lib/Format/ContinuationIndenter.cpp @@ -341,6 +341,8 @@ if (State.Stack.back().BreakBeforeClosingBrace && Current.closesBlockOrBlockTypeList(Style)) return true; + if (State.Stack.back().BreakBeforeClosingParen && Current.is(tok::r_paren)) + return true; if (Previous.is(tok::semi) && State.LineContainsContinuedForLoopSection) return true; if (Style.Language == FormatStyle::LK_ObjC && @@ -944,6 +946,11 @@ opensProtoMessageField(*PreviousNonComment, Style))) State.Stack.back().BreakBeforeClosingBrace = true; + if (PreviousNonComment && PreviousNonComment->is(tok::l_paren)) { + State.Stack.back().BreakBeforeClosingParen = + Style.AlignCloseBracket == FormatStyle::CBAS_AlwaysBreak; + } + if (State.Stack.back().AvoidBinPacking) { // If we are breaking after '(', '{', '<', or this is the break after a ':' // to start a member initializater list in a constructor, this should not @@ -1038,6 +1045,9 @@ (!Current.Next || Current.Next->isOneOf(tok::semi, tok::kw_const, tok::l_brace))) return State.Stack[State.Stack.size() - 2].LastSpace; + if (Style.AlignCloseBracket == FormatStyle::CBAS_AlwaysBreak && + Current.is(tok::r_paren) && State.Stack.size() > 1) + return State.Stack[State.Stack.size() - 2].LastSpace; if (NextNonComment->is(TT_TemplateString) && NextNonComment->closesScope()) return State.Stack[State.Stack.size() - 2].LastSpace; if (Current.is(tok::identifier) && Current.Next && Index: clang/lib/Format/Format.cpp =================================================================== --- clang/lib/Format/Format.cpp +++ clang/lib/Format/Format.cpp @@ -163,6 +163,15 @@ } }; +template <> +struct ScalarEnumerationTraits { + static void enumeration(IO &IO, + FormatStyle::CloseBracketAlignmentStyle &Value) { + IO.enumCase(Value, "DontAlign", FormatStyle::CBAS_DontAlign); + IO.enumCase(Value, "AlwaysBreak", FormatStyle::CBAS_AlwaysBreak); + } +}; + template <> struct ScalarEnumerationTraits { static void enumeration(IO &IO, FormatStyle::ShortIfStyle &Value) { IO.enumCase(Value, "Never", FormatStyle::SIS_Never); @@ -551,6 +560,7 @@ IO.mapOptional("AccessModifierOffset", Style.AccessModifierOffset); IO.mapOptional("AlignAfterOpenBracket", Style.AlignAfterOpenBracket); IO.mapOptional("AlignArrayOfStructures", Style.AlignArrayOfStructures); + IO.mapOptional("AlignCloseBracket", Style.AlignCloseBracket); IO.mapOptional("AlignConsecutiveMacros", Style.AlignConsecutiveMacros); IO.mapOptional("AlignConsecutiveAssignments", Style.AlignConsecutiveAssignments); @@ -1023,6 +1033,7 @@ LLVMStyle.AlignEscapedNewlines = FormatStyle::ENAS_Right; LLVMStyle.AlignAfterOpenBracket = FormatStyle::BAS_Align; LLVMStyle.AlignArrayOfStructures = FormatStyle::AIAS_None; + LLVMStyle.AlignCloseBracket = FormatStyle::CBAS_DontAlign; LLVMStyle.AlignOperands = FormatStyle::OAS_Align; LLVMStyle.AlignTrailingComments = true; LLVMStyle.AlignConsecutiveAssignments = FormatStyle::ACS_None; Index: clang/lib/Format/TokenAnnotator.cpp =================================================================== --- clang/lib/Format/TokenAnnotator.cpp +++ clang/lib/Format/TokenAnnotator.cpp @@ -4181,7 +4181,9 @@ if (Right.is(TT_ImplicitStringLiteral)) return false; - if (Right.is(tok::r_paren) || Right.is(TT_TemplateCloser)) + if (Right.is(tok::r_paren)) + return Style.AlignCloseBracket == FormatStyle::CBAS_AlwaysBreak; + if (Right.is(TT_TemplateCloser)) return false; if (Right.is(tok::r_square) && Right.MatchingParen && Right.MatchingParen->is(TT_LambdaLSquare)) Index: clang/unittests/Format/FormatTest.cpp =================================================================== --- clang/unittests/Format/FormatTest.cpp +++ clang/unittests/Format/FormatTest.cpp @@ -18507,6 +18507,12 @@ CHECK_PARSE("AlignAfterOpenBracket: true", AlignAfterOpenBracket, FormatStyle::BAS_Align); + Style.AlignCloseBracket = FormatStyle::CBAS_AlwaysBreak; + CHECK_PARSE("AlignCloseBracket: DontAlign", AlignCloseBracket, + FormatStyle::CBAS_DontAlign); + CHECK_PARSE("AlignCloseBracket: AlwaysBreak", AlignCloseBracket, + FormatStyle::CBAS_AlwaysBreak); + Style.AlignEscapedNewlines = FormatStyle::ENAS_Left; CHECK_PARSE("AlignEscapedNewlines: DontAlign", AlignEscapedNewlines, FormatStyle::ENAS_DontAlign); @@ -22288,6 +22294,94 @@ "}"; EXPECT_EQ(Code, format(Code, Style)); } + +TEST_F(FormatTest, AlignCloseBracket) { + auto Style = getLLVMStyle(); + Style.AlignCloseBracket = FormatStyle::CBAS_AlwaysBreak; + StringRef Short = "functionCall(paramA, paramB, paramC);\n" + "void functionDecl(int a, int b, int c);"; + + StringRef Medium = "functionCall(paramA, paramB, paramC, paramD, paramE, " + "paramF, paramG, paramH, paramI);\n" + "void functionDecl(int argumentA, int argumentB, int " + "argumentC, int argumentD, int argumentE);"; + + verifyFormat(Short, Style); + + EXPECT_EQ(StringRef("functionCall(paramA, paramB, paramC, paramD, paramE, " + "paramF, paramG, paramH,\n" + " paramI);\n" + "void functionDecl(int argumentA, int argumentB, int " + "argumentC, int argumentD,\n" + " int argumentE);"), + format(Medium, Style)); + + Style.AllowAllArgumentsOnNextLine = false; + Style.AllowAllConstructorInitializersOnNextLine = false; + Style.AllowAllParametersOfDeclarationOnNextLine = false; + + verifyFormat(Short, Style); + + EXPECT_EQ(StringRef("functionCall(paramA, paramB, paramC, paramD, paramE, " + "paramF, paramG, paramH,\n" + " paramI);\n" + "void functionDecl(int argumentA, int argumentB, int " + "argumentC, int argumentD,\n" + " int argumentE);"), + format(Medium, Style)); + + Style.AlignAfterOpenBracket = FormatStyle::BAS_AlwaysBreak; + + verifyFormat(Short, Style); + + EXPECT_EQ(StringRef("functionCall(\n" + " paramA, paramB, paramC, paramD, paramE, paramF, " + "paramG, paramH, paramI\n" + ");\n" + "void functionDecl(\n" + " int argumentA, int argumentB, int argumentC, int " + "argumentD, int argumentE\n" + ");"), + format(Medium, Style)); + + Style.BinPackArguments = false; + Style.BinPackParameters = false; + + verifyFormat(Short, Style); + + EXPECT_EQ(StringRef("functionCall(\n" + " paramA,\n" + " paramB,\n" + " paramC,\n" + " paramD,\n" + " paramE,\n" + " paramF,\n" + " paramG,\n" + " paramH,\n" + " paramI\n" + ");\n" + "void functionDecl(\n" + " int argumentA,\n" + " int argumentB,\n" + " int argumentC,\n" + " int argumentD,\n" + " int argumentE\n" + ");"), + format(Medium, Style)); + + verifyFormat("outerFunctionCall(\n" + " nestedFunctionCall(argument1),\n" + " nestedLongFunctionCall(\n" + " argument1,\n" + " argument2,\n" + " argument3,\n" + " argument4,\n" + " argument5\n" + " )\n" + ");", + Style); +} + } // namespace } // namespace format } // namespace clang