12
12
* with this source code in the file LICENSE.
13
13
*/
14
14
15
+ // PhpCsFixer\Fixer\FunctionNotation
15
16
namespace NetteCodingStandard \Fixer \FunctionNotation ;
16
17
17
18
use PhpCsFixer \AbstractFixer ;
18
19
use PhpCsFixer \Fixer \ConfigurableFixerInterface ;
20
+ use PhpCsFixer \Fixer \ConfigurableFixerTrait ;
19
21
use PhpCsFixer \Fixer \WhitespacesAwareFixerInterface ;
20
22
use PhpCsFixer \FixerConfiguration \FixerConfigurationResolver ;
21
23
use PhpCsFixer \FixerConfiguration \FixerConfigurationResolverInterface ;
22
24
use PhpCsFixer \FixerConfiguration \FixerOptionBuilder ;
23
- use PhpCsFixer \FixerConfiguration \InvalidOptionsForEnvException ;
24
25
use PhpCsFixer \FixerDefinition \CodeSample ;
25
26
use PhpCsFixer \FixerDefinition \FixerDefinition ;
26
27
use PhpCsFixer \FixerDefinition \FixerDefinitionInterface ;
27
- use PhpCsFixer \FixerDefinition \VersionSpecification ;
28
- use PhpCsFixer \FixerDefinition \VersionSpecificCodeSample ;
29
28
use PhpCsFixer \Preg ;
30
29
use PhpCsFixer \Tokenizer \CT ;
31
30
use PhpCsFixer \Tokenizer \Token ;
32
31
use PhpCsFixer \Tokenizer \Tokens ;
33
- use Symfony \Component \OptionsResolver \Options ;
34
32
35
33
/**
36
- * Fixer for rules defined in PSR2 ¶4.4, ¶4.6.
37
- *
38
34
* @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
+ * }
39
50
*/
40
51
final class MethodArgumentSpaceFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
41
52
{
42
- /**
43
- * {@inheritdoc}
44
- */
53
+ /** @use ConfigurableFixerTrait<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration> */
54
+ use ConfigurableFixerTrait;
55
+
45
56
public function getDefinition (): FixerDefinitionInterface
46
57
{
47
58
return new FixerDefinition (
@@ -81,66 +92,66 @@ public function getDefinition(): FixerDefinitionInterface
81
92
'keep_multiple_spaces_after_comma ' => false ,
82
93
]
83
94
),
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 (
85
117
<<<'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
96
128
,
97
- new VersionSpecification (70300 ),
98
129
['after_heredoc ' => true ]
99
130
),
100
- ]
131
+ ],
132
+ 'This fixer covers rules defined in PSR2 ¶4.4, ¶4.6. '
101
133
);
102
134
}
103
135
104
- /**
105
- * {@inheritdoc}
106
- */
107
136
public function isCandidate (Tokens $ tokens ): bool
108
137
{
109
138
return $ tokens ->isTokenKindFound ('( ' );
110
139
}
111
140
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
-
123
141
/**
124
142
* {@inheritdoc}
125
143
*
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 .
128
146
*/
129
147
public function getPriority (): int
130
148
{
131
149
return 30 ;
132
150
}
133
151
134
- /**
135
- * {@inheritdoc}
136
- */
137
152
protected function applyFix (\SplFileInfo $ file , Tokens $ tokens ): void
138
153
{
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 ];
144
155
145
156
for ($ index = $ tokens ->count () - 1 ; $ index > 0 ; --$ index ) {
146
157
$ token = $ tokens [$ index ];
@@ -151,23 +162,25 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
151
162
152
163
$ meaningfulTokenBeforeParenthesis = $ tokens [$ tokens ->getPrevMeaningfulToken ($ index )];
153
164
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
+ }
156
171
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 );
164
180
}
165
181
}
166
182
}
167
183
168
- /**
169
- * {@inheritdoc}
170
- */
171
184
protected function createConfigurationDefinition (): FixerConfigurationResolverInterface
172
185
{
173
186
return new FixerConfigurationResolver ([
@@ -185,13 +198,13 @@ protected function createConfigurationDefinition(): FixerConfigurationResolverIn
185
198
(new FixerOptionBuilder ('after_heredoc ' , 'Whether the whitespace between heredoc end and comma should be removed. ' ))
186
199
->setAllowedTypes (['bool ' ])
187
200
->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 ' )
195
208
->getOption (),
196
209
]);
197
210
}
@@ -255,8 +268,6 @@ private function fixFunction(Tokens $tokens, int $startFunctionIndex): bool
255
268
$ this ->fixSpace ($ tokens , $ index );
256
269
if (!$ isMultiline && $ this ->isNewline ($ tokens [$ index + 1 ])) {
257
270
$ isMultiline = true ;
258
-
259
- break ;
260
271
}
261
272
}
262
273
}
@@ -298,11 +309,7 @@ private function ensureSingleLine(Tokens $tokens, int $index): bool
298
309
299
310
$ content = Preg::replace ('/\R\h*/ ' , '' , $ tokens [$ index ]->getContent ());
300
311
301
- if ('' !== $ content ) {
302
- $ tokens [$ index ] = new Token ([T_WHITESPACE , $ content ]);
303
- } else {
304
- $ tokens ->clearAt ($ index );
305
- }
312
+ $ tokens ->ensureWhitespaceAtIndex ($ index , 0 , $ content );
306
313
307
314
return true ;
308
315
}
@@ -314,7 +321,7 @@ private function ensureFunctionFullyMultiline(Tokens $tokens, int $startFunction
314
321
do {
315
322
$ prevWhitespaceTokenIndex = $ tokens ->getPrevTokenOfKind (
316
323
$ searchIndex ,
317
- [[T_WHITESPACE ]]
324
+ [[T_ENCAPSED_AND_WHITESPACE ], [ T_WHITESPACE ]],
318
325
);
319
326
320
327
$ searchIndex = $ prevWhitespaceTokenIndex ;
@@ -324,13 +331,14 @@ private function ensureFunctionFullyMultiline(Tokens $tokens, int $startFunction
324
331
325
332
if (null === $ prevWhitespaceTokenIndex ) {
326
333
$ existingIndentation = '' ;
334
+ } elseif (!$ tokens [$ prevWhitespaceTokenIndex ]->isGivenKind (T_WHITESPACE )) {
335
+ return ;
327
336
} else {
328
337
$ existingIndentation = $ tokens [$ prevWhitespaceTokenIndex ]->getContent ();
329
338
$ lastLineIndex = strrpos ($ existingIndentation , "\n" );
330
339
$ existingIndentation = false === $ lastLineIndex
331
340
? $ existingIndentation
332
- : substr ($ existingIndentation , $ lastLineIndex + 1 )
333
- ;
341
+ : substr ($ existingIndentation , $ lastLineIndex + 1 );
334
342
}
335
343
336
344
$ indentation = $ existingIndentation .$ this ->whitespacesConfig ->getIndent ();
@@ -369,7 +377,23 @@ private function ensureFunctionFullyMultiline(Tokens $tokens, int $startFunction
369
377
continue ;
370
378
}
371
379
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 (', ' )) {
373
397
$ this ->fixNewline ($ tokens , $ index , $ indentation );
374
398
}
375
399
}
@@ -378,7 +402,7 @@ private function ensureFunctionFullyMultiline(Tokens $tokens, int $startFunction
378
402
}
379
403
380
404
/**
381
- * Method to insert newline after comma or opening parenthesis.
405
+ * Method to insert newline after comma, attribute or opening parenthesis.
382
406
*
383
407
* @param int $index index of a comma
384
408
* @param string $indentation the indentation that should be used
@@ -393,6 +417,10 @@ private function fixNewline(Tokens $tokens, int $index, string $indentation, boo
393
417
if ($ tokens [$ index + 2 ]->isComment ()) {
394
418
$ nextMeaningfulTokenIndex = $ tokens ->getNextMeaningfulToken ($ index + 2 );
395
419
if (!$ this ->isNewline ($ tokens [$ nextMeaningfulTokenIndex - 1 ])) {
420
+ if ($ tokens [$ nextMeaningfulTokenIndex - 1 ]->isWhitespace ()) {
421
+ $ tokens ->clearAt ($ nextMeaningfulTokenIndex - 1 );
422
+ }
423
+
396
424
$ tokens ->ensureWhitespaceAtIndex ($ nextMeaningfulTokenIndex , 0 , $ this ->whitespacesConfig ->getLineEnding ().$ indentation );
397
425
}
398
426
@@ -479,10 +507,4 @@ private function isNewline(Token $token): bool
479
507
{
480
508
return $ token ->isWhitespace () && str_contains ($ token ->getContent (), "\n" );
481
509
}
482
-
483
-
484
- public function getName (): string
485
- {
486
- return 'Nette/ ' . parent ::getName ();
487
- }
488
510
}
0 commit comments