Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 4042b05

Browse files
committed
feat: add 'ensure_spacing' configuration option to control spacing after DocBlocks
1 parent d43f5a5 commit 4042b05

File tree

3 files changed

+70
-5
lines changed

3 files changed

+70
-5
lines changed

‎README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,14 @@ return (new PhpCsFixer\Config())
110110
;
111111
```
112112

113+
## ⚙️ Configuration
114+
115+
- `annotations` (array): DocBlock annotations to add to classes
116+
- `preserve_existing` (boolean, default: true): Keep existing DocBlock annotations
117+
- `separate` (string, default: 'none'): Add blank lines ('top', 'bottom', 'both', 'none')
118+
- `add_structure_name` (boolean, default: false): Add class name as first line in DocBlock
119+
- `ensure_spacing` (boolean, default: true): Ensure proper spacing after DocBlocks to prevent conflicts with PHP-CS-Fixer rules
120+
113121
## 🧑‍💻 Contributing
114122

115123
Please have a look at [`CONTRIBUTING.md`](CONTRIBUTING.md).

‎src/Rules/DocBlockHeaderFixer.php

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ public function getName(): string
6161
return 'KonradMichalik/docblock_header_comment';
6262
}
6363

64+
public function getPriority(): int
65+
{
66+
// Run before single_line_after_imports (0) and no_extra_blank_lines (0)
67+
// Higher priority values run first
68+
return 1;
69+
}
70+
6471
public function isCandidate(Tokens $tokens): bool
6572
{
6673
return $tokens->isTokenKindFound(T_CLASS)
@@ -88,6 +95,10 @@ public function getConfigurationDefinition(): FixerConfigurationResolverInterfac
8895
->setAllowedTypes(['bool'])
8996
->setDefault(false)
9097
->getOption(),
98+
(new FixerOptionBuilder('ensure_spacing', 'Ensure proper spacing after DocBlock to prevent conflicts with PHP-CS-Fixer rules'))
99+
->setAllowedTypes(['bool'])
100+
->setDefault(true)
101+
->getOption(),
91102
]);
92103
}
93104

@@ -189,6 +200,12 @@ private function mergeWithExistingDocBlock(Tokens $tokens, int $docBlockIndex, a
189200

190201
$newDocBlock = $this->buildDocBlock($mergedAnnotations, $structureName);
191202
$tokens[$docBlockIndex] = new Token([T_DOC_COMMENT, $newDocBlock]);
203+
204+
// Ensure there's proper spacing after existing DocBlock
205+
$ensureSpacing = $this->resolvedConfiguration['ensure_spacing'] ?? true;
206+
if ($ensureSpacing) {
207+
$this->ensureProperSpacingAfterDocBlock($tokens, $docBlockIndex);
208+
}
192209
}
193210

194211
/**
@@ -198,6 +215,12 @@ private function replaceDocBlock(Tokens $tokens, int $docBlockIndex, array $anno
198215
{
199216
$newDocBlock = $this->buildDocBlock($annotations, $structureName);
200217
$tokens[$docBlockIndex] = new Token([T_DOC_COMMENT, $newDocBlock]);
218+
219+
// Ensure there's proper spacing after replaced DocBlock
220+
$ensureSpacing = $this->resolvedConfiguration['ensure_spacing'] ?? true;
221+
if ($ensureSpacing) {
222+
$this->ensureProperSpacingAfterDocBlock($tokens, $docBlockIndex);
223+
}
201224
}
202225

203226
/**
@@ -219,8 +242,14 @@ private function insertNewDocBlock(Tokens $tokens, int $structureIndex, array $a
219242
$docBlock = $this->buildDocBlock($annotations, $structureName);
220243
$tokensToInsert[] = new Token([T_DOC_COMMENT, $docBlock]);
221244

222-
// For compatibility with no_blank_lines_after_phpdoc, only add bottom separation when 'separate' is not 'none'
223-
// This prevents conflicts with PHP-CS-Fixer rules that manage DocBlock spacing
245+
// Add a newline after the DocBlock if ensure_spacing is enabled (default)
246+
// This prevents conflicts with single_line_after_imports and no_extra_blank_lines rules
247+
$ensureSpacing = $this->resolvedConfiguration['ensure_spacing'] ?? true;
248+
if ($ensureSpacing) {
249+
$tokensToInsert[] = new Token([T_WHITESPACE, "\n"]);
250+
}
251+
252+
// Add additional separation if configured
224253
if (in_array($separate, ['bottom', 'both'], true)) {
225254
// Check if there's already whitespace after the structure declaration
226255
$nextToken = $tokens[$structureIndex] ?? null;
@@ -327,4 +356,23 @@ private function buildDocBlock(array $annotations, string $structureName): strin
327356

328357
return $docBlock;
329358
}
359+
360+
/**
361+
* Ensures proper spacing after a DocBlock to prevent conflicts with PHP-CS-Fixer rules.
362+
*/
363+
private function ensureProperSpacingAfterDocBlock(Tokens $tokens, int $docBlockIndex): void
364+
{
365+
$nextIndex = $docBlockIndex + 1;
366+
367+
// Check if the next token exists and is not already a newline
368+
if ($nextIndex < $tokens->count()) {
369+
$nextToken = $tokens[$nextIndex];
370+
371+
// If the next token is not whitespace or doesn't contain a newline, add one
372+
if (!$nextToken->isWhitespace() || !str_contains($nextToken->getContent(), "\n")) {
373+
// Insert a newline token after the DocBlock
374+
$tokens->insertAt($nextIndex, [new Token([T_WHITESPACE, "\n"])]);
375+
}
376+
}
377+
}
330378
}

‎tests/src/Rules/DocBlockHeaderFixerTest.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,13 +170,14 @@ public function testConfigurationDefinition(): void
170170
$configDefinition = $this->fixer->getConfigurationDefinition();
171171
$options = $configDefinition->getOptions();
172172

173-
self::assertCount(4, $options);
173+
self::assertCount(5, $options);
174174

175175
$optionNames = array_map(fn ($option) => $option->getName(), $options);
176176
self::assertContains('annotations', $optionNames);
177177
self::assertContains('preserve_existing', $optionNames);
178178
self::assertContains('separate', $optionNames);
179179
self::assertContains('add_structure_name', $optionNames);
180+
self::assertContains('ensure_spacing', $optionNames);
180181
}
181182

182183
public function testParseExistingAnnotations(): void
@@ -237,6 +238,7 @@ public function testApplyFixAddsDocBlockToClass(): void
237238
$this->fixer->configure([
238239
'annotations' => ['author' => 'John Doe'],
239240
'separate' => 'none',
241+
'ensure_spacing' => false,
240242
]);
241243
$method->invoke($this->fixer, $file, $tokens);
242244

@@ -255,6 +257,7 @@ public function testApplyFixHandlesMultipleClasses(): void
255257
$this->fixer->configure([
256258
'annotations' => ['author' => 'John Doe'],
257259
'separate' => 'none',
260+
'ensure_spacing' => false,
258261
]);
259262
$method->invoke($this->fixer, $file, $tokens);
260263

@@ -270,7 +273,7 @@ public function testProcessClassDocBlockWithNewDocBlock(): void
270273

271274
$method = new ReflectionMethod($this->fixer, 'processStructureDocBlock');
272275

273-
$this->fixer->configure(['separate' => 'none']);
276+
$this->fixer->configure(['separate' => 'none', 'ensure_spacing' => false]);
274277
$method->invoke($this->fixer, $tokens, 1, $annotations, 'Foo');
275278

276279
$expected = "<?php /**\n * @author John Doe\n */class Foo {}";
@@ -379,7 +382,7 @@ public function testInsertNewDocBlockWithSeparateNone(): void
379382

380383
$method = new ReflectionMethod($this->fixer, 'insertNewDocBlock');
381384

382-
$this->fixer->configure(['separate' => 'none']);
385+
$this->fixer->configure(['separate' => 'none', 'ensure_spacing' => false]);
383386
$method->invoke($this->fixer, $tokens, 1, $annotations, 'Foo');
384387

385388
$expected = "<?php /**\n * @author John Doe\n */class Foo {}";
@@ -702,6 +705,7 @@ public function testApplyFixAddsDocBlockToInterface(): void
702705
$this->fixer->configure([
703706
'annotations' => ['author' => 'John Doe'],
704707
'separate' => 'none',
708+
'ensure_spacing' => false,
705709
]);
706710
$method->invoke($this->fixer, $file, $tokens);
707711

@@ -720,6 +724,7 @@ public function testApplyFixAddsDocBlockToTrait(): void
720724
$this->fixer->configure([
721725
'annotations' => ['author' => 'Jane Doe'],
722726
'separate' => 'none',
727+
'ensure_spacing' => false,
723728
]);
724729
$method->invoke($this->fixer, $file, $tokens);
725730

@@ -738,6 +743,7 @@ public function testApplyFixAddsDocBlockToEnum(): void
738743
$this->fixer->configure([
739744
'annotations' => ['license' => 'MIT'],
740745
'separate' => 'none',
746+
'ensure_spacing' => false,
741747
]);
742748
$method->invoke($this->fixer, $file, $tokens);
743749

@@ -757,6 +763,7 @@ public function testApplyFixWithClassNameEnabled(): void
757763
'annotations' => ['author' => 'John Doe'],
758764
'add_structure_name' => true,
759765
'separate' => 'none',
766+
'ensure_spacing' => false,
760767
]);
761768
$method->invoke($this->fixer, $file, $tokens);
762769

@@ -776,6 +783,7 @@ public function testApplyFixWithMultipleClassesAndClassName(): void
776783
'annotations' => ['author' => 'John Doe'],
777784
'add_structure_name' => true,
778785
'separate' => 'none',
786+
'ensure_spacing' => false,
779787
]);
780788
$method->invoke($this->fixer, $file, $tokens);
781789

@@ -830,6 +838,7 @@ public function testInsertNewDocBlockWithClassNameAndSeparateNone(): void
830838
$this->fixer->configure([
831839
'add_structure_name' => true,
832840
'separate' => 'none',
841+
'ensure_spacing' => false,
833842
]);
834843
$method->invoke($this->fixer, $tokens, 1, $annotations, 'TestClass');
835844

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /