Repository: FriendsOfPHP/PHP-CS-Fixer (php)

Issue:
Empty line between import groups
Hello,

Redorder of the `use` statements in the header block seems not to take into account the "use function ..." and it order it like other use.

After using `@PhpCsFixer` set, it end up with:
```
use FATBundle\Service;
use function GuzzleHttp\json_encode;
use Pimcore\Controller\FrontendController;
```

Should be:
```
use FATBundle\Service;
use Pimcore\Controller\FrontendController;
use function GuzzleHttp\json_encode;
```

It would be great for lisibility !

---

If we go further, I think it should be spaced, for better visibility:
```
use FATBundle\Service;
use Pimcore\Controller\FrontendController;

use function GuzzleHttp\json_encode;
```

_I know you are working on PSR12 rules, so it would allow PSR12 compliance (i run this tool to check: https://github.com/squizlabs/PHP_CodeSniffer/blob/master/src/Standards/PSR12/Sniffs/Files/FileHeaderSniff.php#L357 , aka rule `Header blocks must be separated by a single blank line`). Please see https://www.php-fig.org/psr/psr-12/#3-declare-statements-namespace-and-import-statements. But PSR12 is another subject, but as this issue is about talking use statement I mention it._

I think `use function...` (and `use const...`) should be ordered at end and space by default.

Best regards.

Pull request description:
feature: BlankLineBetweenImportGroupsFixer - Introduction
Closes #4821 

I've also updated the PSR12 set to include this rule, to be compliant with the [PSR12](https://www.php-fig.org/psr/psr-12/#3-declare-statements-namespace-and-import-statements).

Patch:
diff --git a/doc/list.rst b/doc/list.rst
index 7952d28ed..51448dfab 100644
--- a/doc/list.rst
+++ b/doc/list.rst
@@ -113,6 +113,13 @@ List of Available Rules
    Part of rule sets `@PhpCsFixer <./ruleSets/PhpCsFixer.rst>`_ `@Symfony <./ruleSets/Symfony.rst>`_
 
    `Source PhpCsFixer\\Fixer\\Whitespace\\BlankLineBeforeStatementFixer <./../src/Fixer/Whitespace/BlankLineBeforeStatementFixer.php>`_
+-  `blank_line_between_import_groups <./rules/whitespace/blank_line_between_import_groups.rst>`_
+
+   Putting blank lines between ``use`` statement groups.
+
+   Part of rule sets `@PSR12 <./ruleSets/PSR12.rst>`_ `@PhpCsFixer <./ruleSets/PhpCsFixer.rst>`_ `@Symfony <./ruleSets/Symfony.rst>`_
+
+   `Source PhpCsFixer\\Fixer\\Whitespace\\BlankLineBetweenImportGroupsFixer <./../src/Fixer/Whitespace/BlankLineBetweenImportGroupsFixer.php>`_
 -  `braces <./rules/basic/braces.rst>`_
 
    The body of each structure MUST be enclosed by braces. Braces should be properly placed. Body of braces should be properly indented.
diff --git a/doc/ruleSets/PSR12.rst b/doc/ruleSets/PSR12.rst
index a8a6722c2..f4a138add 100644
--- a/doc/ruleSets/PSR12.rst
+++ b/doc/ruleSets/PSR12.rst
@@ -9,6 +9,7 @@ Rules
 
 - `@PSR2 <./PSR2.rst>`_
 - `blank_line_after_opening_tag <./../rules/php_tag/blank_line_after_opening_tag.rst>`_
+- `blank_line_between_import_groups <./../rules/whitespace/blank_line_between_import_groups.rst>`_
 - `braces <./../rules/basic/braces.rst>`_
   config:
   ``['allow_single_line_anonymous_class_with_empty_body' => true]``
diff --git a/doc/rules/index.rst b/doc/rules/index.rst
index 24e214648..f6d98b399 100644
--- a/doc/rules/index.rst
+++ b/doc/rules/index.rst
@@ -807,6 +807,9 @@ Whitespace
 - `blank_line_before_statement <./whitespace/blank_line_before_statement.rst>`_
 
   An empty line feed must precede any configured statement.
+- `blank_line_between_import_groups <./whitespace/blank_line_between_import_groups.rst>`_
+
+  Putting blank lines between ``use`` statement groups.
 - `compact_nullable_typehint <./whitespace/compact_nullable_typehint.rst>`_
 
   Remove extra spaces in a nullable typehint.
diff --git a/doc/rules/whitespace/blank_line_between_import_groups.rst b/doc/rules/whitespace/blank_line_between_import_groups.rst
new file mode 100644
index 000000000..636407bcd
--- /dev/null
+++ b/doc/rules/whitespace/blank_line_between_import_groups.rst
@@ -0,0 +1,94 @@
+=========================================
+Rule ``blank_line_between_import_groups``
+=========================================
+
+Putting blank lines between ``use`` statement groups.
+
+Examples
+--------
+
+Example #1
+~~~~~~~~~~
+
+.. code-block:: diff
+
+   --- Original
+   +++ New
+    <?php
+
+    use function AAC;
+   +
+    use const AAB;
+   +
+    use AAA;
+
+Example #2
+~~~~~~~~~~
+
+.. code-block:: diff
+
+   --- Original
+   +++ New
+    <?php
+    use const AAAA;
+    use const BBB;
+   +
+    use Bar;
+    use AAC;
+    use Acme;
+   +
+    use function CCC\AA;
+    use function DDD;
+
+Example #3
+~~~~~~~~~~
+
+.. code-block:: diff
+
+   --- Original
+   +++ New
+    <?php
+    use const BBB;
+    use const AAAA;
+   +
+    use Acme;
+    use AAC;
+    use Bar;
+   +
+    use function DDD;
+    use function CCC\AA;
+
+Example #4
+~~~~~~~~~~
+
+.. code-block:: diff
+
+   --- Original
+   +++ New
+    <?php
+    use const AAAA;
+    use const BBB;
+   +
+    use Acme;
+   +
+    use function DDD;
+   +
+    use AAC;
+   +
+    use function CCC\AA;
+   +
+    use Bar;
+
+Rule sets
+---------
+
+The rule is part of the following rule sets:
+
+@PSR12
+  Using the `@PSR12 <./../../ruleSets/PSR12.rst>`_ rule set will enable the ``blank_line_between_import_groups`` rule.
+
+@PhpCsFixer
+  Using the `@PhpCsFixer <./../../ruleSets/PhpCsFixer.rst>`_ rule set will enable the ``blank_line_between_import_groups`` rule.
+
+@Symfony
+  Using the `@Symfony <./../../ruleSets/Symfony.rst>`_ rule set will enable the ``blank_line_between_import_groups`` rule.
diff --git a/src/Fixer/Import/OrderedImportsFixer.php b/src/Fixer/Import/OrderedImportsFixer.php
index b17e50daf..f0408d4b9 100644
--- a/src/Fixer/Import/OrderedImportsFixer.php
+++ b/src/Fixer/Import/OrderedImportsFixer.php
@@ -172,6 +172,7 @@ public function getDefinition(): FixerDefinitionInterface
     /**
      * {@inheritdoc}
      *
+     * Must run before BlankLineBetweenImportGroupsFixer.
      * Must run after GlobalNamespaceImportFixer, NoLeadingImportSlashFixer.
      */
     public function getPriority(): int
diff --git a/src/Fixer/Whitespace/BlankLineBetweenImportGroupsFixer.php b/src/Fixer/Whitespace/BlankLineBetweenImportGroupsFixer.php
new file mode 100644
index 000000000..e5e0de0e5
--- /dev/null
+++ b/src/Fixer/Whitespace/BlankLineBetweenImportGroupsFixer.php
@@ -0,0 +1,192 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of PHP CS Fixer.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *     Dariusz Rumiński <dariusz.ruminski@gmail.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace PhpCsFixer\Fixer\Whitespace;
+
+use PhpCsFixer\AbstractFixer;
+use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
+use PhpCsFixer\FixerDefinition\CodeSample;
+use PhpCsFixer\FixerDefinition\FixerDefinition;
+use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
+use PhpCsFixer\Tokenizer\CT;
+use PhpCsFixer\Tokenizer\Token;
+use PhpCsFixer\Tokenizer\Tokens;
+use PhpCsFixer\Tokenizer\TokensAnalyzer;
+
+/**
+ * @author Sander Verkuil <s.verkuil@pm.me>
+ */
+final class BlankLineBetweenImportGroupsFixer extends AbstractFixer implements WhitespacesAwareFixerInterface
+{
+    private const IMPORT_TYPE_CLASS = 'class';
+
+    private const IMPORT_TYPE_CONST = 'const';
+
+    private const IMPORT_TYPE_FUNCTION = 'function';
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDefinition(): FixerDefinitionInterface
+    {
+        return new FixerDefinition(
+            'Putting blank lines between `use` statement groups.',
+            [
+                new CodeSample(
+                    '<?php
+
+use function AAC;
+use const AAB;
+use AAA;
+'
+                ),
+                new CodeSample(
+                    '<?php
+use const AAAA;
+use const BBB;
+use Bar;
+use AAC;
+use Acme;
+use function CCC\AA;
+use function DDD;
+'
+                ),
+                new CodeSample(
+                    '<?php
+use const BBB;
+use const AAAA;
+use Acme;
+use AAC;
+use Bar;
+use function DDD;
+use function CCC\AA;
+'
+                ),
+                new CodeSample(
+                    '<?php
+use const AAAA;
+use const BBB;
+use Acme;
+use function DDD;
+use AAC;
+use function CCC\AA;
+use Bar;
+'
+                ),
+            ]
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * Must run after OrderedImportsFixer.
+     */
+    public function getPriority(): int
+    {
+        return -40;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isCandidate(Tokens $tokens): bool
+    {
+        return $tokens->isTokenKindFound(T_USE);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
+    {
+        $tokensAnalyzer = new TokensAnalyzer($tokens);
+        $namespacesImports = $tokensAnalyzer->getImportUseIndexes(true);
+
+        foreach (array_reverse($namespacesImports) as $uses) {
+            $this->walkOverUses($tokens, $uses);
+        }
+    }
+
+    /**
+     * @param int[] $uses
+     */
+    private function walkOverUses(Tokens $tokens, array $uses): void
+    {
+        $usesCount = \count($uses);
+
+        if ($usesCount < 2) {
+            return; // nothing to fix
+        }
+
+        $previousType = null;
+
+        for ($i = $usesCount - 1; $i >= 0; --$i) {
+            $index = $uses[$i];
+            $startIndex = $tokens->getNextMeaningfulToken($index + 1);
+            $endIndex = $tokens->getNextTokenOfKind($startIndex, [';', [T_CLOSE_TAG]]);
+
+            if ($tokens[$startIndex]->isGivenKind(CT::T_CONST_IMPORT)) {
+                $type = self::IMPORT_TYPE_CONST;
+            } elseif ($tokens[$startIndex]->isGivenKind(CT::T_FUNCTION_IMPORT)) {
+                $type = self::IMPORT_TYPE_FUNCTION;
+            } else {
+                $type = self::IMPORT_TYPE_CLASS;
+            }
+
+            if (null !== $previousType && $type !== $previousType) {
+                $this->ensureLine($tokens, $endIndex + 1);
+            }
+
+            $previousType = $type;
+        }
+    }
+
+    private function ensureLine(Tokens $tokens, int $index): void
+    {
+        static $lineEnding;
+
+        if (null === $lineEnding) {
+            $lineEnding = $this->whitespacesConfig->getLineEnding();
+            $lineEnding .= $lineEnding;
+        }
+
+        $index = $this->getInsertIndex($tokens, $index);
+
+        if ($tokens[$index]->isWhitespace()) {
+            $tokens[$index] = new Token([T_WHITESPACE, $lineEnding]);
+        } else {
+            $tokens->insertSlices([$index + 1 => [new Token([T_WHITESPACE, $lineEnding])]]);
+        }
+    }
+
+    private function getInsertIndex(Tokens $tokens, int $index): int
+    {
+        $tokensCount = \count($tokens);
+
+        for (; $index < $tokensCount - 1; ++$index) {
+            if (!$tokens[$index]->isWhitespace() && !$tokens[$index]->isComment()) {
+                return $index - 1;
+            }
+
+            $content = $tokens[$index]->getContent();
+
+            if (str_contains($content, "\n")) {
+                return $index;
+            }
+        }
+
+        return $index;
+    }
+}
diff --git a/src/RuleSet/Sets/PSR12Set.php b/src/RuleSet/Sets/PSR12Set.php
index dd04f4c1e..30375c0e7 100644
--- a/src/RuleSet/Sets/PSR12Set.php
+++ b/src/RuleSet/Sets/PSR12Set.php
@@ -26,6 +26,7 @@ public function getRules(): array
         return [
             '@PSR2' => true,
             'blank_line_after_opening_tag' => true,
+            'blank_line_between_import_groups' => true,
             'braces' => [
                 'allow_single_line_anonymous_class_with_empty_body' => true,
             ],


Test patch:
diff --git a/tests/AutoReview/FixerFactoryTest.php b/tests/AutoReview/FixerFactoryTest.php
index 0fa0a069a..acc9359b7 100644
--- a/tests/AutoReview/FixerFactoryTest.php
+++ b/tests/AutoReview/FixerFactoryTest.php
@@ -619,6 +619,9 @@ private static function getFixersPriorityGraph(): array
                 'no_blank_lines_after_class_opening',
                 'space_after_semicolon',
             ],
+            'ordered_imports' => [
+                'blank_line_between_import_groups',
+            ],
             'php_unit_construct' => [
                 'php_unit_dedicate_assert',
             ],
diff --git a/tests/Fixer/Whitespace/BlankLineBetweenImportGroupsFixerTest.php b/tests/Fixer/Whitespace/BlankLineBetweenImportGroupsFixerTest.php
new file mode 100644
index 000000000..2a1a6cbad
--- /dev/null
+++ b/tests/Fixer/Whitespace/BlankLineBetweenImportGroupsFixerTest.php
@@ -0,0 +1,555 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of PHP CS Fixer.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *     Dariusz Rumiński <dariusz.ruminski@gmail.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace PhpCsFixer\Tests\Fixer\Whitespace;
+
+use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
+
+/**
+ * @internal
+ *
+ * @covers \PhpCsFixer\Fixer\Whitespace\BlankLineBetweenImportGroupsFixer
+ */
+final class BlankLineBetweenImportGroupsFixerTest extends AbstractFixerTestCase
+{
+    /**
+     * @dataProvider provideFixTypesOrderAndWhitespaceCases
+     */
+    public function testFixTypesOrderAndNewlines(string $expected, ?string $input = null): void
+    {
+        $this->doTest($expected, $input);
+    }
+
+    public function provideFixTypesOrderAndWhitespaceCases(): iterable
+    {
+        yield [
+            '<?php
+use Aaa\Ccc;
+use Foo\Zar\Baz;
+use some\a\{ClassA};
+use some\b\{ClassD, ClassB, ClassC as C};
+use Bar\Biz\Boooz\Bum;
+use some\b\{
+    ClassF,
+    ClassG
+};
+use Some\Cloz;
+use Aaa\Bbb;
+
+use const some\a\{ConstD};
+use const some\a\{ConstA};
+use const some\a\{ConstB, ConstC as CC};
+use const some\b\{ConstE};
+
+use function some\f\{fn_g, fn_h, fn_i};
+use function some\c\{fn_f};
+use function some\a\{fn_x};
+use function some\b\{fn_c, fn_d, fn_e};
+use function some\a\{fn_a, fn_b};
+',
+            '<?php
+use Aaa\Ccc;
+use Foo\Zar\Baz;
+use some\a\{ClassA};
+use some\b\{ClassD, ClassB, ClassC as C};
+use Bar\Biz\Boooz\Bum;
+use some\b\{
+    ClassF,
+    ClassG
+};
+use Some\Cloz;
+use Aaa\Bbb;
+use const some\a\{ConstD};
+use const some\a\{ConstA};
+use const some\a\{ConstB, ConstC as CC};
+use const some\b\{ConstE};
+use function some\f\{fn_g, fn_h, fn_i};
+use function some\c\{fn_f};
+use function some\a\{fn_x};
+use function some\b\{fn_c, fn_d, fn_e};
+use function some\a\{fn_a, fn_b};
+',
+        ];
+
+        yield [
+            '<?php
+use Aaa\Ccc;
+use Foo\Zar\Baz;
+
+use function some\f\{fn_g, fn_h, fn_i};
+
+use some\a\{ClassA};
+use some\b\{ClassD, ClassB, ClassC as C};
+use Bar\Biz\Boooz\Bum;
+
+use function some\c\{fn_f};
+
+use some\b\{
+    ClassF,
+    ClassG
+};
+
+use const some\a\{ConstD};
+
+use Some\Cloz;
+
+use function some\a\{fn_x};
+
+use const some\a\{ConstA};
+
+use function some\b\{fn_c, fn_d, fn_e};
+
+use const some\a\{ConstB, ConstC as CC};
+
+use Aaa\Bbb;
+
+use const some\b\{ConstE};
+
+use function some\a\{fn_a, fn_b};
+',
+            '<?php
+use Aaa\Ccc;
+use Foo\Zar\Baz;
+use function some\f\{fn_g, fn_h, fn_i};
+use some\a\{ClassA};
+use some\b\{ClassD, ClassB, ClassC as C};
+use Bar\Biz\Boooz\Bum;
+use function some\c\{fn_f};
+use some\b\{
+    ClassF,
+    ClassG
+};
+use const some\a\{ConstD};
+use Some\Cloz;
+use function some\a\{fn_x};
+use const some\a\{ConstA};
+use function some\b\{fn_c, fn_d, fn_e};
+use const some\a\{ConstB, ConstC as CC};
+use Aaa\Bbb;
+use const some\b\{ConstE};
+use function some\a\{fn_a, fn_b};
+',
+        ];
+
+        yield [
+            '<?php
+use Aaa\Ccc;
+use Foo\Zar\Baz;
+#use function some\f\{fn_g, fn_h, fn_i};
+use some\a\{ClassA};
+use some\b\{ClassD, ClassB, ClassC as C};
+use Bar\Biz\Boooz\Bum;
+
+use function some\c\{fn_f};
+
+use some\b\{
+    ClassF,
+    ClassG
+};
+
+use const some\a\{ConstD};
+
+use Some\Cloz;
+
+use function some\a\{fn_x};
+
+/** Import ConstA to do some nice magic */
+use const some\a\{ConstA};
+
+use function some\b\{fn_c, fn_d, fn_e};
+
+use const some\a\{ConstB, ConstC as CC};
+
+use Aaa\Bbb;
+
+use const some\b\{ConstE};
+
+use function some\a\{fn_a, fn_b};
+',
+            '<?php
+use Aaa\Ccc;
+use Foo\Zar\Baz;
+#use function some\f\{fn_g, fn_h, fn_i};
+use some\a\{ClassA};
+use some\b\{ClassD, ClassB, ClassC as C};
+use Bar\Biz\Boooz\Bum;
+use function some\c\{fn_f};
+use some\b\{
+    ClassF,
+    ClassG
+};
+use const some\a\{ConstD};
+use Some\Cloz;
+use function some\a\{fn_x};
+/** Import ConstA to do some nice magic */
+use const some\a\{ConstA};
+use function some\b\{fn_c, fn_d, fn_e};
+use const some\a\{ConstB, ConstC as CC};
+use Aaa\Bbb;
+use const some\b\{ConstE};
+use function some\a\{fn_a, fn_b};
+',
+        ];
+
+        yield [
+            '<?php
+/**
+use Aaa\Ccc;
+use Foo\Zar\Baz;
+ */
+use function some\f\{fn_g, fn_h, fn_i};
+
+use some\a\{ClassA};
+use some\b\{ClassD, ClassB, ClassC as C};
+use Bar\Biz\Boooz\Bum;
+
+use function some\c\{fn_f};
+
+use some\b\{
+    ClassF,
+    ClassG
+};
+
+use const some\a\{ConstD};
+
+use Some\Cloz;
+
+// Ignore the following content
+// use function some\a\{fn_x};
+// use const some\a\{ConstA};
+
+use function some\b\{fn_c, fn_d, fn_e};
+
+use const some\a\{ConstB, ConstC as CC};
+
+use Aaa\Bbb;
+
+use const some\b\{ConstE};
+
+use function some\a\{fn_a, fn_b};
+',
+            '<?php
+/**
+use Aaa\Ccc;
+use Foo\Zar\Baz;
+ */
+use function some\f\{fn_g, fn_h, fn_i};
+
+use some\a\{ClassA};
+use some\b\{ClassD, ClassB, ClassC as C};
+use Bar\Biz\Boooz\Bum;
+
+use function some\c\{fn_f};
+
+use some\b\{
+    ClassF,
+    ClassG
+};
+
+use const some\a\{ConstD};
+
+use Some\Cloz;
+// Ignore the following content
+// use function some\a\{fn_x};
+// use const some\a\{ConstA};
+
+use function some\b\{fn_c, fn_d, fn_e};
+
+use const some\a\{ConstB, ConstC as CC};
+
+use Aaa\Bbb;
+
+use const some\b\{ConstE};
+
+use function some\a\{fn_a, fn_b};
+',
+        ];
+
+        yield [
+            '<?php
+use Aaa\Ccc;
+
+/*use Foo\Zar\Baz;
+use function some\f\{fn_g, fn_h, fn_i};
+use some\a\{ClassA};
+use some\b\{ClassD, ClassB, ClassC as C};
+use Bar\Biz\Boooz\Bum;
+use function some\c\{fn_f};
+use some\b\{
+    ClassF,
+    ClassG
+};
+use const some\a\{ConstD};
+use Some\Cloz;
+use function some\a\{fn_x};
+use const some\a\{ConstA};
+use function some\b\{fn_c, fn_d, fn_e};
+use const some\a\{ConstB, ConstC as CC};
+use Aaa\Bbb;
+use const some\b\{ConstE};
+*/
+use function some\a\{fn_a, fn_b};
+',
+            '<?php
+use Aaa\Ccc;
+/*use Foo\Zar\Baz;
+use function some\f\{fn_g, fn_h, fn_i};
+use some\a\{ClassA};
+use some\b\{ClassD, ClassB, ClassC as C};
+use Bar\Biz\Boooz\Bum;
+use function some\c\{fn_f};
+use some\b\{
+    ClassF,
+    ClassG
+};
+use const some\a\{ConstD};
+use Some\Cloz;
+use function some\a\{fn_x};
+use const some\a\{ConstA};
+use function some\b\{fn_c, fn_d, fn_e};
+use const some\a\{ConstB, ConstC as CC};
+use Aaa\Bbb;
+use const some\b\{ConstE};
+*/
+use function some\a\{fn_a, fn_b};
+',
+        ];
+
+        yield [
+            '<?php
+use Aaa\Ccc;
+
+use function some\a\{fn_a, fn_b};
+use function some\b\{
+    fn_c,
+    fn_d
+};
+',
+            '<?php
+use Aaa\Ccc;
+
+
+
+use function some\a\{fn_a, fn_b};
+use function some\b\{
+    fn_c,
+    fn_d
+};
+',
+        ];
+
+        yield [
+            '<?php
+use Aaa\Ccc;
+
+use function some\a\{fn_a, fn_b}; // Do this because of reasons
+use function some\b\{
+    fn_c,
+    fn_d
+};
+',
+            '<?php
+use Aaa\Ccc;
+
+
+
+use function some\a\{fn_a, fn_b}; // Do this because of reasons
+use function some\b\{
+    fn_c,
+    fn_d
+};
+',
+        ];
+
+        yield [
+            '<?php
+use Aaa\Ccc;
+
+/** Beginning of line comment as well */use function some\a\{fn_a, fn_b};
+use function some\b\{
+    fn_c,
+    fn_d
+};
+',
+            '<?php
+use Aaa\Ccc;
+
+
+
+/** Beginning of line comment as well */use function some\a\{fn_a, fn_b};
+use function some\b\{
+    fn_c,
+    fn_d
+};
+',
+        ];
+
+        yield [
+            '<?php
+
+use /* x */ function /* x */ Psl\Str\trim;
+
+use /* x */ Psl\Str /* x */;
+
+use /* x */ const /* x */ Psl\Str\OTHER_ALPHABET;
+use /* x */ const /* x */ Psl\Str\ALPHABET;
+
+use /* x */ function /* x */ Psl\Str\ /* x */ {
+    /* x */ trim_left /* x */,
+    /* x */ trim_right /* x */,
+};
+',
+            '<?php
+
+use /* x */ function /* x */ Psl\Str\trim;
+use /* x */ Psl\Str /* x */;
+use /* x */ const /* x */ Psl\Str\OTHER_ALPHABET;
+use /* x */ const /* x */ Psl\Str\ALPHABET;
+use /* x */ function /* x */ Psl\Str\ /* x */ {
+    /* x */ trim_left /* x */,
+    /* x */ trim_right /* x */,
+};
+',
+        ];
+
+        yield 'lots of inserts in same namespace' => [
+            '<?php
+namespace A\B6 {
+    use C1\B1;
+
+use const C6\Z1;
+
+use C2\B2;
+
+use const C7\Z2;
+
+use C3\B3;
+
+use const C8\Z3;
+
+use C4\B4;
+
+use const C9\Z4;
+
+use C5\B5;
+
+use const C0\Z5;
+}
+            ',
+            '<?php
+namespace A\B6 {
+    use C1\B1;use const C6\Z1;
+    use C2\B2;use const C7\Z2;
+    use C3\B3;use const C8\Z3;
+    use C4\B4;use const C9\Z4;
+    use C5\B5;use const C0\Z5;
+}
+            ',
+        ];
+
+        yield 'lots of inserts in multiple namespaces' => [
+            '<?php
+namespace A\B1 {
+    use C\B;
+
+use const C\Z;
+}
+namespace A\B2 {
+    use C\B;
+
+use const C\Z;
+}
+namespace A\B3 {
+    use C\B;
+
+use const C\Z;
+}
+namespace A\B4 {
+    use C\B;
+
+use const C\Z;
+}
+namespace A\B5 {
+    use C\B;
+
+use const C\Z;
+}
+            ',
+            '<?php
+namespace A\B1 {
+    use C\B;use const C\Z;
+}
+namespace A\B2 {
+    use C\B;use const C\Z;
+}
+namespace A\B3 {
+    use C\B;use const C\Z;
+}
+namespace A\B4 {
+    use C\B;use const C\Z;
+}
+namespace A\B5 {
+    use C\B;use const C\Z;
+}
+            ',
+        ];
+
+        yield [
+            '<?php use A\B;    /*1*/
+
+use const C\D;',
+            '<?php use A\B;    /*1*/      use const C\D;',
+        ];
+
+        yield [
+            '<?php
+namespace Foo;
+use A\B; /* foo */  /* A */ /* B */  # X
+
+use const C\D; // bar
+',
+            '<?php
+namespace Foo;
+use A\B; /* foo */  /* A */ /* B */  # X
+use const C\D; // bar
+',
+        ];
+    }
+
+    /**
+     * @dataProvider provideFixPre80Cases
+     * @requires PHP <8.0
+     */
+    public function testFixPre80(string $expected, string $input = null): void
+    {
+        $this->doTest($expected, $input);
+    }
+
+    public function provideFixPre80Cases(): \Generator
+    {
+        yield [
+            '<?php
+
+use Some\/**Namespace*/Weird\Be/**ha*/;
+
+use function Other\/**Namespace*/Weird\/**Comments*/have;
+',
+            '<?php
+
+use Some\/**Namespace*/Weird\Be/**ha*/;
+use function Other\/**Namespace*/Weird\/**Comments*/have;
+',
+        ];
+    }
+}
diff --git a/tests/Fixtures/Integration/misc/PHP7_0.test b/tests/Fixtures/Integration/misc/PHP7_0.test
index 643f61949..2ec3b15d7 100644
--- a/tests/Fixtures/Integration/misc/PHP7_0.test
+++ b/tests/Fixtures/Integration/misc/PHP7_0.test
@@ -17,14 +17,19 @@ declare(strict_types=1);
 use some\a\ClassA;
 use some\a\ClassB;
 use some\a\ClassC as C;
+
 use const some\a\ConstA;
 use const some\a\ConstB;
 use const some\a\ConstC;
+
 use function some\a\fn_a;
 use function some\a\fn_b;
 use function some\a\fn_c;
+
 use some\x\ClassX;
+
 use function some\x\D;
+
 use const some\x\E;
 
 function dummyUsage()
diff --git a/tests/Fixtures/Integration/priority/ordered_imports,blank_line_between_import_groups.test b/tests/Fixtures/Integration/priority/ordered_imports,blank_line_between_import_groups.test
new file mode 100644
index 000000000..fbef34573
--- /dev/null
+++ b/tests/Fixtures/Integration/priority/ordered_imports,blank_line_between_import_groups.test
@@ -0,0 +1,30 @@
+--TEST--
+Integration of fixers: ordered_imports,blank_line_between_import_groups.
+--RULESET--
+{"ordered_imports": true, "blank_line_between_import_groups": true}
+--EXPECT--
+<?php
+
+namespace Test;
+
+use function bar;
+
+use Foo;
+use Z;
+
+$foo = new Foo();
+
+bar($foo);
+
+--INPUT--
+<?php
+
+namespace Test;
+
+use Foo;
+use Z;
+use function bar;
+
+$foo = new Foo();
+
+bar($foo);
diff --git a/tests/Fixtures/Integration/set/@PSR12.test-out.php b/tests/Fixtures/Integration/set/@PSR12.test-out.php
index a5e5d673c..678a693ca 100644
--- a/tests/Fixtures/Integration/set/@PSR12.test-out.php
+++ b/tests/Fixtures/Integration/set/@PSR12.test-out.php
@@ -6,7 +6,9 @@
 use FooInterfaceB;
 use BarClass as Bar;
 use OtherVendor\OtherPackage\BazClass;
+
 use function foo;
+
 use const BAR;
 
 class Foo extends Bar implements FooInterfaceA