Skip to content

Commit

Permalink
[clang-format] Add option to group multiple #include blocks when sort…
Browse files Browse the repository at this point in the history
…ing 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

llvm-svn: 319024
  • Loading branch information
krasimirgg committed Nov 27, 2017
1 parent 6e39e68 commit 4c2c9c3
Show file tree
Hide file tree
Showing 4 changed files with 281 additions and 3 deletions.
39 changes: 39 additions & 0 deletions clang/docs/ClangFormatStyleOptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1173,6 +1173,45 @@ the configuration (without a prefix: ``Auto``).
For example: BOOST_FOREACH.

**IncludeBlocks** (``IncludeBlocksStyle``)
Dependent on the value, multiple ``#include`` blocks can be sorted
as one and divided based on category.

Possible values:

* ``IBS_Preserve`` (in configuration: ``Preserve``)
Sort each ``#include`` block separately.

.. code-block:: c++

#include "b.h" into #include "b.h"

#include <lib/main.h> #include "a.h"
#include "a.h" #include <lib/main.h>

* ``IBS_Merge`` (in configuration: ``Merge``)
Merge multiple ``#include`` blocks together and sort as one.

.. code-block:: c++

#include "b.h" into #include "a.h"
#include "b.h"
#include <lib/main.h> #include <lib/main.h>
#include "a.h"

* ``IBS_Regroup`` (in configuration: ``Regroup``)
Merge multiple ``#include`` blocks together and sort as one.
Then split into groups based on category priority. See ``IncludeCategories``.

.. code-block:: c++

#include "b.h" into #include "a.h"
#include "b.h"
#include <lib/main.h>
#include "a.h" #include <lib/main.h>



