Skip to content

Commit 672f179

Browse files
committed
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".
1 parent bb21a68 commit 672f179

File tree

5 files changed

+55
-15
lines changed

5 files changed

+55
-15
lines changed

clang/docs/ClangFormatStyleOptions.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4226,6 +4226,11 @@ the configuration (without a prefix: ``Auto``).
42264226
``ClassImpl.hpp`` would not have the main include file put on top
42274227
before any other include.
42284228

4229+
.. _IncludeSortIgnoreExtension:
4230+
4231+
**IncludeSortIgnoreExtension** (``Boolean``) :versionbadge:`clang-format 21` :ref:`<IncludeSortIgnoreExtension>`
4232+
When sorting includes in each block, ignore file extensions.
4233+
42294234
.. _IndentAccessModifiers:
42304235

42314236
**IndentAccessModifiers** (``Boolean``) :versionbadge:`clang-format 13` :ref:`<IndentAccessModifiers>`

clang/include/clang/Format/Format.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5368,6 +5368,8 @@ struct FormatStyle {
53685368
IncludeStyle.IncludeIsMainSourceRegex ==
53695369
R.IncludeStyle.IncludeIsMainSourceRegex &&
53705370
IncludeStyle.MainIncludeChar == R.IncludeStyle.MainIncludeChar &&
5371+
IncludeStyle.IncludeSortIgnoreExtension ==
5372+
R.IncludeStyle.IncludeSortIgnoreExtension &&
53715373
IndentAccessModifiers == R.IndentAccessModifiers &&
53725374
IndentCaseBlocks == R.IndentCaseBlocks &&
53735375
IndentCaseLabels == R.IndentCaseLabels &&

clang/include/clang/Tooling/Inclusions/IncludeStyle.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@ struct IncludeStyle {
152152
/// \version 10
153153
std::string IncludeIsMainSourceRegex;
154154

155+
/// When sorting includes in each block, ignore file extensions.
156+
/// \version 21
157+
bool IncludeSortIgnoreExtension;
158+
155159
/// Character to consider in the include directives for the main header.
156160
enum MainIncludeCharDiscriminator : int8_t {
157161
/// Main include uses quotes: ``#include "foo.hpp"`` (the default).

clang/lib/Format/Format.cpp

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,6 +1062,8 @@ template <> struct MappingTraits<FormatStyle> {
10621062
IO.mapOptional("IncludeIsMainRegex", Style.IncludeStyle.IncludeIsMainRegex);
10631063
IO.mapOptional("IncludeIsMainSourceRegex",
10641064
Style.IncludeStyle.IncludeIsMainSourceRegex);
1065+
IO.mapOptional("IncludeSortIgnoreExtension",
1066+
Style.IncludeStyle.IncludeSortIgnoreExtension);
10651067
IO.mapOptional("IndentAccessModifiers", Style.IndentAccessModifiers);
10661068
IO.mapOptional("IndentCaseBlocks", Style.IndentCaseBlocks);
10671069
IO.mapOptional("IndentCaseLabels", Style.IndentCaseLabels);
@@ -1581,6 +1583,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
15811583
{"^(<|\"(gtest|gmock|isl|json)/)", 3, 0, false},
15821584
{".*", 1, 0, false}};
15831585
LLVMStyle.IncludeStyle.IncludeIsMainRegex = "(Test)?$";
1586+
LLVMStyle.IncludeStyle.IncludeSortIgnoreExtension = false;
15841587
LLVMStyle.IncludeStyle.MainIncludeChar = tooling::IncludeStyle::MICD_Quote;
15851588
LLVMStyle.IndentAccessModifiers = false;
15861589
LLVMStyle.IndentCaseBlocks = false;
@@ -3217,21 +3220,27 @@ static void sortCppIncludes(const FormatStyle &Style,
32173220
SmallVector<unsigned, 16> Indices =
32183221
llvm::to_vector<16>(llvm::seq<unsigned>(0, Includes.size()));
32193222

3220-
if (Style.SortIncludes == FormatStyle::SI_CaseInsensitive) {
3221-
stable_sort(Indices, [&](unsigned LHSI, unsigned RHSI) {
3222-
const auto LHSFilenameLower = Includes[LHSI].Filename.lower();
3223-
const auto RHSFilenameLower = Includes[RHSI].Filename.lower();
3224-
return std::tie(Includes[LHSI].Priority, LHSFilenameLower,
3225-
Includes[LHSI].Filename) <
3226-
std::tie(Includes[RHSI].Priority, RHSFilenameLower,
3227-
Includes[RHSI].Filename);
3228-
});
3229-
} else {
3230-
stable_sort(Indices, [&](unsigned LHSI, unsigned RHSI) {
3231-
return std::tie(Includes[LHSI].Priority, Includes[LHSI].Filename) <
3232-
std::tie(Includes[RHSI].Priority, Includes[RHSI].Filename);
3233-
});
3234-
}
3223+
stable_sort(Indices, [&](unsigned LHSI, unsigned RHSI) {
3224+
SmallString<128> LHSStem, RHSStem;
3225+
if (Style.IncludeStyle.IncludeSortIgnoreExtension) {
3226+
LHSStem = Includes[LHSI].Filename;
3227+
RHSStem = Includes[RHSI].Filename;
3228+
llvm::sys::path::replace_extension(LHSStem, "");
3229+
llvm::sys::path::replace_extension(RHSStem, "");
3230+
}
3231+
std::string LHSStemLower, RHSStemLower;
3232+
std::string LHSFilenameLower, RHSFilenameLower;
3233+
if (Style.SortIncludes == FormatStyle::SI_CaseInsensitive) {
3234+
LHSStemLower = LHSStem.str().lower();
3235+
RHSStemLower = RHSStem.str().lower();
3236+
LHSFilenameLower = Includes[LHSI].Filename.lower();
3237+
RHSFilenameLower = Includes[RHSI].Filename.lower();
3238+
}
3239+
return std::tie(Includes[LHSI].Priority, LHSStemLower, LHSStem,
3240+
LHSFilenameLower, Includes[LHSI].Filename) <
3241+
std::tie(Includes[RHSI].Priority, RHSStemLower, RHSStem,
3242+
RHSFilenameLower, Includes[RHSI].Filename);
3243+
});
32353244

32363245
// The index of the include on which the cursor will be put after
32373246
// sorting/deduplicating.

clang/unittests/Format/SortIncludesTest.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1485,6 +1485,26 @@ TEST_F(SortIncludesTest, BlockCommentedOutIncludes) {
14851485
verifyFormat(Code, sort(Code, "input.cpp", 0));
14861486
}
14871487

1488+
TEST_F(SortIncludesTest, IncludeSortIgnoreExtension) {
1489+
verifyFormat("#include <a-util.h>\n"
1490+
"#include <a.h>\n"
1491+
"#include <a.inc>",
1492+
sort("#include <a.inc>\n"
1493+
"#include <a-util.h>\n"
1494+
"#include <a.h>",
1495+
"input.h", 1));
1496+
1497+
Style.IncludeSortIgnoreExtension = true;
1498+
1499+
verifyFormat("#include <a.h>\n"
1500+
"#include <a.inc>\n"
1501+
"#include <a-util.h>",
1502+
sort("#include <a.inc>\n"
1503+
"#include <a-util.h>\n"
1504+
"#include <a.h>",
1505+
"input.h", 1));
1506+
}
1507+
14881508
} // end namespace
14891509
} // end namespace format
14901510
} // end namespace clang

0 commit comments

Comments
 (0)