Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit d208752

Browse files
committed
[clang-format] Add option to group multiple #include blocks when sorting includes
Summary: This patch allows grouping multiple #include blocks together and sort all includes as one big block. Additionally, sorted includes can be regrouped after sorting based on configured categories. Contributed by @KrzysztofKapusta! Reviewers: krasimir Reviewed By: krasimir Subscribers: cfe-commits, klimek Differential Revision: https://reviews.llvm.org/D40288 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@319024 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 7e7c081 commit d208752

File tree

4 files changed

+281
-3
lines changed

4 files changed

+281
-3
lines changed

docs/ClangFormatStyleOptions.rst

+39
Original file line numberDiff line numberDiff line change
@@ -1173,6 +1173,45 @@ the configuration (without a prefix: ``Auto``).
11731173
11741174
For example: BOOST_FOREACH.
11751175

1176+
**IncludeBlocks** (``IncludeBlocksStyle``)
1177+
Dependent on the value, multiple ``#include`` blocks can be sorted
1178+
as one and divided based on category.
1179+
1180+
Possible values:
1181+
1182+
* ``IBS_Preserve`` (in configuration: ``Preserve``)
1183+
Sort each ``#include`` block separately.
1184+
1185+
.. code-block:: c++
1186+
1187+
#include "b.h" into #include "b.h"
1188+
1189+
#include <lib/main.h> #include "a.h"
1190+
#include "a.h" #include <lib/main.h>
1191+
1192+
* ``IBS_Merge`` (in configuration: ``Merge``)
1193+
Merge multiple ``#include`` blocks together and sort as one.
1194+
1195+
.. code-block:: c++
1196+
1197+
#include "b.h" into #include "a.h"
1198+
#include "b.h"
1199+
#include <lib/main.h> #include <lib/main.h>
1200+
#include "a.h"
1201+
1202+
* ``IBS_Regroup`` (in configuration: ``Regroup``)
1203+
Merge multiple ``#include`` blocks together and sort as one.
1204+
Then split into groups based on category priority. See ``IncludeCategories``.
1205+
1206+
.. code-block:: c++
1207+
1208+
#include "b.h" into #include "a.h"
1209+
#include "b.h"
1210+
#include <lib/main.h>
1211+
#include "a.h" #include <lib/main.h>
1212+
1213+
1214+
11761215
**IncludeCategories** (``std::vector<IncludeCategory>``)
11771216
Regular expressions denoting the different ``#include`` categories
11781217
used for ordering ``#includes``.

include/clang/Format/Format.h

