vlovich created this revision. vlovich added reviewers: MyDeveloperDay, curdeius. vlovich added a project: clang-format. vlovich requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
Currently the lambda body indents relative to where the lambda signature is located. This instead lets the user choose to align the lambda body relative to the parent scope that contains the lambda declaration. Thus: someFunction([] { lambdaBody(); }); will always have the same indentation of the body even when the lambda signature goes on a new line: someFunction( [] { lambdaBody(); }); whereas before `lambdaBody` would be indented 6 spaces. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D102706 Files: clang/docs/ClangFormatStyleOptions.rst clang/include/clang/Format/Format.h clang/lib/Format/Format.cpp clang/lib/Format/UnwrappedLineFormatter.cpp clang/unittests/Format/FormatTest.cpp
Index: clang/unittests/Format/FormatTest.cpp =================================================================== --- clang/unittests/Format/FormatTest.cpp +++ clang/unittests/Format/FormatTest.cpp @@ -17871,6 +17871,7 @@ " aaaaaaaaaaaaaaaaaaaaaaa;\n" " });", getLLVMStyleWithColumns(60)); + verifyFormat("SomeFunction({[&] {\n" " // comment\n" " },\n" @@ -18423,6 +18424,90 @@ " });\n" " });", LLVMWithBeforeLambdaBody); + + // Lambdas with different indentation styles. + Style = getLLVMStyleWithColumns(100); + EXPECT_EQ("SomeResult doSomething(SomeObject promise) {\n" + " return promise.then(\n" + " [this, &someVariable, someObject = std::mv(s)](std::vector<int> evaluated) mutable {\n" + " return someObject.startAsyncAction().then(\n" + " [this, &someVariable](AsyncActionResult result) mutable { result.processMore(); });\n" + " });\n" + "}\n", + format("SomeResult doSomething(SomeObject promise) {\n" + " return promise.then([this, &someVariable, someObject = std::mv(s)](std::vector<int> evaluated) mutable {\n" + " return someObject.startAsyncAction().then([this, &someVariable](AsyncActionResult result) mutable {\n" + " result.processMore();\n" + " });\n" + " });\n" + "}\n", + Style)); + Style.LambdaBodyIndentation = FormatStyle::LBI_OuterScope; + verifyFormat("test() {\n" + " ([]() -> {\n" + " int b = 32;\n" + " return 3;\n" + " }).foo();\n" + "}", + Style); + verifyFormat("test() {\n" + " (\n" + " []() -> {\n" + " int b = 32;\n" + " return 3;\n" + " },\n" + " foo, bar)\n" + " .foo();\n" + "}", + Style); + verifyFormat("test() {\n" + " ([]() -> {\n" + " int b = 32;\n" + " return 3;\n" + " })\n" + " .foo()\n" + " .bar();\n" + "}", + Style); + EXPECT_EQ("SomeResult doSomething(SomeObject promise) {\n" + " return promise.then(\n" + " [this, &someVariable, someObject = std::mv(s)](std::vector<int> evaluated) mutable {\n" + " return someObject.startAsyncAction().then(\n" + " [this, &someVariable](AsyncActionResult result) mutable { result.processMore(); });\n" + " });\n" + "}\n", + format("SomeResult doSomething(SomeObject promise) {\n" + " return promise.then([this, &someVariable, someObject = std::mv(s)](std::vector<int> evaluated) mutable {\n" + " return someObject.startAsyncAction().then([this, &someVariable](AsyncActionResult result) mutable {\n" + " result.processMore();\n" + " });\n" + " });\n" + "}\n", + Style)); + EXPECT_EQ("SomeResult doSomething(SomeObject promise) {\n" + " return promise.then([this, &someVariable] {\n" + " return someObject.startAsyncAction().then(\n" + " [this, &someVariable](AsyncActionResult result) mutable { result.processMore(); });\n" + " });\n" + "}\n", + format("SomeResult doSomething(SomeObject promise) {\n" + " return promise.then([this, &someVariable] {\n" + " return someObject.startAsyncAction().then([this, &someVariable](AsyncActionResult result) mutable {\n" + " result.processMore();\n" + " });\n" + " });\n" + "}\n", + Style)); + Style = getGoogleStyle(); + Style.LambdaBodyIndentation = FormatStyle::LBI_OuterScope; + EXPECT_EQ("#define A \\\n" + " [] { \\\n" + " xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx( \\\n" + " xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx); \\\n" + " }", + format("#define A [] { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx( \\\n" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx); }", + Style)); } TEST_F(FormatTest, LambdaWithLineComments) { Index: clang/lib/Format/UnwrappedLineFormatter.cpp =================================================================== --- clang/lib/Format/UnwrappedLineFormatter.cpp +++ clang/lib/Format/UnwrappedLineFormatter.cpp @@ -821,8 +821,20 @@ return true; if (NewLine) { - int AdditionalIndent = State.Stack.back().Indent - - Previous.Children[0]->Level * Style.IndentWidth; + const ParenState &P = State.Stack.back(); + + int AdditionalIndent = + P.Indent - Previous.Children[0]->Level * Style.IndentWidth; + + if (Style.LambdaBodyIndentation == FormatStyle::LBI_OuterScope && + P.NestedBlockIndent == P.LastSpace) { + if (State.NextToken->MatchingParen && + State.NextToken->MatchingParen->is(TT_LambdaLBrace)) { + State.Stack.pop_back(); + } + if (LBrace->is(TT_LambdaLBrace)) + AdditionalIndent = 0; + } Penalty += BlockFormatter->format(Previous.Children, DryRun, AdditionalIndent, Index: clang/lib/Format/Format.cpp =================================================================== --- clang/lib/Format/Format.cpp +++ clang/lib/Format/Format.cpp @@ -85,6 +85,15 @@ } }; +template <> +struct ScalarEnumerationTraits<FormatStyle::LambdaBodyIndentationKind> { + static void enumeration(IO &IO, + FormatStyle::LambdaBodyIndentationKind &Value) { + IO.enumCase(Value, "Signature", FormatStyle::LBI_Signature); + IO.enumCase(Value, "OuterScope", FormatStyle::LBI_OuterScope); + } +}; + template <> struct ScalarEnumerationTraits<FormatStyle::UseTabStyle> { static void enumeration(IO &IO, FormatStyle::UseTabStyle &Value) { IO.enumCase(Value, "Never", FormatStyle::UT_Never); @@ -639,6 +648,7 @@ IO.mapOptional("JavaScriptWrapImports", Style.JavaScriptWrapImports); IO.mapOptional("KeepEmptyLinesAtTheStartOfBlocks", Style.KeepEmptyLinesAtTheStartOfBlocks); + IO.mapOptional("LambdaBodyIndentation", Style.LambdaBodyIndentation); IO.mapOptional("MacroBlockBegin", Style.MacroBlockBegin); IO.mapOptional("MacroBlockEnd", Style.MacroBlockEnd); IO.mapOptional("MaxEmptyLinesToKeep", Style.MaxEmptyLinesToKeep); @@ -1025,6 +1035,7 @@ LLVMStyle.JavaScriptQuotes = FormatStyle::JSQS_Leave; LLVMStyle.JavaScriptWrapImports = true; LLVMStyle.TabWidth = 8; + LLVMStyle.LambdaBodyIndentation = FormatStyle::LBI_Signature; LLVMStyle.MaxEmptyLinesToKeep = 1; LLVMStyle.KeepEmptyLinesAtTheStartOfBlocks = true; LLVMStyle.NamespaceIndentation = FormatStyle::NI_None; Index: clang/include/clang/Format/Format.h =================================================================== --- clang/include/clang/Format/Format.h +++ clang/include/clang/Format/Format.h @@ -2452,6 +2452,15 @@ /// Language, this format style is targeted at. LanguageKind Language; + enum LambdaBodyIndentationKind : unsigned char { + /// Align lambda body relative to the lambda signature. This is the default. + LBI_Signature, + /// Align lambda body relative to the indentation level the lambda lives at. + LBI_OuterScope, + }; + + LambdaBodyIndentationKind LambdaBodyIndentation; + /// A regular expression matching macros that start a block. /// \code /// # With: @@ -3324,6 +3333,7 @@ JavaScriptWrapImports == R.JavaScriptWrapImports && KeepEmptyLinesAtTheStartOfBlocks == R.KeepEmptyLinesAtTheStartOfBlocks && + LambdaBodyIndentation == R.LambdaBodyIndentation && MacroBlockBegin == R.MacroBlockBegin && MacroBlockEnd == R.MacroBlockEnd && MaxEmptyLinesToKeep == R.MaxEmptyLinesToKeep && Index: clang/docs/ClangFormatStyleOptions.rst =================================================================== --- clang/docs/ClangFormatStyleOptions.rst +++ clang/docs/ClangFormatStyleOptions.rst @@ -2789,6 +2789,19 @@ +**LambdaBodyIndentation** (``LambdaBodyIndentationKind``) + What is the lambda body indented relative to (default is ``Signature``). + + Possible values: + * ``LBI_Signature`` (in configuration: ``Signature``) + This uses the default indentation relative to the lambda signature. + + * ``LBI_OuterScope`` (in configuration: ``OuterScope``) + This indents relative to the indentation level within which the + lambda is encountered. + + + **MacroBlockBegin** (``std::string``) A regular expression matching macros that start a block.
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits