Skip to content

Commit

Permalink
Updates
Browse files Browse the repository at this point in the history
  • Loading branch information
kubawerlos committed Oct 11, 2018
1 parent 2658c28 commit 90f2ade
Show file tree
Hide file tree
Showing 3 changed files with 212 additions and 79 deletions.
4 changes: 2 additions & 2 deletions README.rst
Expand Up @@ -1188,8 +1188,8 @@ Choose from the list of available rules:

* **operator_linebreak**

Binary operators must always be at the beginning or at the end of the
line.
Operators - when multiline - must always be at the beginning or at the
end of the line.

Configuration options:

Expand Down
197 changes: 123 additions & 74 deletions src/Fixer/Operator/OperatorLinebreakFixer.php
Expand Up @@ -31,75 +31,73 @@ final class OperatorLinebreakFixer extends AbstractFixer implements Configuratio
* @internal
*/
const BOOLEAN_OPERATORS = [
'&&',
'||',
'and',
'or',
'xor',
'&&' => true,
'||' => true,
'and' => true,
'or' => true,
'xor' => true,
];

/**
* @internal
*/
const NON_BOOLEAN_OPERATORS = [
'=',
'*',
'/',
'%',
'<',
'>',
'|',
'^',
'+',
'-',
'&',
'&=',
'.=',
'/=',
'=>',
'==',
'>=',
'===',
'!=',
'<>',
'!==',
'<=',
'-=',
'%=',
'*=',
'|=',
'+=',
'<<',
'<<=',
'>>',
'>>=',
'^=',
'**',
'**=',
'<=>',
'??',
'=' => true,
'*' => true,
'/' => true,
'%' => true,
'<' => true,
'>' => true,
'|' => true,
'^' => true,
'+' => true,
'-' => true,
'&' => true,
'&=' => true,
'.=' => true,
'/=' => true,
'=>' => true,
'==' => true,
'>=' => true,
'===' => true,
'!=' => true,
'<>' => true,
'!==' => true,
'<=' => true,
'-=' => true,
'%=' => true,
'*=' => true,
'|=' => true,
'+=' => true,
'<<' => true,
'<<=' => true,
'>>' => true,
'>>=' => true,
'^=' => true,
'**' => true,
'**=' => true,
'<=>' => true,
'??' => true,
'?' => true,
':' => true,
];

/** @var string[] */
/** @var null|string[] */
private $operators;

/** @var bool */
private $onlyBooleans = false;

/** @var string */
private $position = 'beginning';

public function __construct()
{
$this->operators = \array_merge(self::BOOLEAN_OPERATORS, self::NON_BOOLEAN_OPERATORS);

parent::__construct();
}