**IncludeCategories** (``std::vector<IncludeCategory>``)
Regular expressions denoting the different ``#include`` categories
used for ordering ``#includes``.
Expand Down
35 changes: 35 additions & 0 deletions clang/include/clang/Format/Format.h
Original file line number Diff line number Diff line change
Expand Up @@ -985,6 +985,40 @@ struct FormatStyle {
/// For example: BOOST_FOREACH.
std::vector<std::string> ForEachMacros;

/// \brief Styles for sorting multiple ``#include`` blocks.
enum IncludeBlocksStyle {
/// \brief Sort each ``#include`` block separately.
/// \code
/// #include "b.h" into #include "b.h"
///
/// #include <lib/main.h> #include "a.h"
/// #include "a.h" #include <lib/main.h>
/// \endcode
IBS_Preserve,
/// \brief Merge multiple ``#include`` blocks together and sort as one.
/// \code
/// #include "b.h" into #include "a.h"
/// #include "b.h"
/// #include <lib/main.h> #include <lib/main.h>
/// #include "a.h"
/// \endcode
IBS_Merge,
/// \brief Merge multiple ``#include`` blocks together and sort as one.
/// Then split into groups based on category priority. See
/// ``IncludeCategories``.
/// \code
/// #include "b.h" into #include "a.h"
/// #include "b.h"
/// #include <lib/main.h>
/// #include "a.h" #include <lib/main.h>
/// \endcode
IBS_Regroup,
};

/// \brief Dependent on the value, multiple ``#include`` blocks can be sorted
/// as one and divided based on category.
IncludeBlocksStyle IncludeBlocks;

/// \brief See documentation of ``IncludeCategories``.
struct IncludeCategory {
/// \brief The regular expression that this category matches.
Expand Down Expand Up @@ -1609,6 +1643,7 @@ struct FormatStyle {
R.ExperimentalAutoDetectBinPacking &&
FixNamespaceComments == R.FixNamespaceComments &&
ForEachMacros == R.ForEachMacros &&
IncludeBlocks == R.IncludeBlocks &&
IncludeCategories == R.IncludeCategories &&
IndentCaseLabels == R.IndentCaseLabels &&
IndentPPDirectives == R.IndentPPDirectives &&
Expand Down
28 changes: 25 additions & 3 deletions clang/lib/Format/Format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,7 @@ template <> struct MappingTraits<FormatStyle> {
Style.ExperimentalAutoDetectBinPacking);
IO.mapOptional("FixNamespaceComments", Style.FixNamespaceComments);
IO.mapOptional("ForEachMacros", Style.ForEachMacros);
IO.mapOptional("IncludeBlocks", Style.IncludeBlocks);
IO.mapOptional("IncludeCategories", Style.IncludeCategories);
IO.mapOptional("IncludeIsMainRegex", Style.IncludeIsMainRegex);
IO.mapOptional("IndentCaseLabels", Style.IndentCaseLabels);
Expand Down Expand Up @@ -444,6 +445,14 @@ template <> struct MappingTraits<FormatStyle::IncludeCategory> {
}
};

template <> struct ScalarEnumerationTraits<FormatStyle::IncludeBlocksStyle> {
static void enumeration(IO &IO, FormatStyle::IncludeBlocksStyle &Value) {
IO.enumCase(Value, "Preserve", FormatStyle::IBS_Preserve);
IO.enumCase(Value, "Merge", FormatStyle::IBS_Merge);
IO.enumCase(Value, "Regroup", FormatStyle::IBS_Regroup);
}
};

template <> struct MappingTraits<FormatStyle::RawStringFormat> {
static void mapping(IO &IO, FormatStyle::RawStringFormat &Format) {
IO.mapOptional("Delimiter", Format.Delimiter);
Expand Down Expand Up @@ -614,6 +623,7 @@ FormatStyle getLLVMStyle() {
{"^(<|\"(gtest|gmock|isl|json)/)", 3},
{".*", 1}};
LLVMStyle.IncludeIsMainRegex = "(Test)?$";
LLVMStyle.IncludeBlocks = FormatStyle::IBS_Preserve;
LLVMStyle.IndentCaseLabels = false;
LLVMStyle.IndentPPDirectives = FormatStyle::PPDIS_None;
LLVMStyle.IndentWrappedFunctionNames = false;
Expand Down Expand Up @@ -1420,19 +1430,27 @@ static void sortCppIncludes(const FormatStyle &Style,
}),
Indices.end());

int CurrentCategory = Includes.front().Category;

// If the #includes are out of order, we generate a single replacement fixing
// the entire block. Otherwise, no replacement is generated.
if (Indices.size() == Includes.size() &&
std::is_sorted(Indices.begin(), Indices.end()))
std::is_sorted(Indices.begin(), Indices.end()) &&
Style.IncludeBlocks == FormatStyle::IBS_Preserve)
return;

std::string result;
for (unsigned Index : Indices) {
if (!result.empty())
if (!result.empty()) {
result += "\n";
if (Style.IncludeBlocks == FormatStyle::IBS_Regroup &&
CurrentCategory != Includes[Index].Category)
result += "\n";
}
result += Includes[Index].Text;
if (Cursor && CursorIndex == Index)
*Cursor = IncludesBeginOffset + result.size() - CursorToEOLOffset;
CurrentCategory = Includes[Index].Category;
}

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

const bool EmptyLineSkipped =
Trimmed.empty() && (Style.IncludeBlocks == FormatStyle::IBS_Merge ||
Style.IncludeBlocks == FormatStyle::IBS_Regroup);

if (!FormattingOff && !Line.endswith("\\")) {
if (IncludeRegex.match(Line, &Matches)) {
StringRef IncludeName = Matches[2];
Expand All @@ -1549,7 +1571,7 @@ tooling::Replacements sortCppIncludes(const FormatStyle &Style, StringRef Code,
if (Category == 0)
MainIncludeFound = true;
IncludesInBlock.push_back({IncludeName, Line, Prev, Category});
} else if (!IncludesInBlock.empty()) {
} else if (!IncludesInBlock.empty() && !EmptyLineSkipped) {
sortCppIncludes(Style, IncludesInBlock, Ranges, FileName, Replaces,
Cursor);
IncludesInBlock.clear();
Expand Down
Loading

0 comments on commit 4c2c9c3

Please sign in to comment.