Skip to content

Commit

Permalink
Refactor ArrayIndentationFixer
Browse files Browse the repository at this point in the history
  • Loading branch information
julienfalque committed Apr 12, 2020
1 parent 13492d1 commit e56230b
Showing 1 changed file with 82 additions and 231 deletions.
313 changes: 82 additions & 231 deletions src/Fixer/Whitespace/ArrayIndentationFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,259 +57,139 @@ public function getPriority()

protected function applyFix(\SplFileInfo $file, Tokens $tokens)
{
foreach ($this->findArrays($tokens) as $array) {
$indentLevel = 1;
$scopes = [[
'opening_braces' => $array['start_braces']['opening'],
'unindented' => false,
]];
$currentScope = 0;

$arrayIndent = $this->getLineIndentation($tokens, $array['start']);
$previousLineInitialIndent = $arrayIndent;
$previousLineNewIndent = $arrayIndent;

foreach ($array['braces'] as $index => $braces) {
$currentIndentLevel = $indentLevel;
if (
$braces['starts_with_closing']
&& !$scopes[$currentScope]['unindented']
&& !$this->isClosingLineWithMeaningfulContent($tokens, $index)
) {
--$currentIndentLevel;
}

$token = $tokens[$index];
if ($this->newlineIsInArrayScope($tokens, $index, $array)) {
$content = Preg::replace(
'/(\R+)\h*$/',
'$1'.$arrayIndent.str_repeat($this->whitespacesConfig->getIndent(), $currentIndentLevel),
$token->getContent()
);

$previousLineInitialIndent = $this->extractIndent($token->getContent());
$previousLineNewIndent = $this->extractIndent($content);
} else {
$content = Preg::replace(
'/(\R)'.preg_quote($previousLineInitialIndent, '/').'(\h*)$/',
'$1'.$previousLineNewIndent.'$2',
$token->getContent()
);
}
$scopes = [];
$previousLineInitialIndent = '';
$previousLineNewIndent = '';

$closingBraces = $braces['closing'];
while ($closingBraces-- > 0) {
if (!$scopes[$currentScope]['unindented']) {
--$indentLevel;
$scopes[$currentScope]['unindented'] = true;
}

if (0 === --$scopes[$currentScope]['opening_braces']) {
array_pop($scopes);
--$currentScope;
}
}
foreach ($tokens as $index => $token) {
$currentScope = [] !== $scopes ? \count($scopes) - 1 : null;

if ($braces['opening'] > 0) {
$scopes[] = [
'opening_braces' => $braces['opening'],
'unindented' => false,
];
++$indentLevel;
++$currentScope;
}

$tokens[$index] = new Token([T_WHITESPACE, $content]);
if ($token->isComment()) {
continue;
}
}
}

private function findArrays(Tokens $tokens)
{
$arrays = [];

foreach ($this->findArrayTokenRanges($tokens, 0, \count($tokens) - 1) as $arrayTokenRanges) {
$array = [
'start' => $arrayTokenRanges[0][0],
'end' => $arrayTokenRanges[\count($arrayTokenRanges) - 1][1],
'token_ranges' => $arrayTokenRanges,
];

$array['start_braces'] = $this->getLineSignificantBraces($tokens, $array['start'] - 1, $array);
$array['braces'] = $this->computeArrayLineSignificantBraces($tokens, $array);

$arrays[] = $array;
}

return $arrays;
}

private function findArrayTokenRanges(Tokens $tokens, $from, $to)
{
$arrayTokenRanges = [];
$currentArray = null;
$valueSinceIndex = null;

for ($index = $from; $index <= $to; ++$index) {
$token = $tokens[$index];

if ($token->isGivenKind([T_ARRAY, CT::T_ARRAY_SQUARE_BRACE_OPEN])) {
$arrayStartIndex = $index;

if ($token->isGivenKind(T_ARRAY)) {
$index = $tokens->getNextTokenOfKind($index, ['(']);
}

if (
$token->isGivenKind(CT::T_ARRAY_SQUARE_BRACE_OPEN)
|| ($token->equals('(') && $tokens[$tokens->getPrevMeaningfulToken($index)]->isGivenKind(T_ARRAY))
) {
$endIndex = $tokens->findBlockEnd(
$tokens[$index]->equals('(') ? Tokens::BLOCK_TYPE_PARENTHESIS_BRACE : Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE,
$token->equals('(') ? Tokens::BLOCK_TYPE_PARENTHESIS_BRACE : Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE,
$index
);

if (null === $currentArray) {
$currentArray = [
'start' => $index,
'end' => $endIndex,
'ignored_tokens_ranges' => [],
];
} else {
if (null === $valueSinceIndex) {
$valueSinceIndex = $arrayStartIndex;
}

$index = $endIndex;
}
$scopes[] = [
'type' => 'array',
'end_index' => $endIndex,
'initial_indent' => $this->getLineIndentation($tokens, $index),
];

continue;
}

if (null === $currentArray || $token->isWhitespace() || $token->isComment()) {
if (null === $currentScope) {
continue;
}

if ($currentArray['end'] === $index) {
if (null !== $valueSinceIndex) {
$currentArray['ignored_tokens_ranges'][] = [$valueSinceIndex, $tokens->getPrevMeaningfulToken($index)];
$valueSinceIndex = null;
if ($token->isWhitespace()) {
if (!Preg::match('/\R/', $token->getContent())) {
continue;
}

$rangeIndexes = [$currentArray['start']];
foreach ($currentArray['ignored_tokens_ranges'] as list($start, $end)) {
$rangeIndexes[] = $start - 1;
$rangeIndexes[] = $end + 1;
}
$rangeIndexes[] = $currentArray['end'];
if ('array' === $scopes[$currentScope]['type']) {
$indent = false;

$arrayTokenRanges[] = array_chunk($rangeIndexes, 2);
for ($searchEndIndex = $index + 1; $searchEndIndex < $scopes[$currentScope]['end_index']; ++$searchEndIndex) {
$searchEndToken = $tokens[$searchEndIndex];

foreach ($currentArray['ignored_tokens_ranges'] as list($start, $end)) {
foreach ($this->findArrayTokenRanges($tokens, $start, $end) as $nestedArray) {
$arrayTokenRanges[] = $nestedArray;
if (
(!$searchEndToken->isWhitespace() && !$searchEndToken->isComment())
|| ($searchEndToken->isWhitespace() && Preg::match('/\R/', $searchEndToken->getContent()))
) {
$indent = true;

break;
}
}

$content = Preg::replace(
'/(\R+)\h*$/',
'$1'.$scopes[$currentScope]['initial_indent'].($indent ? $this->whitespacesConfig->getIndent() : ''),
$token->getContent()
);

$previousLineInitialIndent = $this->extractIndent($token->getContent());
$previousLineNewIndent = $this->extractIndent($content);
} else {
$content = Preg::replace(
'/(\R)'.preg_quote($scopes[$currentScope]['initial_indent'], '/').'(\h*)$/',
'$1'.$scopes[$currentScope]['new_indent'].'$2',
$token->getContent()
);
}

$currentArray = null;
$tokens[$index] = new Token([T_WHITESPACE, $content]);

continue;
}

if (null === $valueSinceIndex) {
$valueSinceIndex = $index;
}
if ($index === $scopes[$currentScope]['end_index']) {
while ([] !== $scopes && $index === $scopes[$currentScope]['end_index']) {
array_pop($scopes);
--$currentScope;
}

if (
($token->equals('(') && !$tokens[$tokens->getPrevMeaningfulToken($index)]->isGivenKind(T_ARRAY))
|| $token->equals('{')
) {
$index = $tokens->findBlockEnd(
$token->equals('{') ? Tokens::BLOCK_TYPE_CURLY_BRACE : Tokens::BLOCK_TYPE_PARENTHESIS_BRACE,
$index
);
continue;
}

if ($token->equals(',')) {
$currentArray['ignored_tokens_ranges'][] = [$valueSinceIndex, $tokens->getPrevMeaningfulToken($index)];
$valueSinceIndex = null;
continue;
}
}

return $arrayTokenRanges;
}
if ('expression' !== $scopes[$currentScope]['type']) {
$endIndex = $this->findExpressionEndIndex($tokens, $index, $scopes[$currentScope]['end_index']);

private function computeArrayLineSignificantBraces(Tokens $tokens, array $array)
{
$braces = [];
if ($endIndex === $index) {
continue;
}

for ($index = $array['start']; $index <= $array['end']; ++$index) {
if (!$this->isNewLineToken($tokens, $index)) {
continue;
$scopes[] = [
'type' => 'expression',
'end_index' => $endIndex,
'initial_indent' => $previousLineInitialIndent,
'new_indent' => $previousLineNewIndent,
];
}

$braces[$index] = $this->getLineSignificantBraces($tokens, $index, $array);
}

return $braces;
}

private function getLineSignificantBraces(Tokens $tokens, $index, array $array)
private function findExpressionEndIndex(Tokens $tokens, $index, $parentScopeEndIndex)
{
$deltas = [];
$endIndex = null;

for (++$index; $index <= $array['end']; ++$index) {
if ($this->isNewLineToken($tokens, $index)) {
break;
}
for ($searchEndIndex = $index + 1; $searchEndIndex < $parentScopeEndIndex; ++$searchEndIndex) {
$searchEndToken = $tokens[$searchEndIndex];

if (!$this->indexIsInArrayTokenRanges($index, $array)) {
continue;
}
if ($searchEndToken->equalsAny(['(', '{'])) {
$searchEndIndex = $tokens->findBlockEnd(
$searchEndToken->equals('{') ? Tokens::BLOCK_TYPE_CURLY_BRACE : Tokens::BLOCK_TYPE_PARENTHESIS_BRACE,
$searchEndIndex
);

$token = $tokens[$index];
if ($token->equals('(') && !$tokens[$tokens->getPrevMeaningfulToken($index)]->isGivenKind(T_ARRAY)) {
continue;
}

if ($token->equals(')')) {
$openBraceIndex = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
if (!$tokens[$tokens->getPrevMeaningfulToken($openBraceIndex)]->isGivenKind(T_ARRAY)) {
continue;
}
}

if ($token->equalsAny(['(', [CT::T_ARRAY_SQUARE_BRACE_OPEN]])) {
$deltas[] = 1;
if ($searchEndToken->equals(',')) {
$endIndex = $tokens->getPrevMeaningfulToken($searchEndIndex);

continue;
}

if ($token->equalsAny([')', [CT::T_ARRAY_SQUARE_BRACE_CLOSE]])) {
$deltas[] = -1;
break;
}
}

$braces = [
'opening' => 0,
'closing' => 0,
'starts_with_closing' => -1 === reset($deltas),
];

foreach ($deltas as $delta) {
if (1 === $delta) {
++$braces['opening'];
} elseif ($braces['opening'] > 0) {
--$braces['opening'];
} else {
++$braces['closing'];
}
if (null !== $endIndex) {
return $endIndex;
}

return $braces;
}

private function isClosingLineWithMeaningfulContent(Tokens $tokens, $newLineIndex)
{
$nextMeaningfulIndex = $tokens->getNextMeaningfulToken($newLineIndex);

return !$tokens[$nextMeaningfulIndex]->equalsAny([')', [CT::T_ARRAY_SQUARE_BRACE_CLOSE]]);
return $tokens->getPrevMeaningfulToken($parentScopeEndIndex);
}

private function getLineIndentation(Tokens $tokens, $index)
Expand Down Expand Up @@ -349,35 +229,6 @@ private function getPreviousNewlineTokenIndex(Tokens $tokens, $index)
return null;
}

private function newlineIsInArrayScope(Tokens $tokens, $index, array $array)
{
if ($tokens[$tokens->getPrevMeaningfulToken($index)]->equalsAny(['.', '?', ':'])) {
return false;
}

$nextToken = $tokens[$tokens->getNextMeaningfulToken($index)];
if ($nextToken->isGivenKind(T_OBJECT_OPERATOR) || $nextToken->equalsAny(['.', '?', ':'])) {
return false;
}

return $this->indexIsInArrayTokenRanges($index, $array);
}

private function indexIsInArrayTokenRanges($index, array $array)
{
foreach ($array['token_ranges'] as list($start, $end)) {
if ($index < $start) {
return false;
}

if ($index <= $end) {
return true;
}
}

return false;
}

private function isNewLineToken(Tokens $tokens, $index)
{
if (!$tokens[$index]->equalsAny([[T_WHITESPACE], [T_INLINE_HTML]])) {
Expand Down

0 comments on commit e56230b

Please sign in to comment.