/**
* @return FixerDefinition
*/
public function getDefinition()
{
return new FixerDefinition(
'Binary operators must always be at the beginning or at the end of the line.',
'Operators - when multiline - must always be at the beginning or at the end of the line.',
[new CodeSample('<?php
function foo() {
return $bar ||
Expand All @@ -116,7 +114,7 @@ public function getConfigurationDefinition()
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('only_booleans', 'whether to limit operators to only boolean ones'))
->setDefault(false)
->setDefault($this->onlyBooleans)
->setAllowedTypes(['bool'])
->getOption(),
(new FixerOptionBuilder('position', 'whether to place operators at the beginning or at the end of the line'))
Expand All @@ -133,10 +131,12 @@ public function configure(array $configuration = null)
{
parent::configure($configuration);

if (isset($configuration['only_booleans']) && $configuration['only_booleans']) {
$this->operators = self::BOOLEAN_OPERATORS;
if (isset($configuration['only_booleans'])) {
$this->onlyBooleans = $configuration['only_booleans'];
}
if (isset($configuration['position'])) {
$this->position = $configuration['position'];
}
$this->position = isset($configuration['position']) ? $configuration['position'] : $this->position;
}

/**
Expand Down Expand Up @@ -178,21 +178,16 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens)
private function fixMoveToTheBeginning(Tokens $tokens)
{
for ($index = 0; $index < $tokens->count(); ++$index) {
$tokenContent = \strtolower($tokens[$index]->getContent());

if (!\in_array($tokenContent, $this->operators, true)) {
$indices = $this->getOperatorIndices($tokens, $index);
if (null === $indices) {
continue;
}

$nextIndex = $tokens->getNextMeaningfulToken($index);
$nextIndex = $tokens->getNextMeaningfulToken(max($indices));
for ($i = $nextIndex - 1; $i > $index; --$i) {
if ($tokens[$i]->isWhitespace() && 1 === Preg::match('/\R/u', $tokens[$i]->getContent())) {
$operator = clone $tokens[$index];
$tokens->clearAt($index);
if ($tokens[$index - 1]->isWhitespace()) {
$tokens->clearTokenAndMergeSurroundingWhitespace($index - 1);
}
$tokens->insertAt($nextIndex, [$operator, new Token([T_WHITESPACE, ' '])]);
$operators = $this->getReplacementsAndClear($tokens, $indices, -1);
$tokens->insertAt($nextIndex, array_merge($operators, [new Token([T_WHITESPACE, ' '])]));

break;
}
Expand All @@ -204,26 +199,80 @@ private function fixMoveToTheBeginning(Tokens $tokens)
private function fixMoveToTheEnd(Tokens $tokens)
{
for ($index = $tokens->count() - 1; $index > 0; --$index) {
$tokenContent = \strtolower($tokens[$index]->getContent());

if (!\in_array($tokenContent, $this->operators, true)) {
$indices = $this->getOperatorIndices($tokens, $index);
if (null === $indices) {
continue;
}

$prevIndex = $tokens->getPrevMeaningfulToken($index);
$prevIndex = $tokens->getPrevMeaningfulToken(min($indices));
for ($i = $prevIndex + 1; $i < $index; ++$i) {
if ($tokens[$i]->isWhitespace() && 1 === Preg::match('/\R/u', $tokens[$i]->getContent())) {
$operator = clone $tokens[$index];
$tokens->clearAt($index);
if ($tokens[$index + 1]->isWhitespace()) {
$tokens->clearTokenAndMergeSurroundingWhitespace($index + 1);
}
$tokens->insertAt($prevIndex + 1, [new Token([T_WHITESPACE, ' ']), $operator]);
$operators = $this->getReplacementsAndClear($tokens, $indices, 1);
$tokens->insertAt($prevIndex + 1, array_merge([new Token([T_WHITESPACE, ' '])], $operators));

break;
}
}
$index = $prevIndex;
}
}

/**
* @param Tokens $tokens
* @param int $index
*
* @return null|int[]
*/
private function getOperatorIndices(Tokens $tokens, $index)
{
if (null === $this->operators) {
$this->operators = self::BOOLEAN_OPERATORS;
if (!$this->onlyBooleans) {
$this->operators += self::NON_BOOLEAN_OPERATORS;
}
}

if (!isset($this->operators[\strtolower($tokens[$index]->getContent())])) {
return null;
}

if (isset($this->operators['?']) && '?' === $tokens[$index]->getContent()) {
$nextIndex = $tokens->getNextMeaningfulToken($index);
if (':' === $tokens[$nextIndex]->getContent()) {
return [$index, $nextIndex];
}
}

if (isset($this->operators[':']) && ':' === $tokens[$index]->getContent()) {
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if ('?' === $tokens[$prevIndex]->getContent()) {
return [$prevIndex, $index];
}
}

return [$index];
}

/**
* @param Tokens $tokens
* @param int[] $indices
* @param int $direction
*
* @return Token[]
*/
private function getReplacementsAndClear(Tokens $tokens, array $indices, $direction)
{
return array_map(
function ($index) use ($tokens, $direction) {
$clone = $tokens[$index];
if ($tokens[$index + $direction]->isWhitespace()) {
$tokens->clearAt($index + $direction);
}
$tokens->clearAt($index);

return $clone;
},
$indices
);
}
}
90 changes: 87 additions & 3 deletions tests/Fixer/Operator/OperatorLinebreakFixerTest.php
Expand Up @@ -26,13 +26,16 @@ final class OperatorLinebreakFixerTest extends AbstractFixerTestCase
/**
* @param string $expected
* @param null|string $input
* @param array $configuration
* @param null|array $configuration
*
* @dataProvider provideFixCases
*/
public function testFixControlContinuationBraces($expected, $input = null, array $configuration = [])
public function testFix($expected, $input = null, array $configuration = null)
{
$this->fixer->configure($configuration);
if (null !== $configuration) {
$this->fixer->configure($configuration);
}

$this->doTest($expected, $input);
}

Expand Down Expand Up @@ -71,6 +74,19 @@ public function provideFixCases()
yield [
'<?php
return $foo
? $bar
: $baz;
',
'<?php
return $foo ?
$bar :
$baz;
',
];

yield [
'<?php
return $foo
|| $bar
|| $baz;
',
Expand Down Expand Up @@ -201,6 +217,74 @@ function foo() {
// Third comment
isThisJustFantasy();
}
',
];

yield [
'<?php
function foo() {
return $a
&& (
$b
|| $c
)
&& $d;
}
',
'<?php
function foo() {
return $a &&
(
$b ||
$c
) &&
$d;
}
',
];

yield [
'<?php
return $foo
?: $bar;
',
'<?php
return $foo ?:
$bar;
',
];

yield [
'<?php
return $foo ?:
$bar;
',
'<?php
return $foo
?: $bar;
',
['position' => 'end'],
];

yield [
'<?php
return $foo
?: $bar;
',
'<?php
return $foo ? :
$bar;
',
];

yield [
'<?php
return $foo/* Lorem ipsum */
?: $bar;
',
'<?php
return $foo ?/* Lorem ipsum */:
$bar;
',
];
}
Expand Down

0 comments on commit 90f2ade

Please sign in to comment.