* * This source file is subject to the MIT license that is bundled * with this source code in the file LICENSE. */ namespace Symfony\CS\Fixer\Contrib; use Symfony\CS\AbstractAlignFixer; use Symfony\CS\Tokenizer\Tokens; /** * @author Carlos Cirello * @author Dariusz RumiƄski * @author Graham Campbell */ class AlignDoubleArrowFixer extends AbstractAlignFixer { /** * Level counter of the current nest level. * So one level alignments are not mixed with * other level ones. * * @var int */ private $currentLevel; /** * Keep track of the deepest level ever achieved while * parsing the code. Used later to replace alignment * placeholders with spaces. * * @var int */ private $deepestLevel; /** * {@inheritdoc} */ public function fix(\SplFileInfo $file, $content) { $this->currentLevel = 0; $this->deepestLevel = 0; $tokens = Tokens::fromCode($content); $this->injectAlignmentPlaceholders($tokens, 0, count($tokens)); return $this->replacePlaceholder($tokens, $this->deepestLevel); } /** * {@inheritdoc} */ public function getDescription() { return 'Align double arrow symbols in consecutive lines.'; } /** * {@inheritdoc} */ public function getPriority() { // should be run after the OperatorsSpacesFixer return -10; } /** * Inject into the text placeholders of candidates of vertical alignment. * * @param Tokens $tokens * @param int $startAt * @param int $endAt * * @return array($code, $context_counter) */ private function injectAlignmentPlaceholders(Tokens $tokens, $startAt, $endAt) { for ($index = $startAt; $index < $endAt; ++$index) { $token = $tokens[$index]; if ($token->isGivenKind(array(T_FOREACH, T_FOR, T_WHILE, T_IF, T_SWITCH))) { $index = $tokens->getNextMeaningfulToken($index); $index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index); continue; } if ($token->isGivenKind(T_ARRAY)) { // don't use "$tokens->isArray()" here, short arrays are handled in the next case $from = $tokens->getNextMeaningfulToken($index); $until = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $from); $index = $until; $this->injectArrayAlignmentPlaceholders($tokens, $from, $until); continue; } if ($tokens->isShortArray($index)) { $prevToken = $tokens[$tokens->getPrevMeaningfulToken($index)]; if ($prevToken->isGivenKind(array(T_STRING, T_VARIABLE))) { continue; } $from = $index; $until = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_SQUARE_BRACE, $from); $index = $until; $this->injectArrayAlignmentPlaceholders($tokens, $from + 1, $until - 1); continue; } if ($token->isGivenKind(T_DOUBLE_ARROW)) { $tokenContent = sprintf(self::ALIGNABLE_PLACEHOLDER, $this->currentLevel).$token->getContent(); $nextToken = $tokens[$index + 1]; if (!$nextToken->isWhitespace()) { $tokenContent .= ' '; } elseif ($nextToken->isWhitespace(array('whitespaces' => " \t"))) { $nextToken->setContent(' '); } $token->setContent($tokenContent); continue; } if ($token->equals(';')) { ++$this->deepestLevel; ++$this->currentLevel; continue; } if ($token->equals(',')) { for ($i = $index; $i < $endAt - 1; ++$i) { if (false !== strpos($tokens[$i - 1]->getContent(), "\n")) { break; } if ($tokens->isArray($i + 1)) { $arrayStartIndex = $tokens[$i + 1]->isGivenKind(T_ARRAY) ? $tokens->getNextMeaningfulToken($i + 1) : $i + 1 ; $blockType = Tokens::detectBlockType($tokens[$arrayStartIndex]); $arrayEndIndex = $tokens->findBlockEnd($blockType['type'], $arrayStartIndex); if ($tokens->isPartialCodeMultiline($arrayStartIndex, $arrayEndIndex)) { break; } } ++$index; } } } } /** * @param Tokens $tokens * @param int $from * @param int $until */ private function injectArrayAlignmentPlaceholders(Tokens $tokens, $from, $until) { // Only inject placeholders for multi-line arrays if ($tokens->isPartialCodeMultiline($from, $until)) { ++$this->deepestLevel; ++$this->currentLevel; $this->injectAlignmentPlaceholders($tokens, $from, $until); --$this->currentLevel; } } }