+35
Original file line numberDiff line numberDiff line change
@@ -985,6 +985,40 @@ struct FormatStyle {
985985
/// For example: BOOST_FOREACH.
986986
std::vector<std::string> ForEachMacros;
987987

988+
/// \brief Styles for sorting multiple ``#include`` blocks.
989+
enum IncludeBlocksStyle {
990+
/// \brief Sort each ``#include`` block separately.
991+
/// \code
992+
/// #include "b.h" into #include "b.h"
993+
///
994+
/// #include <lib/main.h> #include "a.h"
995+
/// #include "a.h" #include <lib/main.h>
996+
/// \endcode
997+
IBS_Preserve,
998+
/// \brief Merge multiple ``#include`` blocks together and sort as one.
999+
/// \code
1000+
/// #include "b.h" into #include "a.h"
1001+
/// #include "b.h"
1002+
/// #include <lib/main.h> #include <lib/main.h>
1003+
/// #include "a.h"
1004+
/// \endcode
1005+
IBS_Merge,
1006+
/// \brief Merge multiple ``#include`` blocks together and sort as one.
1007+
/// Then split into groups based on category priority. See
1008+
/// ``IncludeCategories``.
1009+
/// \code
1010+
/// #include "b.h" into #include "a.h"
1011+
/// #include "b.h"
1012+
/// #include <lib/main.h>
1013+
/// #include "a.h" #include <lib/main.h>
1014+
/// \endcode
1015+
IBS_Regroup,
1016+
};
1017+
1018+
/// \brief Dependent on the value, multiple ``#include`` blocks can be sorted
1019+
/// as one and divided based on category.
1020+
IncludeBlocksStyle IncludeBlocks;
1021+
9881022
/// \brief See documentation of ``IncludeCategories``.
9891023
struct IncludeCategory {
9901024
/// \brief The regular expression that this category matches.
@@ -1609,6 +1643,7 @@ struct FormatStyle {
16091643
R.ExperimentalAutoDetectBinPacking &&
16101644
FixNamespaceComments == R.FixNamespaceComments &&
16111645
ForEachMacros == R.ForEachMacros &&
1646+
IncludeBlocks == R.IncludeBlocks &&
16121647
IncludeCategories == R.IncludeCategories &&
16131648
IndentCaseLabels == R.IndentCaseLabels &&
16141649
IndentPPDirectives == R.IndentPPDirectives &&

lib/Format/Format.cpp

+25-3
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,7 @@ template <> struct MappingTraits<FormatStyle> {
361361
Style.ExperimentalAutoDetectBinPacking);
362362
IO.mapOptional("FixNamespaceComments", Style.FixNamespaceComments);
363363
IO.mapOptional("ForEachMacros", Style.ForEachMacros);
364+
IO.mapOptional("IncludeBlocks", Style.IncludeBlocks);
364365
IO.mapOptional("IncludeCategories", Style.IncludeCategories);
365366
IO.mapOptional("IncludeIsMainRegex", Style.IncludeIsMainRegex);
366367
IO.mapOptional("IndentCaseLabels", Style.IndentCaseLabels);
@@ -444,6 +445,14 @@ template <> struct MappingTraits<FormatStyle::IncludeCategory> {
444445
}
445446
};
446447

448+
template <> struct ScalarEnumerationTraits<FormatStyle::IncludeBlocksStyle> {
449+
static void enumeration(IO &IO, FormatStyle::IncludeBlocksStyle &Value) {
450+
IO.enumCase(Value, "Preserve", FormatStyle::IBS_Preserve);
451+
IO.enumCase(Value, "Merge", FormatStyle::IBS_Merge);
452+
IO.enumCase(Value, "Regroup", FormatStyle::IBS_Regroup);
453+
}
454+
};
455+
447456
template <> struct MappingTraits<FormatStyle::RawStringFormat> {
448457
static void mapping(IO &IO, FormatStyle::RawStringFormat &Format) {
449458
IO.mapOptional("Delimiter", Format.Delimiter);
@@ -614,6 +623,7 @@ FormatStyle getLLVMStyle() {
614623
{"^(<|\"(gtest|gmock|isl|json)/)", 3},
615624
{".*", 1}};
616625
LLVMStyle.IncludeIsMainRegex = "(Test)?$";
626+
LLVMStyle.IncludeBlocks = FormatStyle::IBS_Preserve;
617627
LLVMStyle.IndentCaseLabels = false;
618628
LLVMStyle.IndentPPDirectives = FormatStyle::PPDIS_None;
619629
LLVMStyle.IndentWrappedFunctionNames = false;
@@ -1420,19 +1430,27 @@ static void sortCppIncludes(const FormatStyle &Style,
14201430
}),
14211431
Indices.end());
14221432

1433+
int CurrentCategory = Includes.front().Category;
1434+
14231435
// If the #includes are out of order, we generate a single replacement fixing
14241436
// the entire block. Otherwise, no replacement is generated.
14251437
if (Indices.size() == Includes.size() &&
1426-
std::is_sorted(Indices.begin(), Indices.end()))
1438+
std::is_sorted(Indices.begin(), Indices.end()) &&
1439+
Style.IncludeBlocks == FormatStyle::IBS_Preserve)
14271440
return;
14281441

14291442
std::string result;
14301443
for (unsigned Index : Indices) {
1431-
if (!result.empty())
1444+
if (!result.empty()) {
14321445
result += "\n";
1446+
if (Style.IncludeBlocks == FormatStyle::IBS_Regroup &&
1447+
CurrentCategory != Includes[Index].Category)
1448+
result += "\n";
1449+
}
14331450
result += Includes[Index].Text;
14341451
if (Cursor && CursorIndex == Index)
14351452
*Cursor = IncludesBeginOffset + result.size() - CursorToEOLOffset;
1453+
CurrentCategory = Includes[Index].Category;
14361454
}
14371455

14381456
auto Err = Replaces.add(tooling::Replacement(
@@ -1540,6 +1558,10 @@ tooling::Replacements sortCppIncludes(const FormatStyle &Style, StringRef Code,
15401558
else if (Trimmed == "// clang-format on")
15411559
FormattingOff = false;
15421560

1561+
const bool EmptyLineSkipped =
1562+
Trimmed.empty() && (Style.IncludeBlocks == FormatStyle::IBS_Merge ||
1563+
Style.IncludeBlocks == FormatStyle::IBS_Regroup);
1564+
15431565
if (!FormattingOff && !Line.endswith("\\")) {
15441566
if (IncludeRegex.match(Line, &Matches)) {
15451567
StringRef IncludeName = Matches[2];
@@ -1549,7 +1571,7 @@ tooling::Replacements sortCppIncludes(const FormatStyle &Style, StringRef Code,
15491571
if (Category == 0)
15501572
MainIncludeFound = true;
15511573
IncludesInBlock.push_back({IncludeName, Line, Prev, Category});
1552-
} else if (!IncludesInBlock.empty()) {
1574+
} else if (!IncludesInBlock.empty() && !EmptyLineSkipped) {
15531575
sortCppIncludes(Style, IncludesInBlock, Ranges, FileName, Replaces,
15541576
Cursor);
15551577
IncludesInBlock.clear();

0 commit comments

Comments
 (0)