Issue 183802
Summary [clang-format] Feature request: Configurable empty line count between include categories
Labels clang-format
Assignees
Reporter erbsland-dev
    ## Summary

`clang-format` currently allows grouping and sorting of includes via `IncludeBlocks` and `IncludeCategories`, but the number of blank lines between include categories is fixed (effectively one).

This proposal suggests extending `IncludeCategories` with a per-category option to control the number of blank lines inserted between include blocks after regrouping.

This would enable fully automated formatting for codebases that intentionally use more than one blank line between certain include groups.

## Motivation

When enforcing fully automated formatting in large C++ codebases, include layout is often used to encode structural or semantic intent. Two common patterns cannot currently be expressed with `clang-format`:

### 1. Strong separation of the “main” header

Many projects place the corresponding header (e.g., `Main.hpp`) at the top of the `.cpp` file and visually separate it from all other includes using *two* blank lines.

Example:

```cpp
// Copyright header
#include "Main.hpp"


#include "ApplicationHeader.hpp"

#include <library_header1.hpp>
#include <library_header2.hpp>
#include <library_header3.hpp>

#include <std_header>

namespace foo {
```

The extra spacing emphasizes that `Main.hpp` defines the public interface being implemented, while the remaining includes are implementation details.

Currently, `clang-format` can group and order these includes, but cannot preserve or enforce two blank lines between selected categories.

### 2. Isolating problematic or special-case includes

Some includes are intentionally separated to highlight ordering constraints or side effects (e.g., platform headers, legacy headers, or includes with macro pollution).

Example:

```cpp
// Copyright header
#include "Main.hpp"


#include "RegularInclude1.hpp"
#include "RegularInclude2.hpp"
#include "RegularInclude3.hpp"


#include <Windows.h>
#include "Data.cpp"

namespace {
```

Here, the final group is intentionally separated by two blank lines to make its special role visually obvious.

Again, this layout cannot currently be expressed declaratively in `.clang-format`.

## Current Limitation

With:

```yaml
IncludeBlocks: Regroup
IncludeCategories:
  - Regex: ...
    Priority: ...
```

`clang-format`:
- Sorts and regroups includes correctly.
- Inserts a single blank line between distinct include categories.
- Does not allow customizing the number of blank lines per category boundary.

As a result, projects must either:
- Accept a style compromise, or
- Maintain manual edits that conflict with automated formatting.

## Proposed Extension

Extend `IncludeCategories` with an optional attribute:

```yaml
EmptyLines: <unsigned integer>
```

### Semantics

- `EmptyLines` specifies the number of blank lines inserted **before this category**, when it is separated from the previous category during regrouping.
- If omitted, the default behavior remains unchanged (currently one blank line).
- `EmptyLines: 0` would allow explicitly suppressing separation for a category.

This preserves full backward compatibility.

## Example

```yaml
IncludeBlocks: Regroup
IncludeCategories:
  - Regex: '^"[^.][^/]+"'
    Priority: 1
    EmptyLines: 2

  - Regex: '^"[^./]+/.+"'
    Priority: 2

  - Regex: '^"\.\./[^.].+"'
    Priority: 3

  - Regex: '^"\.\./\.\./.+"'
    Priority: 4

  - Regex: '^<.*\.h(pp)?>'
    Priority: 5

  - Regex: '^<[^.]+>'
    Priority: 6

  - Regex: '.*'
    Priority: 7
    EmptyLines: 2
```

This configuration would:

- Insert two blank lines before the first category (if it is not at file start).
- Insert one blank line between unspecified categories.
- Insert two blank lines before the final catch-all category.

## Design Considerations

- The option fits naturally into `IncludeCategories`, as spacing is conceptually part of category boundaries.
- It does not interfere with sorting or regrouping logic.
- It preserves current defaults when not used.
- It generalizes cleanly without introducing special-case handling for “main header” or platform headers.

_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to