Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions blink/internal/fixer/rules/no_blank_line_between_imports.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ func (NoBlankLineBetweenImports) Fix(s *tokens.Stream) bool {
if prev < 0 || s.At(prev).Kind != token.Punct || s.At(prev).Value != ";" {
continue
}
// keep the blank line between different import groups, e.g. class and "use function"
if importType(s, useIndex) != importType(s, uses[k-1]) {
continue
}
ws := useIndex - 1
if ws < 0 || s.At(ws).Kind != token.Whitespace {
continue
Expand All @@ -58,3 +62,17 @@ func (NoBlankLineBetweenImports) Fix(s *tokens.Stream) bool {
}
return changed
}

// importType classifies an import "use" as "function", "const" or "class".
func importType(s *tokens.Stream, useIndex int) string {
j := skipWhitespace(s, useIndex+1)
if j < s.Len() {
switch strings.ToLower(s.At(j).Value) {
case "function":
return "function"
case "const":
return "const"
}
}
return "class"
}
11 changes: 11 additions & 0 deletions blink/internal/fixer/rules/no_blank_line_between_imports_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,15 @@ func TestNoBlankLineBetweenImports(t *testing.T) {
if _, changed := apply(t, f, "<?php\n$f = function () use ($a) {};\n\n$g = function () use ($b) {};\n"); changed {
t.Fatal("closure use must be ignored")
}

// blank line between different import groups stays; within a group it collapses
got, changed = apply(t, f, "<?php\nnamespace X;\n\nuse A\\B;\n\nuse C\\D;\n\nuse function A\\first;\n\nuse function A\\second;\n\nuse const A\\FIRST;\n\nuse E\\F;\nclass Y {}\n")
if want := "<?php\nnamespace X;\n\nuse A\\B;\nuse C\\D;\n\nuse function A\\first;\nuse function A\\second;\n\nuse const A\\FIRST;\n\nuse E\\F;\nclass Y {}\n"; !changed || got != want {
t.Fatalf("groups: changed=%v got=%q", changed, got)
}

// tight class group followed by a function import: no-op, the group gap stays
if _, changed := apply(t, f, "<?php\nnamespace X;\n\nuse A\\B;\nuse C\\D;\n\nuse function A\\first;\n"); changed {
t.Fatal("distinct import groups must be a no-op")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
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;
Expand All @@ -26,7 +27,7 @@ final class NoBlankLineBetweenImportsFixer extends AbstractSymplifyFixer impleme

public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(self::ERROR_MESSAGE, [new CodeSample("<?php\nuse Foo\\Bar;\n\nuse function Foo\\baz;\n")]);
return new FixerDefinition(self::ERROR_MESSAGE, [new CodeSample("<?php\nuse Foo\\Bar;\n\nuse Foo\\Baz;\n")]);
}

// run after OrderedImportsFixer, before BlankLineBetweenImportGroupsFixer
Expand Down Expand Up @@ -67,6 +68,11 @@ public function fix(SplFileInfo $fileInfo, Tokens $tokens): void
continue;
}

// keep the blank line between different import groups, e.g. class and "use function"
if ($this->resolveImportType($tokens, $useIndex) !== $this->resolveImportType($tokens, $useIndexes[$i - 1])) {
continue;
}

$whitespaceIndex = $useIndex - 1;
if (! $tokens[$whitespaceIndex]->isWhitespace()) {
continue;
Expand All @@ -79,4 +85,25 @@ public function fix(SplFileInfo $fileInfo, Tokens $tokens): void
$tokens[$whitespaceIndex] = new Token([T_WHITESPACE, $lineEnding]);
}
}

/**
* @param Tokens<Token> $tokens
*/
private function resolveImportType(Tokens $tokens, int $useIndex): string
{
$nextIndex = $tokens->getNextMeaningfulToken($useIndex);
if ($nextIndex === null) {
return 'class';
}

if ($tokens[$nextIndex]->isGivenKind(CT::T_FUNCTION_IMPORT)) {
return 'function';
}

if ($tokens[$nextIndex]->isGivenKind(CT::T_CONST_IMPORT)) {
return 'const';
}

return 'class';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace Symplify\CodingStandard\Tests\Fixer\Spacing\NoBlankLineBetweenImportsFixer\Fixture;

use Foo\Bar;

use Foo\Baz;

use function Foo\first;

use function Foo\second;

use const Foo\FIRST;

use Foo\Qux;

class KeepBlankLineBetweenImportGroups
{
}

?>
-----
<?php

namespace Symplify\CodingStandard\Tests\Fixer\Spacing\NoBlankLineBetweenImportsFixer\Fixture;

use Foo\Bar;
use Foo\Baz;

use function Foo\first;
use function Foo\second;

use const Foo\FIRST;

use Foo\Qux;

class KeepBlankLineBetweenImportGroups
{
}

?>
Loading