@@ -113,7 +113,7 @@ public function isCandidate(Tokens $tokens)
113113 /**
114114 * {@inheritdoc}
115115 *
116- * Must run after NoBlankLinesAfterPhpdocFixer.
116+ * Must run after DeclareStrictTypesFixer, NoBlankLinesAfterPhpdocFixer.
117117 */
118118 public function getPriority ()
119119 {
@@ -128,30 +128,46 @@ public function getPriority()
128128 */
129129 protected function applyFix (\SplFileInfo $ file , Tokens $ tokens )
130130 {
131- // figure out where the comment should be placed
132- $ headerNewIndex = $ this ->findHeaderCommentInsertionIndex ($ tokens );
131+ $ location = $ this ->configuration ['location ' ];
133132
134- // check if there is already a comment
135- $ headerCurrentIndex = $ this ->findHeaderCommentCurrentIndex ($ tokens , $ headerNewIndex - 1 );
133+ $ locationIndexes = [];
134+ foreach (['after_open ' , 'after_declare_strict ' ] as $ possibleLocation ) {
135+ $ locationIndex = $ this ->findHeaderCommentInsertionIndex ($ tokens , $ possibleLocation );
136136
137- if (null === $ headerCurrentIndex ) {
138- if ('' === $ this ->configuration ['header ' ]) {
139- return ; // header not found and none should be set, return
140- }
137+ if (!isset ($ locationIndexes [$ locationIndex ]) || $ possibleLocation === $ location ) {
138+ $ locationIndexes [$ locationIndex ] = $ possibleLocation ;
141139
142- $ this ->insertHeader ($ tokens , $ headerNewIndex );
143- } elseif ($ this ->getHeaderAsComment () !== $ tokens [$ headerCurrentIndex ]->getContent ()) {
144- $ tokens ->clearTokenAndMergeSurroundingWhitespace ($ headerCurrentIndex );
145- if ('' === $ this ->configuration ['header ' ]) {
146- return ; // header found and cleared, none should be set, return
140+ continue ;
147141 }
148-
149- $ this ->insertHeader ($ tokens , $ headerNewIndex );
150- } else {
151- $ headerNewIndex = $ headerCurrentIndex ;
152142 }
153143
154- $ this ->fixWhiteSpaceAroundHeader ($ tokens , $ headerNewIndex );
144+ foreach (array_values ($ locationIndexes ) as $ possibleLocation ) {
145+ // figure out where the comment should be placed
146+ $ headerNewIndex = $ this ->findHeaderCommentInsertionIndex ($ tokens , $ possibleLocation );
147+
148+ // check if there is already a comment
149+ $ headerCurrentIndex = $ this ->findHeaderCommentCurrentIndex ($ tokens , $ headerNewIndex - 1 );
150+
151+ if (null === $ headerCurrentIndex ) {
152+ if ('' === $ this ->configuration ['header ' ] || $ possibleLocation !== $ location ) {
153+ continue ;
154+ }
155+
156+ $ this ->insertHeader ($ tokens , $ headerNewIndex );
157+ } elseif ($ this ->getHeaderAsComment () !== $ tokens [$ headerCurrentIndex ]->getContent () || $ possibleLocation !== $ location ) {
158+ $ this ->removeHeader ($ tokens , $ headerCurrentIndex );
159+
160+ if ('' === $ this ->configuration ['header ' ]) {
161+ continue ;
162+ }
163+
164+ if ($ possibleLocation === $ location ) {
165+ $ this ->insertHeader ($ tokens , $ headerNewIndex );
166+ }
167+ } else {
168+ $ this ->fixWhiteSpaceAroundHeader ($ tokens , $ headerCurrentIndex );
169+ }
170+ }
155171 }
156172
157173 /**
@@ -228,11 +244,13 @@ private function findHeaderCommentCurrentIndex(Tokens $tokens, $headerNewIndex)
228244 /**
229245 * Find the index where the header comment must be inserted.
230246 *
247+ * @param string $location
248+ *
231249 * @return int
232250 */
233- private function findHeaderCommentInsertionIndex (Tokens $ tokens )
251+ private function findHeaderCommentInsertionIndex (Tokens $ tokens, $ location )
234252 {
235- if ('after_open ' === $ this -> configuration [ ' location ' ] ) {
253+ if ('after_open ' === $ location ) {
236254 return 1 ;
237255 }
238256
@@ -287,24 +305,31 @@ private function fixWhiteSpaceAroundHeader(Tokens $tokens, $headerIndex)
287305 $ lineEnding = $ this ->whitespacesConfig ->getLineEnding ();
288306
289307 // fix lines after header comment
290- $ expectedLineCount = 'both ' === $ this ->configuration ['separate ' ] || 'bottom ' === $ this ->configuration ['separate ' ] ? 2 : 1 ;
308+ if (
309+ ('both ' === $ this ->configuration ['separate ' ] || 'bottom ' === $ this ->configuration ['separate ' ])
310+ && null !== $ tokens ->getNextMeaningfulToken ($ headerIndex )
311+ ) {
312+ $ expectedLineCount = 2 ;
313+ } else {
314+ $ expectedLineCount = 1 ;
315+ }
291316 if ($ headerIndex === \count ($ tokens ) - 1 ) {
292317 $ tokens ->insertAt ($ headerIndex + 1 , new Token ([T_WHITESPACE , str_repeat ($ lineEnding , $ expectedLineCount )]));
293318 } else {
294- $ afterCommentIndex = $ tokens ->getNextNonWhitespace ($ headerIndex );
295- $ lineBreakCount = $ this ->getLineBreakCount ($ tokens , $ headerIndex + 1 , null === $ afterCommentIndex ? \count ($ tokens ) : $ afterCommentIndex );
319+ $ lineBreakCount = $ this ->getLineBreakCount ($ tokens , $ headerIndex , 1 );
296320 if ($ lineBreakCount < $ expectedLineCount ) {
297321 $ missing = str_repeat ($ lineEnding , $ expectedLineCount - $ lineBreakCount );
298322 if ($ tokens [$ headerIndex + 1 ]->isWhitespace ()) {
299323 $ tokens [$ headerIndex + 1 ] = new Token ([T_WHITESPACE , $ missing .$ tokens [$ headerIndex + 1 ]->getContent ()]);
300324 } else {
301325 $ tokens ->insertAt ($ headerIndex + 1 , new Token ([T_WHITESPACE , $ missing ]));
302326 }
303- } elseif ($ lineBreakCount > 2 ) {
304- // remove extra line endings
305- if ($ tokens [$ headerIndex + 1 ]->isWhitespace ()) {
306- $ tokens [$ headerIndex + 1 ] = new Token ([T_WHITESPACE , $ lineEnding .$ lineEnding ]);
307- }
327+ } elseif ($ lineBreakCount > $ expectedLineCount && $ tokens [$ headerIndex + 1 ]->isWhitespace ()) {
328+ $ newLinesToRemove = $ lineBreakCount - $ expectedLineCount ;
329+ $ tokens [$ headerIndex + 1 ] = new Token ([
330+ T_WHITESPACE ,
331+ Preg::replace ("/^ \\R{ {$ newLinesToRemove }}/ " , '' , $ tokens [$ headerIndex + 1 ]->getContent ()),
332+ ]);
308333 }
309334 }
310335
@@ -317,27 +342,80 @@ private function fixWhiteSpaceAroundHeader(Tokens $tokens, $headerIndex)
317342 $ tokens [$ prev ] = new Token ([T_OPEN_TAG , Preg::replace ($ regex , $ lineEnding , $ tokens [$ prev ]->getContent ())]);
318343 }
319344
320- $ lineBreakCount = $ this ->getLineBreakCount ($ tokens , $ prev , $ headerIndex );
345+ $ lineBreakCount = $ this ->getLineBreakCount ($ tokens , $ headerIndex , - 1 );
321346 if ($ lineBreakCount < $ expectedLineCount ) {
322347 // because of the way the insert index was determined for header comment there cannot be an empty token here
323348 $ tokens ->insertAt ($ headerIndex , new Token ([T_WHITESPACE , str_repeat ($ lineEnding , $ expectedLineCount - $ lineBreakCount )]));
324349 }
325350 }
326351
327352 /**
328- * @param int $indexStart
329- * @param int $indexEnd
353+ * @param int $index
354+ * @param int $direction
330355 *
331356 * @return int
332357 */
333- private function getLineBreakCount (Tokens $ tokens , $ indexStart , $ indexEnd )
358+ private function getLineBreakCount (Tokens $ tokens , $ index , $ direction )
359+ {
360+ $ whitespace = '' ;
361+
362+ for ($ index += $ direction ; isset ($ tokens [$ index ]); $ index += $ direction ) {
363+ $ token = $ tokens [$ index ];
364+
365+ if ($ token ->isWhitespace ()) {
366+ $ whitespace .= $ token ->getContent ();
367+
368+ continue ;
369+ }
370+
371+ if (-1 === $ direction && $ token ->isGivenKind (T_OPEN_TAG )) {
372+ $ whitespace .= $ token ->getContent ();
373+ }
374+
375+ if ('' !== $ token ->getContent ()) {
376+ break ;
377+ }
378+ }
379+
380+ return substr_count ($ whitespace , "\n" );
381+ }
382+
383+ private function removeHeader (Tokens $ tokens , $ index )
334384 {
335- $ lineCount = 0 ;
336- for ($ i = $ indexStart ; $ i < $ indexEnd ; ++$ i ) {
337- $ lineCount += substr_count ($ tokens [$ i ]->getContent (), "\n" );
385+ $ prevIndex = $ index - 1 ;
386+ $ prevToken = $ tokens [$ prevIndex ];
387+ $ newlineRemoved = false ;
388+
389+ if ($ prevToken ->isWhitespace ()) {
390+ $ content = $ prevToken ->getContent ();
391+
392+ if (Preg::match ('/\R/ ' , $ content )) {
393+ $ newlineRemoved = true ;
394+ }
395+
396+ $ content = Preg::replace ('/\R?\h*$/ ' , '' , $ content );
397+
398+ if ('' !== $ content ) {
399+ $ tokens [$ prevIndex ] = new Token ([T_WHITESPACE , $ content ]);
400+ } else {
401+ $ tokens ->clearAt ($ prevIndex );
402+ }
338403 }
339404
340- return $ lineCount ;
405+ $ nextIndex = $ index + 1 ;
406+ $ nextToken = isset ($ tokens [$ nextIndex ]) ? $ tokens [$ nextIndex ] : null ;
407+
408+ if (!$ newlineRemoved && null !== $ nextToken && $ nextToken ->isWhitespace ()) {
409+ $ content = Preg::replace ('/^\R/ ' , '' , $ nextToken ->getContent ());
410+
411+ if ('' !== $ content ) {
412+ $ tokens [$ nextIndex ] = new Token ([T_WHITESPACE , $ content ]);
413+ } else {
414+ $ tokens ->clearAt ($ nextIndex );
415+ }
416+ }
417+
418+ $ tokens ->clearTokenAndMergeSurroundingWhitespace ($ index );
341419 }
342420
343421 /**
@@ -346,5 +424,7 @@ private function getLineBreakCount(Tokens $tokens, $indexStart, $indexEnd)
346424 private function insertHeader (Tokens $ tokens , $ index )
347425 {
348426 $ tokens ->insertAt ($ index , new Token ([self ::HEADER_COMMENT === $ this ->configuration ['comment_type ' ] ? T_COMMENT : T_DOC_COMMENT , $ this ->getHeaderAsComment ()]));
427+
428+ $ this ->fixWhiteSpaceAroundHeader ($ tokens , $ index );
349429 }
350430}
0 commit comments