Index: cfe/trunk/docs/ClangFormatStyleOptions.rst =================================================================== --- cfe/trunk/docs/ClangFormatStyleOptions.rst +++ cfe/trunk/docs/ClangFormatStyleOptions.rst @@ -1511,6 +1511,13 @@ can also assign negative priorities if you have certain headers that always need to be first. + There is a third and optional field ``SortPriority`` which can used while + ``IncludeBloks = IBS_Regroup`` to define the priority in which ``#includes`` + should be ordered, and value of ``Priority`` defines the order of + ``#include blocks`` and also enables to group ``#includes`` of different + priority for order.``SortPriority`` is set to the value of ``Priority`` + as default if it is not assigned. + To configure this in the .clang-format file, use: .. code-block:: yaml @@ -1518,12 +1525,14 @@ IncludeCategories: - Regex: '^"(llvm|llvm-c|clang|clang-c)/' Priority: 2 + SortPriority: 2 - Regex: '^(<|"(gtest|gmock|isl|json)/)' Priority: 3 - Regex: '<[[:alnum:].]+>' Priority: 4 - Regex: '.*' Priority: 1 + SortPriority: 0 **IncludeIsMainRegex** (``std::string``) Specify a regular expression of suffixes that are allowed in the Index: cfe/trunk/include/clang/Tooling/Inclusions/HeaderIncludes.h =================================================================== --- cfe/trunk/include/clang/Tooling/Inclusions/HeaderIncludes.h +++ cfe/trunk/include/clang/Tooling/Inclusions/HeaderIncludes.h @@ -32,6 +32,7 @@ /// 0. Otherwise, returns the priority of the matching category or INT_MAX. /// NOTE: this API is not thread-safe! int getIncludePriority(StringRef IncludeName, bool CheckMainHeader) const; + int getSortIncludePriority(StringRef IncludeName, bool CheckMainHeader) const; private: bool isMainHeader(StringRef IncludeName) const; Index: cfe/trunk/include/clang/Tooling/Inclusions/IncludeStyle.h =================================================================== --- cfe/trunk/include/clang/Tooling/Inclusions/IncludeStyle.h +++ cfe/trunk/include/clang/Tooling/Inclusions/IncludeStyle.h @@ -58,6 +58,8 @@ std::string Regex; /// The priority to assign to this category. int Priority; + /// The custom priority to sort before grouping. + int SortPriority; bool operator==(const IncludeCategory &Other) const { return Regex == Other.Regex && Priority == Other.Priority; } Index: cfe/trunk/lib/Format/Format.cpp =================================================================== --- cfe/trunk/lib/Format/Format.cpp +++ cfe/trunk/lib/Format/Format.cpp @@ -1771,6 +1771,7 @@ StringRef Text; unsigned Offset; int Category; + int Priority; }; struct JavaImportDirective { @@ -1834,6 +1835,7 @@ ArrayRef Ranges, StringRef FileName, StringRef Code, tooling::Replacements &Replaces, unsigned *Cursor) { + tooling::IncludeCategoryManager Categories(Style.IncludeStyle, FileName); unsigned IncludesBeginOffset = Includes.front().Offset; unsigned IncludesEndOffset = Includes.back().Offset + Includes.back().Text.size(); @@ -1841,11 +1843,12 @@ if (!affectsRange(Ranges, IncludesBeginOffset, IncludesEndOffset)) return; SmallVector Indices; - for (unsigned i = 0, e = Includes.size(); i != e; ++i) + for (unsigned i = 0, e = Includes.size(); i != e; ++i) { Indices.push_back(i); + } llvm::stable_sort(Indices, [&](unsigned LHSI, unsigned RHSI) { - return std::tie(Includes[LHSI].Category, Includes[LHSI].Filename) < - std::tie(Includes[RHSI].Category, Includes[RHSI].Filename); + return std::tie(Includes[LHSI].Priority, Includes[LHSI].Filename) < + std::tie(Includes[RHSI].Priority, Includes[RHSI].Filename); }); // The index of the include on which the cursor will be put after // sorting/deduplicating. @@ -1960,9 +1963,12 @@ int Category = Categories.getIncludePriority( IncludeName, /*CheckMainHeader=*/!MainIncludeFound && FirstIncludeBlock); + int Priority = Categories.getSortIncludePriority( + IncludeName, !MainIncludeFound && FirstIncludeBlock); if (Category == 0) MainIncludeFound = true; - IncludesInBlock.push_back({IncludeName, Line, Prev, Category}); + IncludesInBlock.push_back( + {IncludeName, Line, Prev, Category, Priority}); } else if (!IncludesInBlock.empty() && !EmptyLineSkipped) { sortCppIncludes(Style, IncludesInBlock, Ranges, FileName, Code, Replaces, Cursor); Index: cfe/trunk/lib/Tooling/Inclusions/HeaderIncludes.cpp =================================================================== --- cfe/trunk/lib/Tooling/Inclusions/HeaderIncludes.cpp +++ cfe/trunk/lib/Tooling/Inclusions/HeaderIncludes.cpp @@ -199,6 +199,20 @@ return Ret; } +int IncludeCategoryManager::getSortIncludePriority(StringRef IncludeName, + bool CheckMainHeader) const { + int Ret = INT_MAX; + for (unsigned i = 0, e = CategoryRegexs.size(); i != e; ++i) + if (CategoryRegexs[i].match(IncludeName)) { + Ret = Style.IncludeCategories[i].SortPriority; + if (Ret == 0) + Ret = Style.IncludeCategories[i].Priority; + break; + } + if (CheckMainHeader && IsMainFile && Ret > 0 && isMainHeader(IncludeName)) + Ret = 0; + return Ret; +} bool IncludeCategoryManager::isMainHeader(StringRef IncludeName) const { if (!IncludeName.startswith("\"")) return false; Index: cfe/trunk/lib/Tooling/Inclusions/IncludeStyle.cpp =================================================================== --- cfe/trunk/lib/Tooling/Inclusions/IncludeStyle.cpp +++ cfe/trunk/lib/Tooling/Inclusions/IncludeStyle.cpp @@ -17,6 +17,7 @@ IO &IO, IncludeStyle::IncludeCategory &Category) { IO.mapOptional("Regex", Category.Regex); IO.mapOptional("Priority", Category.Priority); + IO.mapOptional("SortPriority", Category.SortPriority); } void ScalarEnumerationTraits::enumeration( Index: cfe/trunk/unittests/Format/SortIncludesTest.cpp =================================================================== --- cfe/trunk/unittests/Format/SortIncludesTest.cpp +++ cfe/trunk/unittests/Format/SortIncludesTest.cpp @@ -70,6 +70,77 @@ {tooling::Range(25, 1)})); } +TEST_F(SortIncludesTest, SortedIncludesUsingSortPriorityAttribute) { + FmtStyle.IncludeStyle.IncludeBlocks = tooling::IncludeStyle::IBS_Regroup; + FmtStyle.IncludeStyle.IncludeCategories = { + {"^", 1, 0}, + {"^", 1, 1}, + {"^", 8, 10}, + {"^\".*\\.h\"", 10, 12}}; + EXPECT_EQ("#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "\n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "\n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "\n" + "#include \n" + "\n" + "#include \"pathnames.h\"\n", + sort("#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \"pathnames.h\"\n" + "#include \n" + "#include \n" + "#include \n" + "#include \n")); +} +TEST_F(SortIncludesTest, SortPriorityNotDefined) { + FmtStyle = getLLVMStyle(); + EXPECT_EQ("#include \"FormatTestUtils.h\"\n" + "#include \"clang/Format/Format.h\"\n" + "#include \"llvm/ADT/None.h\"\n" + "#include \"llvm/Support/Debug.h\"\n" + "#include \"gtest/gtest.h\"\n", + sort("#include \"clang/Format/Format.h\"\n" + "#include \"llvm/ADT/None.h\"\n" + "#include \"FormatTestUtils.h\"\n" + "#include \"gtest/gtest.h\"\n" + "#include \"llvm/Support/Debug.h\"\n")); +} + TEST_F(SortIncludesTest, NoReplacementsForValidIncludes) { // Identical #includes have led to a failure with an unstable sort. std::string Code = "#include \n"