https://github.com/DaanDeMeyer updated https://github.com/llvm/llvm-project/pull/137840
>From 73a18a033e0d978d1662a391fedf731dbad7de27 Mon Sep 17 00:00:00 2001 From: Daan De Meyer <daan.j.deme...@gmail.com> Date: Tue, 29 Apr 2025 18:26:36 +0200 Subject: [PATCH] clang-format: Add IncludeSortKey option Sorting by stem gives nicer results when various header file names are substrings of other header file names, for example, a CLI application with a main header named analyze.h and a analyze-xxx.h header for each subcommand currently will always put analyze.h last after all the analyze-xxx.h headers, but putting analyze.h first instead of last is arguable nicer to read. TLDR; Instead of """ /#include "analyze-blame.h" /#include "analyze.h" """ You'd get """ /#include "analyze.h" /#include "analyze-blame.h" """ Let's allow sorting by stem instead of full path by introducing a new option IncludeSortKey with two values "Path" and "Stem". --- clang/docs/ClangFormatStyleOptions.rst | 13 +++++++++ clang/include/clang/Format/Format.h | 1 + .../clang/Tooling/Inclusions/IncludeStyle.h | 18 ++++++++++++ clang/lib/Format/Format.cpp | 29 ++++++++++++++----- clang/lib/Tooling/Inclusions/IncludeStyle.cpp | 6 ++++ clang/unittests/Format/SortIncludesTest.cpp | 16 ++++++++++ 6 files changed, 75 insertions(+), 8 deletions(-) diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 3f8a5f49313b2..248d489b1a878 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -4226,6 +4226,19 @@ the configuration (without a prefix: ``Auto``). ``ClassImpl.hpp`` would not have the main include file put on top before any other include. +.. _IncludeSortKey: + +**IncludeSortKey** (``IncludeSortKeyDiscriminator``) :versionbadge:`clang-format 20` :ref:`¶ <IncludeSortKey>` + When sorting includes in each block, sort by the specified sort key + + Possible values: + + * ``ISK_Path`` (in configuration: ``Path``) + Includes are sorted by their full path (the default). + + * ``ISK_Stem`` (in configuration: ``AngleBracket``) + Includes are sorted by their full path without the file extension. + .. _IndentAccessModifiers: **IndentAccessModifiers** (``Boolean``) :versionbadge:`clang-format 13` :ref:`¶ <IndentAccessModifiers>` diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index f6ceef08b46da..23ce34b560198 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -5368,6 +5368,7 @@ struct FormatStyle { IncludeStyle.IncludeIsMainSourceRegex == R.IncludeStyle.IncludeIsMainSourceRegex && IncludeStyle.MainIncludeChar == R.IncludeStyle.MainIncludeChar && + IncludeStyle.IncludeSortKey == R.IncludeStyle.IncludeSortKey && IndentAccessModifiers == R.IndentAccessModifiers && IndentCaseBlocks == R.IndentCaseBlocks && IndentCaseLabels == R.IndentCaseLabels && diff --git a/clang/include/clang/Tooling/Inclusions/IncludeStyle.h b/clang/include/clang/Tooling/Inclusions/IncludeStyle.h index fba90d8c51a66..6699a667c6daa 100644 --- a/clang/include/clang/Tooling/Inclusions/IncludeStyle.h +++ b/clang/include/clang/Tooling/Inclusions/IncludeStyle.h @@ -166,6 +166,16 @@ struct IncludeStyle { /// directives that use the specified character are considered. /// \version 19 MainIncludeCharDiscriminator MainIncludeChar; + + enum IncludeSortKeyDiscriminator : int8_t { + /// Includes are sorted alphabetically by their full path. + ISK_Path, + /// Includes are sorted alphabetically by their full path without file + /// extension. + ISK_Stem, + }; + + IncludeSortKeyDiscriminator IncludeSortKey; }; } // namespace tooling @@ -197,6 +207,14 @@ struct ScalarEnumerationTraits< clang::tooling::IncludeStyle::MainIncludeCharDiscriminator &Value); }; +template <> +struct ScalarEnumerationTraits< + clang::tooling::IncludeStyle::IncludeSortKeyDiscriminator> { + static void + enumeration(IO &IO, + clang::tooling::IncludeStyle::IncludeSortKeyDiscriminator &Value); +}; + } // namespace yaml } // namespace llvm diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 5a1c3f556b331..c2aa44e4d1f25 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -1062,6 +1062,7 @@ template <> struct MappingTraits<FormatStyle> { IO.mapOptional("IncludeIsMainRegex", Style.IncludeStyle.IncludeIsMainRegex); IO.mapOptional("IncludeIsMainSourceRegex", Style.IncludeStyle.IncludeIsMainSourceRegex); + IO.mapOptional("IncludeSortKey", Style.IncludeStyle.IncludeSortKey); IO.mapOptional("IndentAccessModifiers", Style.IndentAccessModifiers); IO.mapOptional("IndentCaseBlocks", Style.IndentCaseBlocks); IO.mapOptional("IndentCaseLabels", Style.IndentCaseLabels); @@ -3219,17 +3220,29 @@ static void sortCppIncludes(const FormatStyle &Style, if (Style.SortIncludes == FormatStyle::SI_CaseInsensitive) { stable_sort(Indices, [&](unsigned LHSI, unsigned RHSI) { - const auto LHSFilenameLower = Includes[LHSI].Filename.lower(); - const auto RHSFilenameLower = Includes[RHSI].Filename.lower(); - return std::tie(Includes[LHSI].Priority, LHSFilenameLower, - Includes[LHSI].Filename) < - std::tie(Includes[RHSI].Priority, RHSFilenameLower, - Includes[RHSI].Filename); + SmallString<128> LHSStem = Includes[LHSI].Filename; + SmallString<128> RHSStem = Includes[RHSI].Filename; + if (Style.IncludeStyle.IncludeSortKey == + tooling::IncludeStyle::ISK_Stem) { + llvm::sys::path::replace_extension(LHSStem, ""); + llvm::sys::path::replace_extension(RHSStem, ""); + } + const auto LHSStemLower = LHSStem.str().lower(); + const auto RHSStemLower = RHSStem.str().lower(); + return std::tie(Includes[LHSI].Priority, LHSStemLower, LHSStem) < + std::tie(Includes[RHSI].Priority, RHSStemLower, RHSStem); }); } else { stable_sort(Indices, [&](unsigned LHSI, unsigned RHSI) { - return std::tie(Includes[LHSI].Priority, Includes[LHSI].Filename) < - std::tie(Includes[RHSI].Priority, Includes[RHSI].Filename); + SmallString<128> LHSStem = Includes[LHSI].Filename; + SmallString<128> RHSStem = Includes[RHSI].Filename; + if (Style.IncludeStyle.IncludeSortKey == + tooling::IncludeStyle::ISK_Stem) { + llvm::sys::path::replace_extension(LHSStem, ""); + llvm::sys::path::replace_extension(RHSStem, ""); + } + return std::tie(Includes[LHSI].Priority, LHSStem) < + std::tie(Includes[RHSI].Priority, RHSStem); }); } diff --git a/clang/lib/Tooling/Inclusions/IncludeStyle.cpp b/clang/lib/Tooling/Inclusions/IncludeStyle.cpp index 05dfb50589de0..98852ed3aa8fa 100644 --- a/clang/lib/Tooling/Inclusions/IncludeStyle.cpp +++ b/clang/lib/Tooling/Inclusions/IncludeStyle.cpp @@ -35,5 +35,11 @@ void ScalarEnumerationTraits<IncludeStyle::MainIncludeCharDiscriminator>:: IO.enumCase(Value, "Any", IncludeStyle::MICD_Any); } +void ScalarEnumerationTraits<IncludeStyle::IncludeSortKeyDiscriminator>:: + enumeration(IO &IO, IncludeStyle::IncludeSortKeyDiscriminator &Value) { + IO.enumCase(Value, "Path", IncludeStyle::ISK_Path); + IO.enumCase(Value, "Stem", IncludeStyle::ISK_Stem); +} + } // namespace yaml } // namespace llvm diff --git a/clang/unittests/Format/SortIncludesTest.cpp b/clang/unittests/Format/SortIncludesTest.cpp index f20862f5f7461..a7f4256b30e1b 100644 --- a/clang/unittests/Format/SortIncludesTest.cpp +++ b/clang/unittests/Format/SortIncludesTest.cpp @@ -1485,6 +1485,22 @@ TEST_F(SortIncludesTest, BlockCommentedOutIncludes) { verifyFormat(Code, sort(Code, "input.cpp", 0)); } +TEST_F(SortIncludesTest, IncludeSortKey) { + verifyFormat("#include <a-util.h>\n" + "#include <a.h>", + sort("#include <a-util.h>\n" + "#include <a.h>", + "input.h", 0)); + + Style.IncludeSortKey = tooling::IncludeStyle::ISK_Stem; + + verifyFormat("#include <a.h>\n" + "#include <a-util.h>", + sort("#include <a-util.h>\n" + "#include <a.h>", + "input.h", 1)); +} + } // end namespace } // end namespace format } // end namespace clang _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits