-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature Request: Add MultilinePromotedPropertiesWithAttributesFixer #839
Labels
Comments
Hi @patrickcurl, what would you say for a fixer to make attribute single line, similar to single_line_throw? |
+1 for the "or even" writing style |
I implemented the "or even" style myself. See below. I'd be happy to contribute this via a PR, if you're interested, @kubawerlos <?php
declare(strict_types=1);
namespace App\CodeQuality\PhpCsFixer;
use Override;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\Indentation;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\AttributeAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\AttributeAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use SplFileInfo;
/**
* @phpstan-import-type _AttributeItems from AttributeAnalysis
*/
final class AttributeGroupFixer extends AbstractFixer implements WhitespacesAwareFixerInterface
{
use Indentation;
public const string NAME = 'App/attribute_group_fixer';
/**
* @param Tokens<Token> $tokens
*/
#[Override()]
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_ATTRIBUTE);
}
#[Override()]
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Opinionated fixer for attribute groups.',
[
new CodeSample(
<<<PHP
<?php
#[ORM\Entity(repositoryClass: BarRepository::class)]
#[SoftDeleteable(hardDelete: false)]
class Bar
{
#[ApiProperty(identifier: true)]
#[Groups([
'read'
])]
#[ORM\Id]
public function foo() {
return 'foo';
}
#[
ORM\Id(), ApiProperty(identifier: true),
Groups([
'write'
])
]
public function bar() {
return 'bar';
}
#[
Groups(
[
'read',
'write'
]
),
ORM\Id,
]
public function baz() {
return 'baz';
}
}
PHP,
),
],
);
}
#[Override()]
public function getPriority(): int
{
// Lower than StatementIndentationFixer
return -4;
}
#[Override()]
public function getName(): string
{
return self::NAME;
}
/**
* @param Tokens<Token> $tokens
*/
protected function applyFix(SplFileInfo $file, Tokens $tokens): void
{
$index = 0;
while (null !== $index = $tokens->getNextTokenOfKind($index, [[T_ATTRIBUTE]])) {
// Placeholders for data we need
$openingBracketIndex = null;
$closingBracketIndex = null;
$attributes = [];
// Collect the data we need
foreach (AttributeAnalyzer::collect($tokens, $index) as $attributeAnalysis) {
if ($openingBracketIndex === null) {
$openingBracketIndex = $attributeAnalysis->getOpeningBracketIndex();
}
$closingBracketIndex = $attributeAnalysis->getClosingBracketIndex();
$attributes = array_merge($attributes, $attributeAnalysis->getAttributes());
}
// Save the fuzz when there are no attributes (quite unlikely, but better safe than sorry)
if (count($attributes) === 0 || $openingBracketIndex === null || $closingBracketIndex === null) {
++$index;
continue;
}
try {
// Save the fuzz when there's only one attribute
if (count($attributes) === 1) {
continue;
}
$desiredStyle = $this->desiredStyle($tokens, $openingBracketIndex, $attributes);
$tokens->overrideRange($openingBracketIndex, $closingBracketIndex, $desiredStyle);
} finally {
$index = $closingBracketIndex;
}
}
}
/**
* @param Tokens<Token> $tokens
* @param _AttributeItems $attributes
*/
private function desiredStyle(
Tokens $tokens,
int $openingBracketIndex,
array $attributes,
): Tokens {
// Determine the indentation of the line of the opening bracket
$lineIndentationAtOpeningBracket = $this->getLineIndentation($tokens, $openingBracketIndex);
return Tokens::fromArray([
new Token([T_ATTRIBUTE, '#[']),
new Token([T_WHITESPACE, $this->whitespacesConfig->getLineEnding()]),
...array_reduce(
$attributes,
function (array $carry, array $attribute) use ($tokens, $lineIndentationAtOpeningBracket): array {
$trueStart = $tokens[$attribute['start']]->isWhitespace() ? $tokens->getNextNonWhitespace($attribute['start']) : $attribute['start'];
if ($trueStart === null) {
return $carry;
}
$lineIndentationAtStart = $this->getLineIndentation($tokens, $trueStart);
// Determine the code
$attributeBodyTokens = [];
for ($i = $trueStart; $i <= $attribute['end']; ++$i) {
$attributeBodyToken = clone $tokens[$i];
if ($attributeBodyToken->isWhitespace() && str_contains($attributeBodyToken->getContent(), $this->whitespacesConfig->getLineEnding())) {
$attributeBodyToken = new Token([
T_WHITESPACE,
$this->whitespacesConfig->getLineEnding() .
$lineIndentationAtOpeningBracket .
$this->whitespacesConfig->getIndent() .
(
$i === $attribute['end'] ? '' : implode('', array_fill(0, strlen($this->getLineIndentation($tokens, $i + 1)) - strlen($lineIndentationAtStart), ' '))
),
]);
}
$attributeBodyTokens[] = $attributeBodyToken;
}
return array_merge(
$carry,
[
new Token([T_WHITESPACE, $lineIndentationAtOpeningBracket . $this->whitespacesConfig->getIndent()]),
...$attributeBodyTokens,
new Token([T_STRING, ',']),
new Token([T_WHITESPACE, $this->whitespacesConfig->getLineEnding()]),
],
);
},
[],
),
...($lineIndentationAtOpeningBracket !== '' ? [new Token([T_WHITESPACE, $lineIndentationAtOpeningBracket])] : []),
new Token([CT::T_ATTRIBUTE_CLOSE, ']']),
]);
}
} Partial output of --- Original
+++ New
@@ -1,37 +1,42 @@
<?php
-#[ORM\Entity(repositoryClass: BarRepository::class)]
-#[SoftDeleteable(hardDelete: false)]
+#[
+ ORM\Entity(repositoryClass: BarRepository::class),
+ SoftDeleteable(hardDelete: false),
+]
class Bar
{
- #[ApiProperty(identifier: true)]
- #[Groups([
- 'read'
- ])]
- #[ORM\Id]
+ #[
+ ApiProperty(identifier: true),
+ Groups([
+ 'read'
+ ]),
+ ORM\Id,
+ ]
public function foo() {
return 'foo';
}
#[
- ORM\Id(), ApiProperty(identifier: true),
+ ORM\Id(),
+ ApiProperty(identifier: true),
Groups([
'write'
- ])
+ ]),
]
public function bar() {
return 'bar';
}
#[
- Groups(
- [
- 'read',
- 'write'
- ]
- ),
- ORM\Id,
+ Groups(
+ [
+ 'read',
+ 'write'
+ ]
+ ),
+ ORM\Id,
]
public function baz() {
return 'baz';
}
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Currently I'm only able to get :
What I'd like it it to just be:
or even:
The text was updated successfully, but these errors were encountered: