| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- <?php
- declare(strict_types=1);
- /*
- * This file is part of PHP CS Fixer.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- * Dariusz Rumiński <dariusz.ruminski@gmail.com>
- *
- * This source file is subject to the MIT license that is bundled
- * with this source code in the file LICENSE.
- */
- namespace PhpCsFixer\Fixer\Phpdoc;
- use PhpCsFixer\AbstractFixer;
- use PhpCsFixer\DocBlock\DocBlock;
- use PhpCsFixer\DocBlock\Line;
- use PhpCsFixer\DocBlock\ShortDescription;
- use PhpCsFixer\FixerDefinition\CodeSample;
- use PhpCsFixer\FixerDefinition\FixerDefinition;
- use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
- use PhpCsFixer\Tokenizer\Token;
- use PhpCsFixer\Tokenizer\Tokens;
- /**
- * @author Nobu Funaki <nobu.funaki@gmail.com>
- * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
- */
- final class PhpdocTrimConsecutiveBlankLineSeparationFixer extends AbstractFixer
- {
- public function getDefinition(): FixerDefinitionInterface
- {
- return new FixerDefinition(
- 'Removes extra blank lines after summary and after description in PHPDoc.',
- [
- new CodeSample(
- '<?php
- /**
- * Summary.
- *
- *
- * Description that contain 4 lines,
- *
- *
- * while 2 of them are blank!
- *
- *
- * @param string $foo
- *
- *
- * @dataProvider provideFixCases
- */
- function fnc($foo) {}
- '
- ),
- ]
- );
- }
- /**
- * {@inheritdoc}
- *
- * Must run before PhpdocAlignFixer.
- * Must run after AlignMultilineCommentFixer, CommentToPhpdocFixer, PhpUnitAttributesFixer, PhpdocIndentFixer, PhpdocScalarFixer, PhpdocToCommentFixer, PhpdocTypesFixer.
- */
- public function getPriority(): int
- {
- return -41;
- }
- public function isCandidate(Tokens $tokens): bool
- {
- return $tokens->isTokenKindFound(T_DOC_COMMENT);
- }
- protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
- {
- foreach ($tokens as $index => $token) {
- if (!$token->isGivenKind(T_DOC_COMMENT)) {
- continue;
- }
- $doc = new DocBlock($token->getContent());
- $summaryEnd = (new ShortDescription($doc))->getEnd();
- if (null !== $summaryEnd) {
- $this->fixSummary($doc, $summaryEnd);
- $this->fixDescription($doc, $summaryEnd);
- }
- $this->fixAllTheRest($doc);
- $tokens[$index] = new Token([T_DOC_COMMENT, $doc->getContent()]);
- }
- }
- private function fixSummary(DocBlock $doc, int $summaryEnd): void
- {
- $nonBlankLineAfterSummary = $this->findNonBlankLine($doc, $summaryEnd);
- $this->removeExtraBlankLinesBetween($doc, $summaryEnd, $nonBlankLineAfterSummary);
- }
- private function fixDescription(DocBlock $doc, int $summaryEnd): void
- {
- $annotationStart = $this->findFirstAnnotationOrEnd($doc);
- // assuming the end of the Description appears before the first Annotation
- $descriptionEnd = $this->reverseFindLastUsefulContent($doc, $annotationStart);
- if (null === $descriptionEnd || $summaryEnd === $descriptionEnd) {
- return; // no Description
- }
- if ($annotationStart === \count($doc->getLines()) - 1) {
- return; // no content after Description
- }
- $this->removeExtraBlankLinesBetween($doc, $descriptionEnd, $annotationStart);
- }
- private function fixAllTheRest(DocBlock $doc): void
- {
- $annotationStart = $this->findFirstAnnotationOrEnd($doc);
- $lastLine = $this->reverseFindLastUsefulContent($doc, \count($doc->getLines()) - 1);
- if (null !== $lastLine && $annotationStart !== $lastLine) {
- $this->removeExtraBlankLinesBetween($doc, $annotationStart, $lastLine);
- }
- }
- private function removeExtraBlankLinesBetween(DocBlock $doc, int $from, int $to): void
- {
- for ($index = $from + 1; $index < $to; ++$index) {
- $line = $doc->getLine($index);
- $next = $doc->getLine($index + 1);
- $this->removeExtraBlankLine($line, $next);
- }
- }
- private function removeExtraBlankLine(Line $current, Line $next): void
- {
- if (!$current->isTheEnd() && !$current->containsUsefulContent()
- && !$next->isTheEnd() && !$next->containsUsefulContent()) {
- $current->remove();
- }
- }
- private function findNonBlankLine(DocBlock $doc, int $after): ?int
- {
- foreach ($doc->getLines() as $index => $line) {
- if ($index <= $after) {
- continue;
- }
- if ($line->containsATag() || $line->containsUsefulContent() || $line->isTheEnd()) {
- return $index;
- }
- }
- return null;
- }
- private function findFirstAnnotationOrEnd(DocBlock $doc): int
- {
- foreach ($doc->getLines() as $index => $line) {
- if ($line->containsATag()) {
- return $index;
- }
- }
- if (!isset($index)) {
- throw new \LogicException('PHPDoc has empty lines collection.');
- }
- return $index; // no Annotation, return the last line
- }
- private function reverseFindLastUsefulContent(DocBlock $doc, int $from): ?int
- {
- for ($index = $from - 1; $index >= 0; --$index) {
- if ($doc->getLine($index)->containsUsefulContent()) {
- return $index;
- }
- }
- return null;
- }
- }
|