Skip to content

Commit 71070ac

Browse files
committed
updated MethodArgumentSpaceFixer
1 parent f905a4a commit 71070ac

File tree

1 file changed

+105
-83
lines changed

1 file changed

+105
-83
lines changed

src/Fixer/MethodArgumentSpaceFixer.php

+105-83
Original file line numberDiff line numberDiff line change
@@ -12,36 +12,47 @@
1212
* with this source code in the file LICENSE.
1313
*/
1414

15+
// PhpCsFixer\Fixer\FunctionNotation
1516
namespace NetteCodingStandard\Fixer\FunctionNotation;
1617

1718
use PhpCsFixer\AbstractFixer;
1819
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
20+
use PhpCsFixer\Fixer\ConfigurableFixerTrait;
1921
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
2022
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
2123
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
2224
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
23-
use PhpCsFixer\FixerConfiguration\InvalidOptionsForEnvException;
2425
use PhpCsFixer\FixerDefinition\CodeSample;
2526
use PhpCsFixer\FixerDefinition\FixerDefinition;
2627
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
27-
use PhpCsFixer\FixerDefinition\VersionSpecification;
28-
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
2928
use PhpCsFixer\Preg;
3029
use PhpCsFixer\Tokenizer\CT;
3130
use PhpCsFixer\Tokenizer\Token;
3231
use PhpCsFixer\Tokenizer\Tokens;
33-
use Symfony\Component\OptionsResolver\Options;
3432

3533
/**
36-
* Fixer for rules defined in PSR2 ¶4.4, ¶4.6.
37-
*
3834
* @author Kuanhung Chen <[email protected]>
35+
*
36+
* @implements ConfigurableFixerInterface<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration>
37+
*
38+
* @phpstan-type _AutogeneratedInputConfiguration array{
39+
* after_heredoc?: bool,
40+
* attribute_placement?: 'ignore'|'same_line'|'standalone',
41+
* keep_multiple_spaces_after_comma?: bool,
42+
* on_multiline?: 'ensure_fully_multiline'|'ensure_single_line'|'ignore'
43+
* }
44+
* @phpstan-type _AutogeneratedComputedConfiguration array{
45+
* after_heredoc: bool,
46+
* attribute_placement: 'ignore'|'same_line'|'standalone',
47+
* keep_multiple_spaces_after_comma: bool,
48+
* on_multiline: 'ensure_fully_multiline'|'ensure_single_line'|'ignore'
49+
* }
3950
*/
4051
final class MethodArgumentSpaceFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
4152
{
42-
/**
43-
* {@inheritdoc}
44-
*/
53+
/** @use ConfigurableFixerTrait<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration> */
54+
use ConfigurableFixerTrait;
55+
4556
public function getDefinition(): FixerDefinitionInterface
4657
{
4758
return new FixerDefinition(
@@ -81,66 +92,66 @@ public function getDefinition(): FixerDefinitionInterface
8192
'keep_multiple_spaces_after_comma' => false,
8293
]
8394
),
84-
new VersionSpecificCodeSample(
95+
new CodeSample(
96+
"<?php\nfunction sample(#[Foo] #[Bar] \$a=10,\n \$b=20,\$c=30) {}\nsample(1, 2);\n",
97+
[
98+
'on_multiline' => 'ensure_fully_multiline',
99+
'attribute_placement' => 'ignore',
100+
]
101+
),
102+
new CodeSample(
103+
"<?php\nfunction sample(#[Foo]\n #[Bar]\n \$a=10,\n \$b=20,\$c=30) {}\nsample(1, 2);\n",
104+
[
105+
'on_multiline' => 'ensure_fully_multiline',
106+
'attribute_placement' => 'same_line',
107+
]
108+
),
109+
new CodeSample(
110+
"<?php\nfunction sample(#[Foo] #[Bar] \$a=10,\n \$b=20,\$c=30) {}\nsample(1, 2);\n",
111+
[
112+
'on_multiline' => 'ensure_fully_multiline',
113+
'attribute_placement' => 'standalone',
114+
]
115+
),
116+
new CodeSample(
85117
<<<'SAMPLE'
86-
<?php
87-
sample(
88-
<<<EOD
89-
foo
90-
EOD
91-
,
92-
'bar'
93-
);
94-
95-
SAMPLE
118+
<?php
119+
sample(
120+
<<<EOD
121+
foo
122+
EOD
123+
,
124+
'bar'
125+
);
126+
127+
SAMPLE
96128
,
97-
new VersionSpecification(70300),
98129
['after_heredoc' => true]
99130
),
100-
]
131+
],
132+
'This fixer covers rules defined in PSR2 ¶4.4, ¶4.6.'
101133
);
102134
}
103135

