Skip to content

Commit 20939dd

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 20939dd

File tree

6 files changed

+90
-6
lines changed

6 files changed

+90
-6
lines changed

clang/docs/ClangFormatStyleOptions.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4226,6 +4226,22 @@ 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+
.. _IncludeSortKey:
4230+
4231+
**IncludeSortKey** (``IncludeSortKeyDiscriminator``) :versionbadge:`clang-format 21` :ref:`<IncludeSortKey>`
4232+
When sorting includes in each block, sort by the specified sort key.
4233+
4234+
Possible values:
4235+
4236+
* ``ISK_Path`` (in configuration: ``Path``)
4237+
Includes are sorted alphabetically by their full path.
4238+
4239+
* ``ISK_Stem`` (in configuration: ``Stem``)
4240+
Includes are sorted alphabetically by their full path without the
4241+
file extension.
4242+
4243+
4244+
42294245
.. _IndentAccessModifiers:
42304246

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

clang/include/clang/Format/Format.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5368,6 +5368,7 @@ struct FormatStyle {
53685368
IncludeStyle.IncludeIsMainSourceRegex ==
53695369
R.IncludeStyle.IncludeIsMainSourceRegex &&
53705370
IncludeStyle.MainIncludeChar == R.IncludeStyle.MainIncludeChar &&
5371+
IncludeStyle.IncludeSortKey == R.IncludeStyle.IncludeSortKey &&
53715372
IndentAccessModifiers == R.IndentAccessModifiers &&
53725373
IndentCaseBlocks == R.IndentCaseBlocks &&
53735374
IndentCaseLabels == R.IndentCaseLabels &&

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

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

155+
/// Available sort keys that can be used to sort includes in each block.
156+
enum IncludeSortKeyDiscriminator : int8_t {
157+
/// Includes are sorted alphabetically by their full path.
158+
ISK_Path,
159+
/// Includes are sorted alphabetically by their full path without the
160+
/// file extension.
161+
ISK_Stem,
162+
};
163+
164+
/// When sorting includes in each block, sort by the specified sort key.
165+
/// \version 21
166+
IncludeSortKeyDiscriminator IncludeSortKey;
167+
155168
/// Character to consider in the include directives for the main header.
156169
enum MainIncludeCharDiscriminator : int8_t {
157170
/// Main include uses quotes: ``#include "foo.hpp"`` (the default).
@@ -197,6 +210,14 @@ struct ScalarEnumerationTraits<
197210
clang::tooling::IncludeStyle::MainIncludeCharDiscriminator &Value);
198211
};
199212

213+
template <>
214+
struct ScalarEnumerationTraits<
215+
clang::tooling::IncludeStyle::IncludeSortKeyDiscriminator> {
216+
static void
217+
enumeration(IO &IO,
218+
clang::tooling::IncludeStyle::IncludeSortKeyDiscriminator &Value);
219+
};
220+
200221
} // namespace yaml
201222
} // namespace llvm
202223

clang/lib/Format/Format.cpp

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,6 +1062,7 @@ template <> struct MappingTraits<FormatStyle> {
10621062
IO.mapOptional("IncludeIsMainRegex", Style.IncludeStyle.IncludeIsMainRegex);
10631063
IO.mapOptional("IncludeIsMainSourceRegex",
10641064
Style.IncludeStyle.IncludeIsMainSourceRegex);
1065+
IO.mapOptional("IncludeSortKey", Style.IncludeStyle.IncludeSortKey);
10651066
IO.mapOptional("IndentAccessModifiers", Style.IndentAccessModifiers);
10661067
IO.mapOptional("IndentCaseBlocks", Style.IndentCaseBlocks);
10671068
IO.mapOptional("IndentCaseLabels", Style.IndentCaseLabels);
@@ -1581,6 +1582,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
15811582
{"^(<|\"(gtest|gmock|isl|json)/)", 3, 0, false},
15821583
{".*", 1, 0, false}};
15831584
LLVMStyle.IncludeStyle.IncludeIsMainRegex = "(Test)?$";
1585+
LLVMStyle.IncludeStyle.IncludeSortKey = tooling::IncludeStyle::ISK_Path;
15841586
LLVMStyle.IncludeStyle.MainIncludeChar = tooling::IncludeStyle::MICD_Quote;
15851587
LLVMStyle.IndentAccessModifiers = false;
15861588
LLVMStyle.IndentCaseBlocks = false;
@@ -3221,15 +3223,33 @@ static void sortCppIncludes(const FormatStyle &Style,
32213223
stable_sort(Indices, [&](unsigned LHSI, unsigned RHSI) {
32223224
const auto LHSFilenameLower = Includes[LHSI].Filename.lower();
32233225
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);
3226+
SmallString<128> LHSStem = Includes[LHSI].Filename;
3227+
SmallString<128> RHSStem = Includes[RHSI].Filename;
3228+
if (Style.IncludeStyle.IncludeSortKey ==
3229+
tooling::IncludeStyle::ISK_Stem) {
3230+
llvm::sys::path::replace_extension(LHSStem, "");
3231+
llvm::sys::path::replace_extension(RHSStem, "");
3232+
}
3233+
const auto LHSStemLower = LHSStem.str().lower();
3234+
const auto RHSStemLower = RHSStem.str().lower();
3235+
return std::tie(Includes[LHSI].Priority, LHSStemLower, LHSStem,
3236+
LHSFilenameLower, Includes[LHSI].Filename) <
3237+
std::tie(Includes[RHSI].Priority, RHSStemLower, RHSStem,
3238+
RHSFilenameLower, Includes[RHSI].Filename);
32283239
});
32293240
} else {
32303241
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);
3242+
SmallString<128> LHSStem = Includes[LHSI].Filename;
3243+
SmallString<128> RHSStem = Includes[RHSI].Filename;
3244+
if (Style.IncludeStyle.IncludeSortKey ==
3245+
tooling::IncludeStyle::ISK_Stem) {
3246+
llvm::sys::path::replace_extension(LHSStem, "");
3247+
llvm::sys::path::replace_extension(RHSStem, "");
3248+
}
3249+
return std::tie(Includes[LHSI].Priority, LHSStem,
3250+
Includes[LHSI].Filename) <
3251+
std::tie(Includes[RHSI].Priority, RHSStem,
3252+
Includes[RHSI].Filename);
32333253
});
32343254
}
32353255

clang/lib/Tooling/Inclusions/IncludeStyle.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,11 @@ void ScalarEnumerationTraits<IncludeStyle::MainIncludeCharDiscriminator>::
3535
IO.enumCase(Value, "Any", IncludeStyle::MICD_Any);
3636
}
3737

38+
void ScalarEnumerationTraits<IncludeStyle::IncludeSortKeyDiscriminator>::
39+
enumeration(IO &IO, IncludeStyle::IncludeSortKeyDiscriminator &Value) {
40+
IO.enumCase(Value, "Path", IncludeStyle::ISK_Path);
41+
IO.enumCase(Value, "Stem", IncludeStyle::ISK_Stem);
42+
}
43+
3844
} // namespace yaml
3945
} // namespace llvm

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, IncludeSortKey) {
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.IncludeSortKey = tooling::IncludeStyle::ISK_Stem;
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)