diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -1962,10 +1962,12 @@ **JavaImportGroups** (``std::vector``) A vector of prefixes ordered by the desired groups for Java imports. - Each group is separated by a newline. Static imports will also follow the - same grouping convention above all non-static imports. One group's prefix - can be a subset of another - the longest prefix is always matched. Within - a group, the imports are ordered lexicographically. + One group's prefix can be a subset of another - the longest prefix is + always matched. Within a group, the imports are ordered lexicographically. + Static imports are grouped separately and follow the same group rules. + By default, static imports are placed before non-static imports, + but this behavior is changed by another option, + ``JavaStaticImportAfterImport``. In the .clang-format configuration file, this can be configured like in the following yaml example. This will result in imports being @@ -2038,6 +2040,23 @@ false: import {VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying,} from "some/module.js" +**JavaStaticImportAfterImport** (``bool``) + When sorting Java imports, by default static imports are placed before + non-static imports. If ``JavaStaticImportAfterImport`` is true, + static imports are placed after non-static imports. + + .. code-block:: java + + true: + import static org.example.function1; + + import org.example.ClassA; + + false: + import org.example.ClassA; + + import static org.example.function1; + **KeepEmptyLinesAtTheStartOfBlocks** (``bool``) If true, the empty line at the start of blocks is kept. diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -1618,10 +1618,12 @@ /// A vector of prefixes ordered by the desired groups for Java imports. /// - /// Each group is separated by a newline. Static imports will also follow the - /// same grouping convention above all non-static imports. One group's prefix - /// can be a subset of another - the longest prefix is always matched. Within - /// a group, the imports are ordered lexicographically. + /// One group's prefix can be a subset of another - the longest prefix is + /// always matched. Within a group, the imports are ordered lexicographically. + /// Static imports are grouped separately and follow the same group rules. + /// By default, static imports are placed before non-static imports, + /// but this behavior is changed by another option, + /// ``JavaStaticImportAfterImport``. /// /// In the .clang-format configuration file, this can be configured like /// in the following yaml example. This will result in imports being @@ -1689,6 +1691,22 @@ bool JavaScriptWrapImports; // clang-format on + /// When sorting Java imports, by default static imports are placed before + /// non-static imports. If ``JavaStaticImportAfterImport`` is true, + /// static imports are placed after non-static imports. + /// \code{.java} + /// true: + /// import static org.example.function1; + /// + /// import org.example.ClassA; + /// + /// false: + /// import org.example.ClassA; + /// + /// import static org.example.function1; + /// \endcode + bool JavaStaticImportAfterImport; + /// If true, the empty line at the start of blocks is kept. /// \code /// true: false: @@ -2410,6 +2428,7 @@ JavaImportGroups == R.JavaImportGroups && JavaScriptQuotes == R.JavaScriptQuotes && JavaScriptWrapImports == R.JavaScriptWrapImports && + JavaStaticImportAfterImport == R.JavaStaticImportAfterImport && KeepEmptyLinesAtTheStartOfBlocks == R.KeepEmptyLinesAtTheStartOfBlocks && MacroBlockBegin == R.MacroBlockBegin && diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -544,6 +544,8 @@ IO.mapOptional("JavaImportGroups", Style.JavaImportGroups); IO.mapOptional("JavaScriptQuotes", Style.JavaScriptQuotes); IO.mapOptional("JavaScriptWrapImports", Style.JavaScriptWrapImports); + IO.mapOptional("JavaStaticImportAfterImport", + Style.JavaStaticImportAfterImport); IO.mapOptional("KeepEmptyLinesAtTheStartOfBlocks", Style.KeepEmptyLinesAtTheStartOfBlocks); IO.mapOptional("MacroBlockBegin", Style.MacroBlockBegin); @@ -901,6 +903,7 @@ LLVMStyle.InsertTrailingCommas = FormatStyle::TCS_None; LLVMStyle.JavaScriptQuotes = FormatStyle::JSQS_Leave; LLVMStyle.JavaScriptWrapImports = true; + LLVMStyle.JavaStaticImportAfterImport = false; LLVMStyle.TabWidth = 8; LLVMStyle.MaxEmptyLinesToKeep = 1; LLVMStyle.KeepEmptyLinesAtTheStartOfBlocks = true; @@ -2312,12 +2315,15 @@ JavaImportGroups.push_back( findJavaImportGroup(Style, Imports[i].Identifier)); } + bool StaticImportAfterNormalImport = Style.JavaStaticImportAfterImport; llvm::sort(Indices, [&](unsigned LHSI, unsigned RHSI) { // Negating IsStatic to push static imports above non-static imports. - return std::make_tuple(!Imports[LHSI].IsStatic, JavaImportGroups[LHSI], - Imports[LHSI].Identifier) < - std::make_tuple(!Imports[RHSI].IsStatic, JavaImportGroups[RHSI], - Imports[RHSI].Identifier); + return std::make_tuple(!Imports[LHSI].IsStatic ^ + StaticImportAfterNormalImport, + JavaImportGroups[LHSI], Imports[LHSI].Identifier) < + std::make_tuple(!Imports[RHSI].IsStatic ^ + StaticImportAfterNormalImport, + JavaImportGroups[RHSI], Imports[RHSI].Identifier); }); // Deduplicate imports. diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -13929,6 +13929,7 @@ CHECK_PARSE_BOOL(IndentCaseBlocks); CHECK_PARSE_BOOL(IndentGotoLabels); CHECK_PARSE_BOOL(IndentWrappedFunctionNames); + CHECK_PARSE_BOOL(JavaStaticImportAfterImport); CHECK_PARSE_BOOL(KeepEmptyLinesAtTheStartOfBlocks); CHECK_PARSE_BOOL(ObjCSpaceAfterProperty); CHECK_PARSE_BOOL(ObjCSpaceBeforeProtocolList); diff --git a/clang/unittests/Format/SortImportsTestJava.cpp b/clang/unittests/Format/SortImportsTestJava.cpp --- a/clang/unittests/Format/SortImportsTestJava.cpp +++ b/clang/unittests/Format/SortImportsTestJava.cpp @@ -250,6 +250,30 @@ "import org.c;\n")); } +TEST_F(SortImportsTestJava, FormatJavaStaticImportAfterImport) { + FmtStyle.JavaStaticImportAfterImport = true; + + EXPECT_EQ("import com.test.b;\n" + "import com.test.c;\n" + "\n" + "import org.b;\n" + "\n" + "import com.b;\n" + "\n" + "import static com.test.a;\n" + "\n" + "import static org.a;\n" + "\n" + "import static com.a;\n", + sort("import static com.test.a;\n" + "import static org.a;\n" + "import static com.a;\n" + "import com.test.b;\n" + "import org.b;\n" + "import com.b;\n" + "import com.test.c;\n")); +} + TEST_F(SortImportsTestJava, DeduplicateImports) { EXPECT_EQ("import org.a;\n", sort("import org.a;\n" "import org.a;\n"));