104-
/**
105-
* {@inheritdoc}
106-
*/
107136
public function isCandidate(Tokens $tokens): bool
108137
{
109138
return $tokens->isTokenKindFound('(');
110139
}
111140

112-
public function configure(array $configuration): void
113-
{
114-
parent::configure($configuration);
115-
116-
if (isset($configuration['ensure_fully_multiline'])) {
117-
$this->configuration['on_multiline'] = $this->configuration['ensure_fully_multiline']
118-
? 'ensure_fully_multiline'
119-
: 'ignore';
120-
}
121-
}
122-
123141
/**
124142
* {@inheritdoc}
125143
*
126-
* Must run before ArrayIndentationFixer.
127-
* Must run after BracesFixer, CombineNestedDirnameFixer, FunctionDeclarationFixer, ImplodeCallFixer, MethodChainingIndentationFixer, NoUselessSprintfFixer, PowToExponentiationFixer.
144+
* Must run before ArrayIndentationFixer, StatementIndentationFixer.
145+
* Must run after CombineNestedDirnameFixer, FunctionDeclarationFixer, ImplodeCallFixer, LambdaNotUsedImportFixer, NoMultilineWhitespaceAroundDoubleArrowFixer, NoUselessSprintfFixer, PowToExponentiationFixer, StrictParamFixer.
128146
*/
129147
public function getPriority(): int
130148
{
131149
return 30;
132150
}
133151

134-
/**
135-
* {@inheritdoc}
136-
*/
137152
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
138153
{
139-
$expectedTokens = [T_LIST, T_FUNCTION, CT::T_USE_LAMBDA];
140-
141-
if (\PHP_VERSION_ID >= 70400) {
142-
$expectedTokens[] = T_FN;
143-
}
154+
$expectedTokens = [T_LIST, T_FUNCTION, CT::T_USE_LAMBDA, T_FN, T_CLASS];
144155

145156
for ($index = $tokens->count() - 1; $index > 0; --$index) {
146157
$token = $tokens[$index];
@@ -151,23 +162,25 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
151162

152163
$meaningfulTokenBeforeParenthesis = $tokens[$tokens->getPrevMeaningfulToken($index)];
153164

154-
if ($meaningfulTokenBeforeParenthesis->isGivenKind(T_STRING)) {
155-
$isMultiline = $this->fixFunction($tokens, $index);
165+
if (
166+
$meaningfulTokenBeforeParenthesis->isKeyword()
167+
&& !$meaningfulTokenBeforeParenthesis->isGivenKind($expectedTokens)
168+
) {
169+
continue;
170+
}
156171

157-
if (
158-
$isMultiline
159-
&& 'ensure_fully_multiline' === $this->configuration['on_multiline']
160-
&& !$meaningfulTokenBeforeParenthesis->isGivenKind(T_LIST)
161-
) {
162-
$this->ensureFunctionFullyMultiline($tokens, $index);
163-
}
172+
$isMultiline = $this->fixFunction($tokens, $index);
173+
174+
if (
175+
$isMultiline
176+
&& 'ensure_fully_multiline' === $this->configuration['on_multiline']
177+
&& !$meaningfulTokenBeforeParenthesis->isGivenKind(T_LIST)
178+
) {
179+
$this->ensureFunctionFullyMultiline($tokens, $index);
164180
}
165181
}
166182
}
167183

168-
/**
169-
* {@inheritdoc}
170-
*/
171184
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
172185
{
173186
return new FixerConfigurationResolver([
@@ -185,13 +198,13 @@ protected function createConfigurationDefinition(): FixerConfigurationResolverIn
185198
(new FixerOptionBuilder('after_heredoc', 'Whether the whitespace between heredoc end and comma should be removed.'))
186199
->setAllowedTypes(['bool'])
187200
->setDefault(false)
188-
->setNormalizer(static function (Options $options, $value) {
189-
if (\PHP_VERSION_ID < 70300 && $value) {
190-
throw new InvalidOptionsForEnvException('"after_heredoc" option can only be enabled with PHP 7.3+.');
191-
}
192-
193-
return $value;
194-
})
201+
->getOption(),
202+
(new FixerOptionBuilder(
203+
'attribute_placement',
204+
'Defines how to handle argument attributes when function definition is multiline.'
205+
))
206+
->setAllowedValues(['ignore', 'same_line', 'standalone'])
207+
->setDefault('standalone')
195208
->getOption(),
196209
]);
197210
}
@@ -255,8 +268,6 @@ private function fixFunction(Tokens $tokens, int $startFunctionIndex): bool
255268
$this->fixSpace($tokens, $index);
256269
if (!$isMultiline && $this->isNewline($tokens[$index + 1])) {
257270
$isMultiline = true;
258-
259-
break;
260271
}
261272
}
262273
}
@@ -298,11 +309,7 @@ private function ensureSingleLine(Tokens $tokens, int $index): bool
298309

299310
$content = Preg::replace('/\R\h*/', '', $tokens[$index]->getContent());
300311

301-
if ('' !== $content) {
302-
$tokens[$index] = new Token([T_WHITESPACE, $content]);
303-
} else {
304-
$tokens->clearAt($index);
305-
}
312+
$tokens->ensureWhitespaceAtIndex($index, 0, $content);
306313

307314
return true;
308315
}
@@ -314,7 +321,7 @@ private function ensureFunctionFullyMultiline(Tokens $tokens, int $startFunction
314321
do {
315322
$prevWhitespaceTokenIndex = $tokens->getPrevTokenOfKind(
316323
$searchIndex,
317-
[[T_WHITESPACE]]
324+
[[T_ENCAPSED_AND_WHITESPACE], [T_WHITESPACE]],
318325
);
319326

320327
$searchIndex = $prevWhitespaceTokenIndex;
@@ -324,13 +331,14 @@ private function ensureFunctionFullyMultiline(Tokens $tokens, int $startFunction
324331

325332
if (null === $prevWhitespaceTokenIndex) {
326333
$existingIndentation = '';
334+
} elseif (!$tokens[$prevWhitespaceTokenIndex]->isGivenKind(T_WHITESPACE)) {
335+
return;
327336
} else {
328337
$existingIndentation = $tokens[$prevWhitespaceTokenIndex]->getContent();
329338
$lastLineIndex = strrpos($existingIndentation, "\n");
330339
$existingIndentation = false === $lastLineIndex
331340
? $existingIndentation
332-
: substr($existingIndentation, $lastLineIndex + 1)
333-
;
341+
: substr($existingIndentation, $lastLineIndex + 1);
334342
}
335343

336344
$indentation = $existingIndentation.$this->whitespacesConfig->getIndent();
@@ -369,7 +377,23 @@ private function ensureFunctionFullyMultiline(Tokens $tokens, int $startFunction
369377
continue;
370378
}
371379

372-
if ($token->equals(',') && !$tokens[$tokens->getNextMeaningfulToken($index)]->equals(')')) {
380+
if ($tokens[$tokens->getNextMeaningfulToken($index)]->equals(')')) {
381+
continue;
382+
}
383+
384+
if ($token->isGivenKind(CT::T_ATTRIBUTE_CLOSE)) {
385+
if ('standalone' === $this->configuration['attribute_placement']) {
386+
$this->fixNewline($tokens, $index, $indentation);
387+
} elseif ('same_line' === $this->configuration['attribute_placement']) {
388+
$this->ensureSingleLine($tokens, $index + 1);
389+
$tokens->ensureWhitespaceAtIndex($index + 1, 0, ' ');
390+
}
391+
$index = $tokens->findBlockStart(Tokens::BLOCK_TYPE_ATTRIBUTE, $index);
392+
393+
continue;
394+
}
395+
396+
if ($token->equals(',')) {
373397
$this->fixNewline($tokens, $index, $indentation);
374398
}
375399
}
@@ -378,7 +402,7 @@ private function ensureFunctionFullyMultiline(Tokens $tokens, int $startFunction
378402
}
379403

380404
/**
381-
* Method to insert newline after comma or opening parenthesis.
405+
* Method to insert newline after comma, attribute or opening parenthesis.
382406
*
383407
* @param int $index index of a comma
384408
* @param string $indentation the indentation that should be used
@@ -393,6 +417,10 @@ private function fixNewline(Tokens $tokens, int $index, string $indentation, boo
393417
if ($tokens[$index + 2]->isComment()) {
394418
$nextMeaningfulTokenIndex = $tokens->getNextMeaningfulToken($index + 2);
395419
if (!$this->isNewline($tokens[$nextMeaningfulTokenIndex - 1])) {
420+
if ($tokens[$nextMeaningfulTokenIndex - 1]->isWhitespace()) {
421+
$tokens->clearAt($nextMeaningfulTokenIndex - 1);
422+
}
423+
396424
$tokens->ensureWhitespaceAtIndex($nextMeaningfulTokenIndex, 0, $this->whitespacesConfig->getLineEnding().$indentation);
397425
}
398426

@@ -479,10 +507,4 @@ private function isNewline(Token $token): bool
479507
{
480508
return $token->isWhitespace() && str_contains($token->getContent(), "\n");
481509
}
482-
483-
484-
public function getName(): string
485-
{
486-
return 'Nette/' . parent::getName();
487-
}
488510
}

0 commit comments

Comments
 